hivelytracker-0+git20180223/0000775001024000102400000000000013250507450012377 5ustar hivelytracker-0+git20180223/ChangeLog.txt0000775001024000102400000002766613042730153015010 0ustar 1.9 xx-xxx-xxxx =============== - Added clamping to mixchunk() in the example replayer code (but not in the tracker proper as clipping noise is a good indicator to the composer to sort out the gain setting ;-) - White noise is now much more accurate to the real AHX sound - Example replayers come with accurate Amiga PAL/NTSC frequencies - Filters now sound exactly like the real AHX ones - Fixed potential divide by zero - Added workaround for Alt Gr handling on Windows. The windows SDL library sends dummy RCTRL key presses when you hit Alt Gr, so now right alt works as it should (thanks to AceMan) 1.8 07-May-2013 =============== - Fixed some warnings - Fixed E1x and E2x fine slide commands (thanks to passing_by and kode54) - Fixed broken stripping of 4xx upper nibble (thanks to passing_by). Due to a copy/paste bug, the fix for this in 1.6 only affected the left instrument column. - Speed multiplier range is now 1-4 in the GUI. The HVL and AHX module formats only support this range anyway, so you couldn't save a module with a mult. of 5 or 6 (thanks to javamannen) - Fixed some keyboard documentation bugs (thanks to javamannen) - "Alt+Cursor Left" and "Alt+Cursor Right" now behave the same as AHX (suggested by javamannen) - Updated all copyright dates and updated all URLs to hivelytracker.co.uk - Removed all mentions of the sadly defunct IRIS homepage 1.7 27-Oct-2012 =============== All platforms: - Fixed some crash bugs when closing tabs under certain circumstances - Fixed some memory leaks - Added an SDL wrapper that allows HivelyTracker to build for other platforms (Windows, OSX..) OS4: - Replaced some deprecated system calls with newer OS4 APIs 1.6 22-Oct-2008 =============== - Right since 1.0, EDx was broken because of how i'd implimented the two effect columns. Fixed. All replayers and plugins will need updating :-/ Thanks to skope for pointing this out. - Fixed validity check for Cxx command, which broke in the ht replayer because the parameter is signed. (fixes the sidvibes ahx file, which used invalid Cxx commands). Thanks to megacz for pointing it out. - The replayer now strips the upper nibble of 4xx instrument commands from "THX\0" AHX modules. These modules are saved from older versions of AHX that didn't have filters, and so if the upper nibble is set it activated filtering which is wrong since the module was created in an editor that didn't have filters. The AHX editor also does this stripping, but AFAIK WinAHX and its derivatives never did. 1.5 18-Aug-2008 =============== - A parameter in the undo system was declared as a signed byte instead of a signed word, which meant that some song parameters got corrupted when they went over 127. Editing songs over 127 positions long was buggered, for example. Can't believe i didn't spot this until now :( - Added Protracker mod import. Just like the one in AHX, it simply strips out all PT commands that are not equivalent to AHX ones, and all instruments are set to a simple tone, so you'll still have a lot of work to do to make it sound nice. - Added a size optimisation feature. It removes all unused instruments, and duplicate tracks. If you click with the right mousebutton, it'll additionally try and use the transpose column to remove tracks which are the same but transposed. However it doesn't know if sounds continue after the newly transposed track so you may have to manually fix any problems with that afterwards. Both modes keep the original version and open a new tab with the optimise version so you won't lose your work if it messes up. Both modes also use track 0 as the empty track as AHX and HVL both make smaller files if that is the case, however, if there are no empty tracks in the module, it won't keep track 0 blank unnecessarily. - Added a new command! When "EF1" is placed on or after the last note in a track, the "transpose" value for that channel will be preserved until another note is played in the channel. This overrides the default AHX/HVL behaviour, which is to switch to the next transpose value as soon as the next position is reached. - '=' now does row preview, like enter does in protracker - '-' now toggles horizontal advance in the position editor - Added "posedadvance", "notejump" and "inotejump" fields to the ht.prefs file. They don't show up in the prefs window, though, so you have to edit them manually (run hively 1.5 and quit it to make them show up in the file). "posedadvance" is either 0 or 1. When set to 0, horizontal advance is disabled by default. "defnotejump" is 0 to 9, and sets the default notejump in the pattern editor. "definotejump" is the same for the instrument editor. 1.4 24-Aug-2007 =============== - Playback no longer stops when the "Load Mod" filerequester is open - Undo & Redo for almost all edit operations - Cutting an area in the position editor cleared both columns of the left & right edges even if they weren't selected. Fixed. - Cutting or pasting in the position editor didn't update the track editor even if the current tracks were changed by the operation. Fixed. - If you change preference options that require the GUI to be closed and re-opened, it now does that instead of requiring you to quit and restart. - Skin directory, initial song directory and initial instrument directory options all now can be selected via ASL directory requester. - Right-clicking on a channel mute button now mutes all other channels except that channel. If it is already the only un-muted channel, all channels will be unmuted instead (suggested by Syphus). - Added "9xx" panning command to the instrument performancelist. It only changes the panning position for that specific instrument. The next time an instrument is played in that channel, it returns to the last panning position set with the traditional "7xx" track command. - Pasting or marking blocks in the position editor didn't work correctly if channel 1 wasn't the leftmost channel. Fixed. - Pressing Ctrl+B while marking a block now cancels block marking instead of resetting the mark position. - Added a drumpad mode, selectable either by the funky new on/off gadget or via the backslash key. When enabled, selecting instruments via numeric keyboard causes them to be played, and if edit mode is set, the instrument is inserted into the song. Suggested by Syphus. - Changed the release cut gadget from a 0/1 number gadget to the funk- tastic new on/off gadget (W00T!!111) - Fixed a bug in the command line replayer that was really dumb and should have been spotted ages ago! Oops. - Before, if a skin directory existed, and the logo bitmap existed in that directory, but there was any other file missing, hively would bomb out instead of reverting to the default skin and retrying. Now if a skin fails to load for any reason, hively retries with the default skin (SIDMonster-Light). This allows hively to load even if ht.prefs contains an invalid skin (such as SIDMonster-Heavy, which has been discontinued from 1.4 onwards). - Fixed an incorrect pointer in the autogain routine 1.3 05-Mar-2007 (sorry it took so long :) ========================================= - Position editor now has indicators so its clear to which channel each column relates (suggested by syphus). - If you used ring modulation effects in the right hand parameter list of an instrument, but not the left, it would try and save it as an AHX instrument instead of an HVL instrument. Fixed. (thanks to syphus). - Added Zap Song/Instrument/Position options (suggested by m0d) - Added play time indicator (suggested by someone, sorry forgot who). - You can now copy and paste arbitary regions in the position editor. (suggested by a couple of people, and I always intended to do it anyway :) - "prefs_bg" and "Settings" in skins are now "prefs_os4" and "Settings_os4" respectively so that the same skin can be used for both the OS4 and OS3.x versions. - Added some bounds checking to the replayer to avoid weird crashes - Fixed a couple of small bugs in the replayer (thanks to pieknyman) - When changing the position during playback, the current position plays through to the end before jumping to your selected position (suggested by syphus) - Some status information which should have been local to each tab was global. Oops. Fixed now. - Added Ctrl+Tab to jump to the same column in the next channel, and Shift+Ctrl+Tab to jump to the same column in the previous channel. (suggested by syphus) - Added Alt+A to mute all but the current channel (suggested by syphus) - Added Alt+Q to mute all channels - Right clicking on "New Tab" now makes an identical clone of the current tab. - Added speed multiplier gadget. This is a feature of both AHX and HVL tunes that was always implimented, but there was no way to actually set it in hivelytracker until now (oops :) - Pressing the right mousebutton in the instruments list of the instruments editor will copy the current instrument to the slot where you clicked. - Holding control and pressing the right mousebutton on a slot in the instrument list in the instrument editor will zap that instrument. - Changed the default gain values when loading in AHX modules and using stereo mixing. The values before were calculated by running lots of tunes through the Autogain calculations and using the lowest values that came out. Well, it was too high, so now I actually calculated them using actual maths! (thanks again to pieknyman for finding a tune that clipped). - Added some more options to the skin system to enable the cool new "Vintage" theme. 1.2 01-Jan-2007 (another bug fix release) ========================================= - Pressing ESC now swaps between the instrument editor and tracker panel (as in AHX, suggested by syphus) - An "are you sure?" requester now comes up when you try and quit, and if there are modified songs loaded, it warns you of that fact, too. (I always meant to do this but forgot. Thanks to syphus for the suggestion). - Updating instrument number, or FX parameters with notejump set to 0 didn't show the change until you scrolled the track editor. Fixed. (thanks to syphus). - Renamed the "palette" file in the skins to "Settings" and included the ability to set fonts in there (thanks to spot and buzz for the suggestion) - "Ctrl+K" and "Ctrl+Shift+K" keys actually worked without Ctrl pressed. Fixed (thanks to syphus) - Added "BlankZeros" option (suggested by Spot) - Fixed 4xx command which was missing from both HivelyTracker *and* the WinAHX sources that ahxplay.library was based on. The code was in there all these years, but the actual bit of code that decodes the 4xx command was missing! This fixes loads of old AHX tunes that played wrong in windows players. (thanks to syphus for pointing this out) - Fixed a bug that called RectFill() with a negative Y value. This caused no problem under OS4, or under OS3.x on RTG, but under AGA caused a bad crash. Fixed because even though it didn't cause a problem under OS4, it was still a bug. - Added "skinext" option to the skin settings file, so that skins can be in a format other than PNG. 1.1 19-Dec-2006 (bug fix release) ================================= - Ensured all images are actually PNG. Any skin loading issues *should* now be fixed. - Some places in the track editor were using the baseline offset of the wrong font (oops!). It didn't notice on OS4 since the two fonts had the same baseline. Fixed. - Notejump is now taken into account when editing instrument and effect columns as well as the note column. (suggested by syphus) - You can set notejump in the instrument editor, which is independant from the track editor notejump. (suggested by syphus). - Fixed a buffer overrun that somehow didn't cause a DSI under OS4, but caused havoc on BuZz's port. - Fixed an illegal memory access when clicking on buttons. Again, thanks to BuZz ;-) - Added www.hivelytracker.com to the screentitles 1.0 === - First release hivelytracker-0+git20180223/system_includes.h0000664001024000102400000000163313042730153015762 0ustar #ifndef __SYSTEM_INCLUDES_H__ #define __SYSTEM_INCLUDES_H__ #ifndef __SDL_WRAPPER__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Just for the requester... #define ALL_REACTION_CLASSES #define ALL_REACTION_MACROS #include #else #include #include #include #include #endif /* __SDL_WRAPPER */ #endif /* __SYSTEM_INCLUDES_H__ */ hivelytracker-0+git20180223/replay.c0000775001024000102400000036214013042730153014045 0ustar #include #include #include #include #include #include #include "util.h" #include "replay.h" #include "tables.h" #include "gui.h" #include "undo.h" extern BOOL quitting; extern int32 defnotejump, definotejump; APTR rp_mempool = NULL; #ifndef __SDL_WRAPPER__ #define FREQ 48000 extern uint32 gui_tick_sig; struct Task *rp_maintask = NULL; struct Process *rp_subtask = NULL; int32 rp_subtask_sig = -1; struct MsgPort *rp_msgport = NULL; uint32 rp_sigs = 0; uint32 rp_freq = FREQ; float64 rp_freqf = FREQ; uint32 rp_audiobuflen; int8 *rp_audiobuffer[2] = { NULL, NULL }; struct MsgPort *rp_replyport = NULL; #endif struct rp_command *rp_mainmsg = NULL; struct List *rp_tunelist = NULL; struct ahx_tune *rp_curtune = NULL; struct SignalSemaphore *rp_list_ss = NULL; enum { STS_IDLE = 0, STS_PLAYNOTE, STS_PLAYPOS, STS_PLAYSONG, STS_RECORDING, STS_CALCULATING, STS_DEADED, STS_PLAYROW }; uint32 rp_state = STS_IDLE; #ifdef __SDL_WRAPPER__ volatile uint32 rp_state_ack = STS_IDLE; #endif extern int32 pref_defstereo; #ifndef __SDL_WRAPPER__ // Libraries and interfaces struct Library *AHIBase = NULL; struct AHIIFace *IAHI = NULL; // AHI stuff struct MsgPort *ahi_mp; struct AHIRequest *ahi_io[2] = { NULL, NULL }; int32 ahi_dev = -1; #else #define FREQ 48000 #ifdef __linux__ #define OUTPUT_LEN ((FREQ/50)*2) /* Linux can't cope with buffer being too small, so we sacrifice GUI responsiveness... */ #else #define OUTPUT_LEN (FREQ/50) #endif static struct ahx_tune *rp_playtune = NULL; int16 rp_audiobuffer[OUTPUT_LEN*2]; uint32 rp_audiobuflen = OUTPUT_LEN*2; #endif /* ** Waves */ int8 waves[WAVES_SIZE]; uint32 panning_left[256], panning_right[256]; /* static inline void clr_l( uint32 *src, uint32 longs) { do { *src++ = 0; longs--; } while (longs > 0); } */ void rp_GenPanningTables( void ) { uint32 i; float64 aa, ab; // Sine based panning table aa = (3.14159265f*2.0f)/4.0f; // Quarter of the way through the sinewave == top peak ab = 0.0f; // Start of the climb from zero for( i=0; i<256; i++ ) { panning_left[i] = (uint32)(sin(aa)*255.0f); panning_right[i] = (uint32)(sin(ab)*255.0f); aa += (3.14159265*2.0f/4.0f)/256.0f; ab += (3.14159265*2.0f/4.0f)/256.0f; } panning_left[255] = 0; panning_right[0] = 0; /* // Original panning table (linear panning == poo) for( i=0; i<256; i++ ) { // Compensate for -128 to 127 range (ideally we want -128 to 128) if( i < 64 ) panning_left[i] = 256-i; else panning_left[i] = 255-i; if( i > 191 ) panning_right[i] = i+1; else panning_right[i] = i; if( panning_left[i] == 128 ) panning_right[i] = 128; if( panning_right[i] == 128 ) panning_left[i] = 128; } */ } void rp_GenSawtooth( int8 *buf, uint32 len ) { uint32 i; int32 val, add; add = 256 / (len-1); val = -128; for( i=0; i> 2; d1 = 128/d5; d4 = -(d2 >> 1); val = 0; for( i=0; i> 16); if (top > 127) in = 127 << 16; else if (top < -128) in = -(128 << 16); return in; } void rp_GenFilterWaves( const int8 *buf, int8 *lowbuf, int8 *highbuf ) { const int16 * mid_table = &filter_thing[0]; const int16 * low_table = &filter_thing[1395]; int32 freq; int32 i; for( i=0, freq = 25; i<31; i++, freq += 9 ) { uint32 wv; const int8 *a0 = buf; for( wv=0; wv<6+6+0x20+1; wv++ ) { int32 in, fre, high, mid, low; uint32 j; mid = *mid_table++ << 8; low = *low_table++ << 8; for( j=0; j<=lentab[wv]; j++ ) { in = a0[j] << 16; high = clipshifted8( in - mid - low ); fre = (high >> 8) * freq; mid = clipshifted8(mid + fre); fre = (mid >> 8) * freq; low = clipshifted8(low + fre); *highbuf++ = high >> 16; *lowbuf++ = low >> 16; } a0 += lentab[wv]+1; } } } void rp_GenWhiteNoise( int8 *buf, uint32 len ) { uint32 ays; ays = 0x41595321; do { uint16 ax, bx; int8 s; s = ays; if( ays & 0x100 ) { s = 0x7f; if( ays & 0x8000 ) s = 0x80; } *buf++ = s; len--; ays = (ays >> 5) | (ays << 27); ays = (ays & 0xffffff00) | ((ays & 0xff) ^ 0x9a); bx = ays; ays = (ays << 2) | (ays >> 30); ax = ays; bx += ax; ax ^= bx; ays = (ays & 0xffff0000) | ax; ays = (ays >> 3) | (ays << 29); } while( len ); } void rp_clear_instrument( struct ahx_instrument *ins ) { int32 j; ins->ins_Name[0] = 0; ins->ins_Volume = 64; ins->ins_WaveLength = 3; ins->ins_FilterLowerLimit = 1; ins->ins_FilterUpperLimit = 1; ins->ins_FilterSpeed = 0; ins->ins_SquareLowerLimit = 1; ins->ins_SquareUpperLimit = 1; ins->ins_SquareSpeed = 0; ins->ins_VibratoDelay = 0; ins->ins_VibratoSpeed = 0; ins->ins_VibratoDepth = 0; ins->ins_HardCutRelease = 0; ins->ins_HardCutReleaseFrames = 0; ins->ins_Envelope.aFrames = 1; ins->ins_Envelope.aVolume = 0; ins->ins_Envelope.dFrames = 1; ins->ins_Envelope.dVolume = 0; ins->ins_Envelope.sFrames = 1; ins->ins_Envelope.rFrames = 1; ins->ins_Envelope.rVolume = 0; ins->ins_PList.pls_Speed = 0; ins->ins_PList.pls_Length = 0; for( j=0; j<256; j++ ) { ins->ins_PList.pls_Entries[j].ple_Note = 0; ins->ins_PList.pls_Entries[j].ple_Waveform = 0; ins->ins_PList.pls_Entries[j].ple_Fixed = 0; ins->ins_PList.pls_Entries[j].ple_FX[0] = 0; ins->ins_PList.pls_Entries[j].ple_FXParam[0] = 0; ins->ins_PList.pls_Entries[j].ple_FX[1] = 0; ins->ins_PList.pls_Entries[j].ple_FXParam[1] = 0; } ins->ins_ptop = 0; ins->ins_pcurx = 0; ins->ins_pcury = 0; } void rp_clear_tune( struct ahx_tune *at ) { int32 i, j; int32 defgain[] = { 71, 72, 76, 85, 100 }; if( at == NULL ) return; if( at == rp_curtune ) rp_stop(); free_undolists( at ); at->at_Name[0] = 0; at->at_Time = 0; at->at_ExactTime = 0; at->at_LoopDetector = 0; at->at_SongNum = 0; at->at_Frequency = FREQ; at->at_FreqF = (float64)FREQ; at->at_WaveformTab[0] = &waves[WO_TRIANGLE_04]; at->at_WaveformTab[1] = &waves[WO_SAWTOOTH_04]; at->at_WaveformTab[3] = &waves[WO_WHITENOISE]; at->at_Restart = 0; at->at_PositionNr = 1; at->at_SpeedMultiplier = 1; at->at_TrackLength = 64; at->at_TrackNr = 0; at->at_InstrumentNr = 0; at->at_SubsongNr = 0; at->at_PosJump = 0; at->at_PlayingTime = 0; at->at_Tempo = 6; at->at_PosNr = 0; at->at_NextPosNr = -1; at->at_StepWaitFrames = 0; at->at_NoteNr = 0; at->at_PosJumpNote = 0; at->at_GetNewPosition = 0; at->at_PatternBreak = 0; at->at_SongEndReached = 0; at->at_posed_curs = 0; at->at_tracked_curs = 0; at->at_curins = 1; at->at_drumpadnote = 25; at->at_drumpadmode = FALSE; at->at_topins = 1; at->at_topinsb = 1; at->at_curlch = 0; at->at_Channels = 4; at->at_baseins = 0; at->at_curpanel = 0; at->at_modified = FALSE; at->at_defstereo = pref_defstereo; at->at_defpanleft = stereopan_left[pref_defstereo]; at->at_defpanright = stereopan_right[pref_defstereo]; at->at_mixgain = (defgain[pref_defstereo]*256)/100; at->at_mixgainP = defgain[pref_defstereo]; at->at_cbmarktrack = -1; at->at_cbmarkstartnote = -1; at->at_cbmarkendnote = -1; at->at_cbmarkleftx = 0; at->at_cbmarkrightx = 0; at->at_cbpmarklft = -1; at->at_cbpmarkmarklft = -1; at->at_notejump = defnotejump; at->at_inotejump = definotejump; at->at_doing = D_IDLE; at->at_editing = E_TRACK; at->at_idoing = D_IDLE; at->at_rempos[0] = 0; at->at_rempos[1] = 16; at->at_rempos[2] = 32; at->at_rempos[3] = 48; at->at_rempos[4] = 63; for( i=0; iat_Voices[i].vc_VUMeter = 0; at->at_Voices[i].vc_SetTrackOn = 1; } for( i=0; iat_Voices[i+0].vc_Pan = at->at_defpanleft; at->at_Voices[i+0].vc_SetPan = at->at_defpanleft; at->at_Voices[i+0].vc_PanMultLeft = panning_left[at->at_defpanleft]; at->at_Voices[i+0].vc_PanMultRight = panning_right[at->at_defpanleft]; at->at_Voices[i+1].vc_Pan = at->at_defpanright; at->at_Voices[i+1].vc_SetPan = at->at_defpanright; at->at_Voices[i+1].vc_PanMultLeft = panning_left[at->at_defpanright]; at->at_Voices[i+1].vc_PanMultRight = panning_right[at->at_defpanright]; at->at_Voices[i+2].vc_Pan = at->at_defpanright; at->at_Voices[i+2].vc_SetPan = at->at_defpanright; at->at_Voices[i+2].vc_PanMultLeft = panning_left[at->at_defpanright]; at->at_Voices[i+2].vc_PanMultRight = panning_right[at->at_defpanright]; at->at_Voices[i+3].vc_Pan = at->at_defpanleft; at->at_Voices[i+3].vc_SetPan = at->at_defpanleft; at->at_Voices[i+3].vc_PanMultLeft = panning_left[at->at_defpanleft]; at->at_Voices[i+3].vc_PanMultRight = panning_right[at->at_defpanleft]; } for( i=0; i<256; i++ ) { at->at_Subsongs[i] = 0; for( j=0; j<64; j++ ) { at->at_Tracks[i][j].stp_Note = 0; at->at_Tracks[i][j].stp_Instrument = 0; at->at_Tracks[i][j].stp_FX = 0; at->at_Tracks[i][j].stp_FXParam = 0; at->at_Tracks[i][j].stp_FXb = 0; at->at_Tracks[i][j].stp_FXbParam = 0; } } for( i=0; i<1000; i++ ) { for( j=0; jat_Positions[i].pos_Track[j] = 0; at->at_Positions[i].pos_Transpose[j] = 0; } } for( i=0; i<64; i++ ) rp_clear_instrument( &at->at_Instruments[i] ); at->at_ticks = at->at_secs = at->at_mins = at->at_hours = 0; } void rp_reset_some_shit( struct ahx_tune *at ) { uint32 i; at->at_ticks = at->at_secs = at->at_mins = at->at_hours = 0; for( i=0; iat_Voices[i].vc_Delta=1; at->at_Voices[i].vc_OverrideTranspose = 1000; // 1.5 at->at_Voices[i].vc_SamplePos=at->at_Voices[i].vc_Track=at->at_Voices[i].vc_Transpose=at->at_Voices[i].vc_NextTrack = at->at_Voices[i].vc_NextTranspose = 0; at->at_Voices[i].vc_ADSRVolume=at->at_Voices[i].vc_InstrPeriod=at->at_Voices[i].vc_TrackPeriod=at->at_Voices[i].vc_VibratoPeriod=at->at_Voices[i].vc_NoteMaxVolume=at->at_Voices[i].vc_PerfSubVolume=at->at_Voices[i].vc_TrackMasterVolume=0; at->at_Voices[i].vc_NewWaveform=at->at_Voices[i].vc_Waveform=at->at_Voices[i].vc_PlantSquare=at->at_Voices[i].vc_PlantPeriod=at->at_Voices[i].vc_IgnoreSquare=0; at->at_Voices[i].vc_TrackOn=at->at_Voices[i].vc_FixedNote=at->at_Voices[i].vc_VolumeSlideUp=at->at_Voices[i].vc_VolumeSlideDown=at->at_Voices[i].vc_HardCut=at->at_Voices[i].vc_HardCutRelease=at->at_Voices[i].vc_HardCutReleaseF=0; at->at_Voices[i].vc_PeriodSlideSpeed=at->at_Voices[i].vc_PeriodSlidePeriod=at->at_Voices[i].vc_PeriodSlideLimit=at->at_Voices[i].vc_PeriodSlideOn=at->at_Voices[i].vc_PeriodSlideWithLimit=0; at->at_Voices[i].vc_PeriodPerfSlideSpeed=at->at_Voices[i].vc_PeriodPerfSlidePeriod=at->at_Voices[i].vc_PeriodPerfSlideOn=at->at_Voices[i].vc_VibratoDelay=at->at_Voices[i].vc_VibratoCurrent=at->at_Voices[i].vc_VibratoDepth=at->at_Voices[i].vc_VibratoSpeed=0; at->at_Voices[i].vc_SquareOn=at->at_Voices[i].vc_SquareInit=at->at_Voices[i].vc_SquareLowerLimit=at->at_Voices[i].vc_SquareUpperLimit=at->at_Voices[i].vc_SquarePos=at->at_Voices[i].vc_SquareSign=at->at_Voices[i].vc_SquareSlidingIn=at->at_Voices[i].vc_SquareReverse=0; at->at_Voices[i].vc_FilterOn=at->at_Voices[i].vc_FilterInit=at->at_Voices[i].vc_FilterLowerLimit=at->at_Voices[i].vc_FilterUpperLimit=at->at_Voices[i].vc_FilterPos=at->at_Voices[i].vc_FilterSign=at->at_Voices[i].vc_FilterSpeed=at->at_Voices[i].vc_FilterSlidingIn=at->at_Voices[i].vc_IgnoreFilter=0; at->at_Voices[i].vc_PerfCurrent=at->at_Voices[i].vc_PerfSpeed=at->at_Voices[i].vc_WaveLength=at->at_Voices[i].vc_NoteDelayOn=at->at_Voices[i].vc_NoteCutOn=0; at->at_Voices[i].vc_AudioPeriod=at->at_Voices[i].vc_AudioVolume=at->at_Voices[i].vc_VoiceVolume=at->at_Voices[i].vc_VoicePeriod=at->at_Voices[i].vc_VoiceNum=at->at_Voices[i].vc_WNRandom=0; at->at_Voices[i].vc_SquareWait=at->at_Voices[i].vc_FilterWait=at->at_Voices[i].vc_PerfWait=at->at_Voices[i].vc_NoteDelayWait=at->at_Voices[i].vc_NoteCutWait=0; at->at_Voices[i].vc_PerfList=0; at->at_Voices[i].vc_RingSamplePos=at->at_Voices[i].vc_RingDelta=at->at_Voices[i].vc_RingPlantPeriod=at->at_Voices[i].vc_RingAudioPeriod=at->at_Voices[i].vc_RingNewWaveform=at->at_Voices[i].vc_RingWaveform=at->at_Voices[i].vc_RingFixedPeriod=at->at_Voices[i].vc_RingBasePeriod=0; at->at_Voices[i].vc_RingMixSource = NULL; at->at_Voices[i].vc_RingAudioSource = NULL; memset( &at->at_Voices[i].vc_SquareTempBuffer,0,0x80); memset( &at->at_Voices[i].vc_ADSR,0,sizeof(struct ahx_envelope)); memset( &at->at_Voices[i].vc_VoiceBuffer,0,0x281); memset( &at->at_Voices[i].vc_RingVoiceBuffer,0,0x281); } for( i=0; iat_Voices[i].vc_WNRandom = 0x280; at->at_Voices[i].vc_VoiceNum = i; at->at_Voices[i].vc_TrackMasterVolume = 0x40; at->at_Voices[i].vc_TrackOn = at->at_Voices[i].vc_SetTrackOn; at->at_Voices[i].vc_MixSource = at->at_Voices[i].vc_VoiceBuffer; } } BOOL rp_init_subsong( struct ahx_tune *at, uint32 nr ) { uint32 PosNr, i; if( nr > at->at_SubsongNr ) return FALSE; at->at_SongNum = nr; PosNr = 0; if( nr ) PosNr = at->at_Subsongs[nr-1]; at->at_PosNr = PosNr; at->at_PosJump = 0; at->at_PatternBreak = 0; at->at_NoteNr = 0; at->at_PosJumpNote = 0; at->at_Tempo = 6; at->at_StepWaitFrames = 0; at->at_GetNewPosition = 1; at->at_SongEndReached = 0; at->at_PlayingTime = 0; at->at_curss = nr; for( i=0; iat_Voices[i+0].vc_Pan = at->at_defpanleft; at->at_Voices[i+0].vc_SetPan = at->at_defpanleft; at->at_Voices[i+0].vc_PanMultLeft = panning_left[at->at_defpanleft]; at->at_Voices[i+0].vc_PanMultRight = panning_right[at->at_defpanleft]; at->at_Voices[i+1].vc_Pan = at->at_defpanright; at->at_Voices[i+1].vc_SetPan = at->at_defpanright; at->at_Voices[i+1].vc_PanMultLeft = panning_left[at->at_defpanright]; at->at_Voices[i+1].vc_PanMultRight = panning_right[at->at_defpanright]; at->at_Voices[i+2].vc_Pan = at->at_defpanright; at->at_Voices[i+2].vc_SetPan = at->at_defpanright; at->at_Voices[i+2].vc_PanMultLeft = panning_left[at->at_defpanright]; at->at_Voices[i+2].vc_PanMultRight = panning_right[at->at_defpanright]; at->at_Voices[i+3].vc_Pan = at->at_defpanleft; at->at_Voices[i+3].vc_SetPan = at->at_defpanleft; at->at_Voices[i+3].vc_PanMultLeft = panning_left[at->at_defpanleft]; at->at_Voices[i+3].vc_PanMultRight = panning_right[at->at_defpanleft]; } rp_reset_some_shit( at ); return TRUE; } struct ahx_tune *rp_new_tune( BOOL addtolist ) { struct ahx_tune *at; at = (struct ahx_tune *)allocnode(sizeof(struct ahx_tune)); if( !at ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return NULL; } at->at_undolist = IExec->AllocSysObjectTags( ASOT_LIST, TAG_DONE ); if( !at->at_undolist ) { IExec->FreeSysObject( ASOT_NODE, at ); printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return NULL; } at->at_redolist = IExec->AllocSysObjectTags( ASOT_LIST, TAG_DONE ); if( !at->at_redolist ) { IExec->FreeSysObject( ASOT_LIST, at->at_undolist ); IExec->FreeSysObject( ASOT_NODE, at ); printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return NULL; } at->at_undomem = 0; at->at_ln.ln_Succ = NULL; at->at_ln.ln_Pred = NULL; at->at_ln.ln_Name = &at->at_Name[0]; at->at_ln.ln_Pri = 0; at->at_ln.ln_Type = NT_USER; rp_clear_tune( at ); if( addtolist ) { IExec->ObtainSemaphore( rp_list_ss ); IExec->AddTail( rp_tunelist, (struct Node *)at ); IExec->ReleaseSemaphore( rp_list_ss ); } rp_init_subsong( at, 0 ); return at; } void rp_free_tune( struct ahx_tune *at ) { IExec->ObtainSemaphore( rp_list_ss ); IExec->Remove( (struct Node *)at ); free_undolists( at ); IExec->FreeSysObject( ASOT_LIST, at->at_undolist ); IExec->FreeSysObject( ASOT_LIST, at->at_redolist ); IExec->FreeSysObject( ASOT_NODE, at ); IExec->ReleaseSemaphore( rp_list_ss ); } void rp_free_all_tunes( void ) { struct ahx_tune *at, *nat; IExec->ObtainSemaphore( rp_list_ss ); at = (struct ahx_tune *)IExec->GetHead(rp_tunelist); while( at ) { nat = (struct ahx_tune *)IExec->GetSucc(&at->at_ln); rp_free_tune( at ); at = nat; } IExec->ReleaseSemaphore( rp_list_ss ); } void rp_save_hvl_ins( const TEXT *name, struct ahx_instrument *ins ) { FILE *fh; int8 *buf, *bptr; int32 buflen, i; buflen = 26+ins->ins_PList.pls_Length*5; buf = IExec->AllocPooled( rp_mempool, buflen ); if( !buf ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return; } for( i=0; iins_Volume; buf[5] = (ins->ins_FilterSpeed&0x1f)<<3; buf[16] = (ins->ins_FilterSpeed&0x20)<<2; buf[5] |= ins->ins_WaveLength&0x07; buf[6] = ins->ins_Envelope.aFrames; buf[7] = ins->ins_Envelope.aVolume; buf[8] = ins->ins_Envelope.dFrames; buf[9] = ins->ins_Envelope.dVolume; buf[10] = ins->ins_Envelope.sFrames; buf[11] = ins->ins_Envelope.rFrames; buf[12] = ins->ins_Envelope.rVolume; buf[16] |= ins->ins_FilterLowerLimit&0x7f; buf[17] = ins->ins_VibratoDelay; buf[18] = (ins->ins_HardCutReleaseFrames&0x07)<<4; buf[18] |= (ins->ins_HardCutRelease&0x1)<<7; buf[18] |= ins->ins_VibratoDepth&0x0f; buf[19] = ins->ins_VibratoSpeed; buf[20] = ins->ins_SquareLowerLimit; buf[21] = ins->ins_SquareUpperLimit; buf[22] = ins->ins_SquareSpeed; buf[23] = ins->ins_FilterUpperLimit&0x3f; buf[24] = ins->ins_PList.pls_Speed; buf[25] = ins->ins_PList.pls_Length; bptr = &buf[26]; for( i=0; iins_PList.pls_Length; i++ ) { bptr[0] = ins->ins_PList.pls_Entries[i].ple_FX[0]&0xf; bptr[1] = (ins->ins_PList.pls_Entries[i].ple_FX[1]&0xf)<<3; bptr[1] |= ins->ins_PList.pls_Entries[i].ple_Waveform&7; bptr[2] = (ins->ins_PList.pls_Entries[i].ple_Fixed&1)<<6; bptr[2] |= ins->ins_PList.pls_Entries[i].ple_Note&0x3f; bptr[3] = ins->ins_PList.pls_Entries[i].ple_FXParam[0]; bptr[4] = ins->ins_PList.pls_Entries[i].ple_FXParam[1]; bptr += 5; } fh = fopen( name, "wb" ); if( !fh ) { printf( "unable to open file\n" ); IExec->FreePooled( rp_mempool, buf, buflen ); return; } fwrite( buf, buflen, 1, fh ); fwrite( ins->ins_Name, strlen( ins->ins_Name )+1, 1, fh ); fclose( fh ); IExec->FreePooled( rp_mempool, buf, buflen ); } void rp_save_ins( const TEXT *name, struct ahx_tune *at, int32 in ) { FILE *fh; int8 *buf, *bptr; int32 buflen, i, k, l; struct ahx_instrument *ins; BOOL saveahxi; #ifdef __SDL_WRAPPER__ char mkname[4096]; #endif if( at == NULL ) return; #ifdef __SDL_WRAPPER__ // Enforce file extension since windows likes them so much // (although let them use a prefix if they want to keep it oldskool) if (strncasecmp(name, "ins.", 4) != 0) { // No prefix... i = strlen(name); if ((i < 4) || (strcasecmp(&name[i-4], ".ins") != 0)) { // No extension strncpy(mkname, name, 4096); strncat(mkname, ".ins", 4096); mkname[4095] = 0; name = mkname; } } #endif ins = &at->at_Instruments[in]; saveahxi = TRUE; // Have to save as HVL instrument? for( i=0; iins_PList.pls_Length; i++ ) { if( ( ins->ins_PList.pls_Entries[i].ple_FX[0] > 5 ) && ( ins->ins_PList.pls_Entries[i].ple_FX[0] != 12 ) && ( ins->ins_PList.pls_Entries[i].ple_FX[0] != 15 ) ) saveahxi = FALSE; if( ( ins->ins_PList.pls_Entries[i].ple_FX[1] > 5 ) && ( ins->ins_PList.pls_Entries[i].ple_FX[1] != 12 ) && ( ins->ins_PList.pls_Entries[i].ple_FX[1] != 15 ) ) saveahxi = FALSE; } if( saveahxi == FALSE ) { rp_save_hvl_ins( name, ins ); return; } buflen = 26+ins->ins_PList.pls_Length*4; buf = IExec->AllocPooled( rp_mempool, buflen ); if( !buf ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return; } for( i=0; iins_Volume; buf[5] = (ins->ins_FilterSpeed&0x1f)<<3; buf[16] = (ins->ins_FilterSpeed&0x20)<<2; buf[5] |= ins->ins_WaveLength&0x07; buf[6] = ins->ins_Envelope.aFrames; buf[7] = ins->ins_Envelope.aVolume; buf[8] = ins->ins_Envelope.dFrames; buf[9] = ins->ins_Envelope.dVolume; buf[10] = ins->ins_Envelope.sFrames; buf[11] = ins->ins_Envelope.rFrames; buf[12] = ins->ins_Envelope.rVolume; buf[16] |= ins->ins_FilterLowerLimit&0x7f; buf[17] = ins->ins_VibratoDelay; buf[18] = (ins->ins_HardCutReleaseFrames&0x07)<<4; buf[18] |= (ins->ins_HardCutRelease&0x1)<<7; buf[18] |= ins->ins_VibratoDepth&0x0f; buf[19] = ins->ins_VibratoSpeed; buf[20] = ins->ins_SquareLowerLimit; buf[21] = ins->ins_SquareUpperLimit; buf[22] = ins->ins_SquareSpeed; buf[23] = ins->ins_FilterUpperLimit&0x3f; buf[24] = ins->ins_PList.pls_Speed; buf[25] = ins->ins_PList.pls_Length; bptr = &buf[26]; for( i=0; iins_PList.pls_Length; i++ ) { // Convert to AHX format (this means that HVL instruments could have more FX! w00t!) k = ins->ins_PList.pls_Entries[i].ple_FX[1]; if( k == 12 ) k = 6; if( k == 15 ) k = 7; l = ins->ins_PList.pls_Entries[i].ple_FX[0]; if( l == 12 ) l = 6; if( l == 15 ) l = 7; bptr[0] = (k&7)<<5; bptr[0] |= (l&7)<<2; bptr[0] |= (ins->ins_PList.pls_Entries[i].ple_Waveform&6)>>1; bptr[1] = (ins->ins_PList.pls_Entries[i].ple_Waveform&1)<<7; bptr[1] |= (ins->ins_PList.pls_Entries[i].ple_Fixed&1)<<6; bptr[1] |= ins->ins_PList.pls_Entries[i].ple_Note&0x3f; bptr[2] = ins->ins_PList.pls_Entries[i].ple_FXParam[0]; bptr[3] = ins->ins_PList.pls_Entries[i].ple_FXParam[1]; bptr += 4; } fh = fopen( name, "wb" ); if( !fh ) { printf( "unable to open file\n" ); IExec->FreePooled( rp_mempool, buf, buflen ); return; } fwrite( buf, buflen, 1, fh ); fwrite( ins->ins_Name, strlen( ins->ins_Name )+1, 1, fh ); fclose( fh ); IExec->FreePooled( rp_mempool, buf, buflen ); } void rp_load_ins( const TEXT *name, struct ahx_tune *at, int32 in ) { FILE *fh; uint8 *buf; const uint8 *bptr; uint32 buflen, i, k, l; struct ahx_instrument *ni; BOOL ahxi; if( at == NULL ) return; fh = fopen(name, "rb"); if( !fh ) { printf( "Can't open file\n" ); return; } fseek(fh, 0, SEEK_END); buflen = ftell(fh); fseek(fh, 0, SEEK_SET); buf = IExec->AllocPooled( rp_mempool, buflen ); if( !buf ) { fclose(fh); printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return; } if( fread( buf, buflen, 1, fh ) != 1 ) { IExec->FreePooled( rp_mempool, buf, buflen ); fclose(fh); printf( "Unable to read from file!\n" ); return; } fclose(fh); ahxi = TRUE; if( ( buf[0] == 'H' ) && ( buf[1] == 'V' ) && ( buf[2] == 'L' ) && ( buf[3] == 'I' ) ) ahxi = FALSE; if( ( ahxi == TRUE ) && ( ( buf[0] != 'T' ) || ( buf[1] != 'H' ) || ( buf[2] != 'X' ) || ( buf[3] != 'I' ) ) ) { IExec->FreePooled( rp_mempool, buf, buflen ); printf( "Invalid file\n" ); return; } ni = &at->at_Instruments[in]; ni->ins_Volume = buf[4]; ni->ins_FilterSpeed = ((buf[5]>>3)&0x1f)|((buf[16]>>2)&0x20); ni->ins_WaveLength = buf[5]&0x07; ni->ins_Envelope.aFrames = buf[6]; ni->ins_Envelope.aVolume = buf[7]; ni->ins_Envelope.dFrames = buf[8]; ni->ins_Envelope.dVolume = buf[9]; ni->ins_Envelope.sFrames = buf[10]; ni->ins_Envelope.rFrames = buf[11]; ni->ins_Envelope.rVolume = buf[12]; ni->ins_FilterLowerLimit = buf[16]&0x7f; ni->ins_VibratoDelay = buf[17]; ni->ins_HardCutReleaseFrames = (buf[18]>>4)&0x07; ni->ins_HardCutRelease = buf[18]&0x80?1:0; ni->ins_VibratoDepth = buf[18]&0x0f; ni->ins_VibratoSpeed = buf[19]; ni->ins_SquareLowerLimit = buf[20]; ni->ins_SquareUpperLimit = buf[21]; ni->ins_SquareSpeed = buf[22]; ni->ins_FilterUpperLimit = buf[23]&0x3f; ni->ins_PList.pls_Speed = buf[24]; ni->ins_PList.pls_Length = buf[25]; bptr = &buf[26]; if( ahxi ) { for( i=0; iins_PList.pls_Length; i++ ) { k = (bptr[0]>>5)&7; if( k == 6 ) k = 12; if( k == 7 ) k = 15; l = (bptr[0]>>2)&7; if( l == 6 ) l = 12; if( l == 7 ) l = 15; ni->ins_PList.pls_Entries[i].ple_FX[1] = k; ni->ins_PList.pls_Entries[i].ple_FX[0] = l; ni->ins_PList.pls_Entries[i].ple_Waveform = ((bptr[0]<<1)&6) | (bptr[1]>>7); ni->ins_PList.pls_Entries[i].ple_Fixed = (bptr[1]>>6)&1; ni->ins_PList.pls_Entries[i].ple_Note = bptr[1]&0x3f; ni->ins_PList.pls_Entries[i].ple_FXParam[0] = bptr[2]; ni->ins_PList.pls_Entries[i].ple_FXParam[1] = bptr[3]; bptr += 4; } } else { for( i=0; iins_PList.pls_Length; i++ ) { ni->ins_PList.pls_Entries[i].ple_FX[0] = bptr[0]&0xf; ni->ins_PList.pls_Entries[i].ple_FX[1] = (bptr[1]>>3)&0xf; ni->ins_PList.pls_Entries[i].ple_Waveform = bptr[1]&7; ni->ins_PList.pls_Entries[i].ple_Fixed = (bptr[2]>>6)&1; ni->ins_PList.pls_Entries[i].ple_Note = bptr[2]&0x3f; ni->ins_PList.pls_Entries[i].ple_FXParam[0] = bptr[3]; ni->ins_PList.pls_Entries[i].ple_FXParam[1] = bptr[4]; bptr += 5; } } while( i < 256 ) { ni->ins_PList.pls_Entries[i].ple_FX[0] = 0; ni->ins_PList.pls_Entries[i].ple_FX[1] = 0; ni->ins_PList.pls_Entries[i].ple_FXParam[0] = 0; ni->ins_PList.pls_Entries[i].ple_FXParam[1] = 0; ni->ins_PList.pls_Entries[i].ple_Waveform = 0; ni->ins_PList.pls_Entries[i].ple_Fixed = 0; ni->ins_PList.pls_Entries[i].ple_Note = 0; i++; } ni->ins_ptop = 0; ni->ins_pcurx = 0; ni->ins_pcury = 0; strncpy( ni->ins_Name, (TEXT *)bptr, 128 ); // printf( "Loaded '%s'!\n", ni->ins_Name ); } uint32 rp_ahx_test( const struct ahx_tune *at ) { int32 i, j; int32 hvlfeats; const struct ahx_instrument *in; const struct ahx_step *sp; // First, check if the tune adheres to AHX limitations hvlfeats = 0; // >4 channels? if( at->at_Channels > 4 ) hvlfeats |= SWF_MANYCHANS; // Check instruments only use 1, 2, 3, 4, 5, C & F commands for( i=1; i<64; i++ ) { in = &at->at_Instruments[i]; for( j=0; jins_PList.pls_Length; j++ ) { switch( in->ins_PList.pls_Entries[j].ple_FX[0] ) { case 0x6: case 0x7: case 0x8: case 0x9: case 0xa: case 0xb: case 0xd: case 0xe: hvlfeats |= SWF_NEWINSCMD; break; } switch( in->ins_PList.pls_Entries[j].ple_FX[1] ) { case 0x6: case 0x7: case 0x8: case 0x9: case 0xa: case 0xb: case 0xd: case 0xe: hvlfeats |= SWF_NEWINSCMD; break; } if( hvlfeats & SWF_NEWINSCMD ) break; } } // Check for double or new commands for( i=0; i<256; i++ ) { for( j=0; jat_TrackLength; j++ ) { sp = &at->at_Tracks[i][j]; if( ( sp->stp_FX == 7 ) || ( sp->stp_FXb == 7 ) ) hvlfeats |= SWF_PANCMD; if( ((sp->stp_FX==0xe)&&((sp->stp_FXParam&0xf0)==0xf0)) || ((sp->stp_FXb==0xe)&&((sp->stp_FXParam&0xf0)==0xf0)) ) hvlfeats |= SWF_EFXCMD; if( (( sp->stp_FX != 0 ) || ( sp->stp_FXParam != 0 )) && (( sp->stp_FXb != 0 ) || ( sp->stp_FXbParam != 0 )) ) hvlfeats |= SWF_DOUBLECMD; } if( (hvlfeats & (SWF_PANCMD|SWF_EFXCMD|SWF_DOUBLECMD)) == (SWF_PANCMD|SWF_EFXCMD|SWF_DOUBLECMD) ) break; } return hvlfeats; } void rp_save_hvl( const TEXT *name, struct ahx_tune *at ) { FILE *fh; int32 i, j, k, tbl; int32 minver; uint8 emptytrk; uint8 *tbf; const struct ahx_instrument *in; #ifdef __SDL_WRAPPER__ char mkname[4096]; #endif if( at == NULL ) return; #ifdef __SDL_WRAPPER__ // Enforce file extension since windows likes them so much // (although let them use a prefix if they want to keep it oldskool) if (strncasecmp(name, "hvl.", 4) != 0) { // No prefix... i = strlen(name); if ((i < 4) || (strcasecmp(&name[i-4], ".hvl") != 0)) { // No extension strncpy(mkname, name, 4096); strncat(mkname, ".hvl", 4096); mkname[4095] = 0; name = mkname; } } #endif // Calculate TrackNr at->at_TrackNr = 0; for( i=0; iat_PositionNr; i++ ) for( j=0; jat_Channels; j++ ) if( at->at_Positions[i].pos_Track[j] > at->at_TrackNr ) at->at_TrackNr = at->at_Positions[i].pos_Track[j]; if( at->at_TrackNr == 0 ) at->at_TrackNr = 1; // Calculate InstrumentNr at->at_InstrumentNr = 0; for( i=1; i<64; i++ ) if( at->at_Instruments[i].ins_PList.pls_Length > 0 ) at->at_InstrumentNr = i; emptytrk = 0x80; for( i=0; iat_TrackLength; i++ ) if( ( at->at_Tracks[0][i].stp_Note != 0 ) || ( at->at_Tracks[0][i].stp_Instrument != 0 ) || ( at->at_Tracks[0][i].stp_FX != 0 ) || ( at->at_Tracks[0][i].stp_FXParam != 0 ) || ( at->at_Tracks[0][i].stp_FXb != 0 ) || ( at->at_Tracks[0][i].stp_FXbParam != 0 ) ) emptytrk = 0; // Calculate names offset tbl = 16; tbl += at->at_SubsongNr * 2; tbl += at->at_PositionNr * at->at_Channels * 2; minver = 0; for( i=0; i<=at->at_TrackNr; i++ ) { if( ( emptytrk != 0 ) && ( i == 0 ) ) continue; k = 0; for( j=0; jat_TrackLength; j++ ) { if( ( at->at_Tracks[i][j].stp_FX == 0xe ) && ( (at->at_Tracks[i][j].stp_FXParam&0xf0) == 0xf0 ) && ( minver < 1 ) ) minver = 1; if( ( at->at_Tracks[i][j].stp_FXb == 0xe ) && ( (at->at_Tracks[i][j].stp_FXbParam&0xf0) == 0xf0 ) && ( minver < 1 ) ) minver = 1; if( ( at->at_Tracks[i][j].stp_Note == 0 ) && ( at->at_Tracks[i][j].stp_Instrument == 0 ) && ( at->at_Tracks[i][j].stp_FX == 0 ) && ( at->at_Tracks[i][j].stp_FXParam == 0 ) && ( at->at_Tracks[i][j].stp_FXb == 0 ) && ( at->at_Tracks[i][j].stp_FXbParam == 0 ) ) k++; else k+=5; } tbl += k; } for( i=1; i<=at->at_InstrumentNr; i++ ) tbl += 22 + at->at_Instruments[i].ins_PList.pls_Length*5; tbf = IExec->AllocPooled( rp_mempool, tbl ); if( !tbf ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return; } fh = fopen( name, "wb" ); if( !fh ) { IExec->FreePooled( rp_mempool, tbf, tbl ); return; } strcpy( (TEXT *)tbf, "HVL" ); tbf[3] = minver; tbf[4] = (tbl >> 8)&0xff; tbf[5] = tbl&0xff; tbf[6] = (at->at_PositionNr>>8); tbf[6] |= ((at->at_SpeedMultiplier-1)&3)<<5; tbf[6] |= emptytrk; tbf[7] = at->at_PositionNr & 0xff; tbf[8] = (at->at_Channels-4)<<2; tbf[8] |= (at->at_Restart>>8)&0x3; tbf[9] = at->at_Restart&0xff; tbf[10] = at->at_TrackLength; tbf[11] = at->at_TrackNr; tbf[12] = at->at_InstrumentNr; tbf[13] = at->at_SubsongNr; tbf[14] = at->at_mixgainP; tbf[15] = at->at_defstereo; fwrite( tbf, 16, 1, fh ); // Subsongs if( at->at_SubsongNr > 0 ) { for( i=0, k=0; iat_SubsongNr; i++ ) { tbf[k++] = at->at_Subsongs[i]>>8; tbf[k++] = at->at_Subsongs[i]&0xff; } fwrite( tbf, k, 1, fh ); } // Position list for( i=0, k=0; iat_PositionNr; i++ ) { for( j=0; jat_Channels; j++ ) { tbf[k++] = at->at_Positions[i].pos_Track[j]; tbf[k++] = at->at_Positions[i].pos_Transpose[j]; } } fwrite( tbf, k, 1, fh ); // Tracks for( i=0, k=0; i<=at->at_TrackNr; i++ ) { if( ( emptytrk != 0 ) && ( i == 0 ) ) continue; for( j=0; jat_TrackLength; j++ ) { if( ( at->at_Tracks[i][j].stp_Note == 0 ) && ( at->at_Tracks[i][j].stp_Instrument == 0 ) && ( at->at_Tracks[i][j].stp_FX == 0 ) && ( at->at_Tracks[i][j].stp_FXParam == 0 ) && ( at->at_Tracks[i][j].stp_FXb == 0 ) && ( at->at_Tracks[i][j].stp_FXbParam == 0 ) ) { tbf[k++] = 0x3f; continue; } tbf[k++] = at->at_Tracks[i][j].stp_Note; tbf[k++] = at->at_Tracks[i][j].stp_Instrument; tbf[k] = (at->at_Tracks[i][j].stp_FX&0xf)<<4; tbf[k++] |= (at->at_Tracks[i][j].stp_FXb&0xf); tbf[k++] = at->at_Tracks[i][j].stp_FXParam; tbf[k++] = at->at_Tracks[i][j].stp_FXbParam; } } fwrite( tbf, k, 1, fh ); // Instruments for( i=1; i<=at->at_InstrumentNr; i++ ) { in = &at->at_Instruments[i]; k = 0; tbf[0] = in->ins_Volume; tbf[1] = (in->ins_FilterSpeed&0x1f)<<3; tbf[1] |= in->ins_WaveLength&0x7; tbf[2] = in->ins_Envelope.aFrames; tbf[3] = in->ins_Envelope.aVolume; tbf[4] = in->ins_Envelope.dFrames; tbf[5] = in->ins_Envelope.dVolume; tbf[6] = in->ins_Envelope.sFrames; tbf[7] = in->ins_Envelope.rFrames; tbf[8] = in->ins_Envelope.rVolume; tbf[9] = 0; tbf[10] = 0; tbf[11] = 0; tbf[12] = (in->ins_FilterSpeed&0x20)<<2; tbf[12] |= in->ins_FilterLowerLimit&0x7f; tbf[13] = in->ins_VibratoDelay; tbf[14] = (in->ins_HardCutReleaseFrames&0x07)<<4; tbf[14] |= (in->ins_HardCutRelease&1)<<7; tbf[14] |= in->ins_VibratoDepth&0x0f; tbf[15] = in->ins_VibratoSpeed; tbf[16] = in->ins_SquareLowerLimit; tbf[17] = in->ins_SquareUpperLimit; tbf[18] = in->ins_SquareSpeed; tbf[19] = in->ins_FilterUpperLimit&0x3f; tbf[20] = in->ins_PList.pls_Speed; tbf[21] = in->ins_PList.pls_Length; fwrite( tbf, 22, 1, fh ); for( j=0, k=0; jins_PList.pls_Length; j++ ) { tbf[k++] = in->ins_PList.pls_Entries[j].ple_FX[0]&0xf; tbf[k] = (in->ins_PList.pls_Entries[j].ple_FX[1]&0xf)<<3; tbf[k++] |= in->ins_PList.pls_Entries[j].ple_Waveform&7; tbf[k] = (in->ins_PList.pls_Entries[j].ple_Fixed&1)<<6; tbf[k++] |= in->ins_PList.pls_Entries[j].ple_Note&0x3f; tbf[k++] = in->ins_PList.pls_Entries[j].ple_FXParam[0]; tbf[k++] = in->ins_PList.pls_Entries[j].ple_FXParam[1]; } fwrite( tbf, k, 1, fh ); } fwrite( at->at_Name, strlen( at->at_Name )+1, 1, fh ); for( i=1; i<=at->at_InstrumentNr; i++ ) fwrite( at->at_Instruments[i].ins_Name, strlen( at->at_Instruments[i].ins_Name )+1, 1, fh ); fclose( fh ); IExec->FreePooled( rp_mempool, tbf, tbl ); } void rp_save_ahx( const TEXT *name, struct ahx_tune *at ) { FILE *fh; int32 i, j, k, l, m, tbl; uint8 emptytrk; uint8 *tbf; const struct ahx_instrument *in; #ifdef __SDL_WRAPPER__ char mkname[4096]; #endif if( at == NULL ) return; #ifdef __SDL_WRAPPER__ // Enforce file extension since windows likes them so much // (although let them use a prefix if they want to keep it oldskool) if ((strncasecmp(name, "ahx.", 4) != 0) && (strncasecmp(name, "thx.", 4) != 0)) { // No prefix... i = strlen(name); if ((i < 4) || ((strcasecmp(&name[i-4], ".ahx") != 0) && (strcasecmp(&name[i-4], ".thx") != 0))) { // No extension strncpy(mkname, name, 4096); strncat(mkname, ".ahx", 4096); mkname[4095] = 0; name = mkname; } } #endif // Calculate TrackNr at->at_TrackNr = 0; for( i=0; iat_PositionNr; i++ ) for( j=0; j<4; j++ ) if( at->at_Positions[i].pos_Track[j] > at->at_TrackNr ) at->at_TrackNr = at->at_Positions[i].pos_Track[j]; if( at->at_TrackNr == 0 ) at->at_TrackNr = 1; // Calculate InstrumentNr at->at_InstrumentNr = 0; for( i=1; i<64; i++ ) if( at->at_Instruments[i].ins_PList.pls_Length > 0 ) at->at_InstrumentNr = i; emptytrk = 0x80; for( i=0; iat_TrackLength; i++ ) if( ( at->at_Tracks[0][i].stp_Note != 0 ) || ( at->at_Tracks[0][i].stp_Instrument != 0 ) || ( at->at_Tracks[0][i].stp_FX != 0 ) || ( at->at_Tracks[0][i].stp_FXParam != 0 ) || ( at->at_Tracks[0][i].stp_FXb != 0 ) || ( at->at_Tracks[0][i].stp_FXbParam != 0 ) ) emptytrk = 0; // Calculate names offset tbl = 14; tbl += at->at_SubsongNr * 2; tbl += at->at_PositionNr * 8; tbl += (at->at_TrackNr+1) * at->at_TrackLength * 3; for( i=1; i<=at->at_InstrumentNr; i++ ) tbl += 22 + at->at_Instruments[i].ins_PList.pls_Length*4; if( emptytrk != 0 ) tbl -= at->at_TrackLength * 3; tbf = IExec->AllocPooled( rp_mempool, tbl ); if( !tbf ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return; } fh = fopen( name, "wb" ); if( !fh ) { IExec->FreePooled( rp_mempool, tbf, tbl ); return; } strcpy( (TEXT *)tbf, "THX\1" ); tbf[4] = (tbl >> 8)&0xff; tbf[5] = tbl&0xff; tbf[6] = (at->at_PositionNr>>8); tbf[6] |= ((at->at_SpeedMultiplier-1)&3)<<5; tbf[6] |= emptytrk; tbf[7] = at->at_PositionNr & 0xff; tbf[8] = at->at_Restart>>8; tbf[9] = at->at_Restart&0xff; tbf[10] = at->at_TrackLength; tbf[11] = at->at_TrackNr; tbf[12] = at->at_InstrumentNr; tbf[13] = at->at_SubsongNr; fwrite( tbf, 14, 1, fh ); // Subsongs if( at->at_SubsongNr > 0 ) { for( i=0, k=0; iat_SubsongNr; i++ ) { tbf[k++] = at->at_Subsongs[i]>>8; tbf[k++] = at->at_Subsongs[i]&0xff; } fwrite( tbf, k, 1, fh ); } // Position list for( i=0, k=0; iat_PositionNr; i++ ) { for( j=0; j<4; j++ ) { tbf[k++] = at->at_Positions[i].pos_Track[j]; tbf[k++] = at->at_Positions[i].pos_Transpose[j]; } } fwrite( tbf, k, 1, fh ); // Tracks for( i=0, k=0; i<=at->at_TrackNr; i++ ) { if( ( emptytrk != 0 ) && ( i == 0 ) ) continue; for( j=0; jat_TrackLength; j++ ) { tbf[k] = (at->at_Tracks[i][j].stp_Note&0x3f)<<2; tbf[k++] |= (at->at_Tracks[i][j].stp_Instrument>>4)&0x3; tbf[k] = (at->at_Tracks[i][j].stp_Instrument&0xf)<<4; if( ( at->at_Tracks[i][j].stp_FX != 0 ) || ( at->at_Tracks[i][j].stp_FXParam != 0 ) ) { tbf[k++] |= at->at_Tracks[i][j].stp_FX & 0xf; tbf[k++] = at->at_Tracks[i][j].stp_FXParam; } else { tbf[k++] |= at->at_Tracks[i][j].stp_FXb & 0xf; tbf[k++] = at->at_Tracks[i][j].stp_FXbParam; } } } fwrite( tbf, k, 1, fh ); // Instruments for( i=1; i<=at->at_InstrumentNr; i++ ) { in = &at->at_Instruments[i]; k = 0; tbf[0] = in->ins_Volume; tbf[1] = (in->ins_FilterSpeed&0x1f)<<3; tbf[1] |= in->ins_WaveLength&0x7; tbf[2] = in->ins_Envelope.aFrames; tbf[3] = in->ins_Envelope.aVolume; tbf[4] = in->ins_Envelope.dFrames; tbf[5] = in->ins_Envelope.dVolume; tbf[6] = in->ins_Envelope.sFrames; tbf[7] = in->ins_Envelope.rFrames; tbf[8] = in->ins_Envelope.rVolume; tbf[9] = 0; tbf[10] = 0; tbf[11] = 0; tbf[12] = (in->ins_FilterSpeed&0x20)<<2; tbf[12] |= in->ins_FilterLowerLimit&0x7f; tbf[13] = in->ins_VibratoDelay; tbf[14] = (in->ins_HardCutReleaseFrames&0x07)<<4; tbf[14] |= (in->ins_HardCutRelease&1)<<7; tbf[14] |= in->ins_VibratoDepth&0x0f; tbf[15] = in->ins_VibratoSpeed; tbf[16] = in->ins_SquareLowerLimit; tbf[17] = in->ins_SquareUpperLimit; tbf[18] = in->ins_SquareSpeed; tbf[19] = in->ins_FilterUpperLimit&0x3f; tbf[20] = in->ins_PList.pls_Speed; tbf[21] = in->ins_PList.pls_Length; fwrite( tbf, 22, 1, fh ); for( j=0, k=0; jins_PList.pls_Length; j++ ) { l = in->ins_PList.pls_Entries[j].ple_FX[1]; if( l == 12 ) l = 6; if( l == 15 ) l = 7; m = in->ins_PList.pls_Entries[j].ple_FX[0]; if( m == 12 ) m = 6; if( m == 15 ) m = 7; tbf[k] = (l&7)<<5; tbf[k] |= (m&7)<<2; tbf[k++] |= (in->ins_PList.pls_Entries[j].ple_Waveform&6)>>1; tbf[k] = (in->ins_PList.pls_Entries[j].ple_Waveform&1)<<7; tbf[k] |= (in->ins_PList.pls_Entries[j].ple_Fixed&1)<<6; tbf[k++] |= in->ins_PList.pls_Entries[j].ple_Note&0x3f; tbf[k++] = in->ins_PList.pls_Entries[j].ple_FXParam[0]; tbf[k++] = in->ins_PList.pls_Entries[j].ple_FXParam[1]; } fwrite( tbf, k, 1, fh ); } fwrite( at->at_Name, strlen( at->at_Name )+1, 1, fh ); for( i=1; i<=at->at_InstrumentNr; i++ ) fwrite( at->at_Instruments[i].ins_Name, strlen( at->at_Instruments[i].ins_Name )+1, 1, fh ); fclose( fh ); IExec->FreePooled( rp_mempool, tbf, tbl ); } struct ahx_tune *rp_load_ahx( struct ahx_tune *at, uint8 *buf, uint32 buflen, BOOL passed ) { const uint8 *bptr; const TEXT *nptr; uint32 i, j, k, l; at->at_PositionNr = ((buf[6]&0x0f)<<8)|buf[7]; at->at_Restart = (buf[8]<<8)|buf[9]; at->at_SpeedMultiplier = ((buf[6]>>5)&3)+1; at->at_TrackLength = buf[10]; at->at_TrackNr = buf[11]; at->at_InstrumentNr = buf[12]; at->at_SubsongNr = buf[13]; if( at->at_Restart >= at->at_PositionNr ) at->at_Restart = at->at_PositionNr-1; // Do some validation if( ( at->at_PositionNr > 1000 ) || ( at->at_TrackLength > 64 ) || ( at->at_InstrumentNr > 64 ) ) { printf( "%d,%d,%d\n", at->at_PositionNr, at->at_TrackLength, at->at_InstrumentNr ); if( passed ) rp_clear_tune( at ); else rp_free_tune( at ); IExec->FreePooled( rp_mempool, buf, buflen ); printf( "Invalid file.\n" ); return NULL; } strncpy( at->at_Name, (TEXT *)&buf[(buf[4]<<8)|buf[5]], 128 ); nptr = (TEXT *)&buf[((buf[4]<<8)|buf[5])+strlen( at->at_Name )+1]; bptr = &buf[14]; // Subsongs for( i=0; iat_SubsongNr; i++ ) { at->at_Subsongs[i] = (bptr[0]<<8)|bptr[1]; bptr += 2; } // Position list for( i=0; iat_PositionNr; i++ ) { for( j=0; j<4; j++ ) { at->at_Positions[i].pos_Track[j] = *bptr++; at->at_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; } } // Tracks for( i=0; i<=at->at_TrackNr; i++ ) { // rp_new_tune() clears it anyway if( ( ( buf[6]&0x80 ) == 0x80 ) && ( i == 0 ) ) continue; for( j=0; jat_TrackLength; j++ ) { at->at_Tracks[i][j].stp_Note = (bptr[0]>>2)&0x3f; at->at_Tracks[i][j].stp_Instrument = ((bptr[0]&0x3)<<4) | (bptr[1]>>4); at->at_Tracks[i][j].stp_FX = bptr[1]&0xf; at->at_Tracks[i][j].stp_FXParam = bptr[2]; at->at_Tracks[i][j].stp_FXb = 0; at->at_Tracks[i][j].stp_FXbParam = 0; bptr += 3; } } // Instruments for( i=1; i<=at->at_InstrumentNr; i++ ) { if( nptr < (TEXT *)(buf+buflen) ) { strncpy( at->at_Instruments[i].ins_Name, nptr, 128 ); nptr += strlen( nptr )+1; } else { at->at_Instruments[i].ins_Name[0] = 0; } at->at_Instruments[i].ins_Volume = bptr[0]; at->at_Instruments[i].ins_FilterSpeed = ((bptr[1]>>3)&0x1f)|((bptr[12]>>2)&0x20); at->at_Instruments[i].ins_WaveLength = bptr[1]&0x07; at->at_Instruments[i].ins_Envelope.aFrames = bptr[2]; at->at_Instruments[i].ins_Envelope.aVolume = bptr[3]; at->at_Instruments[i].ins_Envelope.dFrames = bptr[4]; at->at_Instruments[i].ins_Envelope.dVolume = bptr[5]; at->at_Instruments[i].ins_Envelope.sFrames = bptr[6]; at->at_Instruments[i].ins_Envelope.rFrames = bptr[7]; at->at_Instruments[i].ins_Envelope.rVolume = bptr[8]; at->at_Instruments[i].ins_FilterLowerLimit = bptr[12]&0x7f; at->at_Instruments[i].ins_VibratoDelay = bptr[13]; at->at_Instruments[i].ins_HardCutReleaseFrames = (bptr[14]>>4)&0x07; at->at_Instruments[i].ins_HardCutRelease = bptr[14]&0x80?1:0; at->at_Instruments[i].ins_VibratoDepth = bptr[14]&0x0f; at->at_Instruments[i].ins_VibratoSpeed = bptr[15]; at->at_Instruments[i].ins_SquareLowerLimit = bptr[16]; at->at_Instruments[i].ins_SquareUpperLimit = bptr[17]; at->at_Instruments[i].ins_SquareSpeed = bptr[18]; at->at_Instruments[i].ins_FilterUpperLimit = bptr[19]&0x3f; at->at_Instruments[i].ins_PList.pls_Speed = bptr[20]; at->at_Instruments[i].ins_PList.pls_Length = bptr[21]; bptr += 22; for( j=0; jat_Instruments[i].ins_PList.pls_Length; j++ ) { k = (bptr[0]>>5)&7; if( k == 6 ) k = 12; if( k == 7 ) k = 15; l = (bptr[0]>>2)&7; if( l == 6 ) l = 12; if( l == 7 ) l = 15; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = k; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = l; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = ((bptr[0]<<1)&6) | (bptr[1]>>7); at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[1]>>6)&1; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[1]&0x3f; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[2]; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[3]; // 1.6: Strip "toggle filter" commands if the module is // version 0 (pre-filters). This is what AHX also does. if( ( buf[3] == 0 ) && ( l == 4 ) && ( (bptr[2]&0xf0) != 0 ) ) at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] &= 0x0f; if( ( buf[3] == 0 ) && ( k == 4 ) && ( (bptr[3]&0xf0) != 0 ) ) at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] &= 0x0f; bptr += 4; } while( j < 256 ) { at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Note = 0; j++; } at->at_Instruments[i].ins_ptop = 0; at->at_Instruments[i].ins_pcurx = 0; at->at_Instruments[i].ins_pcury = 0; } if( !passed ) { IExec->ObtainSemaphore( rp_list_ss ); IExec->AddTail( rp_tunelist, (struct Node *)at ); IExec->ReleaseSemaphore( rp_list_ss ); } rp_init_subsong( at, 0 ); IExec->FreePooled( rp_mempool, buf, buflen ); return at; } struct ahx_tune *rp_mod_import( struct ahx_tune *at, uint8 *buf, uint32 buflen, BOOL passed ) { int32 i, j, k, l; int32 cv_pos, cv_row, cv_chan; rp_clear_tune( at ); if( strncmp( (TEXT *)&buf[1080], "M.K.", 4 ) != 0 ) { if( passed ) rp_clear_tune( at ); else rp_free_tune( at ); IExec->FreePooled( rp_mempool, buf, buflen ); printf( "Invalid file.\n" ); return NULL; } for( i=0; i<20; i++ ) { if( buf[i] == 0 ) break; at->at_Name[i] = buf[i]; } at->at_Name[i] = 0; for( k=1,j=20; k<32; k++,j+=30 ) { for( i=0; i<22; i++ ) { if( buf[j+i] == 0 ) break; at->at_Instruments[k].ins_Name[i] = buf[j+i]; } at->at_Instruments[k].ins_Name[i] = 0; if( ((buf[j+22]<<8)|(buf[j+23])) != 0 ) { at->at_Instruments[k].ins_Volume = 64; at->at_Instruments[k].ins_WaveLength = 5; at->at_Instruments[k].ins_FilterLowerLimit = 1; at->at_Instruments[k].ins_FilterUpperLimit = 31; at->at_Instruments[k].ins_FilterSpeed = 0; at->at_Instruments[k].ins_SquareLowerLimit = 32; at->at_Instruments[k].ins_SquareUpperLimit = 63; at->at_Instruments[k].ins_SquareSpeed = 1; at->at_Instruments[k].ins_VibratoDelay = 0; at->at_Instruments[k].ins_VibratoDepth = 0; at->at_Instruments[k].ins_VibratoSpeed = 0; at->at_Instruments[k].ins_HardCutRelease = 0; at->at_Instruments[k].ins_HardCutReleaseFrames = 0; at->at_Instruments[k].ins_Envelope.aFrames = 1; at->at_Instruments[k].ins_Envelope.aVolume = 64; at->at_Instruments[k].ins_Envelope.dFrames = 1; at->at_Instruments[k].ins_Envelope.dVolume = 64; at->at_Instruments[k].ins_Envelope.sFrames = 1; at->at_Instruments[k].ins_Envelope.rFrames = 1; at->at_Instruments[k].ins_Envelope.rVolume = 64; at->at_Instruments[k].ins_Envelope.aFrames = 1; at->at_Instruments[k].ins_PList.pls_Speed = 1; at->at_Instruments[k].ins_PList.pls_Length = 1; at->at_Instruments[k].ins_PList.pls_Entries[0].ple_Note = 1; at->at_Instruments[k].ins_PList.pls_Entries[0].ple_Waveform = 2; at->at_Instruments[k].ins_PList.pls_Entries[0].ple_Fixed = 0; at->at_Instruments[k].ins_PList.pls_Entries[0].ple_FX[0] = 0; at->at_Instruments[k].ins_PList.pls_Entries[0].ple_FXParam[0] = 0; at->at_Instruments[k].ins_PList.pls_Entries[0].ple_FX[1] = 0; at->at_Instruments[k].ins_PList.pls_Entries[0].ple_FXParam[1] = 0; } } at->at_PositionNr = buf[950]; k=0; for( i=0; i<128; i++ ) { if( i < at->at_PositionNr ) { at->at_Positions[i].pos_Track[0] = buf[i+952]*4; at->at_Positions[i].pos_Track[1] = buf[i+952]*4+1; at->at_Positions[i].pos_Track[2] = buf[i+952]*4+2; at->at_Positions[i].pos_Track[3] = buf[i+952]*4+3; } if( buf[i+952] > k ) k = buf[i+952]; } i = 1084; for( cv_pos=0; cv_pos<=k; cv_pos++ ) { for( cv_row=0; cv_row<64; cv_row++ ) { for( cv_chan=0; cv_chan<4; cv_chan++ ) { l = ((buf[i]&0xf)<<8)|buf[i+1]; for( j=0; j<37; j++ ) if( pt_import_period_tab[j] == l ) break; if( j < 37 ) at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_Note = j; at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_Instrument = (buf[i]&0xf0)|((buf[i+2]>>4)&0xf); l = buf[i+2]&0xf; switch( l ) { case 0x1: case 0x2: case 0x3: case 0x5: case 0xa: case 0xb: case 0xc: case 0xd: at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_FX = l; at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_FXParam = buf[i+3]; break; case 0xe: switch( (buf[i+3]>>4)&0xf ) { case 0x1: case 0x2: case 0xa: case 0xb: case 0xc: case 0xd: at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_FX = l; at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_FXParam = buf[i+3]; break; } break; case 0xf: if( buf[i+3] >= 0x20 ) break; at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_FX = l; at->at_Tracks[cv_pos*4+cv_chan][cv_row].stp_FXParam = buf[i+3]; break; } i+=4; } } } if( !passed ) { IExec->ObtainSemaphore( rp_list_ss ); IExec->AddTail( rp_tunelist, (struct Node *)at ); IExec->ReleaseSemaphore( rp_list_ss ); } rp_init_subsong( at, 0 ); IExec->FreePooled( rp_mempool, buf, buflen ); return at; } struct ahx_tune *rp_load_tune( const TEXT *name, struct ahx_tune *at ) { uint8 *buf; const uint8 *bptr; const TEXT *nptr; uint32 buflen, i, j; FILE *fh; BOOL passed; if( at ) { rp_clear_tune( at ); passed = TRUE; } else { at = rp_new_tune( FALSE ); if( !at ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return NULL; } passed = FALSE; } fh = fopen(name, "rb"); if (!fh) { if( !passed ) rp_free_tune( at ); printf( "Can't open file\n" ); return NULL; } fseek(fh, 0, SEEK_END); buflen = ftell(fh); fseek(fh, 0, SEEK_SET); buf = IExec->AllocPooled( rp_mempool, buflen ); if( !buf ) { if( !passed ) rp_free_tune( at ); fclose(fh); printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return NULL; } if( fread( buf, buflen, 1, fh ) != 1 ) { if( !passed ) rp_free_tune( at ); fclose(fh); printf( "Unable to read from file!\n" ); return NULL; } fclose(fh); if( ( buf[0] == 'T' ) && ( buf[1] == 'H' ) && ( buf[2] == 'X' ) && ( buf[3] < 3 ) ) return rp_load_ahx( at, buf, buflen, passed ); if( ( buf[0] != 'H' ) || ( buf[1] != 'V' ) || ( buf[2] != 'L' ) || ( buf[3] > 1 ) ) return rp_mod_import( at, buf, buflen, passed ); at->at_PositionNr = ((buf[6]&0x0f)<<8)|buf[7]; at->at_Channels = (buf[8]>>2)+4; at->at_Restart = ((buf[8]&3)<<8)|buf[9]; at->at_SpeedMultiplier = ((buf[6]>>5)&3)+1; at->at_TrackLength = buf[10]; at->at_TrackNr = buf[11]; at->at_InstrumentNr = buf[12]; at->at_SubsongNr = buf[13]; at->at_mixgainP = buf[14]; at->at_mixgain = (at->at_mixgainP<<8)/100; at->at_defstereo = buf[15]; at->at_defpanleft = stereopan_left[at->at_defstereo]; at->at_defpanright = stereopan_right[at->at_defstereo]; if( at->at_Restart >= at->at_PositionNr ) at->at_Restart = at->at_PositionNr-1; // Do some validation if( ( at->at_PositionNr > 1000 ) || ( at->at_TrackLength > 64 ) || ( at->at_InstrumentNr > 64 ) ) { printf( "%d,%d,%d\n", at->at_PositionNr, at->at_TrackLength, at->at_InstrumentNr ); if( passed ) rp_clear_tune( at ); else rp_free_tune( at ); IExec->FreePooled( rp_mempool, buf, buflen ); printf( "Invalid file.\n" ); return NULL; } strncpy( at->at_Name, (TEXT *)&buf[(buf[4]<<8)|buf[5]], 128 ); nptr = (TEXT *)&buf[((buf[4]<<8)|buf[5])+strlen( at->at_Name )+1]; bptr = &buf[16]; // Subsongs for( i=0; iat_SubsongNr; i++ ) { at->at_Subsongs[i] = (bptr[0]<<8)|bptr[1]; bptr += 2; } // Position list for( i=0; iat_PositionNr; i++ ) { for( j=0; jat_Channels; j++ ) { if( i < 1000 ) { at->at_Positions[i].pos_Track[j] = *bptr++; at->at_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; } else { bptr++; bptr++; } } } // Tracks for( i=0; i<=at->at_TrackNr; i++ ) { // rp_new_tune() clears it anyway if( ( ( buf[6]&0x80 ) == 0x80 ) && ( i == 0 ) ) continue; for( j=0; jat_TrackLength; j++ ) { if( bptr[0] == 0x3f ) { at->at_Tracks[i][j].stp_Note = 0; at->at_Tracks[i][j].stp_Instrument = 0; at->at_Tracks[i][j].stp_FX = 0; at->at_Tracks[i][j].stp_FXParam = 0; at->at_Tracks[i][j].stp_FXb = 0; at->at_Tracks[i][j].stp_FXbParam = 0; bptr++; continue; } at->at_Tracks[i][j].stp_Note = bptr[0]; at->at_Tracks[i][j].stp_Instrument = bptr[1]; at->at_Tracks[i][j].stp_FX = bptr[2]>>4; at->at_Tracks[i][j].stp_FXParam = bptr[3]; at->at_Tracks[i][j].stp_FXb = bptr[2]&0xf; at->at_Tracks[i][j].stp_FXbParam = bptr[4]; // Strip out EFx commands from older modules // since they did nothing until HVL1 if( buf[3] < 1 ) { if( ((at->at_Tracks[i][j].stp_FX==0xe)&&((at->at_Tracks[i][j].stp_FXParam&0xf0)==0xf0)) || ((at->at_Tracks[i][j].stp_FXb==0xe)&&((at->at_Tracks[i][j].stp_FXParam&0xf0)==0xf0)) ) { at->at_Tracks[i][j].stp_FX = 0; at->at_Tracks[i][j].stp_FXParam = 0; } } bptr += 5; } } // Instruments for( i=1; i<=at->at_InstrumentNr; i++ ) { if( nptr < (TEXT *)(buf+buflen) ) { strncpy( at->at_Instruments[i].ins_Name, nptr, 128 ); nptr += strlen( nptr )+1; } else { at->at_Instruments[i].ins_Name[0] = 0; } at->at_Instruments[i].ins_Volume = bptr[0]; at->at_Instruments[i].ins_FilterSpeed = ((bptr[1]>>3)&0x1f)|((bptr[12]>>2)&0x20); at->at_Instruments[i].ins_WaveLength = bptr[1]&0x07; at->at_Instruments[i].ins_Envelope.aFrames = bptr[2]; at->at_Instruments[i].ins_Envelope.aVolume = bptr[3]; at->at_Instruments[i].ins_Envelope.dFrames = bptr[4]; at->at_Instruments[i].ins_Envelope.dVolume = bptr[5]; at->at_Instruments[i].ins_Envelope.sFrames = bptr[6]; at->at_Instruments[i].ins_Envelope.rFrames = bptr[7]; at->at_Instruments[i].ins_Envelope.rVolume = bptr[8]; at->at_Instruments[i].ins_FilterLowerLimit = bptr[12]&0x7f; at->at_Instruments[i].ins_VibratoDelay = bptr[13]; at->at_Instruments[i].ins_HardCutReleaseFrames = (bptr[14]>>4)&0x07; at->at_Instruments[i].ins_HardCutRelease = bptr[14]&0x80?1:0; at->at_Instruments[i].ins_VibratoDepth = bptr[14]&0x0f; at->at_Instruments[i].ins_VibratoSpeed = bptr[15]; at->at_Instruments[i].ins_SquareLowerLimit = bptr[16]; at->at_Instruments[i].ins_SquareUpperLimit = bptr[17]; at->at_Instruments[i].ins_SquareSpeed = bptr[18]; at->at_Instruments[i].ins_FilterUpperLimit = bptr[19]&0x3f; at->at_Instruments[i].ins_PList.pls_Speed = bptr[20]; at->at_Instruments[i].ins_PList.pls_Length = bptr[21]; bptr += 22; for( j=0; jat_Instruments[i].ins_PList.pls_Length; j++ ) { at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = bptr[0]&0xf; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = (bptr[1]>>3)&0xf; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = bptr[1]&7; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[2]>>6)&1; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[2]&0x3f; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[3]; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[4]; bptr += 5; } while( j < 256 ) { at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = 0; at->at_Instruments[i].ins_PList.pls_Entries[j].ple_Note = 0; j++; } at->at_Instruments[i].ins_ptop = 0; at->at_Instruments[i].ins_pcurx = 0; at->at_Instruments[i].ins_pcury = 0; } if( !passed ) { IExec->ObtainSemaphore( rp_list_ss ); IExec->AddTail( rp_tunelist, (struct Node *)at ); IExec->ReleaseSemaphore( rp_list_ss ); } rp_init_subsong( at, 0 ); IExec->FreePooled( rp_mempool, buf, buflen ); return at; } void rp_process_stepfx_1( struct ahx_tune *at, struct ahx_voice *voice, int32 FX, int32 FXParam ) { switch( FX ) { case 0x0: // Position Jump HI if( ((FXParam&0x0f) > 0) && ((FXParam&0x0f) <= 9) ) at->at_PosJump = FXParam & 0xf; break; case 0x5: // Volume Slide + Tone Portamento case 0xa: // Volume Slide voice->vc_VolumeSlideDown = FXParam & 0x0f; voice->vc_VolumeSlideUp = FXParam >> 4; break; case 0x7: // Panning if( FXParam > 127 ) FXParam -= 256; voice->vc_Pan = (FXParam+128); voice->vc_SetPan = (FXParam+128); voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; break; case 0xb: // Position jump at->at_PosJump = at->at_PosJump*100 + (FXParam & 0x0f) + (FXParam >> 4)*10; at->at_PatternBreak = 1; if( at->at_PosJump <= at->at_PosNr ) at->at_SongEndReached = 1; break; case 0xd: // Pattern break at->at_PosJump = at->at_PosNr+1; at->at_PosJumpNote = (FXParam & 0x0f) + (FXParam>>4)*10; at->at_PatternBreak = 1; if( at->at_PosJumpNote > at->at_TrackLength ) at->at_PosJumpNote = 0; break; case 0xe: // Extended commands switch( FXParam >> 4 ) { case 0xc: // Note cut if( (FXParam & 0x0f) < at->at_Tempo ) { voice->vc_NoteCutWait = FXParam & 0x0f; if( voice->vc_NoteCutWait ) { voice->vc_NoteCutOn = 1; voice->vc_HardCutRelease = 0; } } break; // 1.6: case for 0xd removed } break; case 0xf: // Speed at->at_Tempo = FXParam; if( FXParam == 0 ) at->at_SongEndReached = 1; break; } } void rp_process_stepfx_2( struct ahx_tune *at, struct ahx_voice *voice, int32 FX, int32 FXParam, int32 *Note ) { switch( FX ) { case 0x9: // Set squarewave offset voice->vc_SquarePos = FXParam >> (5 - voice->vc_WaveLength); // voice->vc_PlantSquare = 1; voice->vc_IgnoreSquare = 1; break; case 0x3: // Tone portamento if( FXParam != 0 ) voice->vc_PeriodSlideSpeed = FXParam; case 0x5: // Tone portamento + volume slide if( *Note ) { int32 new, diff; new = period_tab[*Note]; diff = period_tab[voice->vc_TrackPeriod]; diff -= new; new = diff + voice->vc_PeriodSlidePeriod; if( new ) voice->vc_PeriodSlideLimit = -diff; } voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 1; *Note = 0; break; } } void rp_process_stepfx_3( struct ahx_tune *at, struct ahx_voice *voice, int32 FX, int32 FXParam ) { int32 i; switch( FX ) { case 0x01: // Portamento up (period slide down) voice->vc_PeriodSlideSpeed = -FXParam; voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 0; break; case 0x02: // Portamento down voice->vc_PeriodSlideSpeed = FXParam; voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 0; break; case 0x04: // Filter override if( ( FXParam == 0 ) || ( FXParam == 0x40 ) ) break; if( FXParam < 0x40 ) { voice->vc_IgnoreFilter = FXParam; break; } if( FXParam > 0x7f ) break; voice->vc_FilterPos = FXParam - 0x40; break; case 0x0c: // Volume FXParam &= 0xff; if( FXParam <= 0x40 ) { voice->vc_NoteMaxVolume = FXParam; break; } if( (FXParam -= 0x50) < 0 ) break; // 1.6 if( FXParam <= 0x40 ) { for( i=0; iat_Channels; i++ ) at->at_Voices[i].vc_TrackMasterVolume = FXParam; break; } if( (FXParam -= 0xa0-0x50) < 0 ) break; // 1.6 if( FXParam <= 0x40 ) voice->vc_TrackMasterVolume = FXParam; break; case 0xe: // Extended commands; switch( FXParam >> 4 ) { case 0x1: // Fineslide up voice->vc_PeriodSlidePeriod -= (FXParam & 0x0f); voice->vc_PlantPeriod = 1; break; case 0x2: // Fineslide down voice->vc_PeriodSlidePeriod += (FXParam & 0x0f); voice->vc_PlantPeriod = 1; break; case 0x4: // Vibrato control voice->vc_VibratoDepth = FXParam & 0x0f; break; case 0x0a: // Fine volume up voice->vc_NoteMaxVolume += FXParam & 0x0f; if( voice->vc_NoteMaxVolume > 0x40 ) voice->vc_NoteMaxVolume = 0x40; break; case 0x0b: // Fine volume down voice->vc_NoteMaxVolume -= FXParam & 0x0f; if( voice->vc_NoteMaxVolume < 0 ) voice->vc_NoteMaxVolume = 0; break; case 0x0f: // Miscellaneous (1.5+) switch( FXParam & 0x0f ) { case 1: // Preserve track transpose (1.5+) voice->vc_OverrideTranspose = voice->vc_Transpose; break; } break; } break; } } void rp_play_instrument( struct ahx_tune *at, int32 vcnum, int32 instr, int32 period ) { struct ahx_voice *voice; struct ahx_instrument *Ins; int16 SquareLower, SquareUpper, d6, d3, d4; if( at->at_Instruments[instr].ins_PList.pls_Length == 0 ) return; if( period == 0 ) return; if( vcnum >= at->at_Channels ) return; voice = &at->at_Voices[vcnum]; Ins = &at->at_Instruments[instr]; voice->vc_Pan = voice->vc_SetPan; voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; voice->vc_TrackOn = voice->vc_SetTrackOn; voice->vc_Transpose = 0; voice->vc_OverrideTranspose = 1000; // 1.5 voice->vc_PeriodSlideSpeed = voice->vc_PeriodSlidePeriod = voice->vc_PeriodSlideLimit = 0; voice->vc_PerfSubVolume = 0x40; voice->vc_ADSRVolume = 0; voice->vc_Instrument = Ins; voice->vc_SamplePos = 0; voice->vc_ADSR.aFrames = Ins->ins_Envelope.aFrames; voice->vc_ADSR.aVolume = Ins->ins_Envelope.aVolume*256/voice->vc_ADSR.aFrames; voice->vc_ADSR.dFrames = Ins->ins_Envelope.dFrames; voice->vc_ADSR.dVolume = (Ins->ins_Envelope.dVolume-Ins->ins_Envelope.aVolume)*256/voice->vc_ADSR.dFrames; voice->vc_ADSR.sFrames = Ins->ins_Envelope.sFrames; voice->vc_ADSR.rFrames = Ins->ins_Envelope.rFrames; voice->vc_ADSR.rVolume = (Ins->ins_Envelope.rVolume-Ins->ins_Envelope.dVolume)*256/voice->vc_ADSR.rFrames; voice->vc_WaveLength = Ins->ins_WaveLength; voice->vc_NoteMaxVolume = Ins->ins_Volume; voice->vc_VibratoCurrent = 0; voice->vc_VibratoDelay = Ins->ins_VibratoDelay; voice->vc_VibratoDepth = Ins->ins_VibratoDepth; voice->vc_VibratoSpeed = Ins->ins_VibratoSpeed; voice->vc_VibratoPeriod = 0; voice->vc_HardCutRelease = Ins->ins_HardCutRelease; voice->vc_HardCut = Ins->ins_HardCutReleaseFrames; voice->vc_IgnoreSquare = voice->vc_SquareSlidingIn = 0; voice->vc_SquareWait = voice->vc_SquareOn = 0; SquareLower = Ins->ins_SquareLowerLimit >> (5 - voice->vc_WaveLength); SquareUpper = Ins->ins_SquareUpperLimit >> (5 - voice->vc_WaveLength); if( SquareUpper < SquareLower ) { int16 t = SquareUpper; SquareUpper = SquareLower; SquareLower = t; } voice->vc_SquareUpperLimit = SquareUpper; voice->vc_SquareLowerLimit = SquareLower; voice->vc_IgnoreFilter = voice->vc_FilterWait = voice->vc_FilterOn = 0; voice->vc_FilterSlidingIn = 0; d6 = Ins->ins_FilterSpeed; d3 = Ins->ins_FilterLowerLimit; d4 = Ins->ins_FilterUpperLimit; if( d3 & 0x80 ) d6 |= 0x20; if( d4 & 0x80 ) d6 |= 0x40; voice->vc_FilterSpeed = d6; d3 &= ~0x80; d4 &= ~0x80; if( d3 > d4 ) { int16 t = d3; d3 = d4; d4 = t; } voice->vc_FilterUpperLimit = d4; voice->vc_FilterLowerLimit = d3; voice->vc_FilterPos = 32; voice->vc_PerfWait = voice->vc_PerfCurrent = 0; voice->vc_PerfSpeed = Ins->ins_PList.pls_Speed; voice->vc_PerfList = &voice->vc_Instrument->ins_PList; voice->vc_TrackPeriod = period; voice->vc_InstrPeriod = period; voice->vc_PlantPeriod = 1; voice->vc_RingMixSource = NULL; // No ring modulation voice->vc_RingSamplePos = 0; voice->vc_RingPlantPeriod = 0; voice->vc_RingNewWaveform = 0; } void rp_process_step( struct ahx_tune *at, struct ahx_voice *voice ) { int32 Note, Instr, donenotedel; struct ahx_step *Step; if( voice->vc_TrackOn == 0 ) return; if( ( rp_state == STS_PLAYNOTE ) || ( rp_state == STS_PLAYROW ) ) return; voice->vc_VolumeSlideUp = voice->vc_VolumeSlideDown = 0; Step = &at->at_Tracks[at->at_Positions[at->at_PosNr].pos_Track[voice->vc_VoiceNum]][at->at_NoteNr]; Note = Step->stp_Note; Instr = Step->stp_Instrument; // --------- 1.6: from here -------------- donenotedel = 0; // Do notedelay here if( ((Step->stp_FX&0xf)==0xe) && ((Step->stp_FXParam&0xf0)==0xd0) ) { if( voice->vc_NoteDelayOn ) { voice->vc_NoteDelayOn = 0; donenotedel = 1; } else { if( (Step->stp_FXParam&0x0f) < at->at_Tempo ) { voice->vc_NoteDelayWait = Step->stp_FXParam & 0x0f; if( voice->vc_NoteDelayWait ) { voice->vc_NoteDelayOn = 1; return; } } } } if( (donenotedel==0) && ((Step->stp_FXb&0xf)==0xe) && ((Step->stp_FXbParam&0xf0)==0xd0) ) { if( voice->vc_NoteDelayOn ) { voice->vc_NoteDelayOn = 0; } else { if( (Step->stp_FXbParam&0x0f) < at->at_Tempo ) { voice->vc_NoteDelayWait = Step->stp_FXbParam & 0x0f; if( voice->vc_NoteDelayWait ) { voice->vc_NoteDelayOn = 1; return; } } } } // ---------- 1.6: to here ------------- if( Note ) voice->vc_OverrideTranspose = 1000; // 1.5 rp_process_stepfx_1( at, voice, Step->stp_FX&0xf, Step->stp_FXParam ); rp_process_stepfx_1( at, voice, Step->stp_FXb&0xf, Step->stp_FXbParam ); if( Instr ) { struct ahx_instrument *Ins; int16 SquareLower, SquareUpper, d6, d3, d4; /* 1.4: Reset panning to last set position */ voice->vc_Pan = voice->vc_SetPan; voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; voice->vc_PeriodSlideSpeed = voice->vc_PeriodSlidePeriod = voice->vc_PeriodSlideLimit = 0; voice->vc_PerfSubVolume = 0x40; voice->vc_ADSRVolume = 0; voice->vc_Instrument = Ins = &at->at_Instruments[Instr]; voice->vc_SamplePos = 0; voice->vc_ADSR.aFrames = Ins->ins_Envelope.aFrames; voice->vc_ADSR.aVolume = voice->vc_ADSR.aFrames ? Ins->ins_Envelope.aVolume*256/voice->vc_ADSR.aFrames : Ins->ins_Envelope.aVolume * 256; // XXX voice->vc_ADSR.dFrames = Ins->ins_Envelope.dFrames; voice->vc_ADSR.dVolume = voice->vc_ADSR.dFrames ? (Ins->ins_Envelope.dVolume-Ins->ins_Envelope.aVolume)*256/voice->vc_ADSR.dFrames : Ins->ins_Envelope.dVolume * 256; // XXX voice->vc_ADSR.sFrames = Ins->ins_Envelope.sFrames; voice->vc_ADSR.rFrames = Ins->ins_Envelope.rFrames; voice->vc_ADSR.rVolume = voice->vc_ADSR.rFrames ? (Ins->ins_Envelope.rVolume-Ins->ins_Envelope.dVolume)*256/voice->vc_ADSR.rFrames : Ins->ins_Envelope.rVolume * 256; // XXX voice->vc_WaveLength = Ins->ins_WaveLength; voice->vc_NoteMaxVolume = Ins->ins_Volume; voice->vc_VibratoCurrent = 0; voice->vc_VibratoDelay = Ins->ins_VibratoDelay; voice->vc_VibratoDepth = Ins->ins_VibratoDepth; voice->vc_VibratoSpeed = Ins->ins_VibratoSpeed; voice->vc_VibratoPeriod = 0; voice->vc_HardCutRelease = Ins->ins_HardCutRelease; voice->vc_HardCut = Ins->ins_HardCutReleaseFrames; voice->vc_IgnoreSquare = voice->vc_SquareSlidingIn = 0; voice->vc_SquareWait = voice->vc_SquareOn = 0; SquareLower = Ins->ins_SquareLowerLimit >> (5 - voice->vc_WaveLength); SquareUpper = Ins->ins_SquareUpperLimit >> (5 - voice->vc_WaveLength); if( SquareUpper < SquareLower ) { int16 t = SquareUpper; SquareUpper = SquareLower; SquareLower = t; } voice->vc_SquareUpperLimit = SquareUpper; voice->vc_SquareLowerLimit = SquareLower; voice->vc_IgnoreFilter = voice->vc_FilterWait = voice->vc_FilterOn = 0; voice->vc_FilterSlidingIn = 0; d6 = Ins->ins_FilterSpeed; d3 = Ins->ins_FilterLowerLimit; d4 = Ins->ins_FilterUpperLimit; if( d3 & 0x80 ) d6 |= 0x20; if( d4 & 0x80 ) d6 |= 0x40; voice->vc_FilterSpeed = d6; d3 &= ~0x80; d4 &= ~0x80; if( d3 > d4 ) { int16 t = d3; d3 = d4; d4 = t; } voice->vc_FilterUpperLimit = d4; voice->vc_FilterLowerLimit = d3; voice->vc_FilterPos = 32; voice->vc_PerfWait = voice->vc_PerfCurrent = 0; voice->vc_PerfSpeed = Ins->ins_PList.pls_Speed; voice->vc_PerfList = &voice->vc_Instrument->ins_PList; voice->vc_RingMixSource = NULL; // No ring modulation voice->vc_RingSamplePos = 0; voice->vc_RingPlantPeriod = 0; voice->vc_RingNewWaveform = 0; } voice->vc_PeriodSlideOn = 0; rp_process_stepfx_2( at, voice, Step->stp_FX&0xf, Step->stp_FXParam, &Note ); rp_process_stepfx_2( at, voice, Step->stp_FXb&0xf, Step->stp_FXbParam, &Note ); if( Note ) { voice->vc_TrackPeriod = Note; voice->vc_PlantPeriod = 1; } rp_process_stepfx_3( at, voice, Step->stp_FX&0xf, Step->stp_FXParam ); rp_process_stepfx_3( at, voice, Step->stp_FXb&0xf, Step->stp_FXbParam ); } void rp_plist_command_parse( struct ahx_tune *at, struct ahx_voice *voice, int32 FX, int32 FXParam ) { switch( FX ) { case 0: if( ( FXParam > 0 ) && ( FXParam < 0x40 ) ) { if( voice->vc_IgnoreFilter ) { voice->vc_FilterPos = voice->vc_IgnoreFilter; voice->vc_IgnoreFilter = 0; } else { voice->vc_FilterPos = FXParam; } voice->vc_NewWaveform = 1; } break; case 1: voice->vc_PeriodPerfSlideSpeed = FXParam; voice->vc_PeriodPerfSlideOn = 1; break; case 2: voice->vc_PeriodPerfSlideSpeed = -FXParam; voice->vc_PeriodPerfSlideOn = 1; break; case 3: if( voice->vc_IgnoreSquare == 0 ) voice->vc_SquarePos = FXParam >> (5-voice->vc_WaveLength); else voice->vc_IgnoreSquare = 0; break; case 4: if( FXParam == 0 ) { voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); voice->vc_SquareSign = 1; } else { if( FXParam & 0x0f ) { voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); voice->vc_SquareSign = 1; if(( FXParam & 0x0f ) == 0x0f ) voice->vc_SquareSign = -1; } if( FXParam & 0xf0 ) { voice->vc_FilterInit = (voice->vc_FilterOn ^= 1); voice->vc_FilterSign = 1; if(( FXParam & 0xf0 ) == 0xf0 ) voice->vc_FilterSign = -1; } } break; case 5: voice->vc_PerfCurrent = FXParam; break; case 7: // Ring modulate with triangle if(( FXParam >= 1 ) && ( FXParam <= 0x3C )) { voice->vc_RingBasePeriod = FXParam; voice->vc_RingFixedPeriod = 1; } else if(( FXParam >= 0x81 ) && ( FXParam <= 0xBC )) { voice->vc_RingBasePeriod = FXParam-0x80; voice->vc_RingFixedPeriod = 0; } else { voice->vc_RingBasePeriod = 0; voice->vc_RingFixedPeriod = 0; voice->vc_RingNewWaveform = 0; voice->vc_RingAudioSource = NULL; // turn it off voice->vc_RingMixSource = NULL; break; } voice->vc_RingWaveform = 0; voice->vc_RingNewWaveform = 1; voice->vc_RingPlantPeriod = 1; break; case 8: // Ring modulate with sawtooth if(( FXParam >= 1 ) && ( FXParam <= 0x3C )) { voice->vc_RingBasePeriod = FXParam; voice->vc_RingFixedPeriod = 1; } else if(( FXParam >= 0x81 ) && ( FXParam <= 0xBC )) { voice->vc_RingBasePeriod = FXParam-0x80; voice->vc_RingFixedPeriod = 0; } else { voice->vc_RingBasePeriod = 0; voice->vc_RingFixedPeriod = 0; voice->vc_RingNewWaveform = 0; voice->vc_RingAudioSource = NULL; voice->vc_RingMixSource = NULL; break; } voice->vc_RingWaveform = 1; voice->vc_RingNewWaveform = 1; voice->vc_RingPlantPeriod = 1; break; case 9: if( FXParam > 127 ) FXParam -= 256; voice->vc_Pan = (FXParam+128); voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; break; case 12: if( FXParam <= 0x40 ) { voice->vc_NoteMaxVolume = FXParam; break; } if( (FXParam -= 0x50) < 0 ) break; if( FXParam <= 0x40 ) { voice->vc_PerfSubVolume = FXParam; break; } if( (FXParam -= 0xa0-0x50) < 0 ) break; if( FXParam <= 0x40 ) voice->vc_TrackMasterVolume = FXParam; break; case 15: voice->vc_PerfSpeed = voice->vc_PerfWait = FXParam; break; } } void rp_process_frame( struct ahx_tune *at, struct ahx_voice *voice ) { static CONST uint8 Offsets[] = {0x00,0x04,0x04+0x08,0x04+0x08+0x10,0x04+0x08+0x10+0x20,0x04+0x08+0x10+0x20+0x40}; if( voice->vc_TrackOn == 0 ) return; if( rp_state != STS_PLAYNOTE ) { if( voice->vc_NoteDelayOn ) { if( voice->vc_NoteDelayWait <= 0 ) rp_process_step( at, voice ); else voice->vc_NoteDelayWait--; } if( voice->vc_HardCut ) { int32 nextinst; if( at->at_NoteNr+1 < at->at_TrackLength ) nextinst = at->at_Tracks[voice->vc_Track][at->at_NoteNr+1].stp_Instrument; else nextinst = at->at_Tracks[voice->vc_NextTrack][0].stp_Instrument; if( nextinst ) { int32 d1; d1 = at->at_Tempo - voice->vc_HardCut; if( d1 < 0 ) d1 = 0; if( !voice->vc_NoteCutOn ) { voice->vc_NoteCutOn = 1; voice->vc_NoteCutWait = d1; voice->vc_HardCutReleaseF = -(d1-at->at_Tempo); } else { voice->vc_HardCut = 0; } } } if( voice->vc_NoteCutOn ) { if( voice->vc_NoteCutWait <= 0 ) { voice->vc_NoteCutOn = 0; if( voice->vc_HardCutRelease ) { voice->vc_ADSR.rVolume = -(voice->vc_ADSRVolume - (voice->vc_Instrument->ins_Envelope.rVolume << 8)) / voice->vc_HardCutReleaseF; voice->vc_ADSR.rFrames = voice->vc_HardCutReleaseF; voice->vc_ADSR.aFrames = voice->vc_ADSR.dFrames = voice->vc_ADSR.sFrames = 0; } else { voice->vc_NoteMaxVolume = 0; } } else { voice->vc_NoteCutWait--; } } } // ADSR envelope if( voice->vc_ADSR.aFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.aVolume; if( --voice->vc_ADSR.aFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.aVolume << 8; } else if( voice->vc_ADSR.dFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.dVolume; if( --voice->vc_ADSR.dFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.dVolume << 8; } else if( voice->vc_ADSR.sFrames ) { voice->vc_ADSR.sFrames--; } else if( voice->vc_ADSR.rFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.rVolume; if( --voice->vc_ADSR.rFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.rVolume << 8; } if( ( rp_state != STS_PLAYNOTE ) && ( rp_state != STS_PLAYROW ) ) { // VolumeSlide voice->vc_NoteMaxVolume = voice->vc_NoteMaxVolume + voice->vc_VolumeSlideUp - voice->vc_VolumeSlideDown; if( voice->vc_NoteMaxVolume < 0 ) voice->vc_NoteMaxVolume = 0; else if( voice->vc_NoteMaxVolume > 0x40 ) voice->vc_NoteMaxVolume = 0x40; // Portamento if( voice->vc_PeriodSlideOn ) { if( voice->vc_PeriodSlideWithLimit ) { int32 d0, d2; d0 = voice->vc_PeriodSlidePeriod - voice->vc_PeriodSlideLimit; d2 = voice->vc_PeriodSlideSpeed; if( d0 > 0 ) d2 = -d2; if( d0 ) { int32 d3; d3 = (d0 + d2) ^ d0; if( d3 >= 0 ) d0 = voice->vc_PeriodSlidePeriod + d2; else d0 = voice->vc_PeriodSlideLimit; voice->vc_PeriodSlidePeriod = d0; voice->vc_PlantPeriod = 1; } } else { voice->vc_PeriodSlidePeriod += voice->vc_PeriodSlideSpeed; voice->vc_PlantPeriod = 1; } } } // Vibrato if( voice->vc_VibratoDepth ) { if( voice->vc_VibratoDelay <= 0 ) { voice->vc_VibratoPeriod = (vib_tab[voice->vc_VibratoCurrent] * voice->vc_VibratoDepth) >> 7; voice->vc_PlantPeriod = 1; voice->vc_VibratoCurrent = (voice->vc_VibratoCurrent + voice->vc_VibratoSpeed) & 0x3f; } else { voice->vc_VibratoDelay--; } } // PList if( voice->vc_PerfList != 0 ) { if( voice->vc_Instrument && voice->vc_PerfCurrent < voice->vc_Instrument->ins_PList.pls_Length ) { if( --voice->vc_PerfWait <= 0 ) { uint32 i; int32 cur; cur = voice->vc_PerfCurrent++; voice->vc_PerfWait = voice->vc_PerfSpeed; if( voice->vc_PerfList->pls_Entries[cur].ple_Waveform ) { voice->vc_Waveform = voice->vc_PerfList->pls_Entries[cur].ple_Waveform-1; voice->vc_NewWaveform = 1; voice->vc_PeriodPerfSlideSpeed = voice->vc_PeriodPerfSlidePeriod = 0; } // Holdwave voice->vc_PeriodPerfSlideOn = 0; for( i=0; i<2; i++ ) rp_plist_command_parse( at, voice, voice->vc_PerfList->pls_Entries[cur].ple_FX[i]&0xff, voice->vc_PerfList->pls_Entries[cur].ple_FXParam[i]&0xff ); // GetNote if( voice->vc_PerfList->pls_Entries[cur].ple_Note ) { voice->vc_InstrPeriod = voice->vc_PerfList->pls_Entries[cur].ple_Note; voice->vc_PlantPeriod = 1; voice->vc_FixedNote = voice->vc_PerfList->pls_Entries[cur].ple_Fixed; } } } else { if( voice->vc_PerfWait ) voice->vc_PerfWait--; else voice->vc_PeriodPerfSlideSpeed = 0; } } // PerfPortamento if( voice->vc_PeriodPerfSlideOn ) { voice->vc_PeriodPerfSlidePeriod -= voice->vc_PeriodPerfSlideSpeed; if( voice->vc_PeriodPerfSlidePeriod ) voice->vc_PlantPeriod = 1; } if( voice->vc_Waveform == 3-1 && voice->vc_SquareOn ) { if( --voice->vc_SquareWait <= 0 ) { int32 d1, d2, d3; d1 = voice->vc_SquareLowerLimit; d2 = voice->vc_SquareUpperLimit; d3 = voice->vc_SquarePos; if( voice->vc_SquareInit ) { voice->vc_SquareInit = 0; if( d3 <= d1 ) { voice->vc_SquareSlidingIn = 1; voice->vc_SquareSign = 1; } else if( d3 >= d2 ) { voice->vc_SquareSlidingIn = 1; voice->vc_SquareSign = -1; } } // NoSquareInit if( d1 == d3 || d2 == d3 ) { if( voice->vc_SquareSlidingIn ) voice->vc_SquareSlidingIn = 0; else voice->vc_SquareSign = -voice->vc_SquareSign; } d3 += voice->vc_SquareSign; voice->vc_SquarePos = d3; voice->vc_PlantSquare = 1; voice->vc_SquareWait = voice->vc_Instrument->ins_SquareSpeed; } } if( voice->vc_FilterOn && --voice->vc_FilterWait <= 0 ) { uint32 i, FMax; int32 d1, d2, d3; d1 = voice->vc_FilterLowerLimit; d2 = voice->vc_FilterUpperLimit; d3 = voice->vc_FilterPos; if( voice->vc_FilterInit ) { voice->vc_FilterInit = 0; if( d3 <= d1 ) { voice->vc_FilterSlidingIn = 1; voice->vc_FilterSign = 1; } else if( d3 >= d2 ) { voice->vc_FilterSlidingIn = 1; voice->vc_FilterSign = -1; } } // NoFilterInit FMax = (voice->vc_FilterSpeed < 3) ? (5-voice->vc_FilterSpeed) : 1; for( i=0; ivc_FilterSlidingIn ) voice->vc_FilterSlidingIn = 0; else voice->vc_FilterSign = -voice->vc_FilterSign; } d3 += voice->vc_FilterSign; } if( d3 < 1 ) d3 = 1; if( d3 > 63 ) d3 = 63; voice->vc_FilterPos = d3; voice->vc_NewWaveform = 1; voice->vc_FilterWait = voice->vc_FilterSpeed - 3; if( voice->vc_FilterWait < 1 ) voice->vc_FilterWait = 1; } if( voice->vc_Waveform == 3-1 || voice->vc_PlantSquare ) { // CalcSquare uint32 i; int32 Delta; int8 *SquarePtr; int32 X; SquarePtr = &waves[WO_SQUARES+(voice->vc_FilterPos-0x20)*(0xfc+0xfc+0x80*0x1f+0x80+0x280*3)]; X = voice->vc_SquarePos << (5 - voice->vc_WaveLength); if( X > 0x20 ) { X = 0x40 - X; voice->vc_SquareReverse = 1; } // OkDownSquare if( X > 0 ) SquarePtr += (X-1) << 7; Delta = 32 >> voice->vc_WaveLength; at->at_WaveformTab[2] = voice->vc_SquareTempBuffer; for( i=0; i<(1<vc_WaveLength)*4; i++ ) { voice->vc_SquareTempBuffer[i] = *SquarePtr; SquarePtr += Delta; } voice->vc_NewWaveform = 1; voice->vc_Waveform = 3-1; voice->vc_PlantSquare = 0; } if( voice->vc_Waveform == 4-1 ) voice->vc_NewWaveform = 1; if( voice->vc_RingNewWaveform ) { int8 *rasrc; if( voice->vc_RingWaveform > 1 ) voice->vc_RingWaveform = 1; rasrc = at->at_WaveformTab[voice->vc_RingWaveform]; rasrc += Offsets[voice->vc_WaveLength]; voice->vc_RingAudioSource = rasrc; } if( voice->vc_NewWaveform ) { int8 *AudioSource; AudioSource = at->at_WaveformTab[voice->vc_Waveform]; if( voice->vc_Waveform != 3-1 ) AudioSource += (voice->vc_FilterPos-0x20)*(0xfc+0xfc+0x80*0x1f+0x80+0x280*3); if( voice->vc_Waveform < 3-1) { // GetWLWaveformlor2 AudioSource += Offsets[voice->vc_WaveLength]; } if( voice->vc_Waveform == 4-1 ) { // AddRandomMoving AudioSource += ( voice->vc_WNRandom & (2*0x280-1) ) & ~1; // GoOnRandom voice->vc_WNRandom += 2239384; voice->vc_WNRandom = ((((voice->vc_WNRandom >> 8) | (voice->vc_WNRandom << 24)) + 782323) ^ 75) - 6735; } voice->vc_AudioSource = AudioSource; } // Ring modulation period calculation if( voice->vc_RingAudioSource ) { voice->vc_RingAudioPeriod = voice->vc_RingBasePeriod; if( !(voice->vc_RingFixedPeriod) ) { if( voice->vc_OverrideTranspose != 1000 ) // 1.5 voice->vc_RingAudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; else voice->vc_RingAudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; } if( voice->vc_RingAudioPeriod > 5*12 ) voice->vc_RingAudioPeriod = 5*12; if( voice->vc_RingAudioPeriod < 0 ) voice->vc_RingAudioPeriod = 0; voice->vc_RingAudioPeriod = period_tab[voice->vc_RingAudioPeriod]; if( !(voice->vc_RingFixedPeriod) ) voice->vc_RingAudioPeriod += voice->vc_PeriodSlidePeriod; voice->vc_RingAudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; if( voice->vc_RingAudioPeriod > 0x0d60 ) voice->vc_RingAudioPeriod = 0x0d60; if( voice->vc_RingAudioPeriod < 0x0071 ) voice->vc_RingAudioPeriod = 0x0071; } // Normal period calculation voice->vc_AudioPeriod = voice->vc_InstrPeriod; if( !(voice->vc_FixedNote) ) { if( voice->vc_OverrideTranspose != 1000 ) // 1.5 voice->vc_AudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; else voice->vc_AudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; } if( voice->vc_AudioPeriod > 5*12 ) voice->vc_AudioPeriod = 5*12; if( voice->vc_AudioPeriod < 0 ) voice->vc_AudioPeriod = 0; voice->vc_AudioPeriod = period_tab[voice->vc_AudioPeriod]; if( !(voice->vc_FixedNote) ) voice->vc_AudioPeriod += voice->vc_PeriodSlidePeriod; voice->vc_AudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; if( voice->vc_AudioPeriod > 0x0d60 ) voice->vc_AudioPeriod = 0x0d60; if( voice->vc_AudioPeriod < 0x0071 ) voice->vc_AudioPeriod = 0x0071; voice->vc_AudioVolume = (((((((voice->vc_ADSRVolume >> 8) * voice->vc_NoteMaxVolume) >> 6) * voice->vc_PerfSubVolume) >> 6) * voice->vc_TrackMasterVolume) >> 6); } void rp_set_audio( struct ahx_voice *voice, float64 freqf ) { if( voice->vc_TrackOn == 0 ) { voice->vc_VoiceVolume = 0; return; } voice->vc_VoiceVolume = voice->vc_AudioVolume; if( voice->vc_PlantPeriod ) { float64 freq2; uint32 delta; voice->vc_PlantPeriod = 0; voice->vc_VoicePeriod = voice->vc_AudioPeriod; freq2 = Period2Freq( voice->vc_AudioPeriod ); delta = (uint32)(freq2 / freqf); if( delta > (0x280<<16) ) delta -= (0x280<<16); if( delta == 0 ) delta = 1; voice->vc_Delta = delta; } if( voice->vc_NewWaveform ) { int8 *src; src = voice->vc_AudioSource; if( voice->vc_Waveform == 4-1 ) { IExec->CopyMem((void *)src, &voice->vc_VoiceBuffer[0], 0x280); } else { uint32 i, WaveLoops; WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; for( i=0; iCopyMem((void *)src, &voice->vc_VoiceBuffer[i*4*(1<vc_WaveLength)], 4*(1<vc_WaveLength)); } voice->vc_VoiceBuffer[0x280] = voice->vc_VoiceBuffer[0]; voice->vc_MixSource = voice->vc_VoiceBuffer; } /* Ring Modulation */ if( voice->vc_RingPlantPeriod ) { float64 freq2; uint32 delta; voice->vc_RingPlantPeriod = 0; freq2 = Period2Freq( voice->vc_RingAudioPeriod ); delta = (uint32)(freq2 / freqf); if( delta > (0x280<<16) ) delta -= (0x280<<16); if( delta == 0 ) delta = 1; voice->vc_RingDelta = delta; } if( voice->vc_RingNewWaveform ) { int8 *src; uint32 i, WaveLoops; src = voice->vc_RingAudioSource; WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; for( i=0; iCopyMem((void *)src, &voice->vc_RingVoiceBuffer[i*4*(1<vc_WaveLength)], 4*(1<vc_WaveLength)); voice->vc_RingVoiceBuffer[0x280] = voice->vc_RingVoiceBuffer[0]; voice->vc_RingMixSource = voice->vc_RingVoiceBuffer; } } void rp_play_irq( struct ahx_tune *at ) { uint32 i; if( ( rp_state != STS_PLAYNOTE ) && ( rp_state != STS_PLAYROW ) ) { if( at->at_stopnextrow > 0 ) { if( ( at->at_GetNewPosition ) || ( at->at_StepWaitFrames <= 1 ) ) { at->at_stopnextrow--; if( at->at_stopnextrow == 0 ) rp_state = STS_PLAYROW; } } } if( ( rp_state != STS_PLAYNOTE ) && ( rp_state != STS_PLAYROW ) ) { if( at->at_StepWaitFrames <= 0 ) { if( at->at_GetNewPosition ) { int32 nextpos = (at->at_PosNr+1==at->at_PositionNr)?0:(at->at_PosNr+1); for( i=0; iat_Channels; i++ ) { at->at_Voices[i].vc_Track = at->at_Positions[at->at_PosNr].pos_Track[i]; at->at_Voices[i].vc_Transpose = at->at_Positions[at->at_PosNr].pos_Transpose[i]; at->at_Voices[i].vc_NextTrack = at->at_Positions[nextpos].pos_Track[i]; at->at_Voices[i].vc_NextTranspose = at->at_Positions[nextpos].pos_Transpose[i]; } at->at_GetNewPosition = 0; } for( i=0; iat_Channels; i++ ) rp_process_step( at, &at->at_Voices[i] ); at->at_StepWaitFrames = at->at_Tempo; } } for( i=0; iat_Channels; i++ ) rp_process_frame( at, &at->at_Voices[i] ); if( ( rp_state != STS_PLAYNOTE ) && ( rp_state != STS_PLAYROW ) ) { at->at_PlayingTime++; if( at->at_Tempo > 0 && --at->at_StepWaitFrames <= 0 ) { if( !at->at_PatternBreak ) { at->at_NoteNr++; if( at->at_NoteNr >= at->at_TrackLength ) { at->at_PosJump = at->at_PosNr+1; at->at_PosJumpNote = 0; at->at_PatternBreak = 1; } } if( at->at_PatternBreak ) { at->at_PatternBreak = 0; // Manual override? if( at->at_NextPosNr != -1 ) { at->at_PosNr = at->at_NextPosNr; at->at_NextPosNr = -1; at->at_NoteNr = 0; } else { if( rp_state != STS_PLAYPOS ) { at->at_PosNr = at->at_PosJump; at->at_NoteNr = at->at_PosJumpNote; if( at->at_PosNr == at->at_PositionNr ) { at->at_SongEndReached = 1; at->at_PosNr = at->at_Restart; } } else { at->at_NoteNr = 0; } } at->at_PosJumpNote = 0; at->at_PosJump = 0; at->at_GetNewPosition = 1; } } } for( i=0; iat_Channels; i++ ) rp_set_audio( &at->at_Voices[i], (float64)FREQ ); } int32 rp_mix_findloudest( struct ahx_tune *at, uint32 samples ) { const int8 *src[MAX_CHANNELS]; const int8 *rsrc[MAX_CHANNELS]; uint32 delta[MAX_CHANNELS]; uint32 rdelta[MAX_CHANNELS]; int32 vol[MAX_CHANNELS]; uint32 pos[MAX_CHANNELS]; uint32 rpos[MAX_CHANNELS]; uint32 cnt; int32 panl[MAX_CHANNELS]; int32 panr[MAX_CHANNELS]; int32 a=0, b=0, j; uint32 loud; uint32 i, chans, loops; loud = 0; chans = at->at_Channels; for( i=0; iat_Voices[i].vc_Delta; vol[i] = at->at_Voices[i].vc_VoiceVolume; pos[i] = at->at_Voices[i].vc_SamplePos; src[i] = at->at_Voices[i].vc_MixSource; panl[i] = at->at_Voices[i].vc_PanMultLeft; panr[i] = at->at_Voices[i].vc_PanMultRight; /* Ring Modulation */ rdelta[i]= at->at_Voices[i].vc_RingDelta; rpos[i] = at->at_Voices[i].vc_RingSamplePos; rsrc[i] = at->at_Voices[i].vc_RingMixSource; } do { loops = samples; for( i=0; i= (0x280 << 16)) pos[i] -= 0x280<<16; cnt = ((0x280<<16) - pos[i] - 1) / delta[i] + 1; if( cnt < loops ) loops = cnt; if( rsrc[i] ) { if( rpos[i] >= (0x280<<16)) rpos[i] -= 0x280<<16; cnt = ((0x280<<16) - rpos[i] - 1) / rdelta[i] + 1; if( cnt < loops ) loops = cnt; } } samples -= loops; // Inner loop do { a=0; b=0; for( i=0; i>16]*rsrc[i][rpos[i]>>16])>>7)*vol[i]; rpos[i] += rdelta[i]; } else { j = src[i][pos[i]>>16]*vol[i]; } a += (j * panl[i]) >> 7; b += (j * panr[i]) >> 7; pos[i] += delta[i]; } // a = (a*at->at_mixgain)>>8; // b = (b*at->at_mixgain)>>8; a = abs( a ); b = abs( b ); if( a > loud ) loud = a; if( b > loud ) loud = b; loops--; } while( loops > 0 ); } while( samples > 0 ); for( i=0; iat_Voices[i].vc_SamplePos = pos[i]; at->at_Voices[i].vc_RingSamplePos = rpos[i]; } return loud; } void rp_mixchunk( struct ahx_tune *at, uint32 samples, int8 *buf1, int8 *buf2, int32 bufmod ) { const int8 *src[MAX_CHANNELS]; const int8 *rsrc[MAX_CHANNELS]; uint32 delta[MAX_CHANNELS]; uint32 rdelta[MAX_CHANNELS]; int32 vol[MAX_CHANNELS]; uint32 pos[MAX_CHANNELS]; uint32 rpos[MAX_CHANNELS]; uint32 cnt; int32 panl[MAX_CHANNELS]; int32 panr[MAX_CHANNELS]; uint32 vu[MAX_CHANNELS]; int32 a=0, b=0, j; uint32 i, chans, loops; chans = at->at_Channels; for( i=0; iat_Voices[i].vc_Delta; vol[i] = at->at_Voices[i].vc_VoiceVolume; pos[i] = at->at_Voices[i].vc_SamplePos; src[i] = at->at_Voices[i].vc_MixSource; panl[i] = at->at_Voices[i].vc_PanMultLeft; panr[i] = at->at_Voices[i].vc_PanMultRight; /* Ring Modulation */ rdelta[i]= at->at_Voices[i].vc_RingDelta; rpos[i] = at->at_Voices[i].vc_RingSamplePos; rsrc[i] = at->at_Voices[i].vc_RingMixSource; vu[i] = 0; } do { loops = samples; for( i=0; i= (0x280 << 16)) pos[i] -= 0x280<<16; cnt = ((0x280<<16) - pos[i] - 1) / delta[i] + 1; if( cnt < loops ) loops = cnt; if( rsrc[i] ) { if( rpos[i] >= (0x280<<16)) rpos[i] -= 0x280<<16; cnt = ((0x280<<16) - rpos[i] - 1) / rdelta[i] + 1; if( cnt < loops ) loops = cnt; } } samples -= loops; // Inner loop do { a=0; b=0; for( i=0; i>16]*rsrc[i][rpos[i]>>16])>>7)*vol[i]; rpos[i] += rdelta[i]; } else { j = src[i][pos[i]>>16]*vol[i]; } if( abs( j ) > vu[i] ) vu[i] = abs( j ); a += (j * panl[i]) >> 7; b += (j * panr[i]) >> 7; pos[i] += delta[i]; } a = (a*at->at_mixgain)>>8; b = (b*at->at_mixgain)>>8; *(int16 *)buf1 = a; *(int16 *)buf2 = b; loops--; buf1 += bufmod; buf2 += bufmod; } while( loops > 0 ); } while( samples > 0 ); for( i=0; iat_Voices[i].vc_SamplePos = pos[i]; at->at_Voices[i].vc_RingSamplePos = rpos[i]; at->at_Voices[i].vc_VUMeter = vu[i]; } } void rp_decode_frame( struct ahx_tune *at, int8 *buf1, int8 *buf2, int32 bufmod ) { uint32 samples, loops; samples = FREQ/50/at->at_SpeedMultiplier; loops = at->at_SpeedMultiplier; do { rp_play_irq( at ); rp_mixchunk( at, samples, buf1, buf2, bufmod ); buf1 += samples * bufmod; buf2 += samples * bufmod; loops--; } while( loops ); if( ( rp_state == STS_PLAYSONG ) || ( rp_state == STS_PLAYPOS ) ) { if( (at->at_ticks=(at->at_ticks+1)%50) == 0 ) if( (at->at_secs =(at->at_secs +1)%60) == 0 ) if( (at->at_mins =(at->at_mins +1)%60) == 0 ) at->at_hours++; } // Update the gui! rp_curtune = at; #ifndef __SDL_WRAPPER__ IExec->Signal( rp_maintask, gui_tick_sig ); #else { SDL_Event event; SDL_UserEvent userevent; userevent.type = SDL_USEREVENT; userevent.code = 0; userevent.data1 = NULL; userevent.data2 = NULL; event.type = SDL_USEREVENT; event.user = userevent; SDL_PushEvent( &event ); } #endif } // You'd better not be playing this bastard! int32 rp_find_loudest( struct ahx_tune *at ) { uint32 rsamp, rloop; uint32 samples, loops, loud, n, i; rsamp = FREQ/50/at->at_SpeedMultiplier; rloop = at->at_SpeedMultiplier; loud = 0; for( i=0; i<=at->at_SubsongNr; i++ ) { rp_init_subsong( at, i ); at->at_SongEndReached = 0; while( at->at_SongEndReached == 0 ) { samples = rsamp; loops = rloop; // if( IExec->SetSignal( 0L, SIGBREAKF_CTRL_C ) & SIGBREAKF_CTRL_C ) // break; do { rp_play_irq( at ); n = rp_mix_findloudest( at, samples ); if( n > loud ) loud = n; loops--; } while( loops ); } } return loud; } #ifndef __SDL_WRAPPER__ BOOL rp_alloc_buffers( void ) { int32 i; if( rp_audiobuffer[0] ) IExec->FreePooled( rp_mempool, rp_audiobuffer[0], rp_audiobuflen * 2 ); rp_freqf = (float64)FREQ; rp_audiobuflen = FREQ * sizeof( uint16 ) * 2 / 50; rp_audiobuffer[0] = IExec->AllocPooled( rp_mempool, rp_audiobuflen * 2 ); if( rp_audiobuffer[0] == NULL ) return FALSE; rp_audiobuffer[1] = &rp_audiobuffer[0][rp_audiobuflen]; for( i=0; iAllocSysObjectTags(ASOT_PORT, TAG_DONE); if( !rp_msgport ) return FALSE; ahi_mp = IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE); if( !ahi_mp ) return FALSE; ahi_io[0] = (struct AHIRequest *)IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_ReplyPort, ahi_mp, ASOIOR_Size, sizeof( struct AHIRequest ), TAG_DONE); if( ahi_io[0] == NULL ) return FALSE; ahi_io[0]->ahir_Version = 4; ahi_dev = IExec->OpenDevice( AHINAME, AHI_DEFAULT_UNIT, (struct IORequest *)ahi_io[0], 0 ); if( ahi_dev == -1 ) return FALSE; AHIBase = (struct Library *)ahi_io[0]->ahir_Std.io_Device; IAHI = (struct AHIIFace *)IExec->GetInterface( AHIBase, "main", 1, NULL ); if( !IAHI ) return FALSE; ahi_io[1] = IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_Size, sizeof( struct AHIRequest ), ASOIOR_Duplicate, ahi_io[0], TAG_DONE ); if( ahi_io[1] == NULL ) return FALSE; return TRUE; } void rp_subtask_shut( void ) { if( IAHI ) IExec->DropInterface( (struct Interface *)IAHI ); if( ahi_dev != -1 ) { IExec->CloseDevice( (struct IORequest *)ahi_io[0] ); IExec->FreeSysObject( ASOT_IOREQUEST, ahi_io[0] ); } if( ahi_io[1] ) IExec->FreeSysObject( ASOT_IOREQUEST, ahi_io[1] ); if( ahi_mp ) IExec->FreeSysObject( ASOT_PORT, ahi_mp ); if( rp_msgport ) { struct Message *msg; while( ( msg = IExec->GetMsg( rp_msgport ) ) ) IExec->ReplyMsg( msg ); IExec->FreeSysObject( ASOT_PORT, rp_msgport ); rp_msgport = NULL; } if( rp_audiobuffer[0] ) IExec->FreePooled( rp_mempool, rp_audiobuffer[0], rp_audiobuflen * 2 ); } void rp_mix_and_play_sounds( struct ahx_tune *at, BOOL *need_wait, struct AHIRequest **prev_req, uint32 *nextbuf ) { int32 rp_nextbuf; rp_nextbuf = *nextbuf; if( need_wait[rp_nextbuf] ) IExec->WaitIO( (struct IORequest *)ahi_io[rp_nextbuf] ); rp_decode_frame( at, rp_audiobuffer[rp_nextbuf], rp_audiobuffer[rp_nextbuf]+sizeof( int16 ), 4 ); ahi_io[rp_nextbuf]->ahir_Std.io_Command = CMD_WRITE; ahi_io[rp_nextbuf]->ahir_Std.io_Data = rp_audiobuffer[rp_nextbuf]; ahi_io[rp_nextbuf]->ahir_Std.io_Length = rp_audiobuflen; ahi_io[rp_nextbuf]->ahir_Std.io_Offset = 0; ahi_io[rp_nextbuf]->ahir_Type = AHIST_S16S; ahi_io[rp_nextbuf]->ahir_Frequency = rp_freq; ahi_io[rp_nextbuf]->ahir_Volume = 0x10000; ahi_io[rp_nextbuf]->ahir_Position = 0x8000; ahi_io[rp_nextbuf]->ahir_Link = *prev_req; IExec->SendIO( (struct IORequest *)ahi_io[rp_nextbuf] ); *prev_req = ahi_io[rp_nextbuf]; need_wait[rp_nextbuf] = TRUE; rp_nextbuf ^= 1; if( ( need_wait[rp_nextbuf] == TRUE ) && ( IExec->CheckIO( (struct IORequest *)ahi_io[rp_nextbuf] ) != NULL ) ) { IExec->WaitIO( (struct IORequest *)ahi_io[rp_nextbuf] ); need_wait[rp_nextbuf] = FALSE; } if( need_wait[rp_nextbuf] == FALSE ) { rp_decode_frame( at, rp_audiobuffer[rp_nextbuf], rp_audiobuffer[rp_nextbuf]+sizeof( int16 ), 4 ); ahi_io[rp_nextbuf]->ahir_Std.io_Command = CMD_WRITE; ahi_io[rp_nextbuf]->ahir_Std.io_Data = rp_audiobuffer[rp_nextbuf]; ahi_io[rp_nextbuf]->ahir_Std.io_Length = rp_audiobuflen; ahi_io[rp_nextbuf]->ahir_Std.io_Offset = 0; ahi_io[rp_nextbuf]->ahir_Type = AHIST_S16S; ahi_io[rp_nextbuf]->ahir_Frequency = rp_freq; ahi_io[rp_nextbuf]->ahir_Volume = 0x10000; ahi_io[rp_nextbuf]->ahir_Position = 0x8000; ahi_io[rp_nextbuf]->ahir_Link = *prev_req; IExec->SendIO( (struct IORequest *)ahi_io[rp_nextbuf] ); *prev_req = ahi_io[rp_nextbuf]; need_wait[rp_nextbuf] = TRUE; rp_nextbuf ^= 1; } *nextbuf = rp_nextbuf; } void rp_subtask_main( void ) { uint32 gotsigs; uint32 rp_msgsig, rp_ahisig; uint32 rp_nextbuf, i; BOOL need_wait[2]; struct ahx_tune *rp_tune; struct rp_command *msg; if( rp_subtask_init() ) { rp_state = STS_IDLE; rp_tune = NULL; rp_msgsig = 1L<mp_SigBit; rp_ahisig = 1L<mp_SigBit; rp_nextbuf = 0; need_wait[0] = FALSE; need_wait[1] = FALSE; struct AHIRequest *prev_req = NULL; IExec->Signal( rp_maintask, (1L<DebugPrintF( "Replayer subtask, ready for action!\n" ); while( 1 ) { gotsigs = IExec->Wait( rp_msgsig | rp_ahisig | SIGBREAKF_CTRL_C ); if( gotsigs & rp_msgsig ) { while( ( msg = (struct rp_command *)IExec->GetMsg( rp_msgport ) ) ) { // IExec->DebugPrintF( "Got command %ld, %ld, %08lX\n", msg->rpc_Command, msg->rpc_Data, msg->rpc_Tune ); switch( msg->rpc_Command ) { case RPC_STOP: if( need_wait[0] ) IExec->WaitIO( (struct IORequest *)ahi_io[0] ); if( need_wait[1] ) IExec->WaitIO( (struct IORequest *)ahi_io[1] ); need_wait[0] = need_wait[1] = FALSE; if( rp_tune ) { for( i=0; iat_Voices[i].vc_TrackOn = 0; rp_tune->at_Voices[i].vc_VUMeter = 0; } if( rp_tune->at_NextPosNr != -1 ) { rp_tune->at_PosNr = rp_tune->at_NextPosNr; rp_tune->at_NextPosNr = -1; IExec->Signal( rp_maintask, gui_tick_sig ); } rp_tune = NULL; } rp_state = STS_IDLE; break; case RPC_PLAY_NOTE: if( rp_tune != msg->rpc_Tune ) { if( need_wait[0] ) IExec->WaitIO( (struct IORequest *)ahi_io[0] ); if( need_wait[1] ) IExec->WaitIO( (struct IORequest *)ahi_io[1] ); need_wait[0] = need_wait[1] = FALSE; rp_tune = msg->rpc_Tune; } if( rp_tune != NULL ) { rp_play_instrument( rp_tune, msg->rpc_Data2>>8, msg->rpc_Data, msg->rpc_Data2&0xff ); gotsigs |= rp_ahisig; rp_state = STS_PLAYNOTE; } break; case RPC_PLAY_ROW: if( msg->rpc_Tune != NULL ) { struct ahx_tune *ttune; ttune = rp_tune; rp_tune = msg->rpc_Tune; rp_tune->at_stopnextrow = 3; rp_tune->at_GetNewPosition = 1; rp_tune->at_ticks = rp_tune->at_secs = rp_tune->at_mins = rp_tune->at_hours = 0; if( ( rp_tune != ttune ) || ( rp_state != STS_PLAYROW ) ) rp_reset_some_shit( rp_tune ); gotsigs |= rp_ahisig; rp_state = STS_PLAYPOS; } break; case RPC_CONT_POS: case RPC_PLAY_POS: if( rp_tune ) { if( rp_tune->at_doing == D_PLAYING ) rp_tune->at_doing = D_IDLE; } rp_tune = msg->rpc_Tune; if( rp_tune != NULL ) { rp_tune->at_stopnextrow = 0; if( msg->rpc_Command == RPC_PLAY_POS ) rp_tune->at_NoteNr = 0; rp_tune->at_PosJumpNote = 0; rp_tune->at_StepWaitFrames = 0; rp_tune->at_GetNewPosition = 1; rp_tune->at_ticks = rp_tune->at_secs = rp_tune->at_mins = rp_tune->at_hours = 0; rp_reset_some_shit( rp_tune ); gotsigs |= rp_ahisig; rp_state = STS_PLAYPOS; } break; case RPC_CONT_SONG: case RPC_PLAY_SONG: if( rp_tune ) { if( rp_tune->at_doing == D_PLAYING ) rp_tune->at_doing = D_IDLE; } rp_tune = msg->rpc_Tune; if( rp_tune != NULL ) { rp_tune->at_stopnextrow = 0; if( msg->rpc_Command == RPC_PLAY_SONG ) rp_tune->at_NoteNr = 0; rp_tune->at_PosJumpNote = 0; rp_tune->at_StepWaitFrames = 0; rp_tune->at_GetNewPosition = 1; rp_tune->at_ticks = rp_tune->at_secs = rp_tune->at_mins = rp_tune->at_hours = 0; rp_reset_some_shit( rp_tune ); } if( rp_tune ) { rp_state = STS_PLAYSONG; gotsigs |= rp_ahisig; } else { rp_state = STS_IDLE; } msg->rpc_Tune = rp_tune; break; } IExec->ReplyMsg( (struct Message *)msg ); } } if( gotsigs & SIGBREAKF_CTRL_C ) break; if( gotsigs & rp_ahisig ) { BOOL anychan; switch( rp_state ) { case STS_CALCULATING: case STS_IDLE: prev_req = NULL; break; case STS_PLAYNOTE: case STS_PLAYROW: if( rp_tune == NULL ) { rp_state = STS_IDLE; break; } anychan = FALSE; for( i=0; iat_Channels; i++ ) if( rp_tune->at_Voices[i].vc_TrackOn ) { anychan = TRUE; break; } if( anychan == FALSE ) { rp_state = STS_IDLE; break; } rp_mix_and_play_sounds( rp_tune, &need_wait[0], &prev_req, &rp_nextbuf ); break; case STS_PLAYPOS: case STS_PLAYSONG: if( rp_tune == NULL ) { rp_state = STS_IDLE; break; } rp_mix_and_play_sounds( rp_tune, &need_wait[0], &prev_req, &rp_nextbuf ); break; default: // Unknown state?! rp_state = STS_IDLE; } } } } rp_subtask_shut(); rp_state = STS_DEADED; IExec->Signal( rp_maintask, (1L<FindTask( NULL ); rp_subtask_sig = IExec->AllocSignal( -1 ); if( rp_subtask_sig == -1 ) { printf( "Unable to allocate signal\n" ); return FALSE; } rp_mempool = IExec->AllocSysObjectTags( ASOT_MEMPOOL, ASOPOOL_Puddle, 8192, ASOPOOL_Threshold, 8000, ASOPOOL_Protected, TRUE, TAG_DONE ); if( !rp_mempool ) { printf( "Unable to create memory pool\n" ); return FALSE; } #endif rp_tunelist = IExec->AllocSysObjectTags( ASOT_LIST, TAG_DONE ); if( !rp_tunelist ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return FALSE; } rp_list_ss = IExec->AllocSysObjectTags( ASOT_SEMAPHORE, TAG_DONE ); if( !rp_list_ss ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return FALSE; } #ifndef __SDL_WRAPPER__ rp_replyport = IExec->AllocSysObject(ASOT_PORT, TAG_DONE); if( !rp_replyport ) { printf( "Unable to create a message port\n" ); return FALSE; } rp_mainmsg = IExec->AllocSysObjectTags( ASOT_MESSAGE, ASOMSG_Size, sizeof( struct rp_command ), ASOMSG_ReplyPort, rp_replyport, TAG_DONE ); #else rp_mainmsg = malloc(sizeof(struct rp_command)); #endif if( !rp_mainmsg ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return FALSE; } rp_GenPanningTables(); rp_GenSawtooth( &waves[WO_SAWTOOTH_04], 0x04 ); rp_GenSawtooth( &waves[WO_SAWTOOTH_08], 0x08 ); rp_GenSawtooth( &waves[WO_SAWTOOTH_10], 0x10 ); rp_GenSawtooth( &waves[WO_SAWTOOTH_20], 0x20 ); rp_GenSawtooth( &waves[WO_SAWTOOTH_40], 0x40 ); rp_GenSawtooth( &waves[WO_SAWTOOTH_80], 0x80 ); rp_GenTriangle( &waves[WO_TRIANGLE_04], 0x04 ); rp_GenTriangle( &waves[WO_TRIANGLE_08], 0x08 ); rp_GenTriangle( &waves[WO_TRIANGLE_10], 0x10 ); rp_GenTriangle( &waves[WO_TRIANGLE_20], 0x20 ); rp_GenTriangle( &waves[WO_TRIANGLE_40], 0x40 ); rp_GenTriangle( &waves[WO_TRIANGLE_80], 0x80 ); rp_GenSquare( &waves[WO_SQUARES] ); rp_GenWhiteNoise( &waves[WO_WHITENOISE], WHITENOISELEN ); rp_GenFilterWaves( &waves[WO_TRIANGLE_04], &waves[WO_LOWPASSES], &waves[WO_HIGHPASSES] ); #ifndef __SDL_WRAPPER__ rp_subtask = IDOS->CreateNewProcTags( NP_Name, "ahx2006 Player Task", NP_Entry, rp_subtask_main, NP_Priority, 30, NP_Child, TRUE, TAG_DONE ); if( rp_subtask == NULL ) { printf( "Unable to create audio task\n" ); return FALSE; } IExec->Wait( (1L<mp_SigBit)); #endif rp_new_tune( TRUE ); #ifdef __SDL_WRAPPER__ wanted.freq = FREQ; wanted.format = AUDIO_S16SYS; wanted.channels = 2; /* 1 = mono, 2 = stereo */ wanted.samples = OUTPUT_LEN; // HIVELY_LEN; wanted.callback = (void*)do_the_music; wanted.userdata = 0; if( SDL_OpenAudio( &wanted, NULL ) < 0 ) { printf( "SDL says: %s\n", SDL_GetError() ); return FALSE; } SDL_PauseAudio(0); #endif return TRUE; } void rp_shutdown( void ) { if( rp_tunelist ) { rp_free_all_tunes(); IExec->FreeSysObject( ASOT_LIST, rp_tunelist ); } if( rp_list_ss ) IExec->FreeSysObject( ASOT_SEMAPHORE, rp_list_ss ); #ifndef __SDL_WRAPPER__ if( rp_subtask ) { IExec->Signal( (struct Task *)rp_subtask, SIGBREAKF_CTRL_C ); IExec->Wait( (1L<FreeSysObject( ASOT_MESSAGE, rp_mainmsg ); if( rp_replyport ) IExec->FreeSysObject( ASOT_PORT, rp_replyport ); if( rp_mempool ) IExec->FreeSysObject( ASOT_MEMPOOL, rp_mempool ); if( rp_subtask_sig != -1 ) IExec->FreeSignal( rp_subtask_sig ); #else SDL_PauseAudio(1); if( rp_mainmsg ) free(rp_mainmsg); #endif } #ifndef __SDL_WRAPPER__ void rp_handler( uint32 gotsigs ) { // audio subtask has quit! if( gotsigs & (1L<mp_SigBit)|(1L<PutMsg( rp_msgport, (struct Message *)rp_mainmsg ); gsigs = IExec->Wait( wsigs ); if( gsigs & (1L<mp_SigBit)) IExec->GetMsg( rp_replyport ); if( gsigs & (1L<rpc_Command) { case RPC_CONT_POS: case RPC_PLAY_POS: case RPC_CONT_SONG: case RPC_PLAY_SONG: set_player_state(STS_IDLE); if( rp_playtune ) { if( rp_playtune->at_doing == D_PLAYING ) rp_playtune->at_doing = D_IDLE; } rp_playtune = cmd->rpc_Tune; if( rp_playtune != NULL ) { rp_playtune->at_stopnextrow = 0; if( cmd->rpc_Command == RPC_PLAY_SONG ) rp_playtune->at_NoteNr = 0; rp_playtune->at_PosJumpNote = 0; rp_playtune->at_StepWaitFrames = 0; rp_playtune->at_GetNewPosition = 1; rp_playtune->at_ticks = rp_playtune->at_secs = rp_playtune->at_mins = rp_playtune->at_hours = 0; rp_reset_some_shit( rp_playtune ); } break; case RPC_PLAY_ROW: if( cmd->rpc_Tune != NULL ) { if (cmd->rpc_Tune != rp_playtune) set_player_state(STS_IDLE); ; cmd->rpc_Tune->at_stopnextrow = 3; cmd->rpc_Tune->at_GetNewPosition = 1; cmd->rpc_Tune->at_ticks = cmd->rpc_Tune->at_secs = cmd->rpc_Tune->at_mins = cmd->rpc_Tune->at_hours = 0; if( ( rp_playtune != cmd->rpc_Tune ) || ( rp_state != STS_PLAYROW ) ) { rp_reset_some_shit( cmd->rpc_Tune ); rp_playtune = cmd->rpc_Tune; } rp_state = STS_PLAYPOS; } break; case RPC_PLAY_NOTE: if( rp_playtune != cmd->rpc_Tune ) { set_player_state(STS_IDLE); rp_playtune = cmd->rpc_Tune; } if( rp_playtune != NULL ) { rp_play_instrument( rp_playtune, cmd->rpc_Data2>>8, cmd->rpc_Data, cmd->rpc_Data2&0xff ); rp_state = STS_PLAYNOTE; } break; case RPC_STOP: set_player_state(STS_IDLE); if( rp_playtune ) { for( i=0; iat_Voices[i].vc_TrackOn = 0; rp_playtune->at_Voices[i].vc_VUMeter = 0; } if( rp_playtune->at_NextPosNr != -1 ) { SDL_Event event; SDL_UserEvent userevent; rp_playtune->at_PosNr = rp_playtune->at_NextPosNr; rp_playtune->at_NextPosNr = -1; userevent.type = SDL_USEREVENT; userevent.code = 0; userevent.data1 = NULL; userevent.data2 = NULL; event.type = SDL_USEREVENT; event.user = userevent; SDL_PushEvent( &event ); } rp_playtune = NULL; } break; } switch (cmd->rpc_Command) { case RPC_CONT_POS: case RPC_PLAY_POS: rp_state = STS_PLAYPOS; break; case RPC_CONT_SONG: case RPC_PLAY_SONG: if( rp_playtune ) { rp_state = STS_PLAYSONG; } else { rp_state = STS_IDLE; } cmd->rpc_Tune = rp_playtune; rp_curtune = rp_playtune; break; } #endif } BOOL rp_play_song( struct ahx_tune *at, uint32 subsong, BOOL cont ) { rp_mainmsg->rpc_Command = cont ? RPC_CONT_SONG : RPC_PLAY_SONG; rp_mainmsg->rpc_Tune = at; rp_mainmsg->rpc_Data = subsong; rp_mainmsg->rpc_Data2 = 0; rp_send_command( rp_mainmsg ); return (rp_mainmsg->rpc_Tune == at); } BOOL rp_play_pos( struct ahx_tune *at, BOOL cont ) { rp_mainmsg->rpc_Command = cont ? RPC_CONT_POS : RPC_PLAY_POS; rp_mainmsg->rpc_Tune = at; rp_mainmsg->rpc_Data = 0; rp_mainmsg->rpc_Data2 = 0; rp_send_command( rp_mainmsg ); return (rp_mainmsg->rpc_Tune == at); } BOOL rp_play_row( struct ahx_tune *at ) { rp_mainmsg->rpc_Command = RPC_PLAY_ROW; rp_mainmsg->rpc_Tune = at; rp_mainmsg->rpc_Data = 0; rp_mainmsg->rpc_Data2 = 0; rp_send_command( rp_mainmsg ); return (rp_mainmsg->rpc_Tune == at); } BOOL rp_play_note( struct ahx_tune *at, int32 inst, int32 note, int32 channel ) { if( ( note < 1 ) || ( note > 60 ) ) return TRUE; rp_mainmsg->rpc_Command = RPC_PLAY_NOTE; rp_mainmsg->rpc_Tune = at; rp_mainmsg->rpc_Data = inst; rp_mainmsg->rpc_Data2 = (channel<<8)|note; rp_send_command( rp_mainmsg ); return (rp_mainmsg->rpc_Tune == at); } void rp_stop( void ) { rp_mainmsg->rpc_Command = RPC_STOP; rp_mainmsg->rpc_Tune = NULL; rp_mainmsg->rpc_Data = 0; rp_mainmsg->rpc_Data2 = 0; rp_send_command( rp_mainmsg ); #ifdef __SDL_WRAPPER__ memset(rp_audiobuffer, 0, sizeof(rp_audiobuffer)); #endif } void rp_zap_tracks( struct ahx_tune * at ) { uint32 i, j; if( !at ) return; if( at == rp_curtune ) rp_stop(); for( i=0; i<256; i++ ) for( j=0; j<64; j++ ) { at->at_Tracks[i][j].stp_Note = 0; at->at_Tracks[i][j].stp_Instrument = 0; at->at_Tracks[i][j].stp_FX = 0; at->at_Tracks[i][j].stp_FXParam = 0; at->at_Tracks[i][j].stp_FXb = 0; at->at_Tracks[i][j].stp_FXbParam = 0; } } void rp_zap_positions( struct ahx_tune *at ) { uint32 i, j; if( !at ) return; if( at == rp_curtune ) rp_stop(); for( i=0; i<1000; i++ ) { for( j=0; jat_Positions[i].pos_Track[j] = 0; at->at_Positions[i].pos_Transpose[j] = 0; } } for( i=0; i<256; i++ ) at->at_Subsongs[i] = 0; at->at_SubsongNr = 0; at->at_curss = 0; at->at_PosNr = 0; at->at_Restart = 0; } void rp_zap_instruments( struct ahx_tune *at ) { uint32 i; if( !at ) return; if( at == rp_curtune ) rp_stop(); for( i=0; i<64; i++ ) rp_clear_instrument( &at->at_Instruments[i] ); } hivelytracker-0+git20180223/undo.h0000775001024000102400000001117413042730153013521 0ustar enum { UNT_POSITIONNR = 0, UNT_RESTART, UNT_TRACKLEN, UNT_SUBSONGS, UNT_SSPOS, UNT_CHANNELS, UNT_MIXGAIN, UNT_SPMUL, UNT_ENV_AFRAMES, UNT_ENV_AVOLUME, UNT_ENV_DFRAMES, UNT_ENV_DVOLUME, UNT_ENV_SFRAMES, UNT_ENV_RFRAMES, UNT_ENV_RVOLUME, UNT_PLE, UNT_PLE_NOTE, UNT_PLE_WAVEFORM, UNT_PLE_FIXED, UNT_PLE_FX0, UNT_PLE_FX1, UNT_PLE_FXPARAM0, UNT_PLE_FXPARAM1, UNT_PLS_SPEED, UNT_PLS_LENGTH, UNT_INS_VOLUME, UNT_INS_WAVELENGTH, UNT_INS_FILTERLOWERLIMIT, UNT_INS_FILTERUPPERLIMIT, UNT_INS_FILTERSPEED, UNT_INS_SQUARELOWERLIMIT, UNT_INS_SQUAREUPPERLIMIT, UNT_INS_SQUARESPEED, UNT_INS_VIBRATODELAY, UNT_INS_VIBRATOSPEED, UNT_INS_VIBRATODEPTH, UNT_INS_HARDCUTRELEASE, UNT_INS_HARDCUTRELEASEFRAMES, UNT_TRACK, UNT_STP_NOTE, UNT_STP_INSTRUMENT, UNT_STP_NOTEANDINS, UNT_STP_FX, UNT_STP_FXPARAM, UNT_STP_FXANDPARAM, UNT_STP_FXB, UNT_STP_FXBPARAM, UNT_STP_FXBANDPARAM, UNT_POS_TRACK, UNT_POS_TRANS, UNT_POS_REGION, // Strings are treated differently UNT_STRING_SONGNAME, UNT_STRING_INSNAME, UNT_STRING_INSNAME2 }; struct undonode { struct Node un_ln; uint32 un_size; uint16 un_type; void *un_data; }; struct udat_env_w { struct ahx_envelope *ptr; int32 insnum; int16 before; int16 after; }; struct udat_ple_w { struct ahx_plsentry *ptr; struct ahx_instrument *pins; int32 insnum; int32 pcurx, pcury, ptop; int16 before; int16 after; }; struct udat_ple_b { struct ahx_plsentry *ptr; struct ahx_instrument *pins; int32 insnum; int32 pcurx, pcury, ptop; int8 before; int8 after; }; struct udat_pls_w { struct ahx_plist *ptr; int32 insnum; int16 before; int16 after; }; struct udat_ins_b { struct ahx_instrument *ptr; int32 insnum; uint8 before; uint8 after; }; struct udat_stp_b { struct ahx_step *ptr; uint16 posnr; uint8 notenr; int32 tracked_curs; int32 curlch; uint8 before; uint8 after; }; struct udat_stp_w { struct ahx_step *ptr; uint16 posnr; uint8 notenr; int32 tracked_curs; int32 curlch; uint16 before; uint16 after; }; struct udat_track { int32 track; uint16 posnr; uint8 notenr; int32 tracked_curs; int32 curlch; struct ahx_step before[64]; struct ahx_step after[64]; }; struct udat_pos_b { struct ahx_position *ptr; int32 chan; uint16 posnr; int32 posed_curs; int32 curlch; uint8 before; uint8 after; }; struct udat_pos_region { uint16 posnr; int32 posed_curs; int32 curlch; int32 left, pos; int32 chans, rows; uint8 *before; uint8 *after; }; struct udat_whole_plist { struct ahx_plsentry *ptr; int32 insnum; struct ahx_plsentry before[256]; struct ahx_plsentry after[256]; }; struct udat_string { TEXT *ptr; int32 extra; TEXT *before; TEXT *after; }; struct udat_tune_b { uint8 before; uint8 after; }; struct udat_tune_w { int16 before; int16 after; }; struct udat_subsongpos { int32 subsong; uint16 before; uint16 after; }; void setbefore_posregion( struct ahx_tune *at, int32 left, int32 pos, int32 chans, int32 rows ); void setafter_posregion( struct ahx_tune *at, int32 left, int32 pos, int32 chans, int32 rows ); void setbefore_track( struct ahx_tune *at, int32 trk ); void setafter_track( struct ahx_tune *at, int32 trk ); void setbefore_plist( struct ahx_tune *at, struct ahx_plsentry *ptr ); void setafter_plist( struct ahx_tune *at, struct ahx_plsentry *ptr ); void setbefore_string( struct ahx_tune *at, TEXT *ptr ); void setafter_string( int32 which, struct ahx_tune *at, TEXT *ptr ); void modify_env_w( struct ahx_tune *at, struct ahx_envelope *ptr, uint32 field, int16 new ); void modify_ins_b( struct ahx_tune *at, struct ahx_instrument *ptr, uint32 field, uint8 new ); void modify_ple_b( struct ahx_tune *at, struct ahx_instrument *ins, struct ahx_plsentry *ptr, uint32 field, int8 new ); void modify_ple_w( struct ahx_tune *at, struct ahx_instrument *ins, struct ahx_plsentry *ptr, uint32 field, int16 new ); void modify_pls_w( struct ahx_tune *at, struct ahx_plist *ptr, uint32 field, int16 new ); void modify_stp_b( struct ahx_tune *at, struct ahx_step *ptr, uint32 field, int8 new ); void modify_stp_w( struct ahx_tune *at, struct ahx_step *ptr, uint32 field, int16 new ); void modify_pos_b( struct ahx_tune *at, struct ahx_position *ptr, int32 chan, uint32 field, int8 new ); void modify_tune_b( struct ahx_tune *at, uint32 field, int8 new ); void modify_tune_w( struct ahx_tune *at, uint32 field, int16 new ); void modify_sspos( struct ahx_tune *at, int16 new ); void undo( struct ahx_tune *at ); void redo( struct ahx_tune *at ); void free_undolists( struct ahx_tune *at ); hivelytracker-0+git20180223/util.h0000775001024000102400000000024113042730153013522 0ustar #ifndef __SDL_WRAPPER__ BOOL getLibIFace( struct Library **libbase, TEXT *libname, uint32 version, void *ifaceptr ); #endif struct Node *allocnode(int32 size); hivelytracker-0+git20180223/Replayer_Amiga/0000775001024000102400000000000013042730153015255 5ustar hivelytracker-0+git20180223/Replayer_Amiga/play_hvl.c0000775001024000102400000001122513042730153017243 0ustar #include #include #include #include #include #include #include #include #include #include "hvl_replay.h" uint32 audiobuflen; int8 *audiobuffer[2] = { NULL, NULL }; // Libraries and interfaces struct Library *AHIBase = NULL; struct AHIIFace *IAHI = NULL; // AHI stuff struct MsgPort *ahi_mp; struct AHIRequest *ahi_io[2] = { NULL, NULL }; int32 ahi_dev = -1; uint32 ahisig = 0; struct AHIRequest *prev_req = NULL; BOOL need_wait[2]; uint32 nextbuf; #define FREQ 48000 BOOL init( void ) { uint32 i; audiobuflen = FREQ * sizeof( uint16 ) * 2 / 50; audiobuffer[0] = IExec->AllocVec( audiobuflen * 2, MEMF_ANY ); if( audiobuffer[0] == NULL ) { printf( "Out of memory!\n" ); return FALSE; } audiobuffer[1] = &audiobuffer[0][audiobuflen]; for( i=0; iCreateMsgPort(); if( !ahi_mp ) { printf( "Unable to create message port!\n" ); return FALSE; } ahi_io[0] = (struct AHIRequest *)IExec->CreateIORequest( ahi_mp, sizeof( struct AHIRequest ) ); if( ahi_io[0] == NULL ) return FALSE; ahi_io[0]->ahir_Version = 4; ahi_dev = IExec->OpenDevice( AHINAME, AHI_DEFAULT_UNIT, (struct IORequest *)ahi_io[0], 0 ); if( ahi_dev == -1 ) return FALSE; AHIBase = (struct Library *)ahi_io[0]->ahir_Std.io_Device; IAHI = (struct AHIIFace *)IExec->GetInterface( AHIBase, "main", 1, NULL ); if( !IAHI ) return FALSE; ahi_io[1] = IExec->AllocVec( sizeof( struct AHIRequest ), MEMF_ANY ); if( ahi_io[1] == NULL ) return FALSE; IExec->CopyMem( ahi_io[0], ahi_io[1], sizeof( struct AHIRequest ) ); ahisig = 1L<mp_SigBit; return TRUE; } void shut( void ) { if( ahi_io[1] ) IExec->FreeVec( ahi_io[1] ); if( IAHI ) IExec->DropInterface( (struct Interface *)IAHI ); if( ahi_dev != -1 ) { IExec->CloseDevice( (struct IORequest *)ahi_io[0] ); IExec->DeleteIORequest( (struct IORequest *)ahi_io[0] ); } if( ahi_mp ) IExec->DeleteMsgPort( ahi_mp ); if( audiobuffer[0] ) IExec->FreeVec( audiobuffer[0] ); } void mix_and_play( struct hvl_tune *ht ) { if( need_wait[nextbuf] ) IExec->WaitIO( (struct IORequest *)ahi_io[nextbuf] ); hvl_DecodeFrame( ht, audiobuffer[nextbuf], audiobuffer[nextbuf]+sizeof( int16 ), 4 ); ahi_io[nextbuf]->ahir_Std.io_Command = CMD_WRITE; ahi_io[nextbuf]->ahir_Std.io_Data = audiobuffer[nextbuf]; ahi_io[nextbuf]->ahir_Std.io_Length = audiobuflen; ahi_io[nextbuf]->ahir_Std.io_Offset = 0; ahi_io[nextbuf]->ahir_Type = AHIST_S16S; ahi_io[nextbuf]->ahir_Frequency = FREQ; ahi_io[nextbuf]->ahir_Volume = 0x10000; ahi_io[nextbuf]->ahir_Position = 0x8000; ahi_io[nextbuf]->ahir_Link = prev_req; IExec->SendIO( (struct IORequest *)ahi_io[nextbuf] ); prev_req = ahi_io[nextbuf]; need_wait[nextbuf] = TRUE; nextbuf ^= 1; if( ( need_wait[nextbuf] == TRUE ) && ( IExec->CheckIO( (struct IORequest *)ahi_io[nextbuf] ) != NULL ) ) { IExec->WaitIO( (struct IORequest *)ahi_io[nextbuf] ); need_wait[nextbuf] = FALSE; } if( need_wait[nextbuf] == FALSE ) { hvl_DecodeFrame( ht, audiobuffer[nextbuf], audiobuffer[nextbuf]+sizeof( int16 ), 4 ); ahi_io[nextbuf]->ahir_Std.io_Command = CMD_WRITE; ahi_io[nextbuf]->ahir_Std.io_Data = audiobuffer[nextbuf]; ahi_io[nextbuf]->ahir_Std.io_Length = audiobuflen; ahi_io[nextbuf]->ahir_Std.io_Offset = 0; ahi_io[nextbuf]->ahir_Type = AHIST_S16S; ahi_io[nextbuf]->ahir_Frequency = FREQ; ahi_io[nextbuf]->ahir_Volume = 0x10000; ahi_io[nextbuf]->ahir_Position = 0x8000; ahi_io[nextbuf]->ahir_Link = prev_req; IExec->SendIO( (struct IORequest *)ahi_io[nextbuf] ); prev_req = ahi_io[nextbuf]; need_wait[nextbuf] = TRUE; nextbuf ^= 1; } } int main( int argc, char *argv[] ) { struct hvl_tune *tune; if( argc < 2 ) { printf( "Usage: play_hvl \n" ); return 0; } if( init() ) { hvl_InitReplayer(); tune = hvl_LoadTune( argv[1], FREQ, 2 ); if( tune ) { BOOL done; uint32 gotsigs; hvl_InitSubsong( tune, 0 ); nextbuf = 0; mix_and_play( tune ); done = FALSE; while( !done ) { gotsigs = IExec->Wait( ahisig | SIGBREAKF_CTRL_C ); if( gotsigs & ahisig ) mix_and_play( tune ); if( gotsigs & SIGBREAKF_CTRL_C ) done = TRUE; } hvl_FreeTune( tune ); } } shut(); return 0; } hivelytracker-0+git20180223/Replayer_Amiga/README.TXT0000775001024000102400000000227613042730153016625 0ustar This replayer was basically ripped out of HivelyTracker and bodged into a standalone replayer. Its not necessarily optimal, i haven't had a chance to comment anything. There are no documents for the HVL module format, I just knocked it together as I wrote hivelytracker. This replayer is the documentation. Functions: void hvl_InitReplayer( void ) This calculates all waves and stuff. Call it before any other replayer funcs. struct hvl_tune *hvl_LoadTune( TEXT *name, uint32 freq, uint32 defstereo ) Specify the filename and mixing frequency, and it will load the tune into RAM, and return a handle. W00t! defstereo is the stereo seperation for playing AHX tunes (HVL tunes override this setting and ignore it). It can be: 0 = 0% (mono) 1 = 25% 2 = 50% 3 = 75% 4 = 100% (paula) void hvl_FreeTune( struct hvl_tune *ht ) Frees the tune again. BOOL hvl_InitSubsong( struct hvl_tune *ht, uint32 subsong ) Initialises a subsong ready for playing void hvl_DecodeFrame( struct hvl_tune *ht, int8 *buf1, int8 *buf2, int32 bufmod ) Decodes a frame of HVL music. Works the same as the ahx replayer library on OS4Depot. Enjoy. Peter Gordon (pete@petergordon.org.uk) hivelytracker-0+git20180223/Replayer_Amiga/hvl_tables.h0000664001024000102400000004404413042730153017557 0ustar #define WHITENOISELEN (0x280*3) #define WO_LOWPASSES 0 #define WO_TRIANGLE_04 (WO_LOWPASSES+((0xfc+0xfc+0x80*0x1f+0x80+3*0x280)*31)) #define WO_TRIANGLE_08 (WO_TRIANGLE_04+0x04) #define WO_TRIANGLE_10 (WO_TRIANGLE_08+0x08) #define WO_TRIANGLE_20 (WO_TRIANGLE_10+0x10) #define WO_TRIANGLE_40 (WO_TRIANGLE_20+0x20) #define WO_TRIANGLE_80 (WO_TRIANGLE_40+0x40) #define WO_SAWTOOTH_04 (WO_TRIANGLE_80+0x80) #define WO_SAWTOOTH_08 (WO_SAWTOOTH_04+0x04) #define WO_SAWTOOTH_10 (WO_SAWTOOTH_08+0x08) #define WO_SAWTOOTH_20 (WO_SAWTOOTH_10+0x10) #define WO_SAWTOOTH_40 (WO_SAWTOOTH_20+0x20) #define WO_SAWTOOTH_80 (WO_SAWTOOTH_40+0x40) #define WO_SQUARES (WO_SAWTOOTH_80+0x80) #define WO_WHITENOISE (WO_SQUARES+(0x80*0x20)) #define WO_HIGHPASSES (WO_WHITENOISE+WHITENOISELEN) #define WAVES_SIZE (WO_HIGHPASSES+((0xfc+0xfc+0x80*0x1f+0x80+3*0x280)*31)) static const uint16 lentab[45] = { 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, (0x280*3)-1 }; static const int16 vib_tab[] = { 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, 0,-24,-49,-74,-97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253,-255, -253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120,-97,-74,-49,-24 }; static const uint16 period_tab[] = { 0x0000, 0x0D60, 0x0CA0, 0x0BE8, 0x0B40, 0x0A98, 0x0A00, 0x0970, 0x08E8, 0x0868, 0x07F0, 0x0780, 0x0714, 0x06B0, 0x0650, 0x05F4, 0x05A0, 0x054C, 0x0500, 0x04B8, 0x0474, 0x0434, 0x03F8, 0x03C0, 0x038A, 0x0358, 0x0328, 0x02FA, 0x02D0, 0x02A6, 0x0280, 0x025C, 0x023A, 0x021A, 0x01FC, 0x01E0, 0x01C5, 0x01AC, 0x0194, 0x017D, 0x0168, 0x0153, 0x0140, 0x012E, 0x011D, 0x010D, 0x00FE, 0x00F0, 0x00E2, 0x00D6, 0x00CA, 0x00BE, 0x00B4, 0x00AA, 0x00A0, 0x0097, 0x008F, 0x0087, 0x007F, 0x0078, 0x0071 }; static const int32 stereopan_left[] = { 128, 96, 64, 32, 0 }; static const int32 stereopan_right[] = { 128, 160, 193, 225, 255 }; static const int16 filter_thing[] = { -1161, -4413, -7161, -13094, 635, 13255, 2189, 6401, 9041, 16130, 13460, 5360, 6349, 12699, 19049, 25398, 30464, 32512, 32512, 32515, 31625, 29756, 27158, 24060, 20667, 17156, 13970, 11375, 9263, 7543, 6142, 5002, 4074, 3318, 2702, 2178, 1755, 1415, 1141, 909, 716, 563, 444, 331, -665, -2082, -6170, -9235, -13622, 12545, 9617, 3951, 8345, 11246, 18486, 6917, 3848, 8635, 17271, 25907, 32163, 32512, 32455, 30734, 27424, 23137, 18397, 13869, 10429, 7843, 5897, 4435, 3335, 2507, 1885, 1389, 1023, 720, 530, 353, 260, 173, 96, 32, -18, -55, -79, -92, -95, -838, -3229, -7298, -12386, -7107, 13946, 6501, 5970, 9133, 14947, 16881, 6081, 3048, 10921, 21843, 31371, 32512, 32068, 28864, 23686, 17672, 12233, 8469, 5862, 4058, 2809, 1944, 1346, 900, 601, 371, 223, 137, 64, 7, -34, -58, -69, -70, -63, -52, -39, -26, -14, -5, 4984, -4476, -8102, -14892, 2894, 12723, 4883, 8010, 9750, 17887, 11790, 5099, 2520, 13207, 26415, 32512, 32457, 28690, 22093, 14665, 9312, 5913, 3754, 2384, 1513, 911, 548, 330, 143, 3, -86, -130, -139, -125, -97, -65, -35, -11, 6, 15, 19, 19, 16, 12, 8, 6877, -5755, -9129, -15709, 9705, 10893, 4157, 9882, 10897, 19236, 8153, 4285, 2149, 15493, 30618, 32512, 30220, 22942, 14203, 8241, 4781, 2774, 1609, 933, 501, 220, 81, 35, 2, -18, -26, -25, -20, -13, -7, -1, 2, 4, 4, 3, 2, 1, 0, 0, -1, 2431, -6956, -10698, -14594, 12720, 8980, 3714, 10892, 12622, 19554, 6915, 3745, 1872, 17779, 32512, 32622, 26286, 16302, 8605, 4542, 2397, 1265, 599, 283, 45, -92, -141, -131, -93, -49, -14, 8, 18, 18, 14, 8, 3, 0, -2, -3, -2, -2, -1, 0, 0, -3654, -8008, -12743, -11088, 13625, 7342, 3330, 11330, 14859, 18769, 6484, 3319, 1660, 20065, 32512, 30699, 21108, 10616, 5075, 2425, 1159, 477, 196, 1, -93, -109, -82, -44, -12, 7, 14, 13, 9, 4, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, -7765, -8867, -14957, -5862, 13550, 6139, 2988, 11284, 17054, 16602, 6017, 2979, 1489, 22351, 32512, 28083, 15576, 6708, 2888, 1243, 535, 188, 32, -47, -64, -47, -22, -3, 7, 8, 5, 3, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9079, -9532, -16960, -335, 13001, 5333, 2704, 11192, 18742, 13697, 5457, 2703, 1351, 24637, 32512, 24556, 10851, 4185, 1614, 622, 184, 15, -57, -59, -34, -9, 5, 8, 6, 2, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -8576, -10043, -18551, 4372, 12190, 4809, 2472, 11230, 19803, 11170, 4953, 2473, 1236, 26923, 32512, 20567, 7430, 2550, 875, 212, 51, -30, -43, -25, -6, 3, 5, 3, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6960, -10485, -19740, 7864, 11223, 4449, 2279, 11623, 20380, 9488, 4553, 2280, 1140, 29209, 31829, 16235, 4924, 1493, 452, 86, -7, -32, -20, -5, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4739, -10974, -19831, 10240, 10190, 4169, 2114, 12524, 20649, 8531, 4226, 2114, 1057, 31495, 29672, 11916, 3168, 841, 121, 17, -22, -18, -5, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2333, -11641, -19288, 11765, 9175, 3923, 1971, 13889, 20646, 8007, 3942, 1971, 985, 32512, 27426, 8446, 1949, 449, 45, -11, -16, -5, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, -12616, -17971, 12690, 8247, 3693, 1846, 15662, 20271, 7658, 3692, 1846, 923, 32512, 25132, 6284, 1245, 246, -71, -78, -17, 8, 7, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2232, -14001, -15234, 13198, 7447, 3478, 1736, 17409, 19411, 7332, 3472, 1736, 868, 32512, 22545, 4352, 731, 18, -117, -40, 8, 9, 2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4197, -15836, -11480, 13408, 6791, 3281, 1639, 19224, 18074, 6978, 3276, 1639, 819, 32512, 19657, 2706, 380, -148, -86, 2, 13, 3, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5863, -17878, -9460, 13389, 6270, 3104, 1551, 20996, 16431, 6616, 3102, 1551, 776, 32512, 16633, 1921, 221, -95, -39, 5, 5, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7180, -20270, -6194, 13181, 5866, 2946, 1473, 22548, 14746, 6273, 2946, 1473, 737, 32512, 13621, 1263, 116, -53, -15, 4, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8117, -21129, -2795, 12809, 5550, 2804, 1402, 23717, 13326, 5962, 2804, 1402, 701, 32512, 10687, 776, -56, -56, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8560, -19953, 508, 12299, 5295, 2675, 1337, 25109, 12263, 5684, 2675, 1338, 669, 32512, 7905, 433, -36, -22, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8488, -18731, 3672, 11679, 5080, 2558, 1279, 26855, 11480, 5434, 2557, 1279, 639, 32512, 5357, 212, -95, 0, 4, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7977, -24055, 6537, 10986, 4883, 2450, 1225, 28611, 10918, 5206, 2450, 1225, 612, 32512, 3131, 83, -35, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7088, -30584, 9054, 10265, 4696, 2351, 1176, 28707, 10494, 4996, 2351, 1175, 588, 32512, 1920, -155, -13, 4, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5952, -32627, 11249, 9564, 4519, 2260, 1130, 28678, 10113, 4803, 2260, 1130, 565, 32512, 1059, -73, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4629, -32753, 13199, 8934, 4351, 2175, 1088, 28446, 9775, 4623, 2175, 1087, 544, 32512, 434, -22, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3132, -32768, 15225, 8430, 4194, 2097, 1049, 30732, 9439, 4456, 2097, 1049, 524, 32512, 75, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, -32768, 16765, 8107, 4048, 2025, 1012, 32512, 9112, 4302, 2025, 1012, 506, 32385, 392, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -706, -32768, 17879, 8005, 3913, 1956, 978, 32512, 8843, 4157, 1957, 978, 489, 31184, 1671, 122, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3050, -32768, 18923, 8163, 3799, 1893, 946, 32512, 8613, 4022, 1893, 945, 473, 29903, 3074, 316, 52, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5812, -32768, 19851, 8626, 3739, 1833, 917, 32512, 7982, 3889, 1833, 916, 459, 28541, 4567, 731, 206, 66, 23, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9235, -32768, 20587, 9408, 3841, 1784, 889, 32512, 6486, 3688, 1776, 889, 447, 27099, 6112, 1379, 313, 135, 65, 33, 17, 7, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -12713, 1188, 1318, -1178, -4304, -26320, -14931, -1716, -1486, 2494, 3611, 22275, 27450, -31839, -29668, -26258, -21608, -15880, -9560, -3211, 3138, 9369, 15281, 20717, 25571, 29774, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32748, 32600, 32750, 32566, 32659, 32730, 8886, 1762, 506, -1665, -12112, -24641, -8513, -2224, 247, 3288, 9926, 25787, 28909, -31048, -27034, -20726, -12532, -3896, 4733, 13043, 20568, 27010, 32215, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32762, 32696, 32647, 32512, 32665, 32512, 32587, 32638, 32669, 32681, 32679, 32667, 32648, 32624, 32598, 6183, 2141, -630, -2674, -21856, -18306, -5711, -2161, 2207, 4247, 17616, 26475, 29719, -30017, -23596, -13741, -2819, 8029, 18049, 26470, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32738, 32663, 32612, 32756, 32549, 32602, 32629, 32636, 32628, 32610, 32588, 32564, 32542, 32524, 32510, 32500, 32494, 32492, 3604, 2248, -1495, -5612, -26800, -13545, -4745, -1390, 3443, 6973, 23495, 27724, 30246, -28745, -19355, -6335, 6861, 19001, 28690, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32667, 32743, 32757, 32730, 32681, 32624, 32572, 32529, 32500, 32482, 32476, 32477, 32482, 32489, 32497, 32504, 32509, 32513, 7977, 1975, -1861, -9752, -25893, -10150, -4241, 86, 4190, 10643, 25235, 28481, 30618, -27231, -14398, 1096, 15982, 27872, 32512, 32512, 32512, 32512, 32512, 32734, 32631, 32767, 32531, 32553, 32557, 32551, 32539, 32527, 32516, 32509, 32505, 32504, 32505, 32506, 32508, 32510, 32511, 32512, 32512, 32512, 32511, 14529, 1389, -2028, -14813, -22765, -7845, -3774, 1986, 4706, 14562, 25541, 29019, 30894, -25476, -9294, 8516, 23979, 32512, 32512, 32512, 32512, 32512, 32512, 32708, 32762, 32727, 32654, 32579, 32522, 32490, 32478, 32480, 32488, 32498, 32507, 32512, 32515, 32515, 32514, 32513, 32512, 32510, 32510, 32510, 32510, 17663, 557, -2504, -19988, -19501, -6436, -3340, 4135, 5461, 18788, 26016, 29448, 31107, -23481, -4160, 15347, 30045, 32512, 32512, 32512, 32512, 32512, 32674, 32700, 32654, 32586, 32531, 32498, 32486, 32488, 32496, 32504, 32510, 32513, 32514, 32513, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 16286, -402, -3522, -23951, -16641, -5631, -2983, 6251, 6837, 22781, 26712, 29788, 31277, -21244, 1108, 21806, 32512, 32512, 32512, 32512, 32695, 32576, 32622, 32600, 32557, 32520, 32501, 32496, 32500, 32505, 32509, 32512, 32512, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 13436, -1351, -4793, -25948, -14224, -5151, -2702, 7687, 8805, 25705, 27348, 30064, 31415, -18766, 5872, 26652, 32512, 32512, 32512, 32747, 32581, 32620, 32586, 32540, 32508, 32497, 32499, 32505, 32510, 32512, 32512, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10427, -2162, -7136, -26147, -12195, -4810, -2474, 8723, 11098, 27251, 27832, 30293, 31530, -16047, 10877, 30990, 32512, 32512, 32512, 32512, 32584, 32571, 32536, 32511, 32502, 32503, 32507, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 7797, -2748, -10188, -25174, -10519, -4515, -2281, 9397, 13473, 27937, 28213, 30487, 31627, -13087, 15816, 32512, 32512, 32512, 32715, 32550, 32560, 32534, 32512, 32505, 32506, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 5840, -3084, -13327, -23617, -9177, -4231, -2116, 9892, 15843, 28292, 28538, 30652, 31710, -9886, 20235, 32512, 32512, 32512, 32512, 32550, 32534, 32514, 32507, 32507, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 4592, -3215, -15898, -21856, -8141, -3958, -1972, 10401, 18229, 28612, 28824, 30796, 31781, -7103, 24037, 32512, 32512, 32745, 32535, 32534, 32517, 32508, 32508, 32509, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 3964, -3262, -18721, -20087, -7368, -3705, -1847, 11014, 20634, 28996, 29075, 30920, 31843, -4732, 27243, 32512, 32512, 32648, 32627, 32530, 32495, 32500, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 3858, -3404, -21965, -18398, -6801, -3479, -1738, 12009, 22960, 29429, 29294, 31030, 31898, -2281, 30194, 32512, 32512, 32699, 32569, 32496, 32496, 32509, 32513, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 4177, -3869, -24180, -16820, -6380, -3280, -1640, 13235, 25035, 29863, 29490, 31128, 31947, 251, 32758, 32512, 32749, 32652, 32508, 32490, 32507, 32513, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 4837, -4913, -26436, -15364, -6056, -3103, -1553, 14759, 26704, 30256, 29664, 31215, 31991, 2863, 32512, 32512, 32657, 32580, 32503, 32501, 32510, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 5755, -6290, -27702, -14036, -5788, -2947, -1474, 16549, 27912, 30602, 29821, 31294, 32030, 5555, 32512, 32512, 32592, 32541, 32505, 32507, 32511, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 6898, -8911, -27788, -12841, -5550, -2805, -1403, 18509, 28687, 30906, 29963, 31364, 32066, 8328, 32512, 32512, 32623, 32511, 32502, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 8107, -11465, -27077, -11789, -5325, -2676, -1339, 19833, 29213, 31179, 30092, 31429, 32098, 11181, 32512, 32512, 32561, 32508, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 9247, -13203, -25808, -10886, -5109, -2559, -1280, 21060, 29636, 31428, 30209, 31488, 32127, 14114, 32512, 32681, 32529, 32502, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 10252, -16863, -24251, -10137, -4902, -2451, -1226, 21937, 30022, 31656, 30317, 31542, 32154, 17128, 32512, 32581, 32514, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11032, -22427, -22598, -9535, -4705, -2353, -1177, 20999, 30406, 31867, 30415, 31591, 32179, 20222, 32512, 32591, 32501, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11539, -19778, -20962, -9060, -4522, -2261, -1131, 19486, 30789, 32061, 30507, 31637, 32201, 23396, 32512, 32535, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11803, -12759, -19353, -8690, -4353, -2177, -1089, 18499, 31165, 32240, 30591, 31678, 32222, 26651, 32512, 32514, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11826, -7586, -17510, -8384, -4196, -2099, -1050, 26861, 31521, 32406, 30669, 31718, 32241, 29986, 32585, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11599, -2848, -15807, -8097, -4051, -2025, -1014, 30693, 31850, 32561, 30743, 31755, 32261, 32512, 32524, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 11037, -5302, -14051, -7770, -3913, -1958, -980, 28033, 32165, 32705, 30810, 31789, 32278, 32512, 32729, 32536, 32513, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10114, -7837, -12293, -7348, -3782, -1894, -948, 24926, 32473, 32512, 30873, 31819, 32294, 32512, 32512, 32580, 32527, 32515, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 8759, -10456, -10591, -6766, -3638, -1835, -917, 24058, 32600, 32512, 30934, 31850, 32309, 32512, 32512, 32729, 32591, 32537, 32520, 32514, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 6811, -13156, -9045, -5965, -3421, -1776, -890, 31582, 32246, 32512, 30988, 31878, 32324, 32512, 32512, 32512, 32628, 32573, 32541, 32526, 32518, 32514, 32513, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 4835 }; hivelytracker-0+git20180223/Replayer_Amiga/makefile0000775001024000102400000000056113042730153016762 0ustar CFLAGS = -Wall -O3 -gstabs CC = gcc AR = ar RANLIB = ranlib DEBUGLIB = LFLAGS = -lm -gstabs all: play_hvl play_hvl: play_hvl.o hvl_replay.o $(CC) -o play_hvl play_hvl.o hvl_replay.o $(LFLAGS) play_hvl.o: play_hvl.c hvl_replay.h $(CC) -c play_hvl.c -o play_hvl.o $(CFLAGS) hvl_replay.o: hvl_replay.c hvl_replay.h $(CC) -c hvl_replay.c -o hvl_replay.o $(CFLAGS) hivelytracker-0+git20180223/Replayer_Amiga/hvl_replay.h0000775001024000102400000001670513042730153017607 0ustar // Woohoo! #define MAX_CHANNELS 16 // Some handy constants. Thanks eightbitbubsy. #define AMIGA_PAL_XTAL 28375160 #define AMIGA_NTSC_XTAL 28636360 #define AMIGA_CPU_PAL_CLK ((AMIGA_PAL_XTAL / 4)) #define AMIGA_CPU_NTSC_CLK ((AMIGA_NTSC_XTAL / 4)) #define AMIGA_CIA_PAL_CLK ((AMIGA_CPU_PAL_CLK / 10)) #define AMIGA_CIA_NTSC_CLK ((AMIGA_CPU_NTSC_CLK / 10)) #define AMIGA_PAULA_PAL_CLK ((AMIGA_CPU_PAL_CLK / 2)) #define AMIGA_PAULA_NTSC_CLK ((AMIGA_CPU_NTSC_CLK / 2)) #define Period2Freq(period) ((AMIGA_PAULA_PAL_CLK * 65536.f) / (period)) struct hvl_envelope { int16 aFrames, aVolume; int16 dFrames, dVolume; int16 sFrames; int16 rFrames, rVolume; int16 pad; }; struct hvl_plsentry { uint8 ple_Note; uint8 ple_Waveform; int16 ple_Fixed; int8 ple_FX[2]; int8 ple_FXParam[2]; }; struct hvl_plist { int16 pls_Speed; int16 pls_Length; struct hvl_plsentry *pls_Entries; }; struct hvl_instrument { TEXT ins_Name[128]; uint8 ins_Volume; uint8 ins_WaveLength; uint8 ins_FilterLowerLimit; uint8 ins_FilterUpperLimit; uint8 ins_FilterSpeed; uint8 ins_SquareLowerLimit; uint8 ins_SquareUpperLimit; uint8 ins_SquareSpeed; uint8 ins_VibratoDelay; uint8 ins_VibratoSpeed; uint8 ins_VibratoDepth; uint8 ins_HardCutRelease; uint8 ins_HardCutReleaseFrames; struct hvl_envelope ins_Envelope; struct hvl_plist ins_PList; }; struct hvl_position { uint8 pos_Track[MAX_CHANNELS]; int8 pos_Transpose[MAX_CHANNELS]; }; struct hvl_step { uint8 stp_Note; uint8 stp_Instrument; uint8 stp_FX; uint8 stp_FXParam; uint8 stp_FXb; uint8 stp_FXbParam; }; struct hvl_voice { int16 vc_Track; int16 vc_NextTrack; int16 vc_Transpose; int16 vc_NextTranspose; int16 vc_OverrideTranspose; // 1.5 int32 vc_ADSRVolume; struct hvl_envelope vc_ADSR; struct hvl_instrument *vc_Instrument; uint32 vc_SamplePos; uint32 vc_Delta; uint16 vc_InstrPeriod; uint16 vc_TrackPeriod; uint16 vc_VibratoPeriod; uint16 vc_WaveLength; int16 vc_NoteMaxVolume; uint16 vc_PerfSubVolume; uint8 vc_NewWaveform; uint8 vc_Waveform; uint8 vc_PlantPeriod; uint8 vc_VoiceVolume; uint8 vc_PlantSquare; uint8 vc_IgnoreSquare; uint8 vc_FixedNote; int16 vc_VolumeSlideUp; int16 vc_VolumeSlideDown; int16 vc_HardCut; uint8 vc_HardCutRelease; int16 vc_HardCutReleaseF; uint8 vc_PeriodSlideOn; int16 vc_PeriodSlideSpeed; int16 vc_PeriodSlidePeriod; int16 vc_PeriodSlideLimit; int16 vc_PeriodSlideWithLimit; int16 vc_PeriodPerfSlideSpeed; int16 vc_PeriodPerfSlidePeriod; uint8 vc_PeriodPerfSlideOn; int16 vc_VibratoDelay; int16 vc_VibratoSpeed; int16 vc_VibratoCurrent; int16 vc_VibratoDepth; int16 vc_SquareOn; int16 vc_SquareInit; int16 vc_SquareWait; int16 vc_SquareLowerLimit; int16 vc_SquareUpperLimit; int16 vc_SquarePos; int16 vc_SquareSign; int16 vc_SquareSlidingIn; int16 vc_SquareReverse; uint8 vc_FilterOn; uint8 vc_FilterInit; int16 vc_FilterWait; int16 vc_FilterSpeed; int16 vc_FilterUpperLimit; int16 vc_FilterLowerLimit; int16 vc_FilterPos; int16 vc_FilterSign; int16 vc_FilterSlidingIn; int16 vc_IgnoreFilter; int16 vc_PerfCurrent; int16 vc_PerfSpeed; int16 vc_PerfWait; struct hvl_plist *vc_PerfList; int8 *vc_AudioPointer; int8 *vc_AudioSource; uint8 vc_NoteDelayOn; uint8 vc_NoteCutOn; int16 vc_NoteDelayWait; int16 vc_NoteCutWait; int16 vc_AudioPeriod; int16 vc_AudioVolume; int32 vc_WNRandom; int8 *vc_MixSource; int8 vc_SquareTempBuffer[0x80]; int8 vc_VoiceBuffer[0x282*4]; uint8 vc_VoiceNum; uint8 vc_TrackMasterVolume; uint8 vc_TrackOn; int16 vc_VoicePeriod; uint32 vc_Pan; uint32 vc_SetPan; // New for 1.4 uint32 vc_PanMultLeft; uint32 vc_PanMultRight; uint32 vc_RingSamplePos; uint32 vc_RingDelta; int8 *vc_RingMixSource; uint8 vc_RingPlantPeriod; int16 vc_RingInstrPeriod; int16 vc_RingBasePeriod; int16 vc_RingAudioPeriod; int8 *vc_RingAudioSource; uint8 vc_RingNewWaveform; uint8 vc_RingWaveform; uint8 vc_RingFixedPeriod; int8 vc_RingVoiceBuffer[0x282*4]; }; struct hvl_tune { TEXT ht_Name[128]; uint16 ht_SongNum; uint32 ht_Frequency; float64 ht_FreqF; int8 *ht_WaveformTab[MAX_CHANNELS]; uint16 ht_Restart; uint16 ht_PositionNr; uint8 ht_SpeedMultiplier; uint8 ht_TrackLength; uint8 ht_TrackNr; uint8 ht_InstrumentNr; uint8 ht_SubsongNr; uint16 ht_PosJump; uint32 ht_PlayingTime; int16 ht_Tempo; int16 ht_PosNr; int16 ht_StepWaitFrames; int16 ht_NoteNr; uint16 ht_PosJumpNote; uint8 ht_GetNewPosition; uint8 ht_PatternBreak; uint8 ht_SongEndReached; uint8 ht_Stereo; uint16 *ht_Subsongs; uint16 ht_Channels; struct hvl_position *ht_Positions; struct hvl_step ht_Tracks[256][64]; struct hvl_instrument *ht_Instruments; struct hvl_voice ht_Voices[MAX_CHANNELS]; int32 ht_defstereo; int32 ht_defpanleft; int32 ht_defpanright; int32 ht_mixgain; uint8 ht_Version; }; void hvl_DecodeFrame( struct hvl_tune *ht, int8 *buf1, int8 *buf2, int32 bufmod ); void hvl_InitReplayer( void ); BOOL hvl_InitSubsong( struct hvl_tune *ht, uint32 nr ); struct hvl_tune *hvl_LoadTune( TEXT *name, uint32 freq, uint32 defstereo ); void hvl_FreeTune( struct hvl_tune *ht ); hivelytracker-0+git20180223/Replayer_Amiga/hvl_replay.c0000775001024000102400000016205513042730153017602 0ustar /* ** Changes for the 1.4 release are commented. You can do ** a search for "1.4" and merge them into your own replay ** code. ** ** Changes for 1.5 are marked also. ** ** ... as are those for 1.6 ** ** ... and for 1.8 */ #include #include #include #include #include #include #include #include #include "hvl_replay.h" #include "hvl_tables.h" /* ** Waves */ int8 waves[WAVES_SIZE]; uint32 panning_left[256], panning_right[256]; void hvl_GenPanningTables( void ) { uint32 i; float64 aa, ab; // Sine based panning table aa = (3.14159265f*2.0f)/4.0f; // Quarter of the way through the sinewave == top peak ab = 0.0f; // Start of the climb from zero for( i=0; i<256; i++ ) { panning_left[i] = (uint32)(sin(aa)*255.0f); panning_right[i] = (uint32)(sin(ab)*255.0f); aa += (3.14159265*2.0f/4.0f)/256.0f; ab += (3.14159265*2.0f/4.0f)/256.0f; } panning_left[255] = 0; panning_right[0] = 0; } void hvl_GenSawtooth( int8 *buf, uint32 len ) { uint32 i; int32 val, add; add = 256 / (len-1); val = -128; for( i=0; i> 2; d1 = 128/d5; d4 = -(d2 >> 1); val = 0; for( i=0; i> 16); if (top > 127) in = 127 << 16; else if (top < -128) in = -(128 << 16); return in; } void hvl_GenFilterWaves( int8 *buf, int8 *lowbuf, int8 *highbuf ) { const int16 * mid_table = &filter_thing[0]; const int16 * low_table = &filter_thing[1395]; int32 freq; int32 i; for( i=0, freq = 25; i<31; i++, freq += 9 ) { uint32 wv; int8 *a0 = buf; for( wv=0; wv<6+6+0x20+1; wv++ ) { int32 in, fre, high, mid, low; uint32 j; mid = *mid_table++ << 8; low = *low_table++ << 8; for( j=0; j<=lentab[wv]; j++ ) { in = a0[j] << 16; high = clipshifted8( in - mid - low ); fre = (high >> 8) * freq; mid = clipshifted8(mid + fre); fre = (mid >> 8) * freq; low = clipshifted8(low + fre); *highbuf++ = high >> 16; *lowbuf++ = low >> 16; } a0 += lentab[wv]+1; } } } void hvl_GenWhiteNoise( int8 *buf, uint32 len ) { uint32 ays; ays = 0x41595321; do { uint16 ax, bx; int8 s; s = ays; if( ays & 0x100 ) { s = 0x7f; if( ays & 0x8000 ) s = 0x80; } *buf++ = s; len--; ays = (ays >> 5) | (ays << 27); ays = (ays & 0xffffff00) | ((ays & 0xff) ^ 0x9a); bx = ays; ays = (ays << 2) | (ays >> 30); ax = ays; bx += ax; ax ^= bx; ays = (ays & 0xffff0000) | ax; ays = (ays >> 3) | (ays << 29); } while( len ); } void hvl_reset_some_stuff( struct hvl_tune *ht ) { uint32 i; for( i=0; iht_Voices[i].vc_Delta=1; ht->ht_Voices[i].vc_OverrideTranspose=1000; // 1.5 ht->ht_Voices[i].vc_SamplePos=ht->ht_Voices[i].vc_Track=ht->ht_Voices[i].vc_Transpose=ht->ht_Voices[i].vc_NextTrack = ht->ht_Voices[i].vc_NextTranspose = 0; ht->ht_Voices[i].vc_ADSRVolume=ht->ht_Voices[i].vc_InstrPeriod=ht->ht_Voices[i].vc_TrackPeriod=ht->ht_Voices[i].vc_VibratoPeriod=ht->ht_Voices[i].vc_NoteMaxVolume=ht->ht_Voices[i].vc_PerfSubVolume=ht->ht_Voices[i].vc_TrackMasterVolume=0; ht->ht_Voices[i].vc_NewWaveform=ht->ht_Voices[i].vc_Waveform=ht->ht_Voices[i].vc_PlantSquare=ht->ht_Voices[i].vc_PlantPeriod=ht->ht_Voices[i].vc_IgnoreSquare=0; ht->ht_Voices[i].vc_TrackOn=ht->ht_Voices[i].vc_FixedNote=ht->ht_Voices[i].vc_VolumeSlideUp=ht->ht_Voices[i].vc_VolumeSlideDown=ht->ht_Voices[i].vc_HardCut=ht->ht_Voices[i].vc_HardCutRelease=ht->ht_Voices[i].vc_HardCutReleaseF=0; ht->ht_Voices[i].vc_PeriodSlideSpeed=ht->ht_Voices[i].vc_PeriodSlidePeriod=ht->ht_Voices[i].vc_PeriodSlideLimit=ht->ht_Voices[i].vc_PeriodSlideOn=ht->ht_Voices[i].vc_PeriodSlideWithLimit=0; ht->ht_Voices[i].vc_PeriodPerfSlideSpeed=ht->ht_Voices[i].vc_PeriodPerfSlidePeriod=ht->ht_Voices[i].vc_PeriodPerfSlideOn=ht->ht_Voices[i].vc_VibratoDelay=ht->ht_Voices[i].vc_VibratoCurrent=ht->ht_Voices[i].vc_VibratoDepth=ht->ht_Voices[i].vc_VibratoSpeed=0; ht->ht_Voices[i].vc_SquareOn=ht->ht_Voices[i].vc_SquareInit=ht->ht_Voices[i].vc_SquareLowerLimit=ht->ht_Voices[i].vc_SquareUpperLimit=ht->ht_Voices[i].vc_SquarePos=ht->ht_Voices[i].vc_SquareSign=ht->ht_Voices[i].vc_SquareSlidingIn=ht->ht_Voices[i].vc_SquareReverse=0; ht->ht_Voices[i].vc_FilterOn=ht->ht_Voices[i].vc_FilterInit=ht->ht_Voices[i].vc_FilterLowerLimit=ht->ht_Voices[i].vc_FilterUpperLimit=ht->ht_Voices[i].vc_FilterPos=ht->ht_Voices[i].vc_FilterSign=ht->ht_Voices[i].vc_FilterSpeed=ht->ht_Voices[i].vc_FilterSlidingIn=ht->ht_Voices[i].vc_IgnoreFilter=0; ht->ht_Voices[i].vc_PerfCurrent=ht->ht_Voices[i].vc_PerfSpeed=ht->ht_Voices[i].vc_WaveLength=ht->ht_Voices[i].vc_NoteDelayOn=ht->ht_Voices[i].vc_NoteCutOn=0; ht->ht_Voices[i].vc_AudioPeriod=ht->ht_Voices[i].vc_AudioVolume=ht->ht_Voices[i].vc_VoiceVolume=ht->ht_Voices[i].vc_VoicePeriod=ht->ht_Voices[i].vc_VoiceNum=ht->ht_Voices[i].vc_WNRandom=0; ht->ht_Voices[i].vc_SquareWait=ht->ht_Voices[i].vc_FilterWait=ht->ht_Voices[i].vc_PerfWait=ht->ht_Voices[i].vc_NoteDelayWait=ht->ht_Voices[i].vc_NoteCutWait=0; ht->ht_Voices[i].vc_PerfList=0; ht->ht_Voices[i].vc_RingSamplePos=ht->ht_Voices[i].vc_RingDelta=ht->ht_Voices[i].vc_RingPlantPeriod=ht->ht_Voices[i].vc_RingAudioPeriod=ht->ht_Voices[i].vc_RingNewWaveform=ht->ht_Voices[i].vc_RingWaveform=ht->ht_Voices[i].vc_RingFixedPeriod=ht->ht_Voices[i].vc_RingBasePeriod=0; ht->ht_Voices[i].vc_RingMixSource = NULL; ht->ht_Voices[i].vc_RingAudioSource = NULL; memset(&ht->ht_Voices[i].vc_SquareTempBuffer,0,0x80); memset(&ht->ht_Voices[i].vc_ADSR,0,sizeof(struct hvl_envelope)); memset(&ht->ht_Voices[i].vc_VoiceBuffer,0,0x281); memset(&ht->ht_Voices[i].vc_RingVoiceBuffer,0,0x281); } for( i=0; iht_Voices[i].vc_WNRandom = 0x280; ht->ht_Voices[i].vc_VoiceNum = i; ht->ht_Voices[i].vc_TrackMasterVolume = 0x40; ht->ht_Voices[i].vc_TrackOn = 1; ht->ht_Voices[i].vc_MixSource = ht->ht_Voices[i].vc_VoiceBuffer; } } BOOL hvl_InitSubsong( struct hvl_tune *ht, uint32 nr ) { uint32 PosNr, i; if( nr > ht->ht_SubsongNr ) return FALSE; ht->ht_SongNum = nr; PosNr = 0; if( nr ) PosNr = ht->ht_Subsongs[nr-1]; ht->ht_PosNr = PosNr; ht->ht_PosJump = 0; ht->ht_PatternBreak = 0; ht->ht_NoteNr = 0; ht->ht_PosJumpNote = 0; ht->ht_Tempo = 6; ht->ht_StepWaitFrames = 0; ht->ht_GetNewPosition = 1; ht->ht_SongEndReached = 0; ht->ht_PlayingTime = 0; for( i=0; iht_Voices[i+0].vc_Pan = ht->ht_defpanleft; ht->ht_Voices[i+0].vc_SetPan = ht->ht_defpanleft; // 1.4 ht->ht_Voices[i+0].vc_PanMultLeft = panning_left[ht->ht_defpanleft]; ht->ht_Voices[i+0].vc_PanMultRight = panning_right[ht->ht_defpanleft]; ht->ht_Voices[i+1].vc_Pan = ht->ht_defpanright; ht->ht_Voices[i+1].vc_SetPan = ht->ht_defpanright; // 1.4 ht->ht_Voices[i+1].vc_PanMultLeft = panning_left[ht->ht_defpanright]; ht->ht_Voices[i+1].vc_PanMultRight = panning_right[ht->ht_defpanright]; ht->ht_Voices[i+2].vc_Pan = ht->ht_defpanright; ht->ht_Voices[i+2].vc_SetPan = ht->ht_defpanright; // 1.4 ht->ht_Voices[i+2].vc_PanMultLeft = panning_left[ht->ht_defpanright]; ht->ht_Voices[i+2].vc_PanMultRight = panning_right[ht->ht_defpanright]; ht->ht_Voices[i+3].vc_Pan = ht->ht_defpanleft; ht->ht_Voices[i+3].vc_SetPan = ht->ht_defpanleft; // 1.4 ht->ht_Voices[i+3].vc_PanMultLeft = panning_left[ht->ht_defpanleft]; ht->ht_Voices[i+3].vc_PanMultRight = panning_right[ht->ht_defpanleft]; } hvl_reset_some_stuff( ht ); return TRUE; } void hvl_InitReplayer( void ) { hvl_GenPanningTables(); hvl_GenSawtooth( &waves[WO_SAWTOOTH_04], 0x04 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_08], 0x08 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_10], 0x10 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_20], 0x20 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_40], 0x40 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_80], 0x80 ); hvl_GenTriangle( &waves[WO_TRIANGLE_04], 0x04 ); hvl_GenTriangle( &waves[WO_TRIANGLE_08], 0x08 ); hvl_GenTriangle( &waves[WO_TRIANGLE_10], 0x10 ); hvl_GenTriangle( &waves[WO_TRIANGLE_20], 0x20 ); hvl_GenTriangle( &waves[WO_TRIANGLE_40], 0x40 ); hvl_GenTriangle( &waves[WO_TRIANGLE_80], 0x80 ); hvl_GenSquare( &waves[WO_SQUARES] ); hvl_GenWhiteNoise( &waves[WO_WHITENOISE], WHITENOISELEN ); hvl_GenFilterWaves( &waves[WO_TRIANGLE_04], &waves[WO_LOWPASSES], &waves[WO_HIGHPASSES] ); } struct hvl_tune *hvl_load_ahx( uint8 *buf, uint32 buflen, uint32 defstereo, uint32 freq ) { uint8 *bptr; TEXT *nptr; uint32 i, j, k, l, posn, insn, ssn, hs, trkn, trkl; struct hvl_tune *ht; struct hvl_plsentry *ple; int32 defgain[] = { 71, 72, 76, 85, 100 }; posn = ((buf[6]&0x0f)<<8)|buf[7]; insn = buf[12]; ssn = buf[13]; trkl = buf[10]; trkn = buf[11]; hs = sizeof( struct hvl_tune ); hs += sizeof( struct hvl_position ) * posn; hs += sizeof( struct hvl_instrument ) * (insn+1); hs += sizeof( uint16 ) * ssn; // Calculate the size of all instrument PList buffers bptr = &buf[14]; bptr += ssn*2; // Skip past the subsong list bptr += posn*4*2; // Skip past the positions bptr += trkn*trkl*3; if((buf[6]&0x80)==0) bptr += trkl*3; // *NOW* we can finally calculate PList space for( i=1; i<=insn; i++ ) { hs += bptr[21] * sizeof( struct hvl_plsentry ); bptr += 22 + bptr[21]*4; } ht = IExec->AllocVec( hs, MEMF_ANY ); if( !ht ) { IExec->FreeVec( buf ); printf( "Out of memory!\n" ); return NULL; } ht->ht_Frequency = freq; ht->ht_FreqF = (float64)freq; ht->ht_Positions = (struct hvl_position *)(&ht[1]); ht->ht_Instruments = (struct hvl_instrument *)(&ht->ht_Positions[posn]); ht->ht_Subsongs = (uint16 *)(&ht->ht_Instruments[(insn+1)]); ple = (struct hvl_plsentry *)(&ht->ht_Subsongs[ssn]); ht->ht_WaveformTab[0] = &waves[WO_TRIANGLE_04]; ht->ht_WaveformTab[1] = &waves[WO_SAWTOOTH_04]; ht->ht_WaveformTab[3] = &waves[WO_WHITENOISE]; ht->ht_Channels = 4; ht->ht_PositionNr = posn; ht->ht_Restart = (buf[8]<<8)|buf[9]; ht->ht_SpeedMultiplier = ((buf[6]>>5)&3)+1; ht->ht_TrackLength = trkl; ht->ht_TrackNr = trkn; ht->ht_InstrumentNr = insn; ht->ht_SubsongNr = ssn; ht->ht_defstereo = defstereo; ht->ht_defpanleft = stereopan_left[ht->ht_defstereo]; ht->ht_defpanright = stereopan_right[ht->ht_defstereo]; ht->ht_mixgain = (defgain[ht->ht_defstereo]*256)/100; if( ht->ht_Restart >= ht->ht_PositionNr ) ht->ht_Restart = ht->ht_PositionNr-1; // Do some validation if( ( ht->ht_PositionNr > 1000 ) || ( ht->ht_TrackLength > 64 ) || ( ht->ht_InstrumentNr > 64 ) ) { printf( "%d,%d,%d\n", ht->ht_PositionNr, ht->ht_TrackLength, ht->ht_InstrumentNr ); IExec->FreeVec( ht ); IExec->FreeVec( buf ); printf( "Invalid file.\n" ); return NULL; } strncpy( ht->ht_Name, (TEXT *)&buf[(buf[4]<<8)|buf[5]], 128 ); nptr = (TEXT *)&buf[((buf[4]<<8)|buf[5])+strlen( ht->ht_Name )+1]; bptr = &buf[14]; // Subsongs for( i=0; iht_SubsongNr; i++ ) { ht->ht_Subsongs[i] = (bptr[0]<<8)|bptr[1]; if( ht->ht_Subsongs[i] >= ht->ht_PositionNr ) ht->ht_Subsongs[i] = 0; bptr += 2; } // Position list for( i=0; iht_PositionNr; i++ ) { for( j=0; j<4; j++ ) { ht->ht_Positions[i].pos_Track[j] = *bptr++; ht->ht_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; } } // Tracks for( i=0; i<=ht->ht_TrackNr; i++ ) { if( ( ( buf[6]&0x80 ) == 0x80 ) && ( i == 0 ) ) { for( j=0; jht_TrackLength; j++ ) { ht->ht_Tracks[i][j].stp_Note = 0; ht->ht_Tracks[i][j].stp_Instrument = 0; ht->ht_Tracks[i][j].stp_FX = 0; ht->ht_Tracks[i][j].stp_FXParam = 0; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; } continue; } for( j=0; jht_TrackLength; j++ ) { ht->ht_Tracks[i][j].stp_Note = (bptr[0]>>2)&0x3f; ht->ht_Tracks[i][j].stp_Instrument = ((bptr[0]&0x3)<<4) | (bptr[1]>>4); ht->ht_Tracks[i][j].stp_FX = bptr[1]&0xf; ht->ht_Tracks[i][j].stp_FXParam = bptr[2]; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; bptr += 3; } } // Instruments for( i=1; i<=ht->ht_InstrumentNr; i++ ) { if( nptr < (TEXT *)(buf+buflen) ) { strncpy( ht->ht_Instruments[i].ins_Name, nptr, 128 ); nptr += strlen( nptr )+1; } else { ht->ht_Instruments[i].ins_Name[0] = 0; } ht->ht_Instruments[i].ins_Volume = bptr[0]; ht->ht_Instruments[i].ins_FilterSpeed = ((bptr[1]>>3)&0x1f)|((bptr[12]>>2)&0x20); ht->ht_Instruments[i].ins_WaveLength = bptr[1]&0x07; ht->ht_Instruments[i].ins_Envelope.aFrames = bptr[2]; ht->ht_Instruments[i].ins_Envelope.aVolume = bptr[3]; ht->ht_Instruments[i].ins_Envelope.dFrames = bptr[4]; ht->ht_Instruments[i].ins_Envelope.dVolume = bptr[5]; ht->ht_Instruments[i].ins_Envelope.sFrames = bptr[6]; ht->ht_Instruments[i].ins_Envelope.rFrames = bptr[7]; ht->ht_Instruments[i].ins_Envelope.rVolume = bptr[8]; ht->ht_Instruments[i].ins_FilterLowerLimit = bptr[12]&0x7f; ht->ht_Instruments[i].ins_VibratoDelay = bptr[13]; ht->ht_Instruments[i].ins_HardCutReleaseFrames = (bptr[14]>>4)&0x07; ht->ht_Instruments[i].ins_HardCutRelease = bptr[14]&0x80?1:0; ht->ht_Instruments[i].ins_VibratoDepth = bptr[14]&0x0f; ht->ht_Instruments[i].ins_VibratoSpeed = bptr[15]; ht->ht_Instruments[i].ins_SquareLowerLimit = bptr[16]; ht->ht_Instruments[i].ins_SquareUpperLimit = bptr[17]; ht->ht_Instruments[i].ins_SquareSpeed = bptr[18]; ht->ht_Instruments[i].ins_FilterUpperLimit = bptr[19]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Speed = bptr[20]; ht->ht_Instruments[i].ins_PList.pls_Length = bptr[21]; ht->ht_Instruments[i].ins_PList.pls_Entries = ple; ple += bptr[21]; bptr += 22; for( j=0; jht_Instruments[i].ins_PList.pls_Length; j++ ) { k = (bptr[0]>>5)&7; if( k == 6 ) k = 12; if( k == 7 ) k = 15; l = (bptr[0]>>2)&7; if( l == 6 ) l = 12; if( l == 7 ) l = 15; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = k; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = l; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = ((bptr[0]<<1)&6) | (bptr[1]>>7); ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[1]>>6)&1; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[1]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[2]; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[3]; // 1.6: Strip "toggle filter" commands if the module is // version 0 (pre-filters). This is what AHX also does. if( ( buf[3] == 0 ) && ( l == 4 ) && ( (bptr[2]&0xf0) != 0 ) ) ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] &= 0x0f; if( ( buf[3] == 0 ) && ( k == 4 ) && ( (bptr[3]&0xf0) != 0 ) ) ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] &= 0x0f; //1.8 bptr += 4; } } hvl_InitSubsong( ht, 0 ); IExec->FreeVec( buf ); return ht; } struct hvl_tune *hvl_LoadTune( TEXT *name, uint32 freq, uint32 defstereo ) { struct hvl_tune *ht; uint8 *buf, *bptr; TEXT *nptr; uint32 buflen, i, j, posn, insn, ssn, chnn, hs, trkl, trkn; BPTR lock, fh; struct FileInfoBlock *fib; struct hvl_plsentry *ple; fib = IDOS->AllocDosObjectTags( DOS_FIB, TAG_DONE ); if( !fib ) { printf( "Out of memory\n" ); return NULL; } lock = IDOS->Lock( name, ACCESS_READ ); if( !lock ) { IDOS->FreeDosObject( DOS_FIB, fib ); printf( "Unable to find '%s'\n", name ); return NULL; } IDOS->Examine( lock, fib ); if( !FIB_IS_FILE( fib ) ) { IDOS->UnLock( lock ); IDOS->FreeDosObject( DOS_FIB, fib ); printf( "Bad file!\n" ); return NULL; } buflen = fib->fib_Size; buf = IExec->AllocVec( buflen, MEMF_ANY ); if( !buf ) { IDOS->UnLock( lock ); IDOS->FreeDosObject( DOS_FIB, fib ); printf( "Out of memory!\n" ); return NULL; } fh = IDOS->OpenFromLock( lock ); if( !fh ) { IExec->FreeVec( buf ); IDOS->UnLock( lock ); IDOS->FreeDosObject( DOS_FIB, fib ); printf( "Can't open file\n" ); return NULL; } if( IDOS->Read( fh, buf, buflen ) != buflen ) { IExec->FreeVec( buf ); IDOS->Close( fh ); IDOS->FreeDosObject( DOS_FIB, fib ); printf( "Unable to read from file!\n" ); return NULL; } IDOS->Close( fh ); IDOS->FreeDosObject( DOS_FIB, fib ); if( ( buf[0] == 'T' ) && ( buf[1] == 'H' ) && ( buf[2] == 'X' ) && ( buf[3] < 3 ) ) return hvl_load_ahx( buf, buflen, defstereo, freq ); if( ( buf[0] != 'H' ) || ( buf[1] != 'V' ) || ( buf[2] != 'L' ) || ( buf[3] > 1 ) ) // 1.5 { IExec->FreeVec( buf ); printf( "Invalid file.\n" ); return NULL; } posn = ((buf[6]&0x0f)<<8)|buf[7]; insn = buf[12]; ssn = buf[13]; chnn = (buf[8]>>2)+4; trkl = buf[10]; trkn = buf[11]; hs = sizeof( struct hvl_tune ); hs += sizeof( struct hvl_position ) * posn; hs += sizeof( struct hvl_instrument ) * (insn+1); hs += sizeof( uint16 ) * ssn; // Calculate the size of all instrument PList buffers bptr = &buf[16]; bptr += ssn*2; // Skip past the subsong list bptr += posn*chnn*2; // Skip past the positions // Skip past the tracks // 1.4: Fixed two really stupid bugs that cancelled each other // out if the module had a blank first track (which is how // come they were missed. for( i=((buf[6]&0x80)==0x80)?1:0; i<=trkn; i++ ) for( j=0; jAllocVec( hs, MEMF_ANY ); if( !ht ) { IExec->FreeVec( buf ); printf( "Out of memory!\n" ); return NULL; } ht->ht_Version = buf[3]; // 1.5 ht->ht_Frequency = freq; ht->ht_FreqF = (float64)freq; ht->ht_Positions = (struct hvl_position *)(&ht[1]); ht->ht_Instruments = (struct hvl_instrument *)(&ht->ht_Positions[posn]); ht->ht_Subsongs = (uint16 *)(&ht->ht_Instruments[(insn+1)]); ple = (struct hvl_plsentry *)(&ht->ht_Subsongs[ssn]); ht->ht_WaveformTab[0] = &waves[WO_TRIANGLE_04]; ht->ht_WaveformTab[1] = &waves[WO_SAWTOOTH_04]; ht->ht_WaveformTab[3] = &waves[WO_WHITENOISE]; ht->ht_PositionNr = posn; ht->ht_Channels = (buf[8]>>2)+4; ht->ht_Restart = ((buf[8]&3)<<8)|buf[9]; ht->ht_SpeedMultiplier = ((buf[6]>>5)&3)+1; ht->ht_TrackLength = buf[10]; ht->ht_TrackNr = buf[11]; ht->ht_InstrumentNr = insn; ht->ht_SubsongNr = ssn; ht->ht_mixgain = (buf[14]<<8)/100; ht->ht_defstereo = buf[15]; ht->ht_defpanleft = stereopan_left[ht->ht_defstereo]; ht->ht_defpanright = stereopan_right[ht->ht_defstereo]; if( ht->ht_Restart >= ht->ht_PositionNr ) ht->ht_Restart = ht->ht_PositionNr-1; // Do some validation if( ( ht->ht_PositionNr > 1000 ) || ( ht->ht_TrackLength > 64 ) || ( ht->ht_InstrumentNr > 64 ) ) { printf( "%d,%d,%d\n", ht->ht_PositionNr, ht->ht_TrackLength, ht->ht_InstrumentNr ); IExec->FreeVec( ht ); IExec->FreeVec( buf ); printf( "Invalid file.\n" ); return NULL; } strncpy( ht->ht_Name, (TEXT *)&buf[(buf[4]<<8)|buf[5]], 128 ); nptr = (TEXT *)&buf[((buf[4]<<8)|buf[5])+strlen( ht->ht_Name )+1]; bptr = &buf[16]; // Subsongs for( i=0; iht_SubsongNr; i++ ) { ht->ht_Subsongs[i] = (bptr[0]<<8)|bptr[1]; bptr += 2; } // Position list for( i=0; iht_PositionNr; i++ ) { for( j=0; jht_Channels; j++ ) { ht->ht_Positions[i].pos_Track[j] = *bptr++; ht->ht_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; } } // Tracks for( i=0; i<=ht->ht_TrackNr; i++ ) { if( ( ( buf[6]&0x80 ) == 0x80 ) && ( i == 0 ) ) { for( j=0; jht_TrackLength; j++ ) { ht->ht_Tracks[i][j].stp_Note = 0; ht->ht_Tracks[i][j].stp_Instrument = 0; ht->ht_Tracks[i][j].stp_FX = 0; ht->ht_Tracks[i][j].stp_FXParam = 0; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; } continue; } for( j=0; jht_TrackLength; j++ ) { if( bptr[0] == 0x3f ) { ht->ht_Tracks[i][j].stp_Note = 0; ht->ht_Tracks[i][j].stp_Instrument = 0; ht->ht_Tracks[i][j].stp_FX = 0; ht->ht_Tracks[i][j].stp_FXParam = 0; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; bptr++; continue; } ht->ht_Tracks[i][j].stp_Note = bptr[0]; ht->ht_Tracks[i][j].stp_Instrument = bptr[1]; ht->ht_Tracks[i][j].stp_FX = bptr[2]>>4; ht->ht_Tracks[i][j].stp_FXParam = bptr[3]; ht->ht_Tracks[i][j].stp_FXb = bptr[2]&0xf; ht->ht_Tracks[i][j].stp_FXbParam = bptr[4]; bptr += 5; } } // Instruments for( i=1; i<=ht->ht_InstrumentNr; i++ ) { if( nptr < (TEXT *)(buf+buflen) ) { strncpy( ht->ht_Instruments[i].ins_Name, nptr, 128 ); nptr += strlen( nptr )+1; } else { ht->ht_Instruments[i].ins_Name[0] = 0; } ht->ht_Instruments[i].ins_Volume = bptr[0]; ht->ht_Instruments[i].ins_FilterSpeed = ((bptr[1]>>3)&0x1f)|((bptr[12]>>2)&0x20); ht->ht_Instruments[i].ins_WaveLength = bptr[1]&0x07; ht->ht_Instruments[i].ins_Envelope.aFrames = bptr[2]; ht->ht_Instruments[i].ins_Envelope.aVolume = bptr[3]; ht->ht_Instruments[i].ins_Envelope.dFrames = bptr[4]; ht->ht_Instruments[i].ins_Envelope.dVolume = bptr[5]; ht->ht_Instruments[i].ins_Envelope.sFrames = bptr[6]; ht->ht_Instruments[i].ins_Envelope.rFrames = bptr[7]; ht->ht_Instruments[i].ins_Envelope.rVolume = bptr[8]; ht->ht_Instruments[i].ins_FilterLowerLimit = bptr[12]&0x7f; ht->ht_Instruments[i].ins_VibratoDelay = bptr[13]; ht->ht_Instruments[i].ins_HardCutReleaseFrames = (bptr[14]>>4)&0x07; ht->ht_Instruments[i].ins_HardCutRelease = bptr[14]&0x80?1:0; ht->ht_Instruments[i].ins_VibratoDepth = bptr[14]&0x0f; ht->ht_Instruments[i].ins_VibratoSpeed = bptr[15]; ht->ht_Instruments[i].ins_SquareLowerLimit = bptr[16]; ht->ht_Instruments[i].ins_SquareUpperLimit = bptr[17]; ht->ht_Instruments[i].ins_SquareSpeed = bptr[18]; ht->ht_Instruments[i].ins_FilterUpperLimit = bptr[19]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Speed = bptr[20]; ht->ht_Instruments[i].ins_PList.pls_Length = bptr[21]; ht->ht_Instruments[i].ins_PList.pls_Entries = ple; ple += bptr[21]; bptr += 22; for( j=0; jht_Instruments[i].ins_PList.pls_Length; j++ ) { ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = bptr[0]&0xf; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = (bptr[1]>>3)&0xf; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = bptr[1]&7; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[2]>>6)&1; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[2]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[3]; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[4]; bptr += 5; } } hvl_InitSubsong( ht, 0 ); IExec->FreeVec( buf ); return ht; } void hvl_FreeTune( struct hvl_tune *ht ) { if( !ht ) return; IExec->FreeVec( ht ); } void hvl_process_stepfx_1( struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam ) { switch( FX ) { case 0x0: // Position Jump HI if( ((FXParam&0x0f) > 0) && ((FXParam&0x0f) <= 9) ) ht->ht_PosJump = FXParam & 0xf; break; case 0x5: // Volume Slide + Tone Portamento case 0xa: // Volume Slide voice->vc_VolumeSlideDown = FXParam & 0x0f; voice->vc_VolumeSlideUp = FXParam >> 4; break; case 0x7: // Panning if( FXParam > 127 ) FXParam -= 256; voice->vc_Pan = (FXParam+128); voice->vc_SetPan = (FXParam+128); // 1.4 voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; break; case 0xb: // Position jump ht->ht_PosJump = ht->ht_PosJump*100 + (FXParam & 0x0f) + (FXParam >> 4)*10; ht->ht_PatternBreak = 1; if( ht->ht_PosJump <= ht->ht_PosNr ) ht->ht_SongEndReached = 1; break; case 0xd: // Pattern break ht->ht_PosJump = ht->ht_PosNr+1; ht->ht_PosJumpNote = (FXParam & 0x0f) + (FXParam>>4)*10; ht->ht_PatternBreak = 1; if( ht->ht_PosJumpNote > ht->ht_TrackLength ) ht->ht_PosJumpNote = 0; break; case 0xe: // Extended commands switch( FXParam >> 4 ) { case 0xc: // Note cut if( (FXParam & 0x0f) < ht->ht_Tempo ) { voice->vc_NoteCutWait = FXParam & 0x0f; if( voice->vc_NoteCutWait ) { voice->vc_NoteCutOn = 1; voice->vc_HardCutRelease = 0; } } break; // 1.6: 0xd case removed } break; case 0xf: // Speed ht->ht_Tempo = FXParam; if( FXParam == 0 ) ht->ht_SongEndReached = 1; break; } } void hvl_process_stepfx_2( struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam, int32 *Note ) { switch( FX ) { case 0x9: // Set squarewave offset voice->vc_SquarePos = FXParam >> (5 - voice->vc_WaveLength); // voice->vc_PlantSquare = 1; voice->vc_IgnoreSquare = 1; break; case 0x3: // Tone portamento if( FXParam != 0 ) voice->vc_PeriodSlideSpeed = FXParam; case 0x5: // Tone portamento + volume slide if( *Note ) { int32 new, diff; new = period_tab[*Note]; diff = period_tab[voice->vc_TrackPeriod]; diff -= new; new = diff + voice->vc_PeriodSlidePeriod; if( new ) voice->vc_PeriodSlideLimit = -diff; } voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 1; *Note = 0; break; } } void hvl_process_stepfx_3( struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam ) { int32 i; switch( FX ) { case 0x01: // Portamento up (period slide down) voice->vc_PeriodSlideSpeed = -FXParam; voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 0; break; case 0x02: // Portamento down voice->vc_PeriodSlideSpeed = FXParam; voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 0; break; case 0x04: // Filter override if( ( FXParam == 0 ) || ( FXParam == 0x40 ) ) break; if( FXParam < 0x40 ) { voice->vc_IgnoreFilter = FXParam; break; } if( FXParam > 0x7f ) break; voice->vc_FilterPos = FXParam - 0x40; break; case 0x0c: // Volume FXParam &= 0xff; if( FXParam <= 0x40 ) { voice->vc_NoteMaxVolume = FXParam; break; } if( (FXParam -= 0x50) < 0 ) break; // 1.6 if( FXParam <= 0x40 ) { for( i=0; iht_Channels; i++ ) ht->ht_Voices[i].vc_TrackMasterVolume = FXParam; break; } if( (FXParam -= 0xa0-0x50) < 0 ) break; // 1.6 if( FXParam <= 0x40 ) voice->vc_TrackMasterVolume = FXParam; break; case 0xe: // Extended commands; switch( FXParam >> 4 ) { case 0x1: // Fineslide up voice->vc_PeriodSlidePeriod -= (FXParam & 0x0f); // 1.8 voice->vc_PlantPeriod = 1; break; case 0x2: // Fineslide down voice->vc_PeriodSlidePeriod += (FXParam & 0x0f); // 1.8 voice->vc_PlantPeriod = 1; break; case 0x4: // Vibrato control voice->vc_VibratoDepth = FXParam & 0x0f; break; case 0x0a: // Fine volume up voice->vc_NoteMaxVolume += FXParam & 0x0f; if( voice->vc_NoteMaxVolume > 0x40 ) voice->vc_NoteMaxVolume = 0x40; break; case 0x0b: // Fine volume down voice->vc_NoteMaxVolume -= FXParam & 0x0f; if( voice->vc_NoteMaxVolume < 0 ) voice->vc_NoteMaxVolume = 0; break; case 0x0f: // Misc flags (1.5) if( ht->ht_Version < 1 ) break; switch( FXParam & 0xf ) { case 1: voice->vc_OverrideTranspose = voice->vc_Transpose; break; } break; } break; } } void hvl_process_step( struct hvl_tune *ht, struct hvl_voice *voice ) { int32 Note, Instr, donenotedel; struct hvl_step *Step; if( voice->vc_TrackOn == 0 ) return; voice->vc_VolumeSlideUp = voice->vc_VolumeSlideDown = 0; Step = &ht->ht_Tracks[ht->ht_Positions[ht->ht_PosNr].pos_Track[voice->vc_VoiceNum]][ht->ht_NoteNr]; Note = Step->stp_Note; Instr = Step->stp_Instrument; // --------- 1.6: from here -------------- donenotedel = 0; // Do notedelay here if( ((Step->stp_FX&0xf)==0xe) && ((Step->stp_FXParam&0xf0)==0xd0) ) { if( voice->vc_NoteDelayOn ) { voice->vc_NoteDelayOn = 0; donenotedel = 1; } else { if( (Step->stp_FXParam&0x0f) < ht->ht_Tempo ) { voice->vc_NoteDelayWait = Step->stp_FXParam & 0x0f; if( voice->vc_NoteDelayWait ) { voice->vc_NoteDelayOn = 1; return; } } } } if( (donenotedel==0) && ((Step->stp_FXb&0xf)==0xe) && ((Step->stp_FXbParam&0xf0)==0xd0) ) { if( voice->vc_NoteDelayOn ) { voice->vc_NoteDelayOn = 0; } else { if( (Step->stp_FXbParam&0x0f) < ht->ht_Tempo ) { voice->vc_NoteDelayWait = Step->stp_FXbParam & 0x0f; if( voice->vc_NoteDelayWait ) { voice->vc_NoteDelayOn = 1; return; } } } } // --------- 1.6: to here -------------- if( Note ) voice->vc_OverrideTranspose = 1000; // 1.5 hvl_process_stepfx_1( ht, voice, Step->stp_FX&0xf, Step->stp_FXParam ); hvl_process_stepfx_1( ht, voice, Step->stp_FXb&0xf, Step->stp_FXbParam ); if( ( Instr ) && ( Instr <= ht->ht_InstrumentNr ) ) { struct hvl_instrument *Ins; int16 SquareLower, SquareUpper, d6, d3, d4; /* 1.4: Reset panning to last set position */ voice->vc_Pan = voice->vc_SetPan; voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; voice->vc_PeriodSlideSpeed = voice->vc_PeriodSlidePeriod = voice->vc_PeriodSlideLimit = 0; voice->vc_PerfSubVolume = 0x40; voice->vc_ADSRVolume = 0; voice->vc_Instrument = Ins = &ht->ht_Instruments[Instr]; voice->vc_SamplePos = 0; voice->vc_ADSR.aFrames = Ins->ins_Envelope.aFrames; voice->vc_ADSR.aVolume = voice->vc_ADSR.aFrames ? Ins->ins_Envelope.aVolume*256/voice->vc_ADSR.aFrames : Ins->ins_Envelope.aVolume * 256; // XXX voice->vc_ADSR.dFrames = Ins->ins_Envelope.dFrames; voice->vc_ADSR.dVolume = voice->vc_ADSR.dFrames ? (Ins->ins_Envelope.dVolume-Ins->ins_Envelope.aVolume)*256/voice->vc_ADSR.dFrames : Ins->ins_Envelope.dVolume * 256; // XXX voice->vc_ADSR.sFrames = Ins->ins_Envelope.sFrames; voice->vc_ADSR.rFrames = Ins->ins_Envelope.rFrames; voice->vc_ADSR.rVolume = voice->vc_ADSR.rFrames ? (Ins->ins_Envelope.rVolume-Ins->ins_Envelope.dVolume)*256/voice->vc_ADSR.rFrames : Ins->ins_Envelope.rVolume * 256; // XXX voice->vc_WaveLength = Ins->ins_WaveLength; voice->vc_NoteMaxVolume = Ins->ins_Volume; voice->vc_VibratoCurrent = 0; voice->vc_VibratoDelay = Ins->ins_VibratoDelay; voice->vc_VibratoDepth = Ins->ins_VibratoDepth; voice->vc_VibratoSpeed = Ins->ins_VibratoSpeed; voice->vc_VibratoPeriod = 0; voice->vc_HardCutRelease = Ins->ins_HardCutRelease; voice->vc_HardCut = Ins->ins_HardCutReleaseFrames; voice->vc_IgnoreSquare = voice->vc_SquareSlidingIn = 0; voice->vc_SquareWait = voice->vc_SquareOn = 0; SquareLower = Ins->ins_SquareLowerLimit >> (5 - voice->vc_WaveLength); SquareUpper = Ins->ins_SquareUpperLimit >> (5 - voice->vc_WaveLength); if( SquareUpper < SquareLower ) { int16 t = SquareUpper; SquareUpper = SquareLower; SquareLower = t; } voice->vc_SquareUpperLimit = SquareUpper; voice->vc_SquareLowerLimit = SquareLower; voice->vc_IgnoreFilter = voice->vc_FilterWait = voice->vc_FilterOn = 0; voice->vc_FilterSlidingIn = 0; d6 = Ins->ins_FilterSpeed; d3 = Ins->ins_FilterLowerLimit; d4 = Ins->ins_FilterUpperLimit; if( d3 & 0x80 ) d6 |= 0x20; if( d4 & 0x80 ) d6 |= 0x40; voice->vc_FilterSpeed = d6; d3 &= ~0x80; d4 &= ~0x80; if( d3 > d4 ) { int16 t = d3; d3 = d4; d4 = t; } voice->vc_FilterUpperLimit = d4; voice->vc_FilterLowerLimit = d3; voice->vc_FilterPos = 32; voice->vc_PerfWait = voice->vc_PerfCurrent = 0; voice->vc_PerfSpeed = Ins->ins_PList.pls_Speed; voice->vc_PerfList = &voice->vc_Instrument->ins_PList; voice->vc_RingMixSource = NULL; // No ring modulation voice->vc_RingSamplePos = 0; voice->vc_RingPlantPeriod = 0; voice->vc_RingNewWaveform = 0; } voice->vc_PeriodSlideOn = 0; hvl_process_stepfx_2( ht, voice, Step->stp_FX&0xf, Step->stp_FXParam, &Note ); hvl_process_stepfx_2( ht, voice, Step->stp_FXb&0xf, Step->stp_FXbParam, &Note ); if( Note ) { voice->vc_TrackPeriod = Note; voice->vc_PlantPeriod = 1; } hvl_process_stepfx_3( ht, voice, Step->stp_FX&0xf, Step->stp_FXParam ); hvl_process_stepfx_3( ht, voice, Step->stp_FXb&0xf, Step->stp_FXbParam ); } void hvl_plist_command_parse( struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam ) { switch( FX ) { case 0: if( ( FXParam > 0 ) && ( FXParam < 0x40 ) ) { if( voice->vc_IgnoreFilter ) { voice->vc_FilterPos = voice->vc_IgnoreFilter; voice->vc_IgnoreFilter = 0; } else { voice->vc_FilterPos = FXParam; } voice->vc_NewWaveform = 1; } break; case 1: voice->vc_PeriodPerfSlideSpeed = FXParam; voice->vc_PeriodPerfSlideOn = 1; break; case 2: voice->vc_PeriodPerfSlideSpeed = -FXParam; voice->vc_PeriodPerfSlideOn = 1; break; case 3: if( voice->vc_IgnoreSquare == 0 ) voice->vc_SquarePos = FXParam >> (5-voice->vc_WaveLength); else voice->vc_IgnoreSquare = 0; break; case 4: if( FXParam == 0 ) { voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); voice->vc_SquareSign = 1; } else { if( FXParam & 0x0f ) { voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); voice->vc_SquareSign = 1; if(( FXParam & 0x0f ) == 0x0f ) voice->vc_SquareSign = -1; } if( FXParam & 0xf0 ) { voice->vc_FilterInit = (voice->vc_FilterOn ^= 1); voice->vc_FilterSign = 1; if(( FXParam & 0xf0 ) == 0xf0 ) voice->vc_FilterSign = -1; } } break; case 5: voice->vc_PerfCurrent = FXParam; break; case 7: // Ring modulate with triangle if(( FXParam >= 1 ) && ( FXParam <= 0x3C )) { voice->vc_RingBasePeriod = FXParam; voice->vc_RingFixedPeriod = 1; } else if(( FXParam >= 0x81 ) && ( FXParam <= 0xBC )) { voice->vc_RingBasePeriod = FXParam-0x80; voice->vc_RingFixedPeriod = 0; } else { voice->vc_RingBasePeriod = 0; voice->vc_RingFixedPeriod = 0; voice->vc_RingNewWaveform = 0; voice->vc_RingAudioSource = NULL; // turn it off voice->vc_RingMixSource = NULL; break; } voice->vc_RingWaveform = 0; voice->vc_RingNewWaveform = 1; voice->vc_RingPlantPeriod = 1; break; case 8: // Ring modulate with sawtooth if(( FXParam >= 1 ) && ( FXParam <= 0x3C )) { voice->vc_RingBasePeriod = FXParam; voice->vc_RingFixedPeriod = 1; } else if(( FXParam >= 0x81 ) && ( FXParam <= 0xBC )) { voice->vc_RingBasePeriod = FXParam-0x80; voice->vc_RingFixedPeriod = 0; } else { voice->vc_RingBasePeriod = 0; voice->vc_RingFixedPeriod = 0; voice->vc_RingNewWaveform = 0; voice->vc_RingAudioSource = NULL; voice->vc_RingMixSource = NULL; break; } voice->vc_RingWaveform = 1; voice->vc_RingNewWaveform = 1; voice->vc_RingPlantPeriod = 1; break; /* New in HivelyTracker 1.4 */ case 9: if( FXParam > 127 ) FXParam -= 256; voice->vc_Pan = (FXParam+128); voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; break; case 12: if( FXParam <= 0x40 ) { voice->vc_NoteMaxVolume = FXParam; break; } if( (FXParam -= 0x50) < 0 ) break; if( FXParam <= 0x40 ) { voice->vc_PerfSubVolume = FXParam; break; } if( (FXParam -= 0xa0-0x50) < 0 ) break; if( FXParam <= 0x40 ) voice->vc_TrackMasterVolume = FXParam; break; case 15: voice->vc_PerfSpeed = voice->vc_PerfWait = FXParam; break; } } void hvl_process_frame( struct hvl_tune *ht, struct hvl_voice *voice ) { static CONST uint8 Offsets[] = {0x00,0x04,0x04+0x08,0x04+0x08+0x10,0x04+0x08+0x10+0x20,0x04+0x08+0x10+0x20+0x40}; if( voice->vc_TrackOn == 0 ) return; if( voice->vc_NoteDelayOn ) { if( voice->vc_NoteDelayWait <= 0 ) hvl_process_step( ht, voice ); else voice->vc_NoteDelayWait--; } if( voice->vc_HardCut ) { int32 nextinst; if( ht->ht_NoteNr+1 < ht->ht_TrackLength ) nextinst = ht->ht_Tracks[voice->vc_Track][ht->ht_NoteNr+1].stp_Instrument; else nextinst = ht->ht_Tracks[voice->vc_NextTrack][0].stp_Instrument; if( nextinst ) { int32 d1; d1 = ht->ht_Tempo - voice->vc_HardCut; if( d1 < 0 ) d1 = 0; if( !voice->vc_NoteCutOn ) { voice->vc_NoteCutOn = 1; voice->vc_NoteCutWait = d1; voice->vc_HardCutReleaseF = -(d1-ht->ht_Tempo); } else { voice->vc_HardCut = 0; } } } if( voice->vc_NoteCutOn ) { if( voice->vc_NoteCutWait <= 0 ) { voice->vc_NoteCutOn = 0; if( voice->vc_HardCutRelease ) { voice->vc_ADSR.rVolume = -(voice->vc_ADSRVolume - (voice->vc_Instrument->ins_Envelope.rVolume << 8)) / voice->vc_HardCutReleaseF; voice->vc_ADSR.rFrames = voice->vc_HardCutReleaseF; voice->vc_ADSR.aFrames = voice->vc_ADSR.dFrames = voice->vc_ADSR.sFrames = 0; } else { voice->vc_NoteMaxVolume = 0; } } else { voice->vc_NoteCutWait--; } } // ADSR envelope if( voice->vc_ADSR.aFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.aVolume; if( --voice->vc_ADSR.aFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.aVolume << 8; } else if( voice->vc_ADSR.dFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.dVolume; if( --voice->vc_ADSR.dFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.dVolume << 8; } else if( voice->vc_ADSR.sFrames ) { voice->vc_ADSR.sFrames--; } else if( voice->vc_ADSR.rFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.rVolume; if( --voice->vc_ADSR.rFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.rVolume << 8; } // VolumeSlide voice->vc_NoteMaxVolume = voice->vc_NoteMaxVolume + voice->vc_VolumeSlideUp - voice->vc_VolumeSlideDown; if( voice->vc_NoteMaxVolume < 0 ) voice->vc_NoteMaxVolume = 0; else if( voice->vc_NoteMaxVolume > 0x40 ) voice->vc_NoteMaxVolume = 0x40; // Portamento if( voice->vc_PeriodSlideOn ) { if( voice->vc_PeriodSlideWithLimit ) { int32 d0, d2; d0 = voice->vc_PeriodSlidePeriod - voice->vc_PeriodSlideLimit; d2 = voice->vc_PeriodSlideSpeed; if( d0 > 0 ) d2 = -d2; if( d0 ) { int32 d3; d3 = (d0 + d2) ^ d0; if( d3 >= 0 ) d0 = voice->vc_PeriodSlidePeriod + d2; else d0 = voice->vc_PeriodSlideLimit; voice->vc_PeriodSlidePeriod = d0; voice->vc_PlantPeriod = 1; } } else { voice->vc_PeriodSlidePeriod += voice->vc_PeriodSlideSpeed; voice->vc_PlantPeriod = 1; } } // Vibrato if( voice->vc_VibratoDepth ) { if( voice->vc_VibratoDelay <= 0 ) { voice->vc_VibratoPeriod = (vib_tab[voice->vc_VibratoCurrent] * voice->vc_VibratoDepth) >> 7; voice->vc_PlantPeriod = 1; voice->vc_VibratoCurrent = (voice->vc_VibratoCurrent + voice->vc_VibratoSpeed) & 0x3f; } else { voice->vc_VibratoDelay--; } } // PList if( voice->vc_PerfList != 0 ) { if( voice->vc_Instrument && voice->vc_PerfCurrent < voice->vc_Instrument->ins_PList.pls_Length ) { if( --voice->vc_PerfWait <= 0 ) { uint32 i; int32 cur; cur = voice->vc_PerfCurrent++; voice->vc_PerfWait = voice->vc_PerfSpeed; if( voice->vc_PerfList->pls_Entries[cur].ple_Waveform ) { voice->vc_Waveform = voice->vc_PerfList->pls_Entries[cur].ple_Waveform-1; voice->vc_NewWaveform = 1; voice->vc_PeriodPerfSlideSpeed = voice->vc_PeriodPerfSlidePeriod = 0; } // Holdwave voice->vc_PeriodPerfSlideOn = 0; for( i=0; i<2; i++ ) hvl_plist_command_parse( ht, voice, voice->vc_PerfList->pls_Entries[cur].ple_FX[i]&0xff, voice->vc_PerfList->pls_Entries[cur].ple_FXParam[i]&0xff ); // GetNote if( voice->vc_PerfList->pls_Entries[cur].ple_Note ) { voice->vc_InstrPeriod = voice->vc_PerfList->pls_Entries[cur].ple_Note; voice->vc_PlantPeriod = 1; voice->vc_FixedNote = voice->vc_PerfList->pls_Entries[cur].ple_Fixed; } } } else { if( voice->vc_PerfWait ) voice->vc_PerfWait--; else voice->vc_PeriodPerfSlideSpeed = 0; } } // PerfPortamento if( voice->vc_PeriodPerfSlideOn ) { voice->vc_PeriodPerfSlidePeriod -= voice->vc_PeriodPerfSlideSpeed; if( voice->vc_PeriodPerfSlidePeriod ) voice->vc_PlantPeriod = 1; } if( voice->vc_Waveform == 3-1 && voice->vc_SquareOn ) { if( --voice->vc_SquareWait <= 0 ) { int32 d1, d2, d3; d1 = voice->vc_SquareLowerLimit; d2 = voice->vc_SquareUpperLimit; d3 = voice->vc_SquarePos; if( voice->vc_SquareInit ) { voice->vc_SquareInit = 0; if( d3 <= d1 ) { voice->vc_SquareSlidingIn = 1; voice->vc_SquareSign = 1; } else if( d3 >= d2 ) { voice->vc_SquareSlidingIn = 1; voice->vc_SquareSign = -1; } } // NoSquareInit if( d1 == d3 || d2 == d3 ) { if( voice->vc_SquareSlidingIn ) voice->vc_SquareSlidingIn = 0; else voice->vc_SquareSign = -voice->vc_SquareSign; } d3 += voice->vc_SquareSign; voice->vc_SquarePos = d3; voice->vc_PlantSquare = 1; voice->vc_SquareWait = voice->vc_Instrument->ins_SquareSpeed; } } if( voice->vc_FilterOn && --voice->vc_FilterWait <= 0 ) { uint32 i, FMax; int32 d1, d2, d3; d1 = voice->vc_FilterLowerLimit; d2 = voice->vc_FilterUpperLimit; d3 = voice->vc_FilterPos; if( voice->vc_FilterInit ) { voice->vc_FilterInit = 0; if( d3 <= d1 ) { voice->vc_FilterSlidingIn = 1; voice->vc_FilterSign = 1; } else if( d3 >= d2 ) { voice->vc_FilterSlidingIn = 1; voice->vc_FilterSign = -1; } } // NoFilterInit FMax = (voice->vc_FilterSpeed < 3) ? (5-voice->vc_FilterSpeed) : 1; for( i=0; ivc_FilterSlidingIn ) voice->vc_FilterSlidingIn = 0; else voice->vc_FilterSign = -voice->vc_FilterSign; } d3 += voice->vc_FilterSign; } if( d3 < 1 ) d3 = 1; if( d3 > 63 ) d3 = 63; voice->vc_FilterPos = d3; voice->vc_NewWaveform = 1; voice->vc_FilterWait = voice->vc_FilterSpeed - 3; if( voice->vc_FilterWait < 1 ) voice->vc_FilterWait = 1; } if( voice->vc_Waveform == 3-1 || voice->vc_PlantSquare ) { // CalcSquare uint32 i; int32 Delta; int8 *SquarePtr; int32 X; SquarePtr = &waves[WO_SQUARES+(voice->vc_FilterPos-0x20)*(0xfc+0xfc+0x80*0x1f+0x80+0x280*3)]; X = voice->vc_SquarePos << (5 - voice->vc_WaveLength); if( X > 0x20 ) { X = 0x40 - X; voice->vc_SquareReverse = 1; } // OkDownSquare if( X > 0 ) SquarePtr += (X-1) << 7; Delta = 32 >> voice->vc_WaveLength; ht->ht_WaveformTab[2] = voice->vc_SquareTempBuffer; for( i=0; i<(1<vc_WaveLength)*4; i++ ) { voice->vc_SquareTempBuffer[i] = *SquarePtr; SquarePtr += Delta; } voice->vc_NewWaveform = 1; voice->vc_Waveform = 3-1; voice->vc_PlantSquare = 0; } if( voice->vc_Waveform == 4-1 ) voice->vc_NewWaveform = 1; if( voice->vc_RingNewWaveform ) { int8 *rasrc; if( voice->vc_RingWaveform > 1 ) voice->vc_RingWaveform = 1; rasrc = ht->ht_WaveformTab[voice->vc_RingWaveform]; rasrc += Offsets[voice->vc_WaveLength]; voice->vc_RingAudioSource = rasrc; } if( voice->vc_NewWaveform ) { int8 *AudioSource; AudioSource = ht->ht_WaveformTab[voice->vc_Waveform]; if( voice->vc_Waveform != 3-1 ) AudioSource += (voice->vc_FilterPos-0x20)*(0xfc+0xfc+0x80*0x1f+0x80+0x280*3); if( voice->vc_Waveform < 3-1) { // GetWLWaveformlor2 AudioSource += Offsets[voice->vc_WaveLength]; } if( voice->vc_Waveform == 4-1 ) { // AddRandomMoving AudioSource += ( voice->vc_WNRandom & (2*0x280-1) ) & ~1; // GoOnRandom voice->vc_WNRandom += 2239384; voice->vc_WNRandom = ((((voice->vc_WNRandom >> 8) | (voice->vc_WNRandom << 24)) + 782323) ^ 75) - 6735; } voice->vc_AudioSource = AudioSource; } // Ring modulation period calculation if( voice->vc_RingAudioSource ) { voice->vc_RingAudioPeriod = voice->vc_RingBasePeriod; if( !(voice->vc_RingFixedPeriod) ) { if( voice->vc_OverrideTranspose != 1000 ) // 1.5 voice->vc_RingAudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; else voice->vc_RingAudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; } if( voice->vc_RingAudioPeriod > 5*12 ) voice->vc_RingAudioPeriod = 5*12; if( voice->vc_RingAudioPeriod < 0 ) voice->vc_RingAudioPeriod = 0; voice->vc_RingAudioPeriod = period_tab[voice->vc_RingAudioPeriod]; if( !(voice->vc_RingFixedPeriod) ) voice->vc_RingAudioPeriod += voice->vc_PeriodSlidePeriod; voice->vc_RingAudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; if( voice->vc_RingAudioPeriod > 0x0d60 ) voice->vc_RingAudioPeriod = 0x0d60; if( voice->vc_RingAudioPeriod < 0x0071 ) voice->vc_RingAudioPeriod = 0x0071; } // Normal period calculation voice->vc_AudioPeriod = voice->vc_InstrPeriod; if( !(voice->vc_FixedNote) ) { if( voice->vc_OverrideTranspose != 1000 ) // 1.5 voice->vc_AudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; else voice->vc_AudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; } if( voice->vc_AudioPeriod > 5*12 ) voice->vc_AudioPeriod = 5*12; if( voice->vc_AudioPeriod < 0 ) voice->vc_AudioPeriod = 0; voice->vc_AudioPeriod = period_tab[voice->vc_AudioPeriod]; if( !(voice->vc_FixedNote) ) voice->vc_AudioPeriod += voice->vc_PeriodSlidePeriod; voice->vc_AudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; if( voice->vc_AudioPeriod > 0x0d60 ) voice->vc_AudioPeriod = 0x0d60; if( voice->vc_AudioPeriod < 0x0071 ) voice->vc_AudioPeriod = 0x0071; voice->vc_AudioVolume = (((((((voice->vc_ADSRVolume >> 8) * voice->vc_NoteMaxVolume) >> 6) * voice->vc_PerfSubVolume) >> 6) * voice->vc_TrackMasterVolume) >> 6); } void hvl_set_audio( struct hvl_voice *voice, float64 freqf ) { if( voice->vc_TrackOn == 0 ) { voice->vc_VoiceVolume = 0; return; } voice->vc_VoiceVolume = voice->vc_AudioVolume; if( voice->vc_PlantPeriod ) { float64 freq2; uint32 delta; voice->vc_PlantPeriod = 0; voice->vc_VoicePeriod = voice->vc_AudioPeriod; freq2 = Period2Freq( voice->vc_AudioPeriod ); delta = (uint32)(freq2 / freqf); if( delta > (0x280<<16) ) delta -= (0x280<<16); if( delta == 0 ) delta = 1; voice->vc_Delta = delta; } if( voice->vc_NewWaveform ) { int8 *src; src = voice->vc_AudioSource; if( voice->vc_Waveform == 4-1 ) { IExec->CopyMem((void *)src, &voice->vc_VoiceBuffer[0], 0x280); } else { uint32 i, WaveLoops; WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; for( i=0; iCopyMem((void *)src, &voice->vc_VoiceBuffer[i*4*(1<vc_WaveLength)], 4*(1<vc_WaveLength)); } voice->vc_VoiceBuffer[0x280] = voice->vc_VoiceBuffer[0]; voice->vc_MixSource = voice->vc_VoiceBuffer; } /* Ring Modulation */ if( voice->vc_RingPlantPeriod ) { float64 freq2; uint32 delta; voice->vc_RingPlantPeriod = 0; freq2 = Period2Freq( voice->vc_RingAudioPeriod ); delta = (uint32)(freq2 / freqf); if( delta > (0x280<<16) ) delta -= (0x280<<16); if( delta == 0 ) delta = 1; voice->vc_RingDelta = delta; } if( voice->vc_RingNewWaveform ) { int8 *src; uint32 i, WaveLoops; src = voice->vc_RingAudioSource; WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; for( i=0; iCopyMem((void *)src, &voice->vc_RingVoiceBuffer[i*4*(1<vc_WaveLength)], 4*(1<vc_WaveLength)); voice->vc_RingVoiceBuffer[0x280] = voice->vc_RingVoiceBuffer[0]; voice->vc_RingMixSource = voice->vc_RingVoiceBuffer; } } void hvl_play_irq( struct hvl_tune *ht ) { uint32 i; if( ht->ht_StepWaitFrames <= 0 ) { if( ht->ht_GetNewPosition ) { int32 nextpos = (ht->ht_PosNr+1==ht->ht_PositionNr)?0:(ht->ht_PosNr+1); for( i=0; iht_Channels; i++ ) { ht->ht_Voices[i].vc_Track = ht->ht_Positions[ht->ht_PosNr].pos_Track[i]; ht->ht_Voices[i].vc_Transpose = ht->ht_Positions[ht->ht_PosNr].pos_Transpose[i]; ht->ht_Voices[i].vc_NextTrack = ht->ht_Positions[nextpos].pos_Track[i]; ht->ht_Voices[i].vc_NextTranspose = ht->ht_Positions[nextpos].pos_Transpose[i]; } ht->ht_GetNewPosition = 0; } for( i=0; iht_Channels; i++ ) hvl_process_step( ht, &ht->ht_Voices[i] ); ht->ht_StepWaitFrames = ht->ht_Tempo; } for( i=0; iht_Channels; i++ ) hvl_process_frame( ht, &ht->ht_Voices[i] ); ht->ht_PlayingTime++; if( ht->ht_Tempo > 0 && --ht->ht_StepWaitFrames <= 0 ) { if( !ht->ht_PatternBreak ) { ht->ht_NoteNr++; if( ht->ht_NoteNr >= ht->ht_TrackLength ) { ht->ht_PosJump = ht->ht_PosNr+1; ht->ht_PosJumpNote = 0; ht->ht_PatternBreak = 1; } } if( ht->ht_PatternBreak ) { ht->ht_PatternBreak = 0; ht->ht_PosNr = ht->ht_PosJump; ht->ht_NoteNr = ht->ht_PosJumpNote; if( ht->ht_PosNr == ht->ht_PositionNr ) { ht->ht_SongEndReached = 1; ht->ht_PosNr = ht->ht_Restart; } ht->ht_PosJumpNote = 0; ht->ht_PosJump = 0; ht->ht_GetNewPosition = 1; } } for( i=0; iht_Channels; i++ ) hvl_set_audio( &ht->ht_Voices[i], ht->ht_Frequency ); } void hvl_mixchunk( struct hvl_tune *ht, uint32 samples, int8 *buf1, int8 *buf2, int32 bufmod ) { int8 *src[MAX_CHANNELS]; int8 *rsrc[MAX_CHANNELS]; uint32 delta[MAX_CHANNELS]; uint32 rdelta[MAX_CHANNELS]; int32 vol[MAX_CHANNELS]; uint32 pos[MAX_CHANNELS]; uint32 rpos[MAX_CHANNELS]; uint32 cnt; int32 panl[MAX_CHANNELS]; int32 panr[MAX_CHANNELS]; // uint32 vu[MAX_CHANNELS]; int32 a=0, b=0, j; uint32 i, chans, loops; chans = ht->ht_Channels; for( i=0; iht_Voices[i].vc_Delta; vol[i] = ht->ht_Voices[i].vc_VoiceVolume; pos[i] = ht->ht_Voices[i].vc_SamplePos; src[i] = ht->ht_Voices[i].vc_MixSource; panl[i] = ht->ht_Voices[i].vc_PanMultLeft; panr[i] = ht->ht_Voices[i].vc_PanMultRight; /* Ring Modulation */ rdelta[i]= ht->ht_Voices[i].vc_RingDelta; rpos[i] = ht->ht_Voices[i].vc_RingSamplePos; rsrc[i] = ht->ht_Voices[i].vc_RingMixSource; // vu[i] = 0; } do { loops = samples; for( i=0; i= (0x280 << 16)) pos[i] -= 0x280<<16; cnt = ((0x280<<16) - pos[i] - 1) / delta[i] + 1; if( cnt < loops ) loops = cnt; if( rsrc[i] ) { if( rpos[i] >= (0x280<<16)) rpos[i] -= 0x280<<16; cnt = ((0x280<<16) - rpos[i] - 1) / rdelta[i] + 1; if( cnt < loops ) loops = cnt; } } samples -= loops; // Inner loop do { a=0; b=0; for( i=0; i>16]*rsrc[i][rpos[i]>>16])>>7)*vol[i]; rpos[i] += rdelta[i]; } else { j = src[i][pos[i]>>16]*vol[i]; } // if( abs( j ) > vu[i] ) vu[i] = abs( j ); a += (j * panl[i]) >> 7; b += (j * panr[i]) >> 7; pos[i] += delta[i]; } a = (a*ht->ht_mixgain)>>8; b = (b*ht->ht_mixgain)>>8; if (a<-0x8000) a=-0x8000; if (a> 0x7fff) a= 0x7fff; if (b<-0x8000) b=-0x8000; if (b> 0x7fff) b= 0x7fff; *(int16 *)buf1 = a; *(int16 *)buf2 = b; loops--; buf1 += bufmod; buf2 += bufmod; } while( loops > 0 ); } while( samples > 0 ); for( i=0; iht_Voices[i].vc_SamplePos = pos[i]; ht->ht_Voices[i].vc_RingSamplePos = rpos[i]; // ht->ht_Voices[i].vc_VUMeter = vu[i]; } } void hvl_DecodeFrame( struct hvl_tune *ht, int8 *buf1, int8 *buf2, int32 bufmod ) { uint32 samples, loops; samples = ht->ht_Frequency/50/ht->ht_SpeedMultiplier; loops = ht->ht_SpeedMultiplier; do { hvl_play_irq( ht ); hvl_mixchunk( ht, samples, buf1, buf2, bufmod ); buf1 += samples * bufmod; buf2 += samples * bufmod; loops--; } while( loops ); } hivelytracker-0+git20180223/Press/0000775001024000102400000000000013042730153013470 5ustar hivelytracker-0+git20180223/Press/HivelyTrackerLogo01.PNG0000775001024000102400000030115113042730153017600 0ustar PNG  IHDREobKGD pHYsxtIMEB !$ItEXtSaverArtEffect PNG 4.0}u IDATxcDf݉ IDATO IDATxDfxDfxDfxDfxDfxDfxDfxDfxDfDw IDAT                                   IDATb      x    H7%   x  x H7%   H7%  x H7%  H7%                       {          ); IDAT                                    Dfx            #4#4#4#4#4#4#4#4#4#4                                                    ytЌ IDAT                                      "' "' "' "' "' "' "' "' "' "' "' "' "' "'   {bF  x  x      x  {bF  {bF  {bF{bF  {bF  x  {bF    {bF{bF                      "*.  "*."*. "*."*. "*."*. "*."*. "*."*. "*."*. "*. "*.  "*.  "*."*. "*."*.  "*."*. "*."*.     @; IDAT              Dfx       Df   Dfx    Dfx    Df                                                                                                                 L; IDAT                                                                   Z IDATDfDfDfDfDfDfDfDfDfDfDfDfDf IDATƵ IDATP3@CIDAT) 4̌cL&4Ni6|>brD9ovq%ojm-iFԵRfD]׶sIҌ본%XܒfDKQ.[ʌmWdqKF4#z-i~Z%H\fKbfP(r[ҌdYvn3H$X@X-eFѨm7fܛ#KK-w0dsK[q"iY[N+9Je2 Xݜ UӉǣORz: ^ >oaѹ#x@??065:kx`ssWn{úqfm5=xD</Ηq6zZFJjOt$q( គk.3 sgM`6׃ݭ)vqʩWuARGB]RxٻOQqDƻ: x ` L4:n.nH!6%XjJ)w(hbxAIQ#_ B_4pG^=ONzII&IEH6I~.I&mc 7J:K{efIQ m6,Y/2L/+2 m6,YL?9'n;w \ m6,;s͜y݅ |&mcœ*/VnCzNKgg\:::2ioo_L[e0Խu,%j$?YM29s溺:`0ICCB!ill$KSS477/IKK/imm$wQ;1tKD{kERf||/꿴muOtko-t}ln 3UYKfnM|{Fܚkĭi&]n 3 Zf_555FܚfR]]mĭi&UUUFܚfC~fkIEE0k̭e&FܚfRVVfmx<n sWWncn-3q\v4id5ͤĘ[D<4ad5ͤVfRPP`.sww6rմٴ[˼wscp9ekkkyJ y^a>=);RZZqgg}㎩kXHNINXI8 ks`[XJ-cjun.EsVg>,dfx$jA҉lf^\jB)C#\3Θ.Ww]\F~~C#A3sqVe?hivelytracker-0+git20180223/Press/UpRough.EPS0000775001024000102400000003470313042730153015444 0ustar 87.2!6GIFmation!" 2.3.2!6GIFmation!" 2.3.2!6GIFmation!" 2.3.2!6GIFmation!" 2.3.2!6GIFmation!" 2.3xHH%08AG{HH(d'h'&Mortimer Twang & plus8 / Up Rough 2000www.uprough.com^''AuthSTR 'KeyWSTR#'DescTEXT'PrevPICT6x6x=   = = 4= HHVr8ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""= = 44}44}54}B@IIJMOOPZ[[[WVVVWWW[[[UQ]]\[[WW[[Z[[]][NMSRNN==-121111= HHVr8ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ff33̙ff33̙̙̙̙ff̙33̙ffffffffffff33ff33333333ff333333ff33ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33ffffffffffff33ffffffff̙ffffff33ffffffffffffff33ffffffffffffffffffffffff33ffffff33ff33ff33ff33ffff3333ff33ffffffffffff33ff33333333ff333333333333̙33ff33333333333333ff33333333ff33ff33ff33ffff33ff3333ff3333333333333333ff333333333333333333ff333333ff33̙ff33ff33ffffffffffff33ff33333333ff333333ff33wwUUDD""wwUUDD""wwUUDD""wwwwwwUUUUUUDDDDDD""""""= = $44}44}54}B@IIJMOOPZ[[[WVVVWWW[[[UQ]]\[[WW[[Z[[]][NMSRNN==-12111187VP8PRECBTEMPNPAGEZSTR fSTR#rTEXT~pnotPICTV |VV Vv'V0Vq<<VH9Vp Print RecordTemplate Page SizeAuthorKeywords Description InformationPreviewhivelytracker-0+git20180223/sdl/0000775001024000102400000000000013250507415013162 5ustar hivelytracker-0+git20180223/sdl/winicon.png0000664001024000102400000001103613042730153015333 0ustar PNG  IHDR@@% pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FIIDATxoWϹ̮]$4U Px@_/GBH}"Dŏ""!RDUqfػ̽$.1R=o9s} `0 -1.ڶZX ~Ȧ&pg߽x6m8{3{| 'O,\w24K{Kiܼ?,i6٬Y @\^¿"!|֝%"D;r2{×n-,fcaW}ϜġF}泒?kyq)ڽp?dg(g)[ZNn'0lkl0/Y]v?ں5ߩ,1Z6 @v.Mp,gY?VlpA_ Р|\ 4pD g;])gF΅tog0t3)G @GcHD @)[ɔlb+ZtIڮn82ԣQY(ϼ @jm-[#HijMjj),`ZWk 3owy1D՛QtDMyjsol\%̫Ve +vy9Z(7o7|+PxejtcH+3 JUU^x={s#bhc#KGH1<{~zTcjՊ'|50qvٌj8ϽwGBXk+5o@I uO?Ν @q:50 9ƘjݻO2%؋/D0QWҫgM)yl1&I6+̈́^ft-+X;0I$o `0 `:rXIENDB`hivelytracker-0+git20180223/sdl/sdl_wrapper.c0000664001024000102400000001760713042730153015657 0ustar #include #include #include static APTR _AllocVecTags(uint32 size, ...) { return malloc(size); } static void _FreeVec(APTR ptr) { if (ptr) free(ptr); } static APTR _AllocSysObjectTags(uint32 type, ...) { APTR result = NULL; switch (type) { case ASOT_SEMAPHORE: { struct SignalSemaphore *sem = malloc(sizeof(struct SignalSemaphore)); if (!sem) return NULL; sem->mtx = SDL_CreateMutex(); if (!sem->mtx) { free(sem); return NULL; } result = sem; break; } case ASOT_LIST: { struct List *list = malloc(sizeof(struct List)); if (!list) return NULL; list->lh_Head = (struct Node *)(&list->lh_Tail); list->lh_Tail = NULL; list->lh_TailPred = (struct Node *)(&list->lh_Head); result = list; } } return result; } static void _FreeSysObject(uint32 type, APTR ptr) { switch (type) { case ASOT_SEMAPHORE: { struct SignalSemaphore *sem = (struct SignalSemaphore *)ptr; if (sem) { SDL_DestroyMutex(sem->mtx); free(sem); } break; } case ASOT_LIST: case ASOT_NODE: free(ptr); break; } } static void _ObtainSemaphore(struct SignalSemaphore *sem) { SDL_LockMutex(sem->mtx); } static void _ReleaseSemaphore(struct SignalSemaphore *sem) { SDL_UnlockMutex(sem->mtx); } static void _CopyMem(APTR src, APTR dest, uint32 size) { memcpy(dest, src, size); } static struct Node *_GetHead(struct List *list) { if ((!list) || (!list->lh_Head)) return NULL; if (list->lh_Head->ln_Succ == NULL) return NULL; return list->lh_Head; } static struct Node *_GetSucc(struct Node *node) { if (node->ln_Succ->ln_Succ == NULL) return NULL; return node->ln_Succ; } static void _AddTail(struct List *list, struct Node *node) { struct Node *tmp; tmp = list->lh_TailPred; list->lh_TailPred = node; node->ln_Succ = (struct Node *)&list->lh_Tail; node->ln_Pred = tmp; tmp->ln_Succ = node; } static void _Remove(struct Node *node) { node->ln_Succ->ln_Pred = node->ln_Pred; node->ln_Pred->ln_Succ = node->ln_Succ; } static APTR _AllocPooled(APTR pool, uint32 size) { return malloc(size); } static void _FreePooled(APTR pool, APTR ptr, uint32 size) { free(ptr); } static struct Node *_RemHead(struct List *list) { struct Node *node; if ((!list) || (!list->lh_Head) || (!list->lh_Head->ln_Succ)) return NULL; node = list->lh_Head; list->lh_Head = node->ln_Succ; node->ln_Succ->ln_Pred = (struct Node *)list; return node; } static struct Node *_GetTail(struct List *list) { if ((!list) || (!list->lh_TailPred) || (!list->lh_TailPred->ln_Pred)) return NULL; return list->lh_TailPred; } static struct Node *_RemTail(struct List *list) { struct Node *n = list->lh_TailPred; if (n->ln_Pred) { list->lh_TailPred = n->ln_Pred; list->lh_TailPred->ln_Succ = (struct Node *)(&(list->lh_Tail)); return n; } return NULL; } static struct ExecIFace _exec = { .AllocVecTags = _AllocVecTags, .FreeVec = _FreeVec, .AllocSysObjectTags = _AllocSysObjectTags, .FreeSysObject = _FreeSysObject, .ObtainSemaphore = _ObtainSemaphore, .ReleaseSemaphore = _ReleaseSemaphore, .CopyMem = _CopyMem, .GetHead = _GetHead, .GetSucc = _GetSucc, .AddTail = _AddTail, .Remove = _Remove, .AllocPooled = _AllocPooled, .FreePooled = _FreePooled, .RemHead = _RemHead, .GetTail = _GetTail, .RemTail = _RemTail }; struct ExecIFace *IExec = &_exec; extern BOOL pref_rctrlplaypos; uint16 sdl_keysym_to_amiga_rawkey(SDLKey keysym) { /* Convert SDL key symbols to Amiga raw keys */ switch (keysym) { case SDLK_F12: return 13; // \ = drum pad mode (F12 on PC keyboards) case SDLK_BACKSLASH: return 48; // Blank next to Z (shut up) (backslash on PC keyboards) case SDLK_MINUS: return 11; // Minus case SDLK_EQUALS: return 12; // Equals case SDLK_F1: return 80; case SDLK_F2: return 81; case SDLK_F3: return 82; case SDLK_F4: return 83; case SDLK_F5: return 84; case SDLK_F6: return 85; case SDLK_F7: return 86; case SDLK_F8: return 87; case SDLK_F9: return 88; case SDLK_F10: return 89; case SDLK_ESCAPE: return 69; case SDLK_MENU: return 103; case SDLK_RSUPER: return 103; case SDLK_RMETA: return 103; case SDLK_RALT: return 101; #ifdef __linux__ /* On ubuntu, Alt Gr seems to return this (313) instead of SDLK_RALT (307) */ case 313: return 101; #endif case SDLK_KP_PLUS: return 94; case SDLK_KP_MINUS: return 74; case SDLK_LEFT: return 79; case SDLK_RIGHT: return 78; case SDLK_UP: return 76; case SDLK_DOWN: return 77; case SDLK_BACKSPACE: return 65; case SDLK_RETURN: return 68; case SDLK_1: return 1; case SDLK_2: return 2; case SDLK_3: return 3; case SDLK_4: return 4; case SDLK_5: return 5; case SDLK_6: return 6; case SDLK_7: return 7; case SDLK_8: return 8; case SDLK_9: return 9; case SDLK_0: return 10; case SDLK_KP0: return 0x0f; case SDLK_KP1: return 0x1d; case SDLK_KP2: return 0x1e; case SDLK_KP3: return 0x1f; case SDLK_KP4: return 0x2d; case SDLK_KP5: return 0x2e; case SDLK_KP6: return 0x2f; case SDLK_KP7: return 0x3d; case SDLK_KP8: return 0x3e; case SDLK_KP9: return 0x3f; case SDLK_KP_PERIOD: return 60; case SDLK_q: return 16; case SDLK_w: return 17; case SDLK_e: return 18; case SDLK_r: return 19; case SDLK_t: return 20; case SDLK_y: return 21; case SDLK_u: return 22; case SDLK_i: return 23; case SDLK_o: return 24; case SDLK_p: return 25; case SDLK_LEFTBRACKET: return 26; case SDLK_RIGHTBRACKET: return 27; case SDLK_a: return 32; case SDLK_s: return 33; case SDLK_d: return 34; case SDLK_f: return 35; case SDLK_g: return 36; case SDLK_h: return 37; case SDLK_j: return 38; case SDLK_k: return 39; case SDLK_l: return 40; case SDLK_SEMICOLON: return 41; case SDLK_QUOTE: return 42; case SDLK_HASH: return 43; case SDLK_z: return 49; case SDLK_x: return 50; case SDLK_c: return 51; case SDLK_v: return 52; case SDLK_b: return 53; case SDLK_n: return 54; case SDLK_m: return 55; case SDLK_COMMA: return 56; case SDLK_PERIOD: return 57; case SDLK_SLASH: return 58; case SDLK_SPACE: return 64; case SDLK_TAB: return 0x42; case SDLK_DELETE: return 0x46; case SDLK_RCTRL: if (pref_rctrlplaypos) return 103; /* My linux laptop has no RSUPER or MENU, so use RCTRL */ break; } return 0; } hivelytracker-0+git20180223/sdl/linux.c0000664000000000000000000001064413042730153016260 0ustar rootroot #include #include #include #include #include #include #include #include "sdl_wrapper.h" #include typedef Uint32 uint32; typedef Sint32 int32; typedef char TEXT; int32 gui_req( uint32 img, TEXT *title, TEXT *reqtxt, TEXT *buttons ) { GtkWidget *dialog = NULL; GtkButtonsType btns = strcmp((char*)buttons, "OK") ? GTK_BUTTONS_OK_CANCEL : GTK_BUTTONS_OK; GtkMessageType mtyp = GTK_MESSAGE_INFO; SDL_bool result = SDL_FALSE; gint res; dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, mtyp, btns, "%s", reqtxt); gtk_window_set_title(GTK_WINDOW (dialog), title); res = gtk_dialog_run(GTK_DIALOG (dialog)); if ((res == GTK_RESPONSE_OK) || (res == GTK_RESPONSE_YES) || (res == GTK_RESPONSE_ACCEPT)) result = SDL_TRUE; gtk_widget_destroy(dialog); while (gtk_events_pending()) gtk_main_iteration(); return result; } BOOL directoryrequester( char *title, char *path ) { GtkWidget *dialog; GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; BOOL result = FALSE; dialog = gtk_file_chooser_dialog_new(title, NULL, action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { char *filename; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); strncpy(path, filename, 512); path[511] = 0; g_free(filename); result = TRUE; } gtk_widget_destroy(dialog); while (gtk_events_pending()) gtk_main_iteration(); return result; } #define FR_HVLSAVE 0 #define FR_AHXSAVE 1 #define FR_INSSAVE 2 #define FR_MODLOAD 3 #define FR_INSLOAD 4 char *filerequester( char *title, char *path, char *fname, int type ) { GtkWidget *dialog; GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; GtkFileFilter *filter = NULL, *allfilter = gtk_file_filter_new(); char *result = NULL; char *accept_str = GTK_STOCK_OPEN; gtk_file_filter_set_name(allfilter, "All files"); gtk_file_filter_add_pattern(allfilter, "*"); switch( type ) { case FR_HVLSAVE: action = GTK_FILE_CHOOSER_ACTION_SAVE; accept_str = GTK_STOCK_SAVE; filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "HVL module (*.hvl)"); gtk_file_filter_add_pattern(filter, "*.hvl"); break; case FR_AHXSAVE: action = GTK_FILE_CHOOSER_ACTION_SAVE; accept_str = GTK_STOCK_SAVE; filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "AHX module (*.ahx)"); gtk_file_filter_add_pattern(filter, "*.ahx"); break; case FR_INSSAVE: action = GTK_FILE_CHOOSER_ACTION_SAVE; accept_str = GTK_STOCK_SAVE; case FR_INSLOAD: filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "Instrument (*.ins)"); gtk_file_filter_add_pattern(filter, "*.ins"); break; case FR_MODLOAD: filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "Modules (*.ahx, *.hvl, *.mod)"); gtk_file_filter_add_pattern(filter, "*.ahx"); gtk_file_filter_add_pattern(filter, "*.hvl"); gtk_file_filter_add_pattern(filter, "*.mod"); break; default: break; } dialog = gtk_file_chooser_dialog_new(title, NULL, action, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, accept_str, GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); if (action == GTK_FILE_CHOOSER_ACTION_SAVE) gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), fname); if (filter) gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), allfilter); if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { char *filename; int i; filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); result = malloc(strlen(filename)+1); strcpy(result, filename); g_free(filename); } gtk_widget_destroy(dialog); while (gtk_events_pending()) gtk_main_iteration(); return result; } hivelytracker-0+git20180223/sdl/hively.rdef0000664001024000102400000000045613042730153015325 0ustar resource(0) #'VICN' array { $"6E636966030501020006030000003C7FFFC000000000004C00004A400000FFFF" $"FF7F001736FFFFFFFF034D526D020A0C243424542C542C48344834543C543C34" $"343434402C402C340A0A403440545C545C4C484C48485C485C4048404834030A" $"000201001001178500040A020201001839FF01178200040A0102000100" }; hivelytracker-0+git20180223/sdl/Makefile.linux0000664001024000102400000000222713042730153015757 0ustar WRAPPERDIR := $(CURDIR) BASEDIR = $(CURDIR)/.. CC = gcc CFLAGS = -g -D__SDL_WRAPPER__ ifeq (x86_64,$(shell uname -m)) BASELIBDIR := lib64 CFLAGS += -m64 LFLAGS += -m64 else BASELIBDIR := lib CFLAGS += -m32 LFLAGS += -m32 endif CFLAGS += -g $(shell PKG_CONFIG_PATH=/usr/$(BASELIBDIR)/pkgconfig pkg-config sdl --cflags) $(shell PKG_CONFIG_PATH=/usr/$(BASELIBDIR)/pkgconfig pkg-config gtk+-2.0 --cflags) LFLAGS += -lm -L/usr/$(BASELIBDIR) $(shell PKG_CONFIG_PATH=/usr/$(BASELIBDIR)/pkgconfig pkg-config sdl --libs) $(shell PKG_CONFIG_PATH=/usr/$(BASELIBDIR)/pkgconfig pkg-config gtk+-2.0 --libs) -lSDL_image -lSDL_ttf -lfreetype -lz -lX11 CFLAGS += -I$(BASEDIR) \ -I$(WRAPPERDIR) \ -Ic:/mingw/include/SDL TARGET = hivelytracker OBJECTS = sdl_wrapper.o \ ht.o \ linux.o \ ../gui.o \ ../replay.o \ ../util.o \ ../undo.o \ ../about.o all: $(TARGET) -include $(OBJECTS:.o=.d) $(TARGET): $(OBJECTS) $(CC) -o $(TARGET) $(OBJECTS) $(LFLAGS) $(OBJECTS): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ @$(CC) -MM $(CFLAGS) $< > $*.d clean: rm $(OBJECTS) $(OBJECTS:.o=.d) $(TARGET) hivelytracker-0+git20180223/sdl/winicon.ico0000664001024000102400000003210013042730153015314 0ustar @@*4(@fDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDhFoInInInInInInInInInInIoIjFeDeDkGoInInInInInInInInInInInInInInInInInInInInInInInInInInInInInInInInInInIoIhFfDfDfDfDfDfDfDfDfDiFtW:C2"G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$B1!hN4jGiF\E.C2"G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$G5$C2"tW:iFfDfDfDfDfDfDfDfDlH_G/F4#nImI,!_G/lHfDfDfDfDfDfDfDfDlH_G/F4#nImI,!_G/lHfDfDfDfDfDfDfDfDlH_G/LLLkkkF4#nImI,!%%%%%%LLL_G/lHfDfDfDfDfDfDfDfDlH_G/wwwF4#nImI,!999999www_G/lHfDfDfDfDfDfDfDfDlH_G/wwwF4#nImI,!999999www_G/lHfDfDfDfDfDfDfDfDlH_G/wwwF4#nImI,!887:::uts_G/lHfDfDfDfDfDfDfDfDlH_G/wwxF4#nImI,!753:;qlg_G/lHfDfDfDfDfDfDfDfDlH_G/xyz՛F4#nImI,!4/)<>Aoe]_G/lHfDfDfDfDfDfDfDfDlH_G/xz{ϷһһһǘzH6$qKpK."һһһҼ2*#<@DʹһһһһӼ־־־־־־־־־־־־־־odX_G/lHfDfDfDfDfDfDfDfDlH_G/xz|Ǫʮʮʮ׺q=.`A_@'Ӷʮʮʮ˯ӵ0'>BFĦʮʮʮʭ̴fdb_G/lHfDfDfDfDfDfDfDfDlH_G/y{}۹޿޿޿̬|iǩ޿޿޿)!@DH׵޿޿Ƨ_G/lHfDfDfDfDfDfDfDfDlH_G/{}~ҾƤ˭˭˭ظq`Ե˭˭˭ͮƦEIK̮˭˭Ӵy_G/lHfDfDfDfDfDfDfDfDlH_G/~t¤ta'!1)"1)"1)"1)"1)"1)"*#źo KNPµir+$1)"1)"1)"1)"1)"1)"1)"1)"1)"1)"1)"1)"1)"5,%_G/lHfDfDfDfDfDfDfDfDlH_G/hZopposzyyyyyyzlpppp~mwbUORSt]OopppryyyyyyyyyyyyyyOB8_G/lHfDfDfDfDfDfDfDfDlH_G/umL=4cRHnZQs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vr^UiVM^NEL?4SUWB4*aQGlYOs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_Vs_V|f]5,(_G/lHfDfDfDfDfDfDfDfDlH_G/|si9/%J?7OC:NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9OB9MB9H=5A6*TUW1'J?6NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9NB9TG=$_G/lHfDfDfDfDfDfDfDfDlH_G/{qg.!6*5*5*7, 4(- .!.!.!.!.!.!.!/"7+6+5*5*7+;.!TVW)6+5*5*6+5).!.!.!.!.!.!.!.!.!.!.!.!.!.!2$ _G/lHfDfDfDfDfDfDfDfDlH_G/re6#E2H5"E2:(UE7}qwkwkwkwkwkwkymm`T<*A/G4!G4!B0B/SUX1 D1H5"F3 =+I8)znwkwkwkwkwkwkwkwkwkwkwkwkwks=72_G/lHfDfDfDfDfDfDfDfDlH_G/~oXjmjt]{lbsvxtuvtuvtuvtuvtuvtuvqsuӇn\|dlm~fua JMPŷhRimkw`~k]suwtuvtuvtuvtuvtuvtuvtuvtuvtuvtuvtuvtuvtuv}~667_G/lHfDfDfDfDfDfDfDfDlH_G/z|}̸ҴڻܻڻzgWϱټۻۻټ׹$BFIͮڻܻڻq_G/lHfDfDfDfDfDfDfDfDlH_G/yz{ύ|mѼ-%>BEѼζ_G/lHfDfDfDfDfDfDfDfDlH_G/xxy盓5(pT8oT8"41.<=?Ż"oT8hN4hN4hN4hN4hN4hN4hN4hN4hN4hN4hN4hN4hN4fL3~_?gEfDfDfDfDfDfDfDfDlH_G/wxxI7$sMsL/#752:;=ž/#sLkGkGkGkGkGkGkGkGkGkGkGkGkGkGhEfDfDfDfDfDfDfDfDfDlH_G/wxxF4#nImI,!630;<>,!mIfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDlH_G/wxyF4#nImI,!51-;=?Ǻ,!mIfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDlH_G/wxz۞F4#nImI,!60+;>@ɸ,!mIfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDlH_G/llm앒F4#nImI,!321556彺,!mIfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDlH_G/F4#nImI,!,!mIfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDlH_G/F4#nImI,!,!mIfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDkGhN4  U?*lHlHA1   A1 lHfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDhElHlHlHlHlHlHlHlHlHlHlHlHiFfDfDjGlHlHlHlHlHlHlHlHlHlHlHlHlHlHlHlHlHlHlHlHjGfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDhivelytracker-0+git20180223/sdl/hivelytracker.rc0000664001024000102400000000002613042730153016356 0ustar 101 ICON winicon.ico hivelytracker-0+git20180223/sdl/Makefile.haiku0000664001024000102400000000142413042730153015717 0ustar WRAPPERDIR := $(CURDIR) BASEDIR = $(CURDIR)/.. CC = gcc CFLAGS = -g -D__SDL_WRAPPER__ CFLAGS += -I$(BASEDIR) \ -I$(WRAPPERDIR) \ `sdl-config --cflags` LFLAGS += -g -lSDL_ttf `sdl-config --libs` -lSDL_image -lfreetype -lz -lbe -ltracker TARGET = hivelytracker OBJECTS = sdl_wrapper.o \ ht.o \ ../gui.o \ ../replay.o \ ../util.o \ ../undo.o \ ../about.o all: $(TARGET) -include $(OBJECTS:.o=.d) $(TARGET): $(OBJECTS) haiku.o $(CC) -o $(TARGET) $(OBJECTS) haiku.o $(LFLAGS) $(OBJECTS): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ @$(CC) -MM $(CFLAGS) $< > $*.d %.o: %.cpp $(CC) -c $(CFLAGS) $< -o $@ @$(CC) -MM $(CFLAGS) $< > $*.d clean: rm $(OBJECTS) $(OBJECTS:.o=.d) $(TARGET) winicon.o hivelytracker-0+git20180223/sdl/haiku.cpp0000664001024000102400000001065213042730153014767 0ustar #include #include #include #include #include #include #include #include #include #include #include #include //#include "sdl_wrapper.h" // Conflicts with SupportDefs.h #define FR_HVLSAVE 0 #define FR_AHXSAVE 1 #define FR_INSSAVE 2 #define FR_MODLOAD 3 #define FR_INSLOAD 4 #define REQIMAGE_QUESTION 0 #define REQIMAGE_WARNING 1 #define REQIMAGE_ERROR 2 extern "C" int32 gui_req( uint32 img, const char *title, const char *reqtxt, const char *buttons ) { const char* button[3]; // Since buttons is a const char*, we can't use strtok directly on it. // Work on a copy. char* buffer = strdup(buttons); button[0] = strtok(buffer, "|"); button[1] = strtok(NULL, "|"); button[2] = strtok(NULL, "|"); // TODO we don't handle the _ char in the button labels alert_type type; switch(img) { case REQIMAGE_QUESTION: type = B_INFO_ALERT; break; case REQIMAGE_WARNING: type = B_WARNING_ALERT; break; case REQIMAGE_ERROR: type = B_STOP_ALERT; break; } BAlert* alert = new BAlert(title, reqtxt, button[0], button[1], button[2], B_WIDTH_AS_USUAL, type); free(buffer); return alert->Go() == 0; } class FileHandler: public BLooper { public: FileHandler() : BLooper("filepanel listener") { lock = create_sem(0, "filepanel sem"); path = NULL; Run(); } ~FileHandler() { delete_sem(lock); free(path); } void MessageReceived(BMessage* message) { switch(message->what) { case B_REFS_RECEIVED: { entry_ref ref; message->FindRef("refs", 0, &ref); BEntry entry(&ref); BPath xpath; entry.GetPath(&xpath); path = strdup(xpath.Path()); break; } case B_SAVE_REQUESTED: { entry_ref ref; message->FindRef("directory", 0, &ref); BEntry entry(&ref); BPath xpath; entry.GetPath(&xpath); const char* dir = xpath.Path(); const char* name; message->FindString("name", 0, &name); path = (char*)malloc(strlen(name) + strlen(dir) + 1); strcpy(path, dir); strcat(path, name); break; } case B_CANCEL: { // If the path was set, do not change it. // If it was not set, leave it null and HT will not load anything break; } default: printf("Unhandled message %x\n", message->what); BHandler::MessageReceived(message); return; } release_sem(lock); } void Wait() { acquire_sem(lock); } char* path; private: sem_id lock; }; extern "C" bool directoryrequester( char *title, char *path ) { BEntry entry(path); entry_ref ref; entry.GetRef(&ref); FileHandler* handler = new FileHandler; BMessenger messenger(handler); BFilePanel* panel = new BFilePanel(B_OPEN_PANEL, &messenger, &ref, B_DIRECTORY_NODE, false); BWindow* win = panel->Window(); win->LockLooper(); win->SetTitle(title); win->UnlockLooper(); panel->Show(); handler->Wait(); delete panel; if (handler->path) { strcpy(path, handler->path); return true; } else return false; } extern "C" char *filerequester( char *title, char *path, char *fname, int type ) { file_panel_mode mode; switch(type) { case FR_MODLOAD: case FR_INSLOAD: mode = B_OPEN_PANEL; break; default: mode = B_SAVE_PANEL; } BEntry entry(path); entry_ref ref; entry.GetRef(&ref); FileHandler* handler = new FileHandler; BMessenger messenger(handler); BFilePanel* panel = new BFilePanel(mode, &messenger, &ref, B_FILE_NODE, false); BWindow* win = panel->Window(); win->LockLooper(); win->SetTitle(title); if (mode == B_SAVE_PANEL) { BView* background = win->ChildAt(0); BView* nameView = background->FindView("text view"); BTextControl* txt = dynamic_cast(nameView); txt->SetText(fname); } win->UnlockLooper(); panel->Show(); handler->Wait(); delete panel; char* returnpath; if (handler->path) returnpath = strdup(handler->path); else returnpath = NULL; handler->LockLooper(); handler->Quit(); return returnpath; } extern "C" void find_home() { // Fix for haiku not starting apps in their home directory image_info info; int32 cookie; get_next_image_info(0, &cookie, &info); chdir(dirname(info.name)); } hivelytracker-0+git20180223/sdl/ht.c0000664001024000102400000000525113042730153013740 0ustar /* ** HivelyTracker SDL !!!!11 */ #include #include #include #include #include #include #ifdef __linux__ #include #endif BOOL quitting = FALSE; extern BOOL pref_dorestart; extern BOOL needaflip; extern SDL_Surface *ssrf; int srfdepth = 16; extern BOOL pref_fullscr; extern BOOL aboutwin_open; SDL_Event event; BOOL hively_init( void ) { const SDL_VideoInfo *info = NULL; gui_pre_init(); about_pre_init(); // Go SDL! if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER ) < 0 ) { printf( "SDL init failed" ); return FALSE; } atexit(SDL_Quit); if( (info = SDL_GetVideoInfo()) ) srfdepth = info->vfmt->BitsPerPixel; SDL_WM_SetIcon( SDL_LoadBMP( "winicon.bmp" ), NULL ); SDL_EnableUNICODE(SDL_FALSE); // Try to setup the video display #if defined(WIN32) || defined(__APPLE__) || defined(__linux__) // requesters cause all kinds of problems for fullscreen on windows and OSX, so ignore it ssrf = SDL_SetVideoMode( 800, 600, srfdepth, SRFTYPE ); #else ssrf = SDL_SetVideoMode( 800, 600, srfdepth, pref_fullscr ? SDL_FULLSCREEN : SRFTYPE ); #endif if( !ssrf ) { printf( "SDL video failed\n" ); return FALSE; } SDL_WM_SetCaption( "HivelyTracker 1.8", "HivelyTracker 1.8" ); SDL_EnableKeyRepeat(500, 30); if (TTF_Init() == -1) { printf( "SDL TTF failed\n" ); return FALSE; } atexit(TTF_Quit); if( !rp_init() ) return FALSE; if( !gui_init() ) return FALSE; if( !about_init() ) return FALSE; return TRUE; } static void hively_shutdown( void ) { about_shutdown(); gui_shutdown(); rp_shutdown(); } int main( int argc, char *argv[] ) { #ifdef __linux__ if (!gtk_init_check(&argc, &argv)) { printf("GTK is sad :-(\n"); return 0; } #endif #ifdef __HAIKU__ // Fix for haiku not starting apps in their home directory find_home(); #endif if( hively_init() ) { SDL_Flip(ssrf); quitting = FALSE; while( !quitting ) { if (needaflip) { SDL_Flip(ssrf); needaflip = FALSE; } if( !SDL_WaitEvent( &event ) ) break; do { switch( event.type ) { case SDL_QUIT: quitting = gui_maybe_quit(); break; default: if (aboutwin_open) about_handler(0); else gui_handler(0); break; } } while (SDL_PollEvent(&event)); if( pref_dorestart ) { if( !gui_restart() ) quitting = TRUE; pref_dorestart = FALSE; } } rp_stop(); } hively_shutdown(); return 0; } hivelytracker-0+git20180223/sdl/osx/0000775001024000102400000000000013042730153013767 5ustar hivelytracker-0+git20180223/sdl/osx/xcode/0000775001024000102400000000000013042730153015071 5ustar hivelytracker-0+git20180223/sdl/osx/xcode/English.lproj/0000775001024000102400000000000013042730153017607 5ustar hivelytracker-0+git20180223/sdl/osx/xcode/English.lproj/InfoPlist.strings0000664001024000102400000000105413042730153023131 0ustar /* Localized versions of Info.plist keys */ CFBundleName = "Hivelytracker"; CFBundleShortVersionString = "Hivelytracker version 1.8"; CFBundleGetInfoString = "Hivelytracker version 1.8, Copyright 2013 Iris/UpRough."; NSHumanReadableCopyright = "Copyright 2013 Iris/UpRough."; hivelytracker-0+git20180223/sdl/osx/xcode/Hivelytracker.xcodeproj/0000775001024000102400000000000013042730153021701 5ustar hivelytracker-0+git20180223/sdl/osx/xcode/Hivelytracker.xcodeproj/xcshareddata/0000775001024000102400000000000013042730153024334 5ustar hivelytracker-0+git20180223/sdl/osx/xcode/Hivelytracker.xcodeproj/xcshareddata/xcschemes/0000775001024000102400000000000013042730153026316 5ustar ././@LongLink0000644000000000000000000000016000000000000011600 Lustar rootroothivelytracker-0+git20180223/sdl/osx/xcode/Hivelytracker.xcodeproj/xcshareddata/xcschemes/Hivelytracker.xcschemehivelytracker-0+git20180223/sdl/osx/xcode/Hivelytracker.xcodeproj/xcshareddata/xcschemes/Hivelytrack0000664001024000102400000000620713042730153030533 0ustar hivelytracker-0+git20180223/sdl/osx/xcode/Hivelytracker.xcodeproj/project.pbxproj0000664001024000102400000005435213042730153024766 0ustar // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 002F3A2E09D0888800EBEB88 /* SDLMain.m in Sources */ = {isa = PBXBuildFile; fileRef = 002F3A2C09D0888800EBEB88 /* SDLMain.m */; }; 830D2602163DCF880045F28B /* NSFileManager+DirectoryLocations.m in Sources */ = {isa = PBXBuildFile; fileRef = 830D2601163DCF880045F28B /* NSFileManager+DirectoryLocations.m */; }; 830D277F163E13A70045F28B /* DejaVuSans.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 830D277D163E13A70045F28B /* DejaVuSans.ttf */; }; 830D2780163E13A70045F28B /* DejaVuSansMono.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 830D277E163E13A70045F28B /* DejaVuSansMono.ttf */; }; 830E7A9D162F353000AF00A8 /* SDL_image.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830E7A9C162F353000AF00A8 /* SDL_image.framework */; }; 830E7A9F162F353000AF00A8 /* SDL_ttf.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830E7A9E162F353000AF00A8 /* SDL_ttf.framework */; }; 830E7AA1162F353000AF00A8 /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830E7AA0162F353000AF00A8 /* SDL.framework */; }; 830E7B1C162F354100AF00A8 /* SDL_image.framework in Copy Frameworks into .app bundle */ = {isa = PBXBuildFile; fileRef = 830E7A9C162F353000AF00A8 /* SDL_image.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 830E7B1D162F354100AF00A8 /* SDL_ttf.framework in Copy Frameworks into .app bundle */ = {isa = PBXBuildFile; fileRef = 830E7A9E162F353000AF00A8 /* SDL_ttf.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 830E7B1E162F354100AF00A8 /* SDL.framework in Copy Frameworks into .app bundle */ = {isa = PBXBuildFile; fileRef = 830E7AA0162F353000AF00A8 /* SDL.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 8367E2D616342BEF00E0C1C1 /* about.c in Sources */ = {isa = PBXBuildFile; fileRef = 8367E2D516342BEF00E0C1C1 /* about.c */; }; 8367E315163607FA00E0C1C1 /* hivelytracker.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8367E314163607FA00E0C1C1 /* hivelytracker.icns */; }; 83961D27162C9C7A00B4B9E0 /* gui.c in Sources */ = {isa = PBXBuildFile; fileRef = 83961D1C162C9C7A00B4B9E0 /* gui.c */; }; 83961D29162C9C7A00B4B9E0 /* replay.c in Sources */ = {isa = PBXBuildFile; fileRef = 83961D1F162C9C7A00B4B9E0 /* replay.c */; }; 83961D2A162C9C7A00B4B9E0 /* undo.c in Sources */ = {isa = PBXBuildFile; fileRef = 83961D22162C9C7A00B4B9E0 /* undo.c */; }; 83961D2B162C9C7A00B4B9E0 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 83961D24162C9C7A00B4B9E0 /* util.c */; }; 83961D30162C9CBD00B4B9E0 /* ht.c in Sources */ = {isa = PBXBuildFile; fileRef = 83961D2D162C9CBD00B4B9E0 /* ht.c */; }; 83961D31162C9CBD00B4B9E0 /* sdl_wrapper.c in Sources */ = {isa = PBXBuildFile; fileRef = 83961D2E162C9CBD00B4B9E0 /* sdl_wrapper.c */; }; 83961E55162CCBE800B4B9E0 /* osx.m in Sources */ = {isa = PBXBuildFile; fileRef = 83961E54162CCBE800B4B9E0 /* osx.m */; }; 839620BA162EC0D300B4B9E0 /* Skins in Resources */ = {isa = PBXBuildFile; fileRef = 8396202E162EC0D200B4B9E0 /* Skins */; }; 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ 002F39FD09D0883400EBEB88 /* Copy Frameworks into .app bundle */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( 830E7B1C162F354100AF00A8 /* SDL_image.framework in Copy Frameworks into .app bundle */, 830E7B1D162F354100AF00A8 /* SDL_ttf.framework in Copy Frameworks into .app bundle */, 830E7B1E162F354100AF00A8 /* SDL.framework in Copy Frameworks into .app bundle */, ); name = "Copy Frameworks into .app bundle"; runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 002F3A2B09D0888800EBEB88 /* SDLMain.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = SDLMain.h; sourceTree = SOURCE_ROOT; }; 002F3A2C09D0888800EBEB88 /* SDLMain.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = SDLMain.m; sourceTree = SOURCE_ROOT; }; 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 35DEA5081A36F3FD0085CFB3 /* gui.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gui.h; path = ../../../gui.h; sourceTree = ""; }; 35DEA5091A3701030085CFB3 /* undo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = undo.h; path = ../../../undo.h; sourceTree = ""; }; 35DEA50A1A3701030085CFB3 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = util.h; path = ../../../util.h; sourceTree = ""; }; 830D2600163DCF880045F28B /* NSFileManager+DirectoryLocations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFileManager+DirectoryLocations.h"; sourceTree = ""; }; 830D2601163DCF880045F28B /* NSFileManager+DirectoryLocations.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+DirectoryLocations.m"; sourceTree = ""; }; 830D277D163E13A70045F28B /* DejaVuSans.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = DejaVuSans.ttf; path = ../../../ttf/DejaVuSans.ttf; sourceTree = SOURCE_ROOT; }; 830D277E163E13A70045F28B /* DejaVuSansMono.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = DejaVuSansMono.ttf; path = ../../../ttf/DejaVuSansMono.ttf; sourceTree = SOURCE_ROOT; }; 830E7A9C162F353000AF00A8 /* SDL_image.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL_image.framework; path = /Library/Frameworks/SDL_image.framework; sourceTree = ""; }; 830E7A9E162F353000AF00A8 /* SDL_ttf.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL_ttf.framework; path = /Library/Frameworks/SDL_ttf.framework; sourceTree = ""; }; 830E7AA0162F353000AF00A8 /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = /Library/Frameworks/SDL.framework; sourceTree = ""; }; 8367E2D516342BEF00E0C1C1 /* about.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = about.c; path = ../../../about.c; sourceTree = SOURCE_ROOT; }; 8367E2D716342BEF00E0C1C1 /* Hivelytracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Hivelytracker.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8367E2E416342CC500E0C1C1 /* about.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = about.h; path = ../../../about.h; sourceTree = SOURCE_ROOT; }; 8367E2E516342CC500E0C1C1 /* replay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = replay.h; path = ../../../replay.h; sourceTree = SOURCE_ROOT; }; 8367E314163607FA00E0C1C1 /* hivelytracker.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = hivelytracker.icns; sourceTree = ""; }; 83961D1C162C9C7A00B4B9E0 /* gui.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = gui.c; path = ../../../gui.c; sourceTree = SOURCE_ROOT; }; 83961D1F162C9C7A00B4B9E0 /* replay.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = replay.c; path = ../../../replay.c; sourceTree = SOURCE_ROOT; }; 83961D22162C9C7A00B4B9E0 /* undo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = undo.c; path = ../../../undo.c; sourceTree = SOURCE_ROOT; }; 83961D24162C9C7A00B4B9E0 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = util.c; path = ../../../util.c; sourceTree = SOURCE_ROOT; }; 83961D2D162C9CBD00B4B9E0 /* ht.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ht.c; path = ../../ht.c; sourceTree = SOURCE_ROOT; }; 83961D2E162C9CBD00B4B9E0 /* sdl_wrapper.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sdl_wrapper.c; path = ../../sdl_wrapper.c; sourceTree = SOURCE_ROOT; }; 83961D52162C9EDC00B4B9E0 /* sdl_wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sdl_wrapper.h; path = ../../sdl_wrapper.h; sourceTree = SOURCE_ROOT; }; 83961E54162CCBE800B4B9E0 /* osx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = osx.m; sourceTree = ""; }; 8396202E162EC0D200B4B9E0 /* Skins */ = {isa = PBXFileReference; lastKnownFileType = folder; name = Skins; path = ../../../Skins; sourceTree = SOURCE_ROOT; }; 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 8D11072E0486CEB800E47090 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 830E7A9D162F353000AF00A8 /* SDL_image.framework in Frameworks */, 830E7A9F162F353000AF00A8 /* SDL_ttf.framework in Frameworks */, 830E7AA1162F353000AF00A8 /* SDL.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( 830D2600163DCF880045F28B /* NSFileManager+DirectoryLocations.h */, 830D2601163DCF880045F28B /* NSFileManager+DirectoryLocations.m */, 002F3A2B09D0888800EBEB88 /* SDLMain.h */, 002F3A2C09D0888800EBEB88 /* SDLMain.m */, 83961E54162CCBE800B4B9E0 /* osx.m */, ); name = Classes; sourceTree = ""; }; 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( 830E7A9C162F353000AF00A8 /* SDL_image.framework */, 830E7A9E162F353000AF00A8 /* SDL_ttf.framework */, 830E7AA0162F353000AF00A8 /* SDL.framework */, 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, ); name = "Linked Frameworks"; sourceTree = ""; }; 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( 29B97324FDCFA39411CA2CEA /* AppKit.framework */, 29B97325FDCFA39411CA2CEA /* Foundation.framework */, ); name = "Other Frameworks"; sourceTree = ""; }; 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( 8367E2D716342BEF00E0C1C1 /* Hivelytracker.app */, ); name = Products; sourceTree = ""; }; 29B97314FDCFA39411CA2CEA /* Hivelytracker */ = { isa = PBXGroup; children = ( 080E96DDFE201D6D7F000001 /* Classes */, 29B97315FDCFA39411CA2CEA /* Other Sources */, 29B97317FDCFA39411CA2CEA /* Resources */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, ); name = Hivelytracker; sourceTree = ""; }; 29B97315FDCFA39411CA2CEA /* Other Sources */ = { isa = PBXGroup; children = ( 83961D2C162C9CA200B4B9E0 /* SDL */, 8367E2E416342CC500E0C1C1 /* about.h */, 8367E2D516342BEF00E0C1C1 /* about.c */, 35DEA5081A36F3FD0085CFB3 /* gui.h */, 83961D1C162C9C7A00B4B9E0 /* gui.c */, 8367E2E516342CC500E0C1C1 /* replay.h */, 83961D1F162C9C7A00B4B9E0 /* replay.c */, 35DEA5091A3701030085CFB3 /* undo.h */, 83961D22162C9C7A00B4B9E0 /* undo.c */, 35DEA50A1A3701030085CFB3 /* util.h */, 83961D24162C9C7A00B4B9E0 /* util.c */, ); name = "Other Sources"; sourceTree = ""; }; 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( 8367E314163607FA00E0C1C1 /* hivelytracker.icns */, 8396202E162EC0D200B4B9E0 /* Skins */, 830D277C163E13920045F28B /* ttf */, 8D1107310486CEB800E47090 /* Info.plist */, 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, ); name = Resources; sourceTree = ""; }; 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, ); name = Frameworks; sourceTree = ""; }; 830D277C163E13920045F28B /* ttf */ = { isa = PBXGroup; children = ( 830D277D163E13A70045F28B /* DejaVuSans.ttf */, 830D277E163E13A70045F28B /* DejaVuSansMono.ttf */, ); name = ttf; sourceTree = ""; }; 83961D2C162C9CA200B4B9E0 /* SDL */ = { isa = PBXGroup; children = ( 83961D52162C9EDC00B4B9E0 /* sdl_wrapper.h */, 83961D2D162C9CBD00B4B9E0 /* ht.c */, 83961D2E162C9CBD00B4B9E0 /* sdl_wrapper.c */, ); name = SDL; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 8D1107260486CEB800E47090 /* Hivelytracker */ = { isa = PBXNativeTarget; buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Hivelytracker" */; buildPhases = ( 8D1107290486CEB800E47090 /* Resources */, 8D11072C0486CEB800E47090 /* Sources */, 8D11072E0486CEB800E47090 /* Frameworks */, 002F39FD09D0883400EBEB88 /* Copy Frameworks into .app bundle */, 83400D3B162FFD32008E4BC8 /* Fix framework paths */, ); buildRules = ( ); dependencies = ( ); name = Hivelytracker; productInstallPath = "$(HOME)/Applications"; productName = Hivelytracker; productReference = 8367E2D716342BEF00E0C1C1 /* Hivelytracker.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0600; ORGANIZATIONNAME = Hivelytracker; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Hivelytracker" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, Japanese, French, German, ); mainGroup = 29B97314FDCFA39411CA2CEA /* Hivelytracker */; projectDirPath = ""; projectRoot = "/../.."; targets = ( 8D1107260486CEB800E47090 /* Hivelytracker */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 8D1107290486CEB800E47090 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, 839620BA162EC0D300B4B9E0 /* Skins in Resources */, 8367E315163607FA00E0C1C1 /* hivelytracker.icns in Resources */, 830D277F163E13A70045F28B /* DejaVuSans.ttf in Resources */, 830D2780163E13A70045F28B /* DejaVuSansMono.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ 83400D3B162FFD32008E4BC8 /* Fix framework paths */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); name = "Fix framework paths"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "function relocateLibraryPath() {\n install_name_tool -change @rpath/$2.framework/Versions/A/$2 @executable_path/../Frameworks/$2.framework/Versions/A/$2 $CONFIGURATION_BUILD_DIR/$1\n}\n\nfunction stripFramework() {\n prefix=$CONFIGURATION_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/$1.framework\n if [ -h $prefix/Headers ]\n then\n ditto --arch x86_64 $prefix/Versions/A/$1 $prefix/Versions/A/$1.64\n mv $prefix/Versions/A/$1.64 $prefix/Versions/A/$1\n rm -r $prefix/Versions/A/Headers\n rm -r $prefix/Versions/A/Resources\n rm $prefix/Headers $prefix/Resources\n fi\n}\n\nrelocateLibraryPath $EXECUTABLE_PATH SDL_image\nrelocateLibraryPath $EXECUTABLE_PATH SDL_ttf\nrelocateLibraryPath $FRAMEWORKS_FOLDER_PATH/SDL_image.framework/SDL_image SDL\nrelocateLibraryPath $FRAMEWORKS_FOLDER_PATH/SDL_ttf.framework/SDL_ttf SDL\nstripFramework SDL\nstripFramework SDL_image\nstripFramework SDL_ttf"; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 8D11072C0486CEB800E47090 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 002F3A2E09D0888800EBEB88 /* SDLMain.m in Sources */, 83961D27162C9C7A00B4B9E0 /* gui.c in Sources */, 83961D29162C9C7A00B4B9E0 /* replay.c in Sources */, 83961D2A162C9C7A00B4B9E0 /* undo.c in Sources */, 83961D2B162C9C7A00B4B9E0 /* util.c in Sources */, 83961D30162C9CBD00B4B9E0 /* ht.c in Sources */, 83961D31162C9CBD00B4B9E0 /* sdl_wrapper.c in Sources */, 83961E55162CCBE800B4B9E0 /* osx.m in Sources */, 8367E2D616342BEF00E0C1C1 /* about.c in Sources */, 830D2602163DCF880045F28B /* NSFileManager+DirectoryLocations.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( 089C165DFE840E0CC02AAC07 /* English */, ); name = InfoPlist.strings; sourceTree = ""; }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Hivelytracker_Prefix.pch; GCC_PREPROCESSOR_DEFINITIONS = __SDL_WRAPPER__; HEADER_SEARCH_PATHS = ( "$(HOME)/Library/Frameworks/SDL.framework/Headers", /Library/Frameworks/SDL_ttf.framework/Headers, /Library/Frameworks/SDL_image.framework/Headers, /Library/Frameworks/SDL.framework/Headers, "$(HEADER_SEARCH_PATHS)", ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = Hivelytracker; SDKROOT = ""; WRAPPER_EXTENSION = app; }; name = Debug; }; C01FCF4C08A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { COMBINE_HIDPI_IMAGES = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Hivelytracker_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; PRODUCT_NAME = Hivelytracker; WRAPPER_EXTENSION = app; }; name = Release; }; C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( "$(HOME)/Library/Frameworks", /Library/Frameworks, "$(FRAMEWORK_SEARCH_PATHS)", ); GCC_PREPROCESSOR_DEFINITIONS = __SDL_WRAPPER__; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(HOME)/Library/Frameworks/SDL.framework/Headers", "$(HOME)/Library/Frameworks/SDL_ttf.framework/Headers", "$(HOME)/Library/Frameworks/SDL_image.framework/Headers", /Library/Frameworks/SDL.framework/Headers, /Library/Frameworks/SDL_ttf.framework/Headers, /Library/Frameworks/SDL_image.framework/Headers, "$(HEADER_SEARCH_PATHS)", ); ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; SDKROOT = macosx; }; name = Debug; }; C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { FRAMEWORK_SEARCH_PATHS = ( "$(HOME)/Library/Frameworks", /Library/Frameworks, "$(FRAMEWORK_SEARCH_PATHS)", ); GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_PREPROCESSOR_DEFINITIONS = __SDL_WRAPPER__; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; HEADER_SEARCH_PATHS = ( "$(HOME)/Library/Frameworks/SDL.framework/Headers", "$(HOME)/Library/Frameworks/SDL_ttf.framework/Headers", "$(HOME)/Library/Frameworks/SDL_image.framework/Headers", /Library/Frameworks/SDL.framework/Headers, /Library/Frameworks/SDL_ttf.framework/Headers, /Library/Frameworks/SDL_image.framework/Headers, "$(HEADER_SEARCH_PATHS)", ); LD_GENERATE_MAP_FILE = YES; PREBINDING = NO; SDKROOT = macosx; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Hivelytracker" */ = { isa = XCConfigurationList; buildConfigurations = ( C01FCF4B08A954540054247B /* Debug */, C01FCF4C08A954540054247B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Hivelytracker" */ = { isa = XCConfigurationList; buildConfigurations = ( C01FCF4F08A954540054247B /* Debug */, C01FCF5008A954540054247B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; } hivelytracker-0+git20180223/sdl/osx/xcode/SDLMain.m0000664001024000102400000002663313042730153016510 0ustar /* SDLMain.m - main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser Non-NIB-Code & other changes: Max Horn Feel free to customize this file to suit your needs */ #include "SDL.h" #include "SDLMain.h" #include /* for MAXPATHLEN */ #include /* For some reaon, Apple removed setAppleMenu from the headers in 10.4, but the method still is there and works. To avoid warnings, we declare it ourselves here. */ @interface NSApplication(SDL_Missing_Methods) - (void)setAppleMenu:(NSMenu *)menu; @end /* Use this flag to determine whether we use SDLMain.nib or not */ #define SDL_USE_NIB_FILE 0 /* Use this flag to determine whether we use CPS (docking) or not */ #define SDL_USE_CPS 1 #ifdef SDL_USE_CPS /* Portions of CPS.h */ typedef struct CPSProcessSerNum { UInt32 lo; UInt32 hi; } CPSProcessSerNum; extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn); extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn); #endif /* SDL_USE_CPS */ static int gArgc; static char **gArgv; static BOOL gFinderLaunch; static BOOL gCalledAppMainline = FALSE; BOOL enableKeys = FALSE; static NSString *getApplicationName(void) { const NSDictionary *dict; NSString *appName = 0; /* Determine the application name */ dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); if (dict) appName = [dict objectForKey: @"CFBundleName"]; if (![appName length]) appName = [[NSProcessInfo processInfo] processName]; return appName; } #if SDL_USE_NIB_FILE /* A helper category for NSString */ @interface NSString (ReplaceSubString) - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString; @end #endif @interface SDLApplication : NSApplication @end @implementation SDLApplication /* Invoked from the Quit menu item */ - (void)terminate:(id)sender { /* Post a SDL_QUIT event */ SDL_Event event; event.type = SDL_QUIT; SDL_PushEvent(&event); } /* Prevent OSX beeping on unrecognised keystrokes */ - (void)sendEvent:(NSEvent *)anEvent { if( NSKeyDown == [anEvent type] || NSKeyUp == [anEvent type] ) { if( enableKeys || [anEvent modifierFlags] & NSCommandKeyMask ) [super sendEvent: anEvent]; } else [super sendEvent: anEvent]; } @end /* The main class of the application, the application's delegate */ @implementation SDLMain /* Set the working directory to the .app's parent directory */ - (void) setupWorkingDirectory:(BOOL)shouldChdir { if (shouldChdir) { char parentdir[MAXPATHLEN]; CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) { chdir(parentdir); /* chdir to the binary app's parent */ } CFRelease(url); CFRelease(url2); } } #if SDL_USE_NIB_FILE /* Fix menu to contain the real app name instead of "SDL App" */ - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName { NSRange aRange; NSEnumerator *enumerator; NSMenuItem *menuItem; aRange = [[aMenu title] rangeOfString:@"SDL App"]; if (aRange.length != 0) [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]]; enumerator = [[aMenu itemArray] objectEnumerator]; while ((menuItem = [enumerator nextObject])) { aRange = [[menuItem title] rangeOfString:@"SDL App"]; if (aRange.length != 0) [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]]; if ([menuItem hasSubmenu]) [self fixMenu:[menuItem submenu] withAppName:appName]; } [ aMenu sizeToFit ]; } #else static void setApplicationMenu(void) { /* warning: this code is very odd */ NSMenu *appleMenu; NSMenuItem *menuItem; NSString *title; NSString *appName; appName = getApplicationName(); appleMenu = [[NSMenu alloc] initWithTitle:@""]; /* Add menu items */ title = [@"About " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; [appleMenu addItem:[NSMenuItem separatorItem]]; title = [@"Hide " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; [appleMenu addItem:[NSMenuItem separatorItem]]; title = [@"Quit " stringByAppendingString:appName]; [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; /* Put menu into the menubar */ menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; [menuItem setSubmenu:appleMenu]; [[NSApp mainMenu] addItem:menuItem]; /* Tell the application object that this is now the application menu */ [NSApp setAppleMenu:appleMenu]; /* Finally give up our references to the objects */ [appleMenu release]; [menuItem release]; } /* Create a window menu */ static void setupWindowMenu(void) { NSMenu *windowMenu; NSMenuItem *windowMenuItem; NSMenuItem *menuItem; windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; /* "Minimize" item */ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [windowMenu addItem:menuItem]; [menuItem release]; /* Put menu into the menubar */ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; [windowMenuItem setSubmenu:windowMenu]; [[NSApp mainMenu] addItem:windowMenuItem]; /* Tell the application object that this is now the window menu */ [NSApp setWindowsMenu:windowMenu]; /* Finally give up our references to the objects */ [windowMenu release]; [windowMenuItem release]; } /* Replacement for NSApplicationMain */ static void CustomApplicationMain (int argc, char **argv) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; SDLMain *sdlMain; /* Ensure the application object is initialised */ [SDLApplication sharedApplication]; #ifdef SDL_USE_CPS { CPSProcessSerNum PSN; /* Tell the dock about us */ if (!CPSGetCurrentProcess(&PSN)) if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103)) if (!CPSSetFrontProcess(&PSN)) [SDLApplication sharedApplication]; } #endif /* SDL_USE_CPS */ /* Set up the menubar */ [NSApp setMainMenu:[[[NSMenu alloc] init] autorelease]]; setApplicationMenu(); setupWindowMenu(); /* Create SDLMain and make it the app delegate */ sdlMain = [[SDLMain alloc] init]; [NSApp setDelegate:sdlMain]; /* Start the main event loop */ [NSApp run]; [sdlMain release]; [pool release]; } #endif /* * Catch document open requests...this lets us notice files when the app * was launched by double-clicking a document, or when a document was * dragged/dropped on the app's icon. You need to have a * CFBundleDocumentsType section in your Info.plist to get this message, * apparently. * * Files are added to gArgv, so to the app, they'll look like command line * arguments. Previously, apps launched from the finder had nothing but * an argv[0]. * * This message may be received multiple times to open several docs on launch. * * This message is ignored once the app's mainline has been called. */ - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { const char *temparg; size_t arglen; char *arg; char **newargv; if (!gFinderLaunch) /* MacOS is passing command line args. */ return FALSE; if (gCalledAppMainline) /* app has started, ignore this document. */ return FALSE; temparg = [filename UTF8String]; arglen = SDL_strlen(temparg) + 1; arg = (char *) SDL_malloc(arglen); if (arg == NULL) return FALSE; newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2)); if (newargv == NULL) { SDL_free(arg); return FALSE; } gArgv = newargv; SDL_strlcpy(arg, temparg, arglen); gArgv[gArgc++] = arg; gArgv[gArgc] = NULL; return TRUE; } /* Called when the internal event loop has just started running */ - (void) applicationDidFinishLaunching: (NSNotification *) note { int status; /* Set the working directory to the .app's parent directory */ [self setupWorkingDirectory:gFinderLaunch]; #if SDL_USE_NIB_FILE /* Set the main menu to contain the real app name instead of "SDL App" */ [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()]; #endif /* Enable menu shortcuts */ setenv ("SDL_ENABLEAPPEVENTS", "1", 1); /* Hand off to main application code */ gCalledAppMainline = TRUE; status = SDL_main (gArgc, gArgv); /* We're done, thank you for playing */ exit(status); } @end @implementation NSString (ReplaceSubString) - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString { unsigned int bufferSize; unsigned int selfLen = [self length]; unsigned int aStringLen = [aString length]; unichar *buffer; NSRange localRange; NSString *result; bufferSize = selfLen + aStringLen - aRange.length; buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar)); /* Get first part into buffer */ localRange.location = 0; localRange.length = aRange.location; [self getCharacters:buffer range:localRange]; /* Get middle part into buffer */ localRange.location = 0; localRange.length = aStringLen; [aString getCharacters:(buffer+aRange.location) range:localRange]; /* Get last part into buffer */ localRange.location = aRange.location + aRange.length; localRange.length = selfLen - localRange.location; [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange]; /* Build output string */ result = [NSString stringWithCharacters:buffer length:bufferSize]; NSDeallocateMemoryPages(buffer, bufferSize); return result; } @end #ifdef main # undef main #endif /* Main entry point to executable - should *not* be SDL_main! */ int main (int argc, char **argv) { /* Copy the arguments into a global variable */ /* This is passed if we are launched by double-clicking */ if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) { gArgv = (char **) SDL_malloc(sizeof (char *) * 2); gArgv[0] = argv[0]; gArgv[1] = NULL; gArgc = 1; gFinderLaunch = YES; } else { int i; gArgc = argc; gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1)); for (i = 0; i <= argc; i++) gArgv[i] = argv[i]; gFinderLaunch = NO; } #if SDL_USE_NIB_FILE [SDLApplication poseAsClass:[NSApplication class]]; NSApplicationMain (argc, argv); #else CustomApplicationMain (argc, argv); #endif return 0; } hivelytracker-0+git20180223/sdl/osx/xcode/NSFileManager+DirectoryLocations.m0000664001024000102400000001013213042730153023473 0ustar // // NSFileManager+DirectoryLocations.m // // Created by Matt Gallagher on 06 May 2010 // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. Permission is granted to anyone to // use this software for any purpose, including commercial applications, and to // alter it and redistribute it freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source // distribution. // #import "NSFileManager+DirectoryLocations.h" enum { DirectoryLocationErrorNoPathFound, DirectoryLocationErrorFileExistsAtLocation }; NSString * const DirectoryLocationDomain = @"DirectoryLocationDomain"; @implementation NSFileManager (DirectoryLocations) // // findOrCreateDirectory:inDomain:appendPathComponent:error: // // Method to tie together the steps of: // 1) Locate a standard directory by search path and domain mask // 2) Select the first path in the results // 3) Append a subdirectory to that path // 4) Create the directory and intermediate directories if needed // 5) Handle errors by emitting a proper NSError object // // Parameters: // searchPathDirectory - the search path passed to NSSearchPathForDirectoriesInDomains // domainMask - the domain mask passed to NSSearchPathForDirectoriesInDomains // appendComponent - the subdirectory appended // errorOut - any error from file operations // // returns the path to the directory (if path found and exists), nil otherwise // - (NSString *)findOrCreateDirectory:(NSSearchPathDirectory)searchPathDirectory inDomain:(NSSearchPathDomainMask)domainMask appendPathComponent:(NSString *)appendComponent error:(NSError **)errorOut { // // Search for the path // NSArray* paths = NSSearchPathForDirectoriesInDomains( searchPathDirectory, domainMask, YES); if ([paths count] == 0) { if (errorOut) { NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys: NSLocalizedStringFromTable( @"No path found for directory in domain.", @"Errors", nil), NSLocalizedDescriptionKey, [NSNumber numberWithInteger:searchPathDirectory], @"NSSearchPathDirectory", [NSNumber numberWithInteger:domainMask], @"NSSearchPathDomainMask", nil]; *errorOut = [NSError errorWithDomain:DirectoryLocationDomain code:DirectoryLocationErrorNoPathFound userInfo:userInfo]; } return nil; } // // Normally only need the first path returned // NSString *resolvedPath = [paths objectAtIndex:0]; // // Append the extra path component // if (appendComponent) { resolvedPath = [resolvedPath stringByAppendingPathComponent:appendComponent]; } // // Create the path if it doesn't exist // NSError *error = nil; BOOL success = [self createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error]; if (!success) { if (errorOut) { *errorOut = error; } return nil; } // // If we've made it this far, we have a success // if (errorOut) { *errorOut = nil; } return resolvedPath; } // // applicationSupportDirectory // // Returns the path to the applicationSupportDirectory (creating it if it doesn't // exist). // - (NSString *)applicationSupportDirectory { NSString *executableName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleExecutable"]; NSError *error; NSString *result = [self findOrCreateDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appendPathComponent:executableName error:&error]; if (!result) { NSLog(@"Unable to find or create application support directory:\n%@", error); } return result; } @end hivelytracker-0+git20180223/sdl/osx/xcode/SDLMain.h0000664001024000102400000000056613042730153016500 0ustar /* SDLMain.m - main entry point for our Cocoa-ized SDL app Initial Version: Darrell Walisser Non-NIB-Code & other changes: Max Horn Feel free to customize this file to suit your needs */ #ifndef _SDLMain_h_ #define _SDLMain_h_ #import @interface SDLMain : NSObject @end #endif /* _SDLMain_h_ */ hivelytracker-0+git20180223/sdl/osx/xcode/Hivelytracker_Prefix.pch0000664001024000102400000000000013042730153021704 0ustar hivelytracker-0+git20180223/sdl/osx/xcode/osx.m0000664001024000102400000000746713042730153016076 0ustar // // osx.m // Hivelytracker: OSX dialog and file reqester support // // Created by Christopher O'Neill on 15/10/2012. // #import #import "NSFileManager+DirectoryLocations.h" typedef uint32_t uint32; typedef int32_t int32; typedef char TEXT; extern BOOL enableKeys; int32 gui_req( uint32 img, const TEXT *title, const TEXT *reqtxt, const TEXT *buttons ) { int i; NSAlert *alert = [[[NSAlert alloc] init] autorelease]; [alert setMessageText:[NSString stringWithCString:title encoding:NSASCIIStringEncoding]]; [alert setInformativeText:[NSString stringWithCString:reqtxt encoding:NSASCIIStringEncoding]]; NSMutableString *btnStr = [NSMutableString stringWithCString:buttons encoding:NSASCIIStringEncoding]; [btnStr replaceOccurrencesOfString:@"_" withString:@"" options:0 range:NSMakeRange(0, [btnStr length])]; NSArray *btnArr = [btnStr componentsSeparatedByString:@"|"]; for (btnStr in [[btnArr reverseObjectEnumerator] allObjects]) { [alert addButtonWithTitle:btnStr]; } enableKeys = TRUE; i = [alert runModal] - 1000; enableKeys = FALSE; return i; } #define FR_HVLSAVE 0 #define FR_AHXSAVE 1 #define FR_INSSAVE 2 #define FR_MODLOAD 3 #define FR_INSLOAD 4 char *filerequester( const char *title, const char *path, const char *fname, int type ) { NSArray *fileTypes; NSSavePanel *savePanel; NSOpenPanel *panel; switch (type) { case FR_HVLSAVE: fileTypes = [NSArray arrayWithObjects:@"hvl", @"HVL", nil]; break; case FR_AHXSAVE: fileTypes = [NSArray arrayWithObjects:@"ahx", @"AHX", nil]; break; case FR_INSSAVE: case FR_INSLOAD: fileTypes = [NSArray arrayWithObjects:@"ins", @"INS", nil]; break; case FR_MODLOAD: fileTypes = [NSArray arrayWithObjects:@"hvl", @"HVL", @"ahx", @"AHX", nil]; break; default: return NULL; } if ((type == FR_INSLOAD) || (type == FR_MODLOAD)) { panel = [NSOpenPanel openPanel]; } else { savePanel = [NSSavePanel savePanel]; panel = (NSOpenPanel *)savePanel; } [panel setAllowedFileTypes:fileTypes]; [panel setAllowsOtherFileTypes:YES]; [panel setTitle:[NSString stringWithCString:title encoding:NSASCIIStringEncoding]]; [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithCString:path encoding:NSASCIIStringEncoding] isDirectory:YES]]; [panel setNameFieldStringValue:[NSString stringWithCString:fname encoding:NSASCIIStringEncoding]]; enableKeys = TRUE; int i = [panel runModal]; enableKeys = FALSE; if (i == 1) { const char *cstr = [[[panel URL] path] UTF8String]; char *rval = (char *)malloc(strlen(cstr) + 1); strcpy(rval, cstr); return rval; }; return NULL; } BOOL directoryrequester( const char *title, char *path ) { NSOpenPanel *panel = [NSOpenPanel openPanel]; [panel setCanChooseFiles:NO]; [panel setCanChooseDirectories:YES]; [panel setTitle: [NSString stringWithCString:title encoding:NSASCIIStringEncoding]]; [panel setDirectoryURL:[NSURL fileURLWithPath:[NSString stringWithCString:path encoding:NSASCIIStringEncoding] isDirectory:YES]]; enableKeys = FALSE; int i = [panel runModal]; enableKeys = FALSE; if (i == 1) { const char *cstr = [[[panel URL] path] UTF8String]; strncpy(path, cstr, 512); return TRUE; }; return FALSE; } char *osxGetPrefsPath() { static char *path; if(!path) { NSString *strPath = [[NSFileManager defaultManager] applicationSupportDirectory]; strPath = [strPath stringByAppendingPathComponent:@"ht.prefs"]; path = malloc([strPath length] + 1); strcpy(path, [strPath UTF8String]); } return path; } char *osxGetResourcesPath(char *path, const char *pathAppend) { NSBundle *bundle = [NSBundle mainBundle]; NSString *strPath = [bundle resourcePath]; strPath = [strPath stringByAppendingPathComponent:[NSString stringWithUTF8String:pathAppend]]; if(!path) path = malloc([strPath length] + 1); strcpy(path, [strPath UTF8String]); return path; } hivelytracker-0+git20180223/sdl/osx/xcode/NSFileManager+DirectoryLocations.h0000664001024000102400000000257713042730153023504 0ustar // // NSFileManager+DirectoryLocations.h // // Created by Matt Gallagher on 06 May 2010 // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. Permission is granted to anyone to // use this software for any purpose, including commercial applications, and to // alter it and redistribute it freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // 3. This notice may not be removed or altered from any source // distribution. // #import // // DirectoryLocations is a set of global methods for finding the fixed location // directoriess. // @interface NSFileManager (DirectoryLocations) - (NSString *)findOrCreateDirectory:(NSSearchPathDirectory)searchPathDirectory inDomain:(NSSearchPathDomainMask)domainMask appendPathComponent:(NSString *)appendComponent error:(NSError **)errorOut; - (NSString *)applicationSupportDirectory; @end hivelytracker-0+git20180223/sdl/osx/xcode/hivelytracker.icns0000664001024000102400000000562213042730153020630 0ustar icns il32vDDEEFEEDEFEFEEDB@?ACCB@??@A?ACDIMPKGEINPPNLPLGDC  1B  ,GCEDCE;28$E%77*;19LBFEDEAKHc&J_' A39769>Xiegcknĵ3#ƁWƱ:9>ncgdj`O7<\hdh]3J TV5obgdj`|tuwqDNxyyzz{y{u=obgdkk|T ?ncgcltNҚ\߿Φ=obgcmz4ta ;pbgcmu-i\< c Cmcf adihhacfafhhgdhgadgfhfdggfheddefdhgfefeffefeff@)&$**c4+%%)/%%(*X7+5K#03$ 6+67|?b*-{sLZ%8y T^(ZIPNMPSup7!Y?S abJMdqnoiE^/UhINcqpqnT ^R?NJIFJGFN!RX;OGGFyES `VMRn ؖU'Tw+f^곥RyH_Z Ps?Z Q{CaP BFdp4R!SYl8mkicnV Bffhivelytracker-0+git20180223/sdl/osx/xcode/Info.plist0000664001024000102400000000266713042730153017054 0ustar CFBundleDevelopmentRegion English CFBundleDocumentTypes CFBundleTypeExtensions hvl ahx CFBundleTypeName HivelyTracker Module CFBundleTypeRole Editor LSTypeIsPackage NSPersistentStoreTypeKey XML CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier com.iris.Hivelytracker CFBundleInfoDictionaryVersion 6.0 CFBundleIconFile hivelytracker CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 1.0 LSMinimumSystemVersionByArchitecture i386 10.4.0 ppc 10.4.0 x86_64 10.6.0 NSMainNibFile SDLMain NSPrincipalClass NSApplication hivelytracker-0+git20180223/sdl/win32.c0000664001024000102400000001074513042730153014273 0ustar #include #include #include #include #include #include #define WANT_WMINFO #include #include // The amiga wrapper and the windows header don't get along... typedef Uint32 uint32; typedef Sint32 int32; typedef char TEXT; int32 gui_req( uint32 img, TEXT *title, TEXT *reqtxt, TEXT *buttons ) { SDL_SysWMinfo wmi; HWND hwnd; hwnd = NULL; SDL_VERSION(&wmi.version); if( SDL_GetWMInfo( &wmi ) ) hwnd = (HWND)wmi.window; return MessageBoxA(hwnd, reqtxt, title, strcmp(buttons, "OK") ? MB_OKCANCEL : MB_OK ) == IDOK; } static int CALLBACK BrowseCallbackProc(HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData) { // If the BFFM_INITIALIZED message is received // set the path to the start path. switch (uMsg) { case BFFM_INITIALIZED: { if (((LPARAM)NULL) != lpData) { SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData); } } } return 0; // The function should always return 0. } BOOL directoryrequester( char *title, char *path ) { SDL_SysWMinfo wmi; HWND hwnd; LPITEMIDLIST pidl = NULL; BROWSEINFO bi = { 0 }; BOOL bResult = FALSE; static BOOL coinitialised = FALSE; static char tmp[MAX_PATH]; GetFullPathName(path, MAX_PATH, tmp, NULL); if (!coinitialised) { CoInitialize(NULL); coinitialised = TRUE; } hwnd = NULL; SDL_VERSION(&wmi.version); if( SDL_GetWMInfo( &wmi ) ) hwnd = (HWND)wmi.window; bi.hwndOwner = hwnd; bi.pszDisplayName = tmp; bi.pidlRoot = NULL; bi.lpszTitle = title; bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; bi.lpfn = BrowseCallbackProc; bi.lParam = (LPARAM) tmp; if ((pidl = SHBrowseForFolder(&bi)) != NULL) { bResult = SHGetPathFromIDList(pidl, tmp); CoTaskMemFree(pidl); if (bResult) { strncpy(path, tmp, 512); path[511] = 0; SDL_WM_SetCaption(path, path); } else { SDL_WM_SetCaption("No", "No"); } } else { SDL_WM_SetCaption("No 2", "No 2"); } return bResult; } #define FR_HVLSAVE 0 #define FR_AHXSAVE 1 #define FR_INSSAVE 2 #define FR_MODLOAD 3 #define FR_INSLOAD 4 char *filerequester( char *title, char *path, char *fname, int type ) { SDL_SysWMinfo wmi; static OPENFILENAME ofn; HWND hwnd; char *result; char *odir; char tmp[4096]; strcpy(tmp, fname); hwnd = NULL; SDL_VERSION(&wmi.version); if( SDL_GetWMInfo( &wmi ) ) hwnd = (HWND)wmi.window; ZeroMemory( &ofn, sizeof( ofn ) ); ofn.lStructSize = sizeof( ofn ); ofn.hwndOwner = hwnd; ofn.nMaxFile = 4096; ofn.lpstrFile = tmp; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; switch( type ) { case FR_HVLSAVE: ofn.Flags = OFN_PATHMUSTEXIST; ofn.lpstrFilter = "All Files\0*.*\0HVL module (*.hvl)\0*.HVL\0"; ofn.nFilterIndex = 2; break; case FR_AHXSAVE: ofn.Flags = OFN_PATHMUSTEXIST; ofn.lpstrFilter = "All Files\0*.*\0AHX module (*.ahx)\0*.AHX\0"; ofn.nFilterIndex = 2; break; case FR_INSSAVE: ofn.Flags = OFN_PATHMUSTEXIST; ofn.lpstrFilter = "All Files\0*.*\0Instrument (*.ins)\0*.INS\0"; ofn.nFilterIndex = 2; break; case FR_MODLOAD: ofn.lpstrFilter = "All Files\0*.*\0Modules (*.ahx, *.hvl, *.mod)\0*.AHX;*.HVL;*.MOD;HVL.*;AHX.*;MOD.*\0"; ofn.nFilterIndex = 2; break; case FR_INSLOAD: ofn.lpstrFilter = "All Files\0*.*\0Instrument (*.ins)\0*.INS;INS.*\0"; ofn.nFilterIndex = 2; break; default: ofn.lpstrFilter = "All Files\0*.*\0"; ofn.nFilterIndex = 1; break; } ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = path; odir = getcwd( NULL, 0 ); switch( type ) { case FR_HVLSAVE: case FR_AHXSAVE: case FR_INSSAVE: if( !GetSaveFileName( &ofn ) ) { chdir( odir ); free( odir ); return NULL; } break; default: if( !GetOpenFileName( &ofn ) ) { chdir( odir ); free( odir ); return NULL; } break; } chdir( odir ); free( odir ); if (strlen(ofn.lpstrFile)==0) return NULL; result = malloc(strlen(ofn.lpstrFile)+1); if (result) { strcpy(result, ofn.lpstrFile); } return result; } hivelytracker-0+git20180223/sdl/sdl_wrapper.h0000664001024000102400000000541713042730153015660 0ustar #ifndef __SDL_WRAPPER_H__ #define __SDL_WRAPPER_H__ /* Map amiga types to SDL types */ typedef SDL_bool BOOL; typedef Uint32 uint32; typedef Sint32 int32; typedef Uint16 uint16; typedef Sint16 int16; typedef Uint8 uint8; typedef Sint8 int8; typedef char TEXT; typedef char * STRPTR; typedef void * APTR; typedef float float32; typedef double float64; typedef Sint32 LONG; #define CONST const #define TRUE SDL_TRUE #define FALSE SDL_FALSE #define ASOT_SEMAPHORE 1 #define ASOT_LIST 2 #define ASOT_NODE 3 struct SignalSemaphore { SDL_mutex *mtx; }; struct List { struct Node *lh_Head; struct Node *lh_Tail; struct Node *lh_TailPred; }; #define NT_USER 0 struct Node { struct Node *ln_Succ; struct Node *ln_Pred; STRPTR ln_Name; int8 ln_Pri; uint8 ln_Type; }; struct ExecIFace { APTR (*AllocVecTags)(uint32, ...); void (*FreeVec)(void *); APTR (*AllocSysObjectTags)(uint32, ...); void (*FreeSysObject)(uint32, APTR); void (*ObtainSemaphore)(struct SignalSemaphore *); void (*ReleaseSemaphore)(struct SignalSemaphore *); void (*CopyMem)(APTR src, APTR dest, uint32 size); struct Node * (*GetHead)(struct List *); struct Node * (*GetSucc)(struct Node *); void (*AddTail)(struct List *, struct Node *); void (*Remove)(struct Node *); APTR (*AllocPooled)(APTR, uint32); void (*FreePooled)(APTR, APTR, uint32); struct Node *(*RemHead)(struct List *); struct Node *(*GetTail)(struct List *); struct Node *(*RemTail)(struct List *); }; extern struct ExecIFace *IExec; #define SRFTYPE SDL_HWSURFACE //#define SRFTYPE SDL_SWSURFACE #define IEQUALIFIER_RBUTTON 1 #define IEQUALIFIER_LCOMMAND 2 #define IEQUALIFIER_CONTROL 4 #define IEQUALIFIER_LSHIFT 8 #define IEQUALIFIER_RSHIFT 16 #define IEQUALIFIER_LALT 32 #define SELECTDOWN SDL_BUTTON_LEFT #define SELECTUP SDL_BUTTON_LEFT|0x100 #define MENUDOWN SDL_BUTTON_RIGHT #define MENUUP SDL_BUTTON_RIGHT|0x100 #define IDCMP_MOUSEMOVE 1 #define IDCMP_MOUSEBUTTONS 2 #define IDCMP_RAWKEY 4 #define IDCMP_EXTENDEDMOUSE 8 #define IMSGCODE_INTUIWHEELDATA 1 struct IntuiMessage { uint16 Qualifier; uint16 Class; uint16 Code; int32 MouseX; int32 MouseY; void * IAddress; }; struct IntuiWheelData { int16 WheelY; }; uint16 sdl_keysym_to_amiga_rawkey(SDLKey keysym); #define TAG_DONE 0 #define REQIMAGE_QUESTION 0 #define REQIMAGE_WARNING 1 #define REQIMAGE_ERROR 2 #define FR_HVLSAVE 0 #define FR_AHXSAVE 1 #define FR_INSSAVE 2 #define FR_MODLOAD 3 #define FR_INSLOAD 4 char *filerequester( char *title, char *path, char *fname, int type ); BOOL directoryrequester( char *title, char *path ); #endif /* __SDL_WRAPPER_H__ */ hivelytracker-0+git20180223/sdl/Makefile0000664001024000102400000000153213042730153014617 0ustar WRAPPERDIR := $(CURDIR) BASEDIR = $(CURDIR)/.. CC = gcc CFLAGS = -g -Dmain=SDL_main -D__SDL_WRAPPER__ CFLAGS += -I$(BASEDIR) \ -I$(WRAPPERDIR) \ -Ic:/mingw/include/SDL LFLAGS += -g -lm -mwindows -lmingw32 -lSDL_ttf -lSDLmain -lSDL -lSDL_image -lfreetype -lz -lole32 -loleaut32 TARGET = hivelytracker.exe OBJECTS = sdl_wrapper.o \ ht.o \ win32.o \ ../gui.o \ ../replay.o \ ../util.o \ ../undo.o \ ../about.o all: $(TARGET) -include $(OBJECTS:.o=.d) $(TARGET): $(OBJECTS) winicon.o $(CC) -o $(TARGET) $(OBJECTS) winicon.o $(LFLAGS) winicon.o: winicon.ico hivelytracker.rc windres -i hivelytracker.rc -o winicon.o $(OBJECTS): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ @$(CC) -MM $(CFLAGS) $< > $*.d clean: rm $(OBJECTS) $(OBJECTS:.o=.d) $(TARGET) winicon.o hivelytracker-0+git20180223/Songs/0000775001024000102400000000000013042730153013465 5ustar hivelytracker-0+git20180223/Songs/there_you_are.hvl0000775001024000102400000000616713042730153017047 0ustar HVL 302                  ! "#       ! "#$%  &'-(.'- )/'-*0+1 ,2$ %  p@?$?%?$??$?%?$?p@?$?%?$??$?%?$?p@?$?%?$??$?%?$?p@?$?%?$?? ???硐硠硰|???$?%?$??$?%|$??$?%?$??$?%|$??$?%?$??$?%|$??$?%?$?? ? (08@HPX`hpx??????? ???????"?$?%?"?$?? ?????????$?%?)?xph`XPH@80( 硰硠硐础硐硠硰p)%?))%?)%$%?$""."%?0?p?????? ????|@???????? ??p?????? ??"???|@??????? ??"p?$?%? ??????|@????$?%? ????p??????????|@?????????p?????? ????|@????????? ?p?????? "????|@????????? "?p???"?$?%??$???|@??????"?$?%??$?"p??????????|@??"????????p???????????????|@?????????????????????????????p??!?"?$?%?$?"?$p?????????????)p?(?$?"?????????$p???"?)?????????p?)?%?$?? ??? p????????|@?????!?"?$?%?$|@"?$??????????|@"?)?(?$?"??????|@??$???"?)??????|@????)?%?$?? ?|@? ??????7 @!@? $nc;*  ?( @  ? zc  @@*? l0 q@ m@ k& j@ j&;*  ?(there you are--> Xeron/IRIS <-- 10.07.06so there you are..hivelytracker-0+git20180223/Songs/karma.ahx0000775001024000102400000003665413042730153015303 0ustar THX=p&@J 8 8 8 F:G9;=:>?=IADIBHIC$%&*J+,$%&*J+,'!()- !"#@%&'!()- !"#E%& !"#@%&$%&*J+,$%&*J+,.1!()45!"#000 e<<<00<<?000000 y y u u <<<<<  y yL'L'L'L'y\\\\yl l l l y||||yyyyL'L'L'L'\\\\l l l l ||||@@@           0 0  0 L'L'L'L'\\\\l l l l |||||||| 5050<<    0*  0*  0*  0 *  0*       0*  0*  0*  0 *  0*   @P`p@P@P`p@P@P`p@P@P`p@P`p@P@P`p@P`p@P@P`p@@@P`p@P@P`p@P`p@P@P`p @@@@@@@@@@@@@@@@@@@@ yyyePePePePePePePePePePmPmPmPmPmPmPmPmPmPmPppppppppppppppppu@u@u@u@u@u@u@u@u@u@u@u@u@u@u@u@u@u@u@u@u@ yyyuuuPPPPPPPPPP@@@@@@@@@@tppptppptpptppptppptpp@@@@@@@@@@@@@@@@@@@@@ PP P P P PQP P P P P P    0*  0*  0*  0 *  0*       0*  0*  0*  0 *  0*   @P`p@P@P`p@P@P`p@P@P`p@P`p@P@P`p@P`p@P@P`p@P@P`p@P@P`p@P`p@P@P`pP P P P PP PP P    0*  0*  0*  0 *  0*       0*  0*  0*  0 *  0*   @P`p@P@P`p@P@P`p@P@P`p@P`p@P@P`p@P`p@P@P`p@P@P`p@P@P`p@P`p@P@P`p P P PPPPPPPPP P P P P P P P    0*  0*  0*  0 *  0*       0*  0*  0*  0 *  0*   @P`p@P@P`p@P@P`p@P@P`p@P`p@P@P`p@P`p@P@P`p@P@P`p@P@P`p@P`p@P@P`p)`eey u Ap=Ap=<<<<<< ApIp@@@  mm          50     &@(e??-3@'[?!&@(e??3@ #e???-3@'[?!7@@@ ?"??:@@@@ ?@@@ ?@@@ ?@@@ ?@@@ ?@@@ ?%@oo.?!zy  @?z̃" @@?: h?XP-@X?zy 0@oo.?zy@@y ?"7?a 7?a ?a @@y ?"@@y ?"(@ ?  @=)  ?-@'X?zy 0@@@ ?,-?z  le`Y2@@@ ?@ ? z%0Karma#Oxide/Soniktomfastdrumhivelytracker-0+git20180223/Songs/sliding_away.hvl0000775001024000102400000000544313042730153016663 0ustar HVL %4Q             + !, "- #. $/ %0 &1 '2 (3*)4 ??????????? ????????????? ???????????????z@? ? ? ? ? ? ???%???????????'???*???????,?*?(???'???????*?(?'?(?%??????????? ???$???????*?(?'( ?%?????????%??%%%?%??%?%?%?*??*%*?,??*?%?(?'??'%'?*??(?%( ?%??%%%?%??,?%?,?$??$%$?%*?(%%( ?p? ???#0000%0000????????'?(00*00,000?*?????(0000'00000???%?#00%000???????$??? 00000000000?????????????????? ???00000000|@??? ???#0000%0000 ???????'?(00*00,00<?*?????(0000'0000????????????????<???%?#00%000????? ????? ???000000<0$??? 0000000000<???????????????????????????????? ???%???????'0000(0000????,???*00??(00??????'?%00'0000(00??(?????'00????#00??%???????????????%?!? ?? ?!?%?'?(???????????????(?????'%'???#???%??????%??%%%?%???%??? ????????????? ? ?%???????'0000(00 ?????,???*00??(00 ?????'?%00'0000(00 ?(?????'00????#00 ?%????????????? ?%?!? ?? ?!?%?' ?(????????????? ?(?????'%'???#? ?%??????' S ?c@`<@@@? zh 0  @@ ?dz o qP@P zh,v63@8* ?#Dsliding awayxeron / IRIS2006hivelytracker-0+git20180223/Songs/syphus-Afterstorm.hvl0000775001024000102400000002077313042730153017673 0ustar HVL  @4&34 2 !  " "      *+ ,- *+ ,- ./01#$%&)(p??????$????????????????????????????$?????????????????????p ???????? ??????? ????????????????????? ??????? ??????? ?????p??? ????????????????????????????%??????????????%???????????????%????????????????????????????'?????????????? ?????????? ?'? ?')000??'0?$?%?? ???%?$? ?????"?000??? ?%?%'000??,0000)???????????????????????????????????????????????????????????????1 (4@??,01310,')000??????)?'?$?"$000"$ $%000'%'),1"%,.00?,???',10,*')000?????,01310,531,530,')000'? ?"$??"$$%??$%',*)'%,.??,???',*)% ?? ?'?$?$%????$? ? @"? @???%?$? ?????"???????   ?000???????????? ??????????????? ??????p? ?????????p??| ??p??????p? ????????"* * ????? ?  ? ?  ? ? ??????????????????????????????????????????????????????????????????????? ??? ??????? ??? ????0 p???? ??   ? ???? ?????  *@*@ ???? 000?????? ??? ?????  pZ????????? ??? ??????????? ????? ????? ????????? ????? ??? ????? _????????? ??? ??????????? ????? ????? ????????? ????? ??? ????? p_????????? ??? ??????????? ????? ? ???" ???# ? ??? ?????# ???% ????? p0?? ?? 0?? ?? ? ? ?? ?? 0?? ?? ? ? ? ?? 0?? ?? ? ? ? ?? 0?? ??  ? p?? ?? 0?? ?? ? ? ?? ?? 0?? ?? ? ? ?? ?? 0?? ?? ? ? ?? ?? 0?? ?? ? ?" p??" ??" 0??" ??" ?" ? ?? ?? 0?? ?? ? ?# ??# ??" 0??" ?? ? ?# ??# ??% 0??% ??* @ ????? |0 ?? ?? ?? ?? ? ? ?? ?? ?? ?? ? ? ? ?? ?? ?? ? ? ? ?? ?? ?????? | ?? ?? ?? ?? ? ? ?? ?? ?? ?? ? ? ?? ?? ?? ?? ? ? ?? ?? ?? ??????" | ??" ??" ??" ??" ?" ? ?? ?? ?? ?? ? ?# ??# ??" ??" ?? ? ?# ??# ??% ??% ??p? ???|?? ??p? ???p? ???|?? ??p? ???p? ???|?? ??PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~p????? ????????????????????????????????????????????????????????p ?????? ??????????????????????????????????????????????????????p??? ????? ??? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? p0?? ?? ?? ?????0???????????????????????????????????????????? p?? ?? ?? ??????  ?????????????????????????????????" p??" ??" ??" ??????????????????????????????????????????????????????p? ??????????????????????????????????????????????????????????? p???? ??   ? ???? ????  *@*@ ???? ??  ??? ???? ? @ ? ? $ @??)?? ??? 000???????????? ?%?????$????? ???%?????'????? ???p??????????000????????????????? ?????"??? ???????*?)?%?"??%?$? ??? ?"?*?)???%???$? ??000???? ????????????????p??? ?????????? ?%???????????????$?%?????????? ???????????????????????????????????????????????????????????????? p???????????????? ??????????????????????????????????????????????????????? ???????????????????????????????????????????????????????7?5?3?.?2?.?3?.?)?+?'?&???'?.?+?$?&???'???&?$"? ?"?'?&???"???'?&???$&??'?????"?'???&?"????"?$??????$???"???????????????"?????"??&?????"??????'?&?"??"$000????????????????????????.,+'" ?????????&"??????????????????????????????+??"??? 000????????????"?'?????&?????"???'?????)?????"???p??????????000?????????????????"??? ??$???"???????,?+?'?$??'?&?"???"?$?,?+???'???&?"??000????"? ???????????????p???"????? ???? ?"?'???????????????&?'??????????* * *  *@*@*@????? pp??? @ ???| 0|0 |@|P??????? ???????????0? ???????6 6 ,0 6 , ???? ????????????????????????????. ????????????????????????????????????????????? ?????????????????????????????????$ ???????????????@2@@G" `` ``@"@@G  7 @@2T BM@ @0@q0@@()lM?@@@ 98@4@@@@6B@C@@@@" B 4@3@@ )   22@ )  0@!6[YP0P@"=@@[3  7 @@@@@syphus-afterstormafterstorm by syphus^bdseI love xeron'stracker! This isabout the calm after the storm ofwaiting for therelease of HVL68k!Greitse: xeron... buzz,spotUP,monk,d0pefish,deltafirepete,zabutom,mia,#ukscene and all...(c)syphus_2006hivelytracker-0+git20180223/Songs/yoake_no_myoujou.hvl0000775001024000102400000005356313042730153017612 0ustar HVLV0@-:;<>?@ABC5&'(6 )*+7EFGHIJD6 )*+7 ,-.K8 /01L9234ab^_`vvv]Z[\abYughiwy{mopqcdefjklxz|nrst5&'(MNWX6 )*+"OP7 ,-.#QR8 /01 $ST9234!%UV}=}~%?%?%???%%??%??%%?%?%???%%??%??%%?%?%???%%??%??%%?%?%???%%??%?%?%?????  $?  '?   ?%?  %???   ?,?  *?  *???   ?1???  0   0???????  .???  1?   ?1?  3?  /?????  /?   ?1?  5?   ?1?  0? -?????????? .   ?*???   ???/ /???   ?(?????  1?  4???   ?,?  0?  3?????????   ???4???   ?(?  ,?  /???   ?3?  7?  8???   ?3?   ???.?????????????????????????????????????????????????????????????????????????%???????   ?????*???????   ?????'???????   ?????,???????   ?????)???????   ?????.???????   ?????)???????   ?????0???????????????,???????   ?????1???????????????.???????   ?????3???????????????.???????   ?????'???????   ?????,???????   ?????.???????   ?????1???????????????*???????   ?????0???????   ?????0???????????????5???????????????/???????   ?????3???????   ?????5???????????????,???????   ?????-?????   ???????-???????   ?????*?????   ???????/???????????????1?????   ???????1???????   ?????.?????   ???????3???????????????4???????????????4???????????????1?????   ???????1???????   ?????'???????   ?????'???????   ?????)???????   ?????4???????????????*???????   ?????+???????   ?????-???????   ?????8???????????????.???????   ?????.???????   ?????0???????   ?????%?%???%?$??????????????????????'??????? ???!??????!"??? ?????)?? ????????#???#?$????? ?%??????????????????? ? ? ? ? ? ???!?!?!?!?????!?!?!????????? ? ? ? ? ?%?%?%?%?%?%?%?%?'?'?'?'? ? ? ? ?'?'?'?'?'?'?'?'?)?)?)?)?"?"?"?"?%?????  $?  '?   ?%?  %???   ???*?  '?   ?%???  '?  +?   ???*?  %???????   ?????*???????   ?????'?????   ???????'???????   ?????)???????   ?????.???????   ?????*?????   ???????+???????   ?????,???????   ?????1???????????????.?????   ???????.???????   ?????%?%???%?$???????????????????????????????? ????? ?$?%?%%%?%?%%?%%???%%?%%?%%%%?%???%%?%%%?%?%%?%%???%%?%%?%%%%?%???%% 0??????????????????????????????????????????????????????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?z  ?????)???????   ?????)???????   ???%?????????   ?????$???????z  ?????-???????   ?????,???????   ???)?????????   ?????'???????z  ?????0???????   ???????????????????,?????????   ?????,???????   ?????)???????   ?????)???????   ?????'???????   ?????-???????   ?????-???????   ?????,???????   ?????*???????   ?????????????   ?????0???????   ?????????????????????.???????   ?????????????   ?????,???????   ?????/???????   ?????,???????   ?????-???????   ?????????????????????3???????   ?????1???????   ?????????????   ?????????????????????6???????   ?????5???????   ?????????????   ?????*???????   ???,?????????   ?????(???????   ???'?????????   ???????????????????/?????????   ?????-???????   ???,?????????   ?????????????????????????????????????/???????   ???0?????????   ?????/???????   ?????'???????   ?????,???????   ?????.???????   ?????????????????????*???????   ?????/???????   ?????2???????   ?????????????????????/???????   ?????3???????   ?????5???????z  ??? ?  %?  '???  $?   ? ?????  )?  )???   ?%?  )?  ,?????  )?   ??? ?  %?  '???  $?   ?"???  ,?  )?   ?'???   ?0? 1??   ?????1?   ?1?  ,???  /?  .?   ???.?  0???  3?  ,???  .?  +   ????????/ /???   ?'????? .   ?,???   ???-?  4?   ?/???  .?  1?   ???????3?????   ?1???  *?  .?   ?1???  8?  3?   ?7???  5?????   ???????%R T% V X Z \ ^ `% b% d f h j l n% p% r t% v x z | ~ % % %????????????????????????????????????????????????????????? ?? ??????????????????????????????????0??????????????????????? ?  ?  ????????????????????????????????   ~ ~ } } | | { { z z y y x x w w v v u u t t s s r r q q p p8 ??????????????? ?? ?? ?? ????????????????????????????????????????????????????6 ?? ?? ?? ??????????????????????????????????????0????????????????????????????????3 ???????????????????????????????6 ??????????????????????????????? ? ????? ? ???? ? ????? ? ?????????????????????????????????????"??? ?????)?? ????????#???#?$????? ?%??????????????? ??$.???????   ?????'???????   ?????,???????   ?????.???????   ?,???1???????????????*???????   ?????0???????   ?????0???????????0???5???????????????/???????   ?????3???????   ?????5???????????3???   ?????,???????   ?????/???????   ?????,???????   ?????-???   ?   ?????????????????????3???????   ?????1???????   ?????????????   ?????????????????????6???????   ?????5???????   ????????????????????????????????????????????????????????????????????????  0 ??????????????????????????????????????????????????????????????????????????????????????????????????????????????? Ǡ      ???????????????????????????????????????????????? Ǡ0      Ǽ0 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Ǽ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????????????? Ǡ0      ????????????????????????????????????????????????? Ǡ     0 ?????  0 ?????????  0 ?   ?4 ???  , ???????????????  / ?  2 ???   ?+ ?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?????????????????????????????????%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?????????????????????????????????(?(?(?(?(?(?(?(?(?(?(?(?(?(?(?(?????????????????????????????????!?!????!!?!????!!?!????!!?!????!????????????????????????????????-Ǡ????????????????????????????????/Ǡ????????????????????????????????4Ǡ ????????????????????????????????%??%%??%%??%%??%%??%%??%%??%%??%%??%%??%%??%%??%%??%%??%%??%%??%%%%?%%%?%%%?%%%?%%%?%%%?%?%?%%%?%%%?%%%?%%%?%%%?%%%?%%%?%%%?%%%?%??%???%%??%????%??%???%%??%????%??%???%%??%????%??%???%%??%????%%%?%%%?%%%?%%%?%%%?%%%?%?%?%%%?%%%?%%%?%%%?%%%?%%%?%%%?%%%?%???   ???4 ???  0 ?   ?5 ???????  + ?   ?????0 ???  3 ?   ???????????????0 ?????   ?2 ?  . ???   ?????) ?  , ???????   ?2 ?  0 ????????????????   ?????????????????????????????????????????????-???????????????   ?????????????????????2???????   ?????????????0???????????????   ?????????????5???????????????   ?????????????5???????????????   ?????????????)???????????????   ?????????????????????????????   ?????????????.???????????????   ?????????????????????????????   ?????????????2???????????????   ?????????????????????????????!?!????!!?!????!&?&????&&?&????&(?(????((?(????()?)????))?)????)$?$????$$?$????$"?"????""?"????")?)????))?)????) ? ????  ? ? ???!?!?!?!?!?!?!?!?!?!?!?!?!?!?!?!? ? ? ? ? ? ? ? ?!?!?!?!?!?!?!?!?$?$?$?$?$?$?$?$?$?$?$?$?&?&?&?&?#?#?#?#?#?#?#?#?$?$?$?$?$?$?$?$?(?(?(?(?(?(?(?(?)?)?)?)?)?)?)?)?(?(?(?(?(?(?(?(?)?)?)?)?)?)?)?)?????????"?"?"?"?"?"?"?"? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???$?$?$?$?$?$?$?$?&?&?&?&?&?&?&?&?$?$?$?$?$?$?$?$?$?$?$?$?$?$?$???(?(?(?(?(?(?(?(?)?)?)?)?)?)?)?)?'?'?'?'?'?'?'?'?'?'?'?'?'?'?'?????????/ ?   ???????/ ?  5 ???   ?/ ?   ?????????????- ?  0 ?   ?0 ???  ?????????????????????????????????????????????????????????????-???????????????????????????????,???????????????   ?????????????)???????????????   ?????????????'??????????????????????????????0???????????????????????   ?????/???????????????   ?????????????+???????????????   ?????????????,??????????????????????????????4 ???????????????   ?????????????2???????????????   ?????????????0???????????????   ?????????????0??????????????????????????????%?%?????%%?????%%?%?????%%?????%%?%?????%%?????%%?%?????%%?????? o o n n m m l l k k j j i i h h g g f f e e d d c c b b a a ` ` _ _ ^ ^ ] ] \ \ [ [ Z Z Y Y X X W W V V U U T T S S R R Q Q P P: ???????????????     ???????????7 ??????????     ???????????????????????????8 ??????????     ???????????8 ??????????258"?"?????"?"???? "?"?????"?"???? "?"?????"?"???? "?"?????"?"????  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 7 ??????????     ???????????7 ??????????     ???????????     ???????????5 ??????????     ???????????8 ?????????????????????????????????????????????????????????????????????????@ @@  s#e@@  ?z qP@Pz@@ ^ "@@@#@ @@,4&@ :?&@:((( e8"@@Dq 0z(@@ #% @@ #% 6@  ?:% :0%@@@@"@(#@@@@:=#?Yoake no Myoujou#Varthall/Up Rough07/12/06Inspired by the OPtune of MamotteShugogetten.Enjoy this 16chtune!hivelytracker-0+git20180223/Songs/moderate_sellotaping.hvl0000775001024000102400000000523713042730153020413 0ustar HVL .' @              !""#$%&'""" %(,(8/HX1h #'*(8/HX3h"'(8*HX.h%p???????????????%p?%?%?%?%?%?%?%?%?%%%?%?%?%?%?%??? p????????????#p??? ??? ??????%p???%???#? ?????#???#?#?#??????'???'?'?'?#?????*???*?,?*?'???"?????"???"?????'?????'???'?????*?????,???*????? ???????????????#???????????????%?????????????????#???#?#?#?'?#???'???'?'?'?,?*???*???*?,?*?/?.?????"???? ?"???????'???"?"?'???????*???'?(?*???p???%?%?????%???%p@???????????????, p?????    ??% ???p@?????( ?????        ????????( ???' ???????????        ??' ?????    * ?????    ??" ???# ???????????????    ????????????$ %????????????????, ?  ' ?  , ?  3 ???  * ?  ( ?  1 ?    4 ?  . ?????????6 ?  3 ?    ??????-@** ?@E@6)p +?#@ @?zz vq:@?z ?z @  qp? @?|? @?|?(@?  @  ??6 @8* ??"@moderate sellotapingxeron/irisNot my best (sorry)Greetings to:Spot,Buzz,Syphus,m0d,Virgill,Monk,Lavaburn,Dr.Doom,BriteLite andall AHX and Hivelyfans!hivelytracker-0+git20180223/Songs/waiting_for_a_message.hvl0000664001024000102400000000335013042730153020515 0ustar HVL@ d  ???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????0????????????????????????????? ??????????0????????? ? ? ?? ? ? ?? ? ? ? ? ? ?? ? ? ?? ? ? ?? ? ? ? ? ? ?? ????0 ?0 ???????0 ?. ?0 ?. ?' ?????????????' ?) ?, ?0 ???0 ?. ?0 ?. ?& ?????????. ?0 ?. ?' ?????????. ?0 ?. ?' ???????' ?) ?' ?) ?. ???0 ???2 ??????????????????????0 ?0 ???????0 ?. ?0 ?. ?' ?????????????' ?) ?, ?0 ???0 ?. ?0 ?. ?& ????????. ?0 ?. ?' ?????????. ?0 ?. ?' ???????' ?) ?' ?) ?. ???0 ???2 ???????????????(@?(@?(@? (@? (@? (@?  (@? @ @Z?#`p@@?q0 F`6@ ?dz o qP@P zh,v(@?Waiting for a message#varthall/up rough22.10.12hivelytracker-0+git20180223/Songs/drainage_proble.hvl0000775001024000102400000001165413042730153017327 0ustar HVL@X ) U                        !"!!!#%$&#%$&#%$&#%$&#%$&#%$&#%$&#%!!!$& #% $& #% $& #% $& #% $& #% $& #% $& #% $& #% $& #% $& #% $& #% $& #%!!!$&)!!'((%???????%???00???01???1???????%???????%???00???01???1??666p@????????????????????????????????????????????????????????????????????????????????????????%???????%???00?????????????????%????%???00???01?1??????%????%???00??01?1????|???????????????????????????????????????????????????????????????????????????????????????0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0@@@@@@@ @ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0@@@@@@@p@???????????????????????????????%????%???00???????????????? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ????????????????????????????????????????????????????????????0????0????0???00????0??p@?????0????0????0???00????0???0????0????0???00????0??p@?????0????0????0???00????0???0????0????0???00????0??p@?????0????0????0???00????0??????????????????????????????????????????????????????????????????????????????????% ,,,% ,, , % ,,,% ,,,p@??? ??? ? ??? ??? ? ??? ???# ?) ?p@??? ??? ? ??? ??? ? ??? ???????|??????? ??? ? ??? ??? ? ??? ???# |?) ????? ??? ? ??? ??? ??? ? ??? ????????????????????????????????????????????????????????????????@ @?zz vq@@(  gb( ^! ]&//@/??@@@0,  @?|?:@?z ?z @  qp 7b@@@<*D@6?? #@** ?2"@@ ? z c Mdrainage problemxeron / iris2008I wanted to dosomething differentand i guess thisis different :-)hivelytracker-0+git20180223/Songs/illuminated.hvl0000775001024000102400000000273313042730153016517 0ustar HVLL               ????????????????????????#?? ?"???#? ?  ?" ? ? ?# ? ?  ?"???#?? ?"??,  ??????????????( 0?* 0?' 0?% 0?????????????????# 0?" 0? 0?????  0???????????????????,  ????????????????. 0?/ 0?3 0?????????????????????????2 0?1 0?& 0???????( 0???% 0????????????????????         %?%?%????%?%?%?%????%' 0???????# 0? 0?  0??????? 0? 0? 0???????????????????????' 0???????& 0?' 0?* 0?????* 0?, 0?) 0? #"  # "  # "   # "   # "   # "   # "   # ????????????1??????????? ??????????????? ???????@@5(? @?|?@  ??@&?  a @@@):8888888888888886@@> "       illuminatedxeron2008instruments mostlystoleni suck.hivelytracker-0+git20180223/Songs/sunspots.hvl0000775001024000102400000001007113042730153016100 0ustar HVL  @      @? ?@? ?@? ?@? ?@? ?@? ?@? ?@?!????,?????,?????,??????,??,???-?? @?? @?? @?? @?? @?? @?? @??!@?p??????????????????????????%%?%?%p?%p@%p%p?%p@%p%p@%p%p?%p?%p@%p%p?%p@%p%p?%p@%p%p@%p%p?%p?%p@%pp` #%#  #%# ('%#%# %# %# %# ???| #%#  #%# ('%#%# %# %# 'p`?+,++'"'+,.,+'"  "%'+,.1???%|# '?+,++'"'+,.,+'"  "%'+,.1p`?/./?/./?.?*?%???/./?/./?1?/?.?|????/./?/./?.?*?%???/./?/./?1?/p`???*?(?%???????????????  # #|??????*?(?%???????????????  p`?%$%???(?*?,?1?/???.??(??#?? ??#| #??%$%???(?*?,?1?/???.??(??#?? p` #%'*, #%'*,/3/.,3/.,3/.3/.*% |??  #%'*, #%'*,/3/.,3/.,3/.3/. p`?????????????????"?#?%?'?*|% ?????????????????"?#?%p`???%?$?%?????????????%?#?"? ?|??????%?$?%?????????????%?#?"? %ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0ppppp|pp%ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0pp,ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0ppppp|pp,ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0pp/ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0ppppp|pp/ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0pp*ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0ppppp|pp*ppppppp0pHp`pHp0pppppppppppp0pHp`pHp0pp6 @@* ?# !1@ @?zz vq@@@?d: h`?X`P@ @@ ?& l // 1h <@@@? zh 0  @ @8* ??"@#,@ny?/!q @ 0  ( 0sunspots# xeron / IRIS #This is the secondever HVL tune, andmy first 'proper'attempt at usingthis tracker i'vecreated.. :-)hivelytracker-0+git20180223/Songs/chiprolled.hvl0000775001024000102400000001326413042730153016336 0ustar HVL T V-. -  -. - TTTT TT TTTTTT TTTT TT TTTTTT  11 11 !11"11 11#11$11 %11&'-%().% *-+,% &'-%/0.% *-+,  2TT3TT 4TT 5TT6TT7TT8 9TT:TT 4TT 5TT6TT;TT < 11=11 >11?11 @11A11B11 %11&'-%().% *-+,% &'-%/0.% *-+, %&'-%().% *-+,% &'-%/0.% *-+, CGDEHIFCGDEHIFC%GDJEHIFC%GDJEHIF 2TT3TT4TT5TT6TT7TT89TT:TT4TT5TT6TT;TT < 11=11 >11?11 @11A11B11 %11&'-%().% *-+,% &'-%/0.% *-+, %&'-%().% *-+,% &'-%/0.% *-+, K&'-L%M().N% O*-P+Q,R% SSSSSS??????%?#???!???!????????!?%p???%?%?%???%?%???????%?#???????#?#?#?%???#?p???????????????????????????????????????????????????!????p@???!?!?#?&???#?&???????????????%?%?%?%?%???%?%?#???#?#?#?&?%???!???#???????!?#?p ???????# ???% ???& ???& ???( ???% ???????# ?! ??????????# ???# ???% ???& ???# ???# ???- ??????- ???( ??????????!??????????????# ???# ???% ???& ???# ???& ???( ??????% ???# ???! ???????# ???# ???% ???& ???% ???! ???????( ???( ???( ???* ???( ???????& ???????????????????( ???* ???& ???( ???( ???( ???* ???( ???!???! ???????# ???% ???& ???# ???( ???* ???( ???????????# ?% ?& ?# ?* ???????* ???( ???*p???????*???(???( ???????( ???& ???(???????(???&???& ???????( ???% ?????# ?! ???????! ???( ???????& ???????p????????????????????????????- ???????( ???* ???-???????(???*???????# ?????# ?????# ???# ???% ???& ???( ???????% ???# ???! ???????# ???# ???% ???& ???# ???! ???????????- ???- ???( ???????* ???( ???& ???????& ???& ???# ???& ???# ???& ???( ???????& ???( ???( ???????& ???????????????( ???* ???( ???????( ???( ???* ???( ???( ???! ???????# ???# ???% ???& ???# ???????( ???* ???( ???(???&?(???+?*???&???(???????!?"?#???!?#???&?%???????????????#?&?- ???????????????/ ??/ ??- ???2 ??2 ??1 ???* ???# ?% ?& ?# ?* ???% ? ?% ?% ?% ? ?% ?% ?% ? ?% ?% ?% ? ?% ?% ?% ? ~?% }?% |?% {? z?% y?% x?% w? v?% u?% t?% s? r?% q?% p?% o? n?% m?% l?% k? j?% i?% h?% g? f?% e?% d?% c? b?% a?% `?% _? ^?% ]?% \?% [? Z?% Y?% X?% W? V?% U?% T?% S? R?% Q?% P????????????????# ???????????????@@@  ? @ @?zz vq @?|? @?|?:@?z ?z @  qp *"?"@:"5:06R@@!* (@?  2B ?@*G ? never gonna give you upi think i justout-cheesed thetop gun andbilly ocean covers!should i own up tothis? ... okthe crime wascommited byXeron/IRIS(sorry :)hivelytracker-0+git20180223/Songs/doobrey_gubbins.hvl0000775001024000102400000001173413042730153017365 0ustar HVL{%@N                       %???%???%???%???%???%???%???-???%??? ???%??? ???%??? ???%??????%???!???%???!???%???!???%???-???%???*???%???*???%???,???%???-???%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%@?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,???%?%???-?%??????%?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,???%?%???-?Ǭ???????%?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,???%?%?%???%?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,?*?(?'?(?'?Ǭ?-?%???%?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,?*?(?'?%@?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,?*?(?'?(?'?%?#?%?#?%?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,???%?%???-?(Ǭ?'?%?#?%?%???,???%?%???,???%?%???-?%???%?%???,???%?%???,???%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%??????%???%??????%??????%?????? ?????? ?????? ??????????(??????(???*??????(??????*??????%??????%??????#??????????,??????,???-??????,??????-??????(??????(??????'???*???,???%??????%???%??????(??????(??????(???%???%???%???%???#???!???(??????(???*??????-??????-??????*???*???*???*???(???(???%???-??????-???/??????1??????4??????1???/???-???/???,???*???(???????????????????????????????????????????????????????????????????? ?????????????????????????????????????????? ??? ?????#?????%?????#?????!??????? ?????#????? ????? ?????!??????????????????????????????????????????????????????????????????????????!?????!???????????????????????????!?????????!?????#?????!?????#?????%???????!?????#?????%?????(?????-???%???????%???????%???????%???????%???????%???????%???????%???%???%???????%???????%???????%???????%???????%???????%???????%???%???%???????%???????%???????%???????????????????????????????%???????%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?%?????????????????????????????????????????????????????????????????????????? ???????????????????????????????????????????????????????????????@ @?zz vq@ @@ ?& l // 1h @E@6)p +?# @?|? @?|?, @@* ?! !1@55 ?@!?  doobrey gubbinsXeron / IRIS2008Perhaps a little bitrepetetive.But that is becauseI suck.hivelytracker-0+git20180223/replay.h0000775001024000102400000002716713042730153014061 0ustar // Woohoo! #define MAX_CHANNELS 16 // Some handy constants. Thanks eightbitbubsy. #define AMIGA_PAL_XTAL 28375160 #define AMIGA_NTSC_XTAL 28636360 #define AMIGA_CPU_PAL_CLK ((AMIGA_PAL_XTAL / 4)) #define AMIGA_CPU_NTSC_CLK ((AMIGA_NTSC_XTAL / 4)) #define AMIGA_CIA_PAL_CLK ((AMIGA_CPU_PAL_CLK / 10)) #define AMIGA_CIA_NTSC_CLK ((AMIGA_CPU_NTSC_CLK / 10)) #define AMIGA_PAULA_PAL_CLK ((AMIGA_CPU_PAL_CLK / 2)) #define AMIGA_PAULA_NTSC_CLK ((AMIGA_CPU_NTSC_CLK / 2)) #define Period2Freq(period) ((AMIGA_PAULA_PAL_CLK * 65536.f) / (period)) //#define Period2Freq(period) (3579545.25f / (period)) struct ahx_envelope { int16 aFrames, aVolume; int16 dFrames, dVolume; int16 sFrames; int16 rFrames, rVolume; int16 pad; }; struct ahx_plsentry { uint8 ple_Note; uint8 ple_Waveform; int16 ple_Fixed; int8 ple_FX[2]; int8 ple_FXParam[2]; }; struct ahx_plist { int16 pls_Speed; int16 pls_Length; struct ahx_plsentry pls_Entries[256]; }; struct ahx_instrument { TEXT ins_Name[128]; uint8 ins_Volume; uint8 ins_WaveLength; uint8 ins_FilterLowerLimit; uint8 ins_FilterUpperLimit; uint8 ins_FilterSpeed; uint8 ins_SquareLowerLimit; uint8 ins_SquareUpperLimit; uint8 ins_SquareSpeed; uint8 ins_VibratoDelay; uint8 ins_VibratoSpeed; uint8 ins_VibratoDepth; uint8 ins_HardCutRelease; uint8 ins_HardCutReleaseFrames; struct ahx_envelope ins_Envelope; struct ahx_plist ins_PList; int32 ins_ptop; int32 ins_pcurx; int32 ins_pcury; }; struct ahx_position { uint8 pos_Track[MAX_CHANNELS]; int8 pos_Transpose[MAX_CHANNELS]; }; struct ahx_step { uint8 stp_Note; uint8 stp_Instrument; uint8 stp_FX; uint8 stp_FXParam; uint8 stp_FXb; uint8 stp_FXbParam; }; struct ahx_voice { int16 vc_Track; int16 vc_NextTrack; int16 vc_Transpose; int16 vc_NextTranspose; int16 vc_OverrideTranspose; int32 vc_ADSRVolume; struct ahx_envelope vc_ADSR; struct ahx_instrument *vc_Instrument; uint32 vc_SamplePos; uint32 vc_Delta; uint16 vc_InstrPeriod; uint16 vc_TrackPeriod; uint16 vc_VibratoPeriod; uint16 vc_WaveLength; int16 vc_NoteMaxVolume; uint16 vc_PerfSubVolume; uint8 vc_NewWaveform; uint8 vc_Waveform; uint8 vc_PlantPeriod; uint8 vc_VoiceVolume; uint8 vc_PlantSquare; uint8 vc_IgnoreSquare; uint8 vc_FixedNote; int16 vc_VolumeSlideUp; int16 vc_VolumeSlideDown; int16 vc_HardCut; uint8 vc_HardCutRelease; int16 vc_HardCutReleaseF; uint8 vc_PeriodSlideOn; int16 vc_PeriodSlideSpeed; int16 vc_PeriodSlidePeriod; int16 vc_PeriodSlideLimit; int16 vc_PeriodSlideWithLimit; int16 vc_PeriodPerfSlideSpeed; int16 vc_PeriodPerfSlidePeriod; uint8 vc_PeriodPerfSlideOn; int16 vc_VibratoDelay; int16 vc_VibratoSpeed; int16 vc_VibratoCurrent; int16 vc_VibratoDepth; int16 vc_SquareOn; int16 vc_SquareInit; int16 vc_SquareWait; int16 vc_SquareLowerLimit; int16 vc_SquareUpperLimit; int16 vc_SquarePos; int16 vc_SquareSign; int16 vc_SquareSlidingIn; int16 vc_SquareReverse; uint8 vc_FilterOn; uint8 vc_FilterInit; int16 vc_FilterWait; int16 vc_FilterSpeed; int16 vc_FilterUpperLimit; int16 vc_FilterLowerLimit; int16 vc_FilterPos; int16 vc_FilterSign; int16 vc_FilterSlidingIn; int16 vc_IgnoreFilter; int16 vc_PerfCurrent; int16 vc_PerfSpeed; int16 vc_PerfWait; struct ahx_plist *vc_PerfList; int8 *vc_AudioPointer; int8 *vc_AudioSource; uint8 vc_NoteDelayOn; uint8 vc_NoteCutOn; int16 vc_NoteDelayWait; int16 vc_NoteCutWait; int16 vc_AudioPeriod; int16 vc_AudioVolume; int32 vc_WNRandom; int8 *vc_MixSource; int8 vc_SquareTempBuffer[0x80]; int8 vc_VoiceBuffer[0x282*4]; uint8 vc_VoiceNum; uint8 vc_TrackMasterVolume; uint8 vc_TrackOn; uint8 vc_SetTrackOn; int16 vc_VoicePeriod; uint32 vc_Pan; uint32 vc_SetPan; uint32 vc_PanMultLeft; uint32 vc_PanMultRight; int32 vc_VUMeter; /* Ring modulation! */ uint32 vc_RingSamplePos; uint32 vc_RingDelta; int8 *vc_RingMixSource; uint8 vc_RingPlantPeriod; int16 vc_RingInstrPeriod; int16 vc_RingBasePeriod; int16 vc_RingAudioPeriod; int8 *vc_RingAudioSource; uint8 vc_RingNewWaveform; uint8 vc_RingWaveform; uint8 vc_RingFixedPeriod; int8 vc_RingVoiceBuffer[0x282*4]; }; // Store all tunes in a list so we can have more than // one loaded at once (= tabs!) struct ahx_tune { struct Node at_ln; struct List *at_undolist; struct List *at_redolist; uint32 at_undomem; /**** ***** Anything that can't just be copied from one ahx_tune structure ***** into another with a single CopyMem call should be above at_Name ****/ TEXT at_Name[128]; uint32 at_Time; uint32 at_ExactTime; uint16 at_LoopDetector; uint16 at_SongNum; uint32 at_Frequency; float64 at_FreqF; int8 *at_WaveformTab[MAX_CHANNELS]; uint16 at_Restart; uint16 at_PositionNr; uint8 at_SpeedMultiplier; uint8 at_TrackLength; uint8 at_TrackNr; uint8 at_InstrumentNr; uint8 at_SubsongNr; uint16 at_PosJump; uint32 at_PlayingTime; int16 at_Tempo; int16 at_PosNr; int16 at_NextPosNr; int16 at_StepWaitFrames; int16 at_NoteNr; uint16 at_PosJumpNote; uint8 at_GetNewPosition; uint8 at_PatternBreak; uint8 at_SongEndReached; uint8 at_Stereo; uint16 at_Subsongs[256]; uint16 at_Channels; struct ahx_position at_Positions[1000]; struct ahx_step at_Tracks[256][64]; struct ahx_instrument at_Instruments[64]; struct ahx_voice at_Voices[MAX_CHANNELS]; int32 at_posed_curs; int32 at_tracked_curs; int32 at_curins; int32 at_drumpadnote; BOOL at_drumpadmode; int16 at_topins; int16 at_topinsb; int32 at_curss; int32 at_curlch; int32 at_baseins; int32 at_curpanel; BOOL at_modified; int32 at_mixgain; int32 at_mixgainP; // Tracked mark int16 at_cbmarkmarknote; int16 at_cbmarkmarkx; int16 at_cbmarkcurnote; int16 at_cbmarkcurx; int16 at_cbmarktrack; int16 at_cbmarkstartnote; int16 at_cbmarkendnote; int16 at_cbmarkleftx; int16 at_cbmarkrightx; int16 at_cbmarkbits; // Posed mark int16 at_cbpmarkmarkpos; int16 at_cbpmarkmarklft; int16 at_cbpmarklft; int16 at_cbpmarktop; int16 at_cbpmarkrgt; int16 at_cbpmarkbot; int16 at_cbpmarklftcol; int16 at_cbpmarkrgtcol; int16 at_notejump; int16 at_inotejump; int16 at_rempos[5]; int32 at_defstereo; int32 at_defpanleft; int32 at_defpanright; uint8 at_ticks; uint8 at_secs; uint8 at_mins; uint8 at_hours; int32 at_doing; int32 at_editing; int32 at_idoing; // Undo stuff struct ahx_plsentry at_rem_pls_Entries[256]; TEXT at_rem_string[128]; struct ahx_step at_rem_track[64]; uint8 at_rem_posbuf[1000*MAX_CHANNELS*2]; int32 at_rem_pbleft, at_rem_pbpos; int32 at_rem_pbchans, at_rem_pbrows; uint8 at_stopnextrow; }; enum { RPC_STOP, RPC_PLAY_SONG, RPC_PLAY_POS, RPC_PLAY_NOTE, RPC_CONT_SONG, RPC_CONT_POS, RPC_PLAY_ROW }; struct rp_command { #ifndef __SDL_WRAPPER__ struct Message rpc_Message; #endif uint16 rpc_Command; uint16 rpc_Data; uint16 rpc_Data2; struct ahx_tune *rpc_Tune; }; struct ahx_tune *rp_load_tune( const TEXT *name, struct ahx_tune *at ); BOOL rp_init( void ); void rp_shutdown( void ); void rp_handler( uint32 gotsigs ); void rp_stop( void ); struct ahx_tune *rp_new_tune( BOOL addtolist ); void rp_free_tune( struct ahx_tune *at ); void rp_clear_tune( struct ahx_tune *at ); BOOL rp_play_note( struct ahx_tune *at, int32 inst, int32 note, int32 channel ); void rp_load_ins( const TEXT *name, struct ahx_tune *at, int32 in ); void rp_save_ins( const TEXT *name, struct ahx_tune *at, int32 in ); BOOL rp_play_song( struct ahx_tune *at, uint32 subsong, BOOL cont ); BOOL rp_play_pos( struct ahx_tune *at, BOOL cont ); BOOL rp_init_subsong( struct ahx_tune *at, uint32 nr ); uint32 rp_ahx_test( const struct ahx_tune *at ); void rp_save_ahx( const TEXT *name, struct ahx_tune *at ); void rp_save_hvl( const TEXT *name, struct ahx_tune *at ); int32 rp_find_loudest( struct ahx_tune *at ); void rp_clear_instrument( struct ahx_instrument *ins ); void rp_zap_tracks( struct ahx_tune * at ); void rp_zap_positions( struct ahx_tune *at ); void rp_zap_instruments( struct ahx_tune *at ); BOOL rp_play_row( struct ahx_tune *at ); #define SWB_DOUBLECMD 0 #define SWF_DOUBLECMD (1L<?=}`???`p`@ `@p0p@>0p00 8ߠ80|8|p??psa0|@@|; ?  4xp0|0zx0~???? `|  4p0oxx|?^&>_OC#n??? ! 0< p0 `@@@??s|;? x|~~?s|;?x|~~..m`|CO'?À'ŀO<ǐq'7?pcxxxx}uu?u~uuuu|q|s~=}@@|??~- ?|{\{ Ph0????SYS:Utilities/MultiViewFORMICONFACE--ARGB  xWP[=y؇lCl!ynx2nc66^L3WѫMћh0 LTQf0Հ9Ex<&q$cM&{gsn:issS#mll8{vv* sssXXX"A__NNN(83VVV;7٫:##<BQQ sss١cfzN_sqq1B(cQ]]]@SSajj':gGNN&氽㚚ػ!8~wwwp 7cFBByR:qocggQ bCAAN2 ñͩojr#TCBggWۄN3?~~~ԉ!3J(3 crr{}}\˓j9wU|l>i)pNC1 ` Jqx;^]]ؘza6y|=z4Vid}2%RGpx]#[i-"T<9dyfѲw½mV022@ j444p3_ԤD4Wd&j+ᐼ,;z=<"dQVZ 6zgܯL---r̅ 8D"Zؙ^ñ_ OQr(\5|%K!XFU$Iv-]Gb&yq=ߛufVw=gNlb{+SYn L iF"| /:/ xl#*4N"fClN_gޟ[\&V%Se~ 3BpV#r;!q(c@{`6{qkrΘ|=.Ǹe,bYmT^O HeĞM쩒]e[ėܚ0<33c\ zXG4BKnF~z3E1yIoͭ)3oOFyZ@6,@v;q[9bf'/--qRCwϯBUU***#'p |_1=G\qLGZDI`5)R yO²۰C*Q}o[|q_^W8v5__ 8nB ? '(Q8o7a{-@{{+ػfrʵ<.w.ۼ]y{ag IE (ER軉qɽ W<+`Ukwqxap l-x9d>Fy; ӵ Ԏ=@b -V` hDV&ybyM\u$լr} ]rNҖ[r'H]b?o?D lNfRq+fK((hiܳɓ|յx'g%BgyQeWC8VS8Ys^=_ qɿ  ^V ~DL#;az`ߋ };poQ.#\+r Ж{0U'޴6)G\i7 m 36Ԗ{q=z#sFE' aܢ5svɏ6|ٟ|O2f5-S7Y>)<ģ~{s[/֞qH 5Fl"|s*Ta=l7yvA}Ѐ{]7ՠWjqѥgqNZy,NYePwX<>ۇ,}0o}pZcARGB  xŚTWǷfs$'-'g7gcD#D" i aH>HoR"1e7u%Y5++~8hQo}ww'0t9 +n~ x}qm?A90۝]}jq2Dmr_a~}p;\p/߈Dmrxpl=8Ёmq6) ~0^WmmmZ[[K---m;@yĞX 555jkkQ__/hǎhllΝ;555 "}nc ,zp?3p?3z4_wٵb\-C7~ )0@?R-4C\nbMϣ,s/1?{4sjj*|}}aff,\V\.GII8fVyy9]Cq:'Z,pzz53ppw1g68;;!,LBH氶FZZ̜?{8bw[pJ維wޫ9y111BU.#Ϫldff !EU@[u)*A^cw<>Y'^lD"qQ.o54JQZ<\L*BVV06#6XMKD%<6s5k/%]-5k3w=W'E~VsN\E1Ok}r:Jڵ=))I'&3_ y:)pY` ȷi,נ| s)֎avNF2)Dxȩo!iLMWGDp=ўGﻶX3\{i 7cϜ(78"j%hzIkx-ٖFlo[±3NCיΞ /fOMgϩˣ/cʔWʿb0uxm͘:s0c\̘6ޘs0-̙o.xKMk,%V32[;,4ub3',6wR  -`R,^!9WX,8 iϏcy?*9=4U6xlrI,l'zW9){e9zjp:k늤t8Pi>6F:6VckuP1샩TmoV٭Z|̆51q-\<Zr?N,lcϽx_4׿¥ qFP| =TuTBI.sI[ DX(J"T,a8]xvTc6xf+f^Y0ɦZ<+/+b+jJnH㘅u,J\TGT矛q)`yoX|S,,lqˢqR^}TI3 3{B73 313|y0'Cqش/6kMEWIfS<) q{3[Gg^xÉF2Q ,99s;2+-kFmrˠ)% = )96?w3U.A Lƌ|֥ܫqx#N4MQT=QL|"|ca~9w[,4oE| . ฬ  zEg c>'+Q ". ר͈$EDoD"8H k\a澑jq )$k۽ a3y:kmM8_Iy0ΚtgCalk]u5/W~zV2A6Xl #g,$[`'"c:!tzKu~DtD֬O.38>ސGT{g3o,竧 Tz$TSI"7^K"!'n^T362KyPos܄[ g[N=su}L|×&q^=Y:m{ GxQ57\':{NG̭;=G]hzp^eG9ͺC+\z;]Ȕ~qGtO\r[ewAgww̓u-S0ym33ӪG%3+ϩ {:8o 71֮;|cL\5 W?}\G>̼Gzwώhivelytracker-0+git20180223/makefile0000775001024000102400000000120613042730153014076 0ustar CFLAGS = -Wall -O3 -gstabs -I. CC = gcc AR = ar RANLIB = ranlib DEBUGLIB = LFLAGS = -use-dynld -lm -gstabs all: ht ht: ht.o gui.o util.o replay.o about.o undo.o $(CC) -o ht ht.o gui.o util.o replay.o about.o undo.o $(LFLAGS) ht.o: ht.c gui.h replay.h about.h $(CC) -c ht.c -o ht.o $(CFLAGS) gui.o: gui.c gui.h util.h replay.h $(CC) -c gui.c -o gui.o $(CFLAGS) util.o: util.c util.h $(CC) -c util.c -o util.o $(CFLAGS) replay.o: replay.c replay.h undo.h $(CC) -c replay.c -o replay.o $(CFLAGS) about.o: about.c about.h gui.h $(CC) -c about.c -o about.o $(CFLAGS) undo.o: undo.c undo.h replay.h $(CC) -c undo.c -o undo.o $(CFLAGS) hivelytracker-0+git20180223/gui.h0000775001024000102400000000347513042730153013345 0ustar struct rawbm { uint16 w; uint16 h; int fpen, bpen; int findex; BOOL jam2; BOOL fpenset, bpenset, fontset; #ifndef __SDL_WRAPPER__ struct RastPort rp; struct BitMap *bm; int baseline; #else struct SDL_Surface *srf; SDL_Color fsc, bsc; TTF_Font *font; int offx, offy; #endif }; struct textbox { int16 x, y; int16 w; TEXT *content; int32 maxlen; int16 flags; int32 spos; int32 cpos; int16 inpanel; struct rawbm bm; }; void gui_pre_init( void ); BOOL gui_init( void ); BOOL gui_maybe_quit( void ); void gui_shutdown( void ); void gui_handler( uint32 gotsigs ); int32 gui_req( uint32 img, const TEXT *title, const TEXT *reqtxt, const TEXT *buttons ); void gui_render_tunepanel( BOOL force ); void gui_render_tracker( BOOL force ); void gui_render_perf( struct ahx_tune *at, struct ahx_instrument *ins, BOOL force ); void gui_set_various_things( struct ahx_tune *at ); void gui_render_inslistb( BOOL force ); void gui_render_inslist( BOOL force ); void gui_render_tbox( struct rawbm *bm, struct textbox *tb ); void gui_render_tabs( void ); void gui_render_vumeters( void ); void gui_render_wavemeter( void ); void gui_render_everything( void ); BOOL gui_restart( void ); BOOL make_image( struct rawbm *bm, uint16 w, uint16 h ); BOOL open_image( const TEXT *name, struct rawbm *bm ); void set_fcol(struct rawbm *bm, uint32 col); void fillrect_xy(struct rawbm *bm, int x, int y, int x2, int y2); void bm_to_bm(const struct rawbm *src, int sx, int sy, struct rawbm *dest, int dx, int dy, int w, int h); enum { D_IDLE = 0, D_EDITING, D_PLAYING, D_RECORDING }; enum { E_POS = 0, E_TRACK }; enum { PN_TRACKER = 0, PN_INSED, PN_END }; enum { TB_SONGNAME = 0, TB_INSNAME, TB_INSNAME2, TB_END }; #define FONT_FIX 0 #define FONT_SFX 1 #define FONT_PRP 2 hivelytracker-0+git20180223/Instruments/0000775001024000102400000000000013042730153014727 5ustar hivelytracker-0+git20180223/Instruments/as_plink.ins0000775001024000102400000000005713042730153017247 0ustar THXI(@?z ́ 0 as_plinkhivelytracker-0+git20180223/Instruments/blead.ins0000775001024000102400000000006013042730153016510 0ustar THXI @@* ? !1bleadhivelytracker-0+git20180223/Instruments/strangebass.ins0000775001024000102400000000005613042730153017762 0ustar THXI@@@.?x́ strangebasshivelytracker-0+git20180223/Instruments/Ring Modulation Test 4.ins0000775001024000102400000000053513042730153021467 0ustar HVLI@@@=[<A;B:C9D8E7F6G5H4I3J2K1L0M/N.O-P,Q+R*S)T(U'V&W%X$Y#Z"[!\ ]^_`abcdefghijklmno p q r s tuvwxyz{(|Ring Modulation Test 4hivelytracker-0+git20180223/Instruments/apol.7-b5.ins0000775001024000102400000000006413042730153017051 0ustar THXI@@?q0 apol.7-b5hivelytracker-0+git20180223/Instruments/Ring Modulation Test 1.ins0000775001024000102400000000006613042730153021463 0ustar HVLI@@@=[Ring Modulation Test 1hivelytracker-0+git20180223/Instruments/evlead1.ins0000775001024000102400000000005213042730153016763 0ustar THXI=@* ?  evlead1hivelytracker-0+git20180223/Instruments/explos.ins0000775001024000102400000000007113042730153016755 0ustar THXI@@@"?z  @YPJ@exploshivelytracker-0+git20180223/Instruments/plerk2.ins0000775001024000102400000000005513042730153016644 0ustar THXI @  ?ź  plerk2hivelytracker-0+git20180223/Instruments/die_plinken.ins0000775001024000102400000000005613042730153017727 0ustar THXI@"@@3 ? die_plinkenhivelytracker-0+git20180223/Instruments/nice_short.ins0000775001024000102400000000005513042730153017602 0ustar THXI@&? ԋnice_shorthivelytracker-0+git20180223/Instruments/gluc_globber.ins0000775001024000102400000000006713042730153020076 0ustar THXI,@@#?q@gluc_blobberhivelytracker-0+git20180223/Instruments/apol.major7.ins0000775001024000102400000000006613042730153017600 0ustar THXI@@?0apol.major7hivelytracker-0+git20180223/Instruments/buzzorgan.ins0000775001024000102400000000005013042730153017461 0ustar THXI-@** ?buzzorganhivelytracker-0+git20180223/Instruments/Filtered Technobassdrum1.ins0000775001024000102400000000014713042730153022230 0ustar THXI@@  ? `z0\ p\ `T Filtered Technobassdrum1hivelytracker-0+git20180223/Instruments/Ring Modulation Test 8.ins0000775001024000102400000000012413042730153021465 0ustar HVLI@@@=[(Ring Modulation Test 8hivelytracker-0+git20180223/Instruments/nice_bass2.ins0000775001024000102400000000005513042730153017455 0ustar THXI@@ ?anice_bass2hivelytracker-0+git20180223/Instruments/crimlead.ins0000775001024000102400000000005313042730153017223 0ustar THXI((@?a  crimleadhivelytracker-0+git20180223/Instruments/Filtered Lead1.ins0000775001024000102400000000006513042730153020113 0ustar THXI@#@&& ? < 0Filtered Lead1hivelytracker-0+git20180223/Instruments/Filtered FX1.ins0000775001024000102400000000011313042730153017555 0ustar THXI@D@ @?? !  Filtered FX1hivelytracker-0+git20180223/Instruments/fbasspop.ins0000775001024000102400000000005313042730153017260 0ustar THXI@@@ ? fbasspophivelytracker-0+git20180223/Instruments/panicblip.ins0000775001024000102400000000006013042730153017402 0ustar THXI@6 5? ?panicbliphivelytracker-0+git20180223/Instruments/spleado.ins0000775001024000102400000000006213042730153017072 0ustar THXI6 @@* ? !1spleadohivelytracker-0+git20180223/Instruments/Filtered Hihat1.ins0000775001024000102400000000007613042730153020305 0ustar THXI@@@?: h?XPFiltered Hihat1hivelytracker-0+git20180223/Instruments/apol.Snare5.ins0000775001024000102400000000007613042730153017537 0ustar THXI@@?|  | @@apol.Snare5hivelytracker-0+git20180223/Instruments/mvt_snare.ins0000775001024000102400000000007013042730153017440 0ustar THXI@@ ?z oYP@@Pzhvmvt_snarehivelytracker-0+git20180223/Instruments/snipsnare.ins0000775001024000102400000000011413042730153017443 0ustar THXI@ @@ ?& a @/@/1h snipsnarehivelytracker-0+git20180223/Instruments/wowbuzz.ins0000775001024000102400000000006613042730153017176 0ustar THXI@ @8* ??@wowbuzzhivelytracker-0+git20180223/Instruments/mll_lead.ins0000775001024000102400000000004713042730153017217 0ustar THXI@@5(?mll_leadhivelytracker-0+git20180223/Instruments/Ring Modulation Test 3.ins0000775001024000102400000000053513042730153021466 0ustar HVLI@@@=[<ABCDEFGHIJ K L M N OPQRSTUVWXYZ[\]^_`a b!c"d#e$f%g&h'i(j)k*l+m,n-o.p/q0r1s2t3u4v5w6x7y8z9{:|;Ring Modulation Test 3hivelytracker-0+git20180223/Instruments/plerk1.ins0000775001024000102400000000005513042730153016643 0ustar THXI,@  ?u plerk1hivelytracker-0+git20180223/Instruments/apol.lead5.ins0000775001024000102400000000005113042730153017365 0ustar THXI<(@?apol.lead5hivelytracker-0+git20180223/Instruments/Filtered Bassdrum1.ins0000775001024000102400000000010113042730153021015 0ustar THXI@ @?z vqFiltered Bassdrum1hivelytracker-0+git20180223/Instruments/pooba.ins0000775001024000102400000000004413042730153016543 0ustar THXI@3@8* ?Dpoobahivelytracker-0+git20180223/Instruments/blackysnare.ins0000775001024000102400000000007613042730153017746 0ustar THXI@@*?zl0 @m@k&@j&blackysnarehivelytracker-0+git20180223/Instruments/Ring Modulation Test 6.ins0000775001024000102400000000053513042730153021471 0ustar HVLI@@@yy<A;B:C9D8E7F6G5H4I3J2K1L0M/N.O-P,Q+R*S)T(U'V&W%X$Y#Z"[!\ ]^_`abcdefghijklmno p q r s tuvwxyz{(|Ring Modulation Test 6hivelytracker-0+git20180223/Instruments/as_reallynice.ins0000775001024000102400000000006013042730153020253 0ustar THXI*@6?E as_reallynicehivelytracker-0+git20180223/Instruments/Filtered Bass2.ins0000775001024000102400000000014113042730153020132 0ustar THXI<@@@0?zhy0  Filtered Bass2hivelytracker-0+git20180223/Instruments/asshort.ins0000775001024000102400000000006313042730153017127 0ustar THXI;*  ? asshortyhivelytracker-0+git20180223/Instruments/asbassx.ins0000775001024000102400000000006213042730153017107 0ustar THXI7 @!? ńasbassxhivelytracker-0+git20180223/Instruments/Ring Modulation Test 7.ins0000775001024000102400000000053513042730153021472 0ustar HVLI@@@yy* <A; B:C9D8E7F6G5H4I3J2K1L0M/N.O-P,Q+R*S)T(U'V&W%X$Y#Z"[!\ ]^_`abcdefghijklmno p q r s tuvwxyz{|Ring Modulation Test 7hivelytracker-0+git20180223/Instruments/buzzplinkenenenen.ins0000775001024000102400000000007013042730153021206 0ustar THXI@@  ?z ́ Mbuzzplinkenenenenhivelytracker-0+git20180223/Instruments/Filtered Bass1.ins0000775001024000102400000000007513042730153020137 0ustar THXI@ !@ &z?Filtered Bass1hivelytracker-0+git20180223/Instruments/aslead2.ins0000775001024000102400000000005613042730153016761 0ustar THXI2K@@G??aslead2hivelytracker-0+git20180223/Instruments/aslead.ins0000775001024000102400000000005113042730153016672 0ustar THXI(@? asleadhivelytracker-0+git20180223/Instruments/futlead.ins0000775001024000102400000000004613042730153017071 0ustar THXI35 ?futleadhivelytracker-0+git20180223/Instruments/w!m_pllead.ins0000775001024000102400000000005513042730153017452 0ustar THXI@@:"5:0w!m_plleadhivelytracker-0+git20180223/Instruments/asbassx2.ins0000775001024000102400000000010213042730153017164 0ustar THXI7 @!? ńultimate sausage masterhivelytracker-0+git20180223/Instruments/Filtered Snare1.ins0000775001024000102400000000010213042730153020306 0ustar THXI@@@?z  @YPz@pFiltered Snare1hivelytracker-0+git20180223/Instruments/Ring Modulation Test 2.ins0000775001024000102400000000053513042730153021465 0ustar HVLI@@@=[<ABCDEFGHIJ K L M N OPQRSTUVWXYZ[\]^_`a b!c"d#e$f%g&h'i(j)k*l+m,n-o.p/q0r1s2t3u4v5w6x7y8z9{:|;Ring Modulation Test 2hivelytracker-0+git20180223/Instruments/apol.m7.ins0000775001024000102400000000006213042730153016720 0ustar THXI@@?0apol.m7hivelytracker-0+git20180223/Instruments/tightsnare.ins0000775001024000102400000000007513042730153017617 0ustar THXI2@z?e ? q0z?tightsnarehivelytracker-0+git20180223/Instruments/Ring Modulation Test 5.ins0000775001024000102400000000053513042730153021470 0ustar HVLI@@@=[<A;B:C9D8E7F6G5H4I3J2K1L0M/N.O-P,Q+R*S)T(U'V&W%X$Y#Z"[!\ ]^_`abcdefghijklmno p q r s tuvwxyz{(|Ring Modulation Test 5hivelytracker-0+git20180223/Instruments/myfbass.ins0000775001024000102400000000010613042730153017106 0ustar THXI@=@6)> ?  myfbasshivelytracker-0+git20180223/Instruments/mt_lead.ins0000775001024000102400000000007613042730153017055 0ustar THXI#,@y?6q@0  0mt_leadhivelytracker-0+git20180223/Instruments/tbass.ins0000775001024000102400000000013013042730153016553 0ustar THXI<@@@?zhy0  tbasshivelytracker-0+git20180223/Instruments/Filtered Lead2.ins0000775001024000102400000000006113042730153020110 0ustar THXI($@@@( Filtered Lead2hivelytracker-0+git20180223/util.c0000775001024000102400000000171513042730153013524 0ustar #include #include #ifndef __SDL_WRAPPER__ /* ** A small routine to open a library and get its default ** interface, and report errors if anything fails. */ BOOL getLibIFace( struct Library **libbase, TEXT *libname, uint32 version, void *ifaceptr ) { struct Interface **ifptr = (struct Interface **)ifaceptr; *libbase = IExec->OpenLibrary( libname, version ); if( *libbase == NULL ) { printf( "Unable to open '%s' version %ld\n", libname, version ); return FALSE; } *ifptr = IExec->GetInterface( *libbase, "main", 1, NULL ); if( *ifptr == NULL ) { printf( "Unable to get the main interface for '%s'\n", libname ); return FALSE; } return TRUE; } #endif struct Node *allocnode(int32 size) { #ifndef __SDL_WRAPPER__ return IExec->AllocSysObjectTags(ASOT_NODE, ASONODE_Size, size, TAG_DONE); #else struct Node *ptr = malloc(size); if (ptr) memset(ptr, 0, sizeof(struct Node)); return ptr; #endif } hivelytracker-0+git20180223/gui.c0000775000000000000000000103236413042730153015132 0ustar rootroot/* ** Abandon all hope all ye who enter here ** (consider this fair warning) */ #include #include #include #include #include #include "replay.h" #include "gui.h" #include "util.h" #include "about.h" #include "undo.h" #ifdef __APPLE__ char *osxGetPrefsPath(); char *osxGetResourcesPath(char *, const char*); #endif #ifndef __SDL_WRAPPER__ struct Library *IntuitionBase = NULL; struct Library *P96Base = NULL; struct Library *GfxBase = NULL; struct Library *DataTypesBase = NULL; struct Library *DiskfontBase = NULL; struct Library *AslBase = NULL; struct Library *KeymapBase = NULL; struct Library *RequesterBase = NULL; struct IntuitionIFace *IIntuition = NULL; struct P96IFace *IP96 = NULL; struct GraphicsIFace *IGraphics = NULL; struct DataTypesIFace *IDataTypes = NULL; struct DiskfontIFace *IDiskfont = NULL; struct AslIFace *IAsl = NULL; struct KeymapIFace *IKeymap = NULL; struct RequesterIFace *IRequester = NULL; struct InputEvent iev; struct Window *mainwin = NULL; struct Window *prefwin = NULL; struct Screen *scr = NULL; struct Gadget gad_drag; struct Gadget gad_drag2; struct Gadget gad_depth; #else struct SDL_Surface *ssrf = NULL; BOOL needaflip = FALSE; extern int srfdepth; extern SDL_Event event; extern int16 rp_audiobuffer[]; extern uint32 rp_audiobuflen; extern BOOL aboutwin_open; #endif BOOL gui_savethem; #ifndef __SDL_WRAPPER__ int32 gui_tick_signum = -1; uint32 gui_tick_sig = 0; uint32 mainwin_sig = 0; uint32 prefwin_sig = 0; uint32 gui_sigs = 0; extern int8 *rp_audiobuffer[]; #endif int16 pw_x = -1, pw_y = -1; int16 pw_bl = 0, pw_bt = 0; int32 basekey = 24; uint8 *cbpbuf = NULL; uint32 cbpblen = 0; uint16 cbplcol = 0; uint16 cbprcol = 0; uint16 cbpchns = 0; uint16 cbprows = 0; extern uint32 rp_audiobuflen; #define TRACKED_X 3 #define TRACKED_Y 324 #define TRACKED_W 740 #define TRACKED_H 272 #define TRACKED_MX (TRACKED_W-1) #define TRACKED_MY (TRACKED_H-1) #define POSED_X 298 #define POSED_Y 190 #define POSED_W 316 #define POSED_H 98 #define POSED_MX (POSED_W-1) #define POSED_MY (POSED_H-1) #define INSLIST_X 622 #define INSLIST_Y 152 #define INSLIST_W 168 #define INSLIST_H 136 #define INSLIST_MX (INSLIST_W-1) #define INSLIST_MY (INSLIST_H-1) #define PERF_X 291 #define PERF_Y 144 #define PERF_W 146 #define PERF_H 438 #define PERF_MX (PERF_W-1) #define PERF_MY (PERF_H-1) #define INSLSTB_X 474 #define INSLSTB_Y 144 #define INSLSTB_W 168 #define INSLSTB_H 438 #define INSLSTB_MX (INSLSTB_W-1) #define INSLSTB_MY (INSLSTB_H-1) int32 qual; BOOL qualRMB = FALSE; //BOOL qualShift=FALSE, qualCtrl=FALSE, qualRMB=FALSE, qualAlt, qualAmiga; extern struct List *rp_tunelist; extern struct SignalSemaphore *rp_list_ss; struct ahx_tune *curtune = NULL; extern struct ahx_tune *rp_curtune; struct ahx_instrument *perf_lastinst = NULL; int32 perf_laststep = -1; int32 perf_lastcx = -1; int32 perf_lastcy = -1; int32 perf_lastplen = -1; struct ahx_tune *tmr_lasttune = NULL; uint32 tmr_lasttime = 0; int32 vum_last[MAX_CHANNELS]; BOOL vum_needclr = 0; int32 wm_count = 0; int32 pref_defstereo = 4; int32 pref_maxundobuf = 2; BOOL pref_fullscr = FALSE; BOOL pref_blankzeros = FALSE; BOOL pref_dorestart = FALSE; BOOL pref_oldfullscr; TEXT pref_oldskindir[512]; #ifdef __SDL_WRAPPER__ BOOL pref_rctrlplaypos = FALSE; #endif BOOL fullscr = FALSE; extern BOOL quitting; enum { PAL_BACK = 0, PAL_BARDARK, PAL_BARMID, PAL_BARLIGHT, PAL_TEXT, PAL_DISABLEDTEXT, PAL_WAVEMETER, PAL_CURSNORM, PAL_CURSEDIT, PAL_BTNTEXT, PAL_BTNSHADOW, PAL_FKEYHIGHLIGHT, PAL_POSEDCHIND, PAL_TABTEXT, PAL_TABSHADOW, PAL_END }; uint32 pal[] = { 0x000000, 0x500000, 0x780000, 0xff5555, 0xffffff, 0x808080, 0xff88ff, 0xffffff, 0xffff88, 0xffffff, 0x000000, 0xffffff, 0xffffff, 0xffffff, 0x000000 }; #ifdef __SDL_WRAPPER__ uint32 mappal[sizeof(pal)/sizeof(uint32)]; #endif const TEXT *notenames[] = { "---", "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" }; const int32 posed_xoff[] = { 4*7, 5*7, 6*7, 8*7, 9*7, 11*7, 12*7, 13*7, 15*7, 16*7, 18*7, 19*7, 20*7, 22*7, 23*7, 25*7, 26*7, 27*7, 29*7, 30*7, 32*7, 33*7, 34*7, 36*7, 37*7, 39*7, 40*7, 41*7, 43*7, 44*7 }; const int32 tracked_xoff[] = { 3*8, 7*8, 8*8, 10*8, 11*8, 12*8, 14*8, 15*8, 16*8, 18*8, 22*8, 23*8, 25*8, 26*8, 27*8, 29*8, 30*8, 31*8, 33*8, 37*8, 38*8, 40*8, 41*8, 42*8, 44*8, 45*8, 46*8, 48*8, 52*8, 53*8, 55*8, 56*8, 57*8, 59*8, 60*8, 61*8, 63*8, 67*8, 68*8, 70*8, 71*8, 72*8, 74*8, 75*8, 76*8, 78*8, 82*8, 83*8, 85*8, 86*8, 87*8, 89*8, 90*8, 91*8 }; const int32 perf_xoff[] = { 4*8, 9*8, 11*8, 12*8, 13*8, 15*8, 16*8, 17*8 }; struct czone { int16 x; int16 y; int16 w; int16 h; }; #define NBB_ENABLED 0 #define NBF_ENABLED (1L<0) { if (file[i] == PATHSEP) break; i--; } if (i>511) return; strcpy(path, file); path[i] = 0; } #endif BOOL isws( TEXT c ) { if( ( c == 9 ) || ( c == 32 ) ) return TRUE; return FALSE; } BOOL isnum( TEXT c ) { if( ( c >= '0' ) && ( c <= '9' ) ) return TRUE; return FALSE; } BOOL ishex( TEXT c ) { if( ( c >= '0' ) && ( c <= '9' ) ) return TRUE; if( ( c >= 'A' ) && ( c <= 'F' ) ) return TRUE; if( ( c >= 'a' ) && ( c <= 'f' ) ) return TRUE; return FALSE; } #ifndef __SDL_WRAPPER__ int32 gui_req( uint32 img, const TEXT *title, const TEXT *reqtxt, const TEXT *buttons ) { Object *req_obj; int32 n; req_obj = (Object *)IIntuition->NewObject( IRequester->REQUESTER_GetClass(), NULL, REQ_TitleText, title, REQ_BodyText, reqtxt, REQ_GadgetText, buttons, REQ_Image, img, REQ_WrapBorder, 40, TAG_DONE ); if( !req_obj ) return 1; n = IIntuition->IDoMethod( req_obj, RM_OPENREQ, NULL, NULL, scr ); IIntuition->DisposeObject( req_obj ); return n; } #endif void set_fpen(struct rawbm *bm, int pen) { if ((bm->fpenset) && (bm->fpen == pen)) return; #ifndef __SDL_WRAPPER__ IGraphics->SetRPAttrs( &bm->rp, RPTAG_APenColor, pal[pen]|0xff000000, TAG_DONE ); #else bm->fsc.r = pal[pen]>>16; bm->fsc.g = pal[pen]>>8; bm->fsc.b = pal[pen]; #endif bm->fpen = pen; bm->fpenset = TRUE; } void set_fcol(struct rawbm *bm, uint32 col) { #ifndef __SDL_WRAPPER__ IGraphics->SetRPAttrs( &bm->rp, RPTAG_APenColor, col|0xff000000, TAG_DONE ); #else bm->fsc.r = col>>16; bm->fsc.g = col>>8; bm->fsc.b = col; #endif bm->fpenset = FALSE; } void set_bpen(struct rawbm *bm, int pen) { if ((bm->bpenset) && (bm->bpen == pen)) return; #ifndef __SDL_WRAPPER__ IGraphics->SetRPAttrs( &bm->rp, RPTAG_BPenColor, pal[pen]|0xff000000, TAG_DONE ); #else bm->bsc.r = pal[pen]>>16; bm->bsc.g = pal[pen]>>8; bm->bsc.b = pal[pen]; #endif bm->bpen = pen; bm->bpenset = TRUE; } void set_pens(struct rawbm *bm, int fpen, int bpen) { set_fpen(bm, fpen); set_bpen(bm, bpen); } void fillrect_xy(struct rawbm *bm, int x, int y, int x2, int y2) { #ifndef __SDL_WRAPPER__ IGraphics->RectFill( &bm->rp, x, y, x2, y2 ); #else SDL_Rect rect = { .x = x+bm->offx, .y = y+bm->offy, .w = (x2-x)+1, .h = (y2-y)+1 }; Uint32 col; if (bm->fpenset) col = mappal[bm->fpen]; else #ifdef __APPLE__ // Work-around for SDL bug on OSX col = SDL_MapRGB(bm->srf->format, bm->fsc.b, bm->fsc.g, bm->fsc.r) >> 8; #else col = SDL_MapRGB(bm->srf->format, bm->fsc.r, bm->fsc.g, bm->fsc.b); #endif SDL_FillRect(bm->srf, &rect, col); #endif } void bm_to_bm(const struct rawbm *src, int sx, int sy, struct rawbm *dest, int dx, int dy, int w, int h) { #ifndef __SDL_WRAPPER__ IGraphics->BltBitMapRastPort(src->bm, sx, sy, &dest->rp, dx, dy, w, h, 0x0C0); #else SDL_Rect srect = { .x = sx+src->offx, .y = sy+src->offy, .w = w, .h = h }; SDL_Rect drect = { .x = dx+dest->offx, .y = dy+dest->offy, .w = w, .h = h }; SDL_BlitSurface(src->srf, &srect, dest->srf, &drect); if (dest == &mainbm) needaflip = TRUE; #endif } void set_font(struct rawbm *bm, int findex, BOOL jam2) { if ((bm->fontset) && (bm->findex == findex) && (bm->jam2 == jam2)) return; #ifndef __SDL_WRAPPER__ struct TextFont *thefont; switch (findex) { case FONT_SFX: thefont = sfxfont; break; case FONT_PRP: thefont = prpfont; break; default: thefont = fixfont; break; } IGraphics->SetRPAttrs( &bm->rp, RPTAG_DrMd, jam2 ? JAM2 : JAM1, RPTAG_Font, thefont, TAG_DONE ); bm->baseline = thefont->tf_Baseline; #else switch (findex) { case FONT_SFX: bm->font = sfxttf; break; case FONT_PRP: bm->font = prpttf; break; default: bm->font = fixttf; break; } #endif bm->findex = findex; bm->jam2 = jam2; bm->fontset = TRUE; } void printstr(struct rawbm *bm, const char *str, int x, int y) { #ifndef __SDL_WRAPPER__ IGraphics->Move(&bm->rp, x, y+bm->baseline); IGraphics->Text(&bm->rp, str, strlen(str)); #else SDL_Surface *srf; SDL_Rect rect = { .x = x+bm->offx, .y = y+bm->offy }; if (bm->jam2) { srf = TTF_RenderText_Shaded(bm->font, str, bm->fsc, bm->bsc); } else { srf = TTF_RenderText_Blended(bm->font, str, bm->fsc); } if (!srf) return; SDL_BlitSurface(srf, NULL, bm->srf, &rect); SDL_FreeSurface(srf); #endif } void printstrlen(struct rawbm *bm, const char *str, int len, int x, int y) { #ifndef __SDL_WRAPPER__ IGraphics->Move(&bm->rp, x, y+bm->baseline); IGraphics->Text(&bm->rp, str, len); #else char tmp[len+1]; SDL_Surface *srf; SDL_Rect rect = { .x = x+bm->offx, .y = y+bm->offy }; strncpy(tmp, str, len); tmp[len] = 0; if (bm->jam2) { srf = TTF_RenderText_Shaded(bm->font, tmp, bm->fsc, bm->bsc); } else { srf = TTF_RenderText_Blended(bm->font, tmp, bm->fsc); } if (!srf) return; SDL_BlitSurface(srf, NULL, bm->srf, &rect); SDL_FreeSurface(srf); #endif } int textfit( struct rawbm *bm, const char *str, int w ) { #ifndef __SDL_WRAPPER__ struct TextExtent te; return IGraphics->TextFit( &bm->rp, str, strlen(str), &te, NULL, 1, w, 24 ); #else int tw, th, c; char tmp[512]; strncpy(tmp, str, 512); tmp[511] = 0; c = strlen(tmp); while (c>0) { TTF_SizeText(bm->font, tmp, &tw, &th); if (tw <= w) break; tmp[--c] = 0; } return c; #endif } int32 gui_add_zone( struct czone *zn, int32 *numzn, int32 maxz, int16 x, int16 y, int16 w, int16 h ) { int32 fz; for( fz=0; fz<(*numzn); fz++ ) if( zn[fz].x == -1 ) break; if( fz == *numzn ) { if( (*numzn) == maxz ) return -1; (*numzn)++; } zn[fz].x = x; zn[fz].y = y; zn[fz].w = w; zn[fz].h = h; // printf( "z: %ld = %d,%d,%d,%d\n", fz, x, y, w, h ); return fz; } void gui_set_nbox( struct numberbox *nb, int16 x, int16 y, int16 w, int16 h, int32 min, int32 max, int32 cnum, const TEXT *fmt, uint16 flags ) { nb->x = x; nb->y = y; nb->w = w; nb->h = h; nb->min = min; nb->max = max; nb->cnum = cnum; nb->flags = flags; nb->pressed = 0; if( fmt == NULL ) strcpy( nb->fmt, "%d" ); else strcpy( nb->fmt, fmt ); } void gui_zap_zone( struct czone *zn, int32 *numzn, int32 max, int32 *zone ) { int32 tz; tz = *zone; *zone = -1; if( ( tz < 0 ) || ( tz >= max ) ) return; zn[tz].x = -1; if( (*numzn) == 0 ) return; while( (*numzn) > 0 ) { if( zn[(*numzn)-1].x != -1 ) break; (*numzn)--; } } int32 gui_get_zone( struct czone *zn, int32 numz, int16 x, int16 y ) { int32 i; for( i=0; i= zn[i].x ) && ( y >= zn[i].y ) && ( x < ( zn[i].x+zn[i].w ) ) && ( y < ( zn[i].y+zn[i].h ) ) ) return i; } } return -1; } BOOL make_image( struct rawbm *bm, uint16 w, uint16 h ) { bm->w = w; bm->h = h; bm->fontset = FALSE; bm->fpenset = FALSE; bm->bpenset = FALSE; #ifndef __SDL_WRAPPER__ IGraphics->InitRastPort( &bm->rp ); bm->bm = IGraphics->AllocBitMap( bm->w, bm->h, 8, 0, mainwin->RPort->BitMap ); if( !bm->bm ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return FALSE; } bm->rp.BitMap = bm->bm; #else bm->offx = 0; bm->offy = 0; bm->srf = SDL_CreateRGBSurface(SRFTYPE, w, h, srfdepth, 0, 0, 0, 0); if( !bm->srf ) { printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return FALSE; } #endif return TRUE; } BOOL open_image( const TEXT *name, struct rawbm *bm ) { #ifndef __SDL_WRAPPER__ uint8 *data; Object *io = NULL; struct BitMapHeader *bmh; struct RenderInfo ri; TEXT tmp[1024]; strcpy( tmp, skindir ); IDOS->AddPart( tmp, name, 1024 ); strcat( tmp, skinext ); io = IDataTypes->NewDTObject( tmp, DTA_SourceType, DTST_FILE, DTA_GroupID, GID_PICTURE, PDTA_FreeSourceBitMap, TRUE, PDTA_DestMode, PMODE_V43, TAG_DONE ); if( !io ) { printf( "Error opening '%s'\n", tmp ); return FALSE; } if( IDataTypes->GetDTAttrs( io, PDTA_BitMapHeader, &bmh, TAG_DONE ) == 0 ) { IDataTypes->DisposeDTObject( io ); printf( "Unable to get information about '%s'\n", tmp ); return FALSE; } bm->w = bmh->bmh_Width; bm->h = bmh->bmh_Height; IGraphics->InitRastPort( &bm->rp ); bm->bm = IGraphics->AllocBitMap( bm->w, bm->h, 8, 0, mainwin->RPort->BitMap ); if( !bm->bm ) { IDataTypes->DisposeDTObject( io ); printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return FALSE; } bm->rp.BitMap = bm->bm; data = IExec->AllocVecTags( bm->w*bm->h*4, TAG_DONE ); if( !data ) { IDataTypes->DisposeDTObject( io ); IGraphics->FreeBitMap( bm->bm ); printf( "Out of memory @ %s:%d\n", __FILE__, __LINE__ ); return FALSE; } IIntuition->IDoMethod( io, PDTM_READPIXELARRAY, data, PBPAFMT_ARGB, bm->w<<2, 0, 0, bm->w,bm->h ); ri.Memory = data; ri.BytesPerRow = bm->w<<2; ri.RGBFormat = RGBFB_A8R8G8B8; IP96->p96WritePixelArray( &ri, 0, 0, &bm->rp, 0, 0, bm->w, bm->h ); IExec->FreeVec( data ); IDataTypes->DisposeDTObject( io ); #else SDL_Surface *tmpsrf; TEXT tmp[1024]; #ifdef __APPLE__ if (strncmp(skindir, "Skins/", 6) == 0) osxGetResourcesPath(tmp, skindir); #else strncpy( tmp, skindir, 1024 ); #endif strncat( tmp, "/", 1024 ); strncat( tmp, name, 1024 ); strncat( tmp, skinext, 1024 ); tmpsrf = IMG_Load(tmp); if (!tmpsrf) { printf("Error loading '%s': %s\n", tmp, IMG_GetError()); return FALSE; } if (!make_image(bm, tmpsrf->w, tmpsrf->h)) { SDL_FreeSurface(tmpsrf); return FALSE; } /* Convert to the screen surface format */ SDL_BlitSurface(tmpsrf, NULL, bm->srf, NULL); SDL_FreeSurface(tmpsrf); #endif return TRUE; } BOOL gui_set_tbox( struct textbox *tb, int16 x, int16 y, int16 w, TEXT *content, int32 maxlen, int16 flags, int16 inpanel ) { tb->x = x; tb->y = y; tb->w = w; tb->content = content; tb->maxlen = maxlen; tb->flags = flags; tb->spos = 0; tb->cpos = 0; tb->inpanel = inpanel; if( !make_image( &tb->bm, w, 16 ) ) return FALSE; set_font(&tb->bm, FONT_FIX, FALSE); return TRUE; } void put_partbitmap( uint32 bm, uint16 bx, uint16 by, uint16 x, uint16 y, uint16 w, uint16 h ) { #ifndef __SDL_WRAPPER__ IGraphics->BltBitMapRastPort( bitmaps[bm].bm, bx, by, mainwin->RPort, x, y, w, h, 0x0C0 ); #else SDL_Rect srect = { .x = bx+bitmaps[bm].offx, .y = by+bitmaps[bm].offy, .w = w, .h = h }; SDL_Rect drect = { .x = x , .y = y, .w = w, .h = h }; SDL_BlitSurface(bitmaps[bm].srf, &srect, ssrf, &drect); needaflip = TRUE; #endif } void put_bitmap( uint32 bm, uint16 x, uint16 y, uint16 w, uint16 h ) { #ifndef __SDL_WRAPPER__ IGraphics->BltBitMapRastPort( bitmaps[bm].bm, 0, 0, mainwin->RPort, x, y, w, h, 0x0C0 ); #else SDL_Rect srect = { .x = bitmaps[bm].offx, .y = bitmaps[bm].offy, .w = w, .h = h }; SDL_Rect drect = { .x = x, .y = y, .w = w, .h = h }; SDL_BlitSurface(bitmaps[bm].srf, &srect, ssrf, &drect); needaflip = TRUE; #endif } void put_ppartbitmap( uint32 bm, uint16 bx, uint16 by, uint16 x, uint16 y, uint16 w, uint16 h ) { #ifndef __SDL_WRAPPER__ IGraphics->BltBitMapRastPort( bitmaps[bm].bm, bx, by, prefwin->RPort, x+pw_bl, y+pw_bt, w, h, 0x0C0 ); #else SDL_Rect srect = { .x = bx+bitmaps[bm].offx, .y = by+bitmaps[bm].offy, .w = w, .h = h }; SDL_Rect drect = { .x = x+4 , .y = y+4, .w = w, .h = h }; SDL_BlitSurface(bitmaps[bm].srf, &srect, prefbm.srf, &drect); #endif } void put_pbitmap( uint32 bm, uint16 x, uint16 y, uint16 w, uint16 h ) { #ifndef __SDL_WRAPPER__ IGraphics->BltBitMapRastPort( bitmaps[bm].bm, 0, 0, prefwin->RPort, x+pw_bl, y+pw_bt, w, h, 0x0C0 ); #else SDL_Rect srect = { .x = bitmaps[bm].offx, .y = bitmaps[bm].offy, .w = w, .h = h }; SDL_Rect drect = { .x = x+4, .y = y+4, .w = w, .h = h }; SDL_BlitSurface(bitmaps[bm].srf, &srect, prefbm.srf, &drect); needaflip = TRUE; #endif } void gui_render_nbox( struct ahx_tune *at, int32 panel, struct numberbox *nb ) { TEXT pbuf[32]; if( nb->cnum < nb->min ) nb->cnum = nb->min; if( nb->cnum > nb->max ) nb->cnum = nb->max; if( panel != at->at_curpanel ) return; set_fpen(&mainbm, PAL_BACK); fillrect_xy(&mainbm, nb->x, nb->y, nb->x+nb->w-25, nb->y+nb->h-1); set_font(&mainbm, FONT_FIX, FALSE); if( ( nb->flags & NBF_ENABLED ) != 0 ) set_fpen(&mainbm, PAL_TEXT); else set_fpen(&mainbm, PAL_DISABLEDTEXT); switch( nb->flags & (NBF_WAVELEN|NBF_ONOFF) ) { case NBF_WAVELEN: sprintf( pbuf, nb->fmt, (4L<cnum) ); break; case NBF_ONOFF: if( nb->cnum ) strcpy( pbuf, "On " ); else strcpy( pbuf, "Off" ); break; default: sprintf( pbuf, nb->fmt, nb->cnum ); } printstr(&mainbm, pbuf, nb->x+4, nb->y); put_partbitmap( BM_PLUSMINUS, 0, 0, nb->x+nb->w-28, nb->y-1, 28, 19 ); nb->pressed = 0; } void gui_render_tbox( struct rawbm *bm, struct textbox *tb ) { int16 panel; int32 w; if( ( tb->flags & TBF_VISIBLE ) == 0 ) return; if( bm == &mainbm ) { panel = PN_TRACKER; if( curtune ) panel = curtune->at_curpanel; if( panel != tb->inpanel ) return; } set_fpen(&tb->bm, PAL_BACK); fillrect_xy(&tb->bm, 0, 0, tb->w-1, 15); if( tb->content == NULL ) { bm_to_bm(&tb->bm, 0, 0, bm, tb->x, tb->y, tb->w, 16); return; } if( tb->cpos < 0 ) tb->cpos = 0; if( tb->cpos > strlen( tb->content ) ) tb->cpos = strlen( tb->content ); if( tb->cpos < tb->spos ) tb->spos = tb->cpos; if( tb->cpos >= (tb->spos + (tb->w>>3)) ) tb->spos = tb->cpos - ((tb->w>>3)-1); w = strlen( &tb->content[tb->spos] ); if( w > (tb->w>>3) ) w = (tb->w>>3); if( tb->flags & TBF_ACTIVE ) { set_fpen(&tb->bm, PAL_BARLIGHT); fillrect_xy(&tb->bm, (tb->cpos-tb->spos)*8, 0, (tb->cpos-tb->spos)*8+7, 15); } if( tb->flags & TBF_ENABLED ) set_fpen(&tb->bm, PAL_TEXT); else set_fpen(&tb->bm, PAL_DISABLEDTEXT); printstrlen(&tb->bm, &tb->content[tb->spos], w, 0, 0); bm_to_bm(&tb->bm, 0, 0, bm, tb->x, tb->y, tb->w, 16); } void gui_render_perf( struct ahx_tune *at, struct ahx_instrument *ins, BOOL force ) { TEXT pbuf[32]; int32 i, j, k, n; int32 cx, cy, mx, my; if( ins == NULL ) { set_fpen(&mainbm, PAL_BACK); fillrect_xy(&mainbm, PERF_X, PERF_Y, PERF_MX, PERF_MY); perf_lastinst = NULL; return; } if( ins->ins_pcury < 0 ) ins->ins_pcury = 0; if( ins->ins_pcury > 254 ) ins->ins_pcury = 254; if( ins->ins_pcurx < 0 ) ins->ins_pcurx = 0; if( ins->ins_pcurx > 7 ) ins->ins_pcurx = 7; if( ins->ins_pcury < ins->ins_ptop ) ins->ins_ptop = ins->ins_pcury; if( ins->ins_pcury >= ins->ins_ptop+(PERF_H>>4) ) ins->ins_ptop = ins->ins_pcury-((PERF_H>>4)-1); if( ins->ins_ptop < 0 ) ins->ins_ptop = 0; if( ins->ins_ptop > 255-(PERF_H>>4) ) ins->ins_ptop = (255-(PERF_H>>4)); if( ( force == FALSE ) && ( ins == perf_lastinst ) && ( ins->ins_ptop == perf_laststep ) && ( ins->ins_pcurx == perf_lastcx ) && ( ins->ins_pcury == perf_lastcy ) && ( ins->ins_PList.pls_Length == perf_lastplen ) ) return; perf_lastinst = ins; perf_laststep = ins->ins_ptop; perf_lastcx = ins->ins_pcurx; perf_lastcy = ins->ins_pcury; perf_lastplen = ins->ins_PList.pls_Length; set_fpen(&bitmaps[BM_PERF], PAL_BACK); fillrect_xy(&bitmaps[BM_PERF], 0, 0, PERF_MX, PERF_MY); set_pens(&bitmaps[BM_PERF], PAL_TEXT, PAL_BACK); if( ins->ins_ptop >= ins->ins_PList.pls_Length ) set_fpen(&bitmaps[BM_PERF], PAL_DISABLEDTEXT); k = ins->ins_pcury - ins->ins_ptop; for( i=0, j=ins->ins_ptop; i<(PERF_H>>4); i++, j++ ) { if( j > 254 ) break; if( j == ins->ins_pcury ) set_bpen(&bitmaps[BM_PERF], PAL_BARMID); if( j == ins->ins_pcury+1 ) set_bpen(&bitmaps[BM_PERF], PAL_BACK); if( j == ins->ins_PList.pls_Length ) set_fpen(&bitmaps[BM_PERF], PAL_DISABLEDTEXT); n = ins->ins_PList.pls_Entries[j].ple_Note; if( n > 60 ) n = 0; sprintf( pbuf, "%03d %s%c %01d %01X%02X %01X%02X ", (int)j, notenames[n], ins->ins_PList.pls_Entries[j].ple_Fixed ? '*' : ' ', ins->ins_PList.pls_Entries[j].ple_Waveform, ins->ins_PList.pls_Entries[j].ple_FX[0]&0xf, ins->ins_PList.pls_Entries[j].ple_FXParam[0]&0xff, ins->ins_PList.pls_Entries[j].ple_FX[1]&0xf, ins->ins_PList.pls_Entries[j].ple_FXParam[1]&0xff ); printstr(&bitmaps[BM_PERF], pbuf, 0, (i*16)+4); } if( k >= 0 ) { set_fpen(&bitmaps[BM_PERF], PAL_BARLIGHT); fillrect_xy(&bitmaps[BM_PERF], 0, k*16+3, PERF_MX, k*16+3); } if( k < ((PERF_H>>4)-1) ) { set_fpen(&bitmaps[BM_PERF], PAL_BARDARK); fillrect_xy(&bitmaps[BM_PERF], 0, k*16+20, PERF_MX, k*16+20); } set_fpen(&bitmaps[BM_PERF], PAL_CURSNORM); fillrect_xy(&bitmaps[BM_PERF], 3*8+4, 0, 3*8+4, PERF_MY); fillrect_xy(&bitmaps[BM_PERF], 8*8+4, 0, 8*8+4, PERF_MY); fillrect_xy(&bitmaps[BM_PERF], 10*8+4, 0, 10*8+4, PERF_MY); fillrect_xy(&bitmaps[BM_PERF], 14*8+4, 0, 14*8+4, PERF_MY); if( at->at_idoing == D_EDITING ) set_fpen(&bitmaps[BM_PERF], PAL_CURSEDIT); else set_fpen(&bitmaps[BM_PERF], PAL_CURSNORM); cx = perf_xoff[ins->ins_pcurx]-2; cy = (k<<4)+2; mx = cx+8+3; my = cy+16+3; if( ins->ins_pcurx == 0 ) mx = cx+32+3; fillrect_xy(&bitmaps[BM_PERF], cx, cy, mx, cy+1); fillrect_xy(&bitmaps[BM_PERF], cx, my-1, mx, my); fillrect_xy(&bitmaps[BM_PERF], cx, cy, cx+1, my); fillrect_xy(&bitmaps[BM_PERF], mx-1, cy, mx, my); put_bitmap( BM_PERF, PERF_X, PERF_Y, PERF_W, PERF_H ); } void gui_render_inslistb( BOOL force ) { struct ahx_tune *at; struct ahx_instrument *in; int16 curi; int32 i, j, k; TEXT tmp[32]; curi = 0; at = curtune; if( at ) { if( at->at_curpanel != PN_INSED ) return; curi = at->at_curins; } if( ( inslsb_lastcuri == curi ) && ( inslsb_lasttune == at ) && ( inslsb_lasttopi == at->at_topinsb ) && ( force == FALSE ) ) return; set_fpen(&bitmaps[BM_INSLISTB], PAL_BACK); fillrect_xy(&bitmaps[BM_INSLISTB], 0, 0, INSLSTB_MX, INSLSTB_MY); if( at ) { if( curi < at->at_topinsb ) at->at_topinsb = curi; if( curi > (at->at_topinsb+((INSLSTB_H>>4)-1)) ) at->at_topinsb = curi-((INSLSTB_H>>4)-1); if( at->at_topinsb < 1 ) at->at_topinsb = 1; set_pens(&bitmaps[BM_INSLISTB], PAL_TEXT, PAL_BACK); for( i=0, j=at->at_topinsb; i<(INSLSTB_H>>4); i++, j++ ) { sprintf( tmp, "%02d ", (int)j ); in = &at->at_Instruments[j]; for( k=0; k<18; k++ ) { if( in->ins_Name[k] == 0 ) break; tmp[k+3] = in->ins_Name[k]; } if( j == curi ) set_bpen(&bitmaps[BM_INSLISTB], PAL_BARMID); if( j == curi+1 ) set_bpen(&bitmaps[BM_INSLISTB], PAL_BACK); printstrlen(&bitmaps[BM_INSLISTB], tmp, 24, 0, i*16+4); } if( curi >= at->at_topinsb ) { k = (curi-at->at_topinsb)*16+4; set_fpen(&bitmaps[BM_INSLISTB], PAL_BARLIGHT); fillrect_xy(&bitmaps[BM_INSLISTB], 0, k-1, INSLSTB_MX, k-1); set_fpen(&bitmaps[BM_INSLISTB], PAL_BARDARK); fillrect_xy(&bitmaps[BM_INSLISTB], 0, k+16, INSLSTB_MX, k+16); } set_fpen(&bitmaps[BM_INSLISTB], PAL_TEXT); fillrect_xy(&bitmaps[BM_INSLISTB], 20, 0, 20, INSLSTB_MY); } put_bitmap( BM_INSLISTB, INSLSTB_X, INSLSTB_Y, INSLSTB_W, INSLSTB_H ); inslsb_lastcuri = curi; inslsb_lasttune = at; inslsb_lasttopi = at->at_topinsb; } void gui_check_nb( struct ahx_tune *at, uint32 nb, int32 val ) { if( trk_nb[nb].cnum != val ) { trk_nb[nb].cnum = val; gui_render_nbox( at, PN_TRACKER, &trk_nb[nb] ); } } void gui_check_inb( struct ahx_tune *at, uint32 nb, int32 val ) { if( ins_nb[nb].cnum != val ) { ins_nb[nb].cnum = val; gui_render_nbox( at, PN_INSED, &ins_nb[nb] ); } } void gui_set_various_things( struct ahx_tune *at ) { int32 i; if( at->at_NextPosNr != -1 ) gui_check_nb( at, NB_POSNR, at->at_NextPosNr ); else gui_check_nb( at, NB_POSNR, at->at_PosNr ); gui_check_nb( at, NB_LENGTH, at->at_PositionNr ); gui_check_nb( at, NB_TRKLEN, at->at_TrackLength ); gui_check_nb( at, NB_RESPOS, at->at_Restart ); gui_check_nb( at, NB_SSNR, at->at_SubsongNr ); if( at->at_SubsongNr > 0 ) { if( ( trk_nb[NB_CURSS].cnum != at->at_curss ) || ( ( trk_nb[NB_CURSS].flags & NBF_ENABLED ) == 0 ) ) { trk_nb[NB_CURSS].flags |= NBF_ENABLED; trk_nb[NB_CURSS].cnum = at->at_curss; gui_render_nbox( at, PN_TRACKER, &trk_nb[NB_CURSS] ); } } else { if( ( trk_nb[NB_CURSS].flags & NBF_ENABLED ) != 0 ) { trk_nb[NB_CURSS].flags &= ~NBF_ENABLED; trk_nb[NB_CURSS].cnum = 0; gui_render_nbox( at, PN_TRACKER, &trk_nb[NB_CURSS] ); } } if( at->at_curss > 0 ) { if( ( trk_nb[NB_SSPOS].cnum != at->at_Subsongs[at->at_curss-1] ) || ( ( trk_nb[NB_SSPOS].flags & NBF_ENABLED ) == 0 ) ) { if( at->at_SubsongNr > 0 ) { trk_nb[NB_SSPOS].flags |= NBF_ENABLED; trk_nb[NB_SSPOS].cnum = at->at_Subsongs[at->at_curss-1]; gui_render_nbox( at, PN_TRACKER, &trk_nb[NB_SSPOS] ); } else { trk_nb[NB_SSPOS].flags &= ~NBF_ENABLED; trk_nb[NB_SSPOS].cnum = 0; gui_render_nbox( at, PN_TRACKER, &trk_nb[NB_SSPOS] ); } } } else { if( ( trk_nb[NB_SSPOS].flags & NBF_ENABLED ) != 0 ) { trk_nb[NB_SSPOS].cnum = 0; trk_nb[NB_SSPOS].flags &= ~NBF_ENABLED; gui_render_nbox( at, PN_TRACKER, &trk_nb[NB_SSPOS] ); } } gui_check_nb( at, NB_CHANS, at->at_Channels ); gui_check_nb( at, NB_MIXG, at->at_mixgainP ); gui_check_nb( at, NB_SPEEDMULT, at->at_SpeedMultiplier ); gui_check_nb( at, NB_DRUMPADMODE, at->at_drumpadmode ); if( ( at != NULL ) && ( ins_nb[INB_INS].cnum != at->at_curins ) ) { ins_nb[INB_INS].cnum = at->at_curins; gui_render_nbox( at, PN_INSED, &ins_nb[INB_INS] ); } if( ( at == NULL ) || ( at->at_curins == 0 ) ) { for( i=1; iat_Instruments[at->at_curins]; for( i=1; iins_Volume ); gui_check_inb( at, INB_WAVELEN, ip->ins_WaveLength ); switch( ip->ins_WaveLength ) { case 0: i = 0x20; break; case 1: i = 0x10; break; case 2: i = 0x08; break; case 3: i = 0x04; break; case 4: i = 0x02; break; default: i = 0x01; break; } ins_nb[INB_SQRLOWER].min = i; ins_nb[INB_SQRUPPER].min = i; if( ip->ins_SquareLowerLimit < i ) ip->ins_SquareLowerLimit = i; if( ip->ins_SquareUpperLimit < i ) ip->ins_SquareUpperLimit = i; gui_check_inb( at, INB_ATTACK, ip->ins_Envelope.aFrames ); gui_check_inb( at, INB_AVOL, ip->ins_Envelope.aVolume ); gui_check_inb( at, INB_DECAY, ip->ins_Envelope.dFrames ); gui_check_inb( at, INB_DVOL, ip->ins_Envelope.dVolume ); gui_check_inb( at, INB_SUSTAIN, ip->ins_Envelope.sFrames ); gui_check_inb( at, INB_RELEASE, ip->ins_Envelope.rFrames ); gui_check_inb( at, INB_RVOL, ip->ins_Envelope.rVolume ); gui_check_inb( at, INB_VIBDELAY, ip->ins_VibratoDelay ); gui_check_inb( at, INB_VIBDEPTH, ip->ins_VibratoDepth ); gui_check_inb( at, INB_VIBSPEED, ip->ins_VibratoSpeed ); gui_check_inb( at, INB_SQRLOWER, ip->ins_SquareLowerLimit ); gui_check_inb( at, INB_SQRUPPER, ip->ins_SquareUpperLimit ); gui_check_inb( at, INB_SQRSPEED, ip->ins_SquareSpeed ); gui_check_inb( at, INB_FLTLOWER, ip->ins_FilterLowerLimit ); gui_check_inb( at, INB_FLTUPPER, ip->ins_FilterUpperLimit ); gui_check_inb( at, INB_FLTSPEED, ip->ins_FilterSpeed ); gui_check_inb( at, INB_PERFSPEED, ip->ins_PList.pls_Speed ); gui_check_inb( at, INB_PERFLEN, ip->ins_PList.pls_Length ); gui_check_inb( at, INB_HARDCUT, ip->ins_HardCutReleaseFrames ); gui_check_inb( at, INB_RELCUT, ip->ins_HardCutRelease ); } if( at == NULL ) { if( tbx[TB_SONGNAME].content != NULL ) { tbx[TB_SONGNAME].content = NULL; gui_render_tbox( &mainbm, &tbx[TB_SONGNAME] ); } } else { if( tbx[TB_SONGNAME].content != at->at_Name ) { tbx[TB_SONGNAME].content = at->at_Name; tbx[TB_SONGNAME].spos = 0; tbx[TB_SONGNAME].cpos = 0; gui_render_tbox( &mainbm, &tbx[TB_SONGNAME] ); } } if( at == NULL ) { if( tbx[TB_INSNAME].content != NULL ) { tbx[TB_INSNAME].content = NULL; gui_render_tbox( &mainbm, &tbx[TB_INSNAME] ); } } else { if( tbx[TB_INSNAME].content != at->at_Instruments[at->at_curins].ins_Name ) { tbx[TB_INSNAME].content = at->at_Instruments[at->at_curins].ins_Name; tbx[TB_INSNAME].spos = 0; tbx[TB_INSNAME].cpos = 0; gui_render_tbox( &mainbm, &tbx[TB_INSNAME] ); } } if( ( curtune ) && ( curtune->at_curpanel == PN_INSED ) ) { gui_render_perf( curtune, &curtune->at_Instruments[curtune->at_curins], FALSE ); gui_render_inslistb( FALSE ); } } void gui_setup_trkbbank( int32 which ) { int32 i; // Blank the bank! for( i=0; i<12; i++ ) { tbank[i].x = 744; tbank[i].y = 120+200 + i*23; tbank[i].name = NULL; tbank[i].action = BBA_NOTHING; tbank[i].raction = BBA_NOTHING; } switch( which ) { case 0: tbank[0].name = "Cut"; tbank[0].action = BBA_CUTTRK; tbank[0].raction= BBA_CUTTRK; tbank[1].name = "Copy"; tbank[1].action = BBA_COPYTRK; tbank[1].raction= BBA_COPYTRK; tbank[2].name = "Paste"; tbank[2].action = BBA_PASTE; tbank[2].raction= BBA_PASTE; tbank[3].name = "Nt. Up"; tbank[3].action = BBA_NOTEUP; tbank[4].name = "Nt. Dn"; tbank[4].action = BBA_NOTEDN; tbank[5].name = "Scl. Up"; tbank[5].action = BBA_SCRLUP; tbank[6].name = "Scl. Dn"; tbank[6].action = BBA_SCRLDN; tbank[7].name = "Rvrs."; tbank[7].action = BBA_REVERSE; break; } set_font(&mainbm, FONT_PRP, FALSE); for( i=0; i<11; i++ ) { if( tbank[i].name != NULL ) { #ifndef __SDL_WRAPPER__ tbank[i].xo = 26 - ( IGraphics->TextLength( &mainbm.rp, tbank[i].name, strlen( tbank[i].name ) ) >> 1 ); #else int w, h; TTF_SizeText(mainbm.font, tbank[i].name, &w, &h); tbank[i].xo = 26 - (w/2); #endif } } } void gui_setup_buttonbank( int32 which ) { int32 i; // Blank the bank! for( i=0; i<16; i++ ) { bbank[i].x = 256 + (i&3)*80; bbank[i].y = (i>>2)*24; bbank[i].name = NULL; bbank[i].action = BBA_NOTHING; bbank[i].raction = BBA_NOTHING; } switch( which ) { case 0: bbank[0].name = "New Tab"; bbank[0].action = BBA_NEWTAB; bbank[0].raction = BBA_CLONETAB; bbank[1].name = "Load Mod"; bbank[1].action = BBA_LOADTUNE; bbank[2].name = "Save AHX"; bbank[2].action = BBA_SAVEAHX; bbank[3].name = "Save HVL"; bbank[3].action = BBA_SAVEHVL; bbank[4].name = "Load Ins"; bbank[4].action = BBA_LOADINS; bbank[5].name = "Save Ins"; bbank[5].action = BBA_SAVEINS; bbank[6].name = "Ins Edit"; bbank[6].action = BBA_INSEDIT; bbank[7].name = "Prefs"; bbank[7].action = BBA_PREFS; bbank[8].name = "Autogain"; bbank[8].action = BBA_AUTOGAIN; bbank[9].name = "Undo"; bbank[9].action = BBA_UNDO; bbank[9].raction = BBA_REDO; bbank[10].name = "Optim"; bbank[10].action = BBA_OPTIMISE; bbank[10].raction= BBA_OPTIMISE_MORE; bbank[11].name = "About"; bbank[11].action = BBA_ABOUT; bbank[12].name = "Play Song"; bbank[12].action = BBA_PLAYSONG; bbank[12].raction= BBA_CONTSONG; bbank[13].name = "Play Pos"; bbank[13].action = BBA_PLAYPOS; bbank[13].raction= BBA_CONTPOS; bbank[14].name = "Stop"; bbank[14].action = BBA_STOP; bbank[14].raction= BBA_STOP; bbank[15].name = "Zap"; bbank[15].action = BBA_ZAP; break; #ifdef __SDL_WRAPPER__ case 1: bbank[0].name = "Zap song"; bbank[0].action = BBA_ZAP_SONG; bbank[1].name = "Zap Tracks"; bbank[1].action = BBA_ZAP_TRACKS; bbank[2].name = "Zap Posns"; bbank[2].action = BBA_ZAP_POSNS; bbank[3].name = "Zap Instrs"; bbank[3].action = BBA_ZAP_INSTRS; bbank[12].name = "Zap All"; bbank[12].action = BBA_ZAP_ALL; bbank[15].name = "Cancel"; bbank[15].action = BBA_BACK; break; #endif } set_font(&mainbm, FONT_PRP, FALSE); for( i=0; i<16; i++ ) { if( bbank[i].name != NULL ) { #ifndef __SDL_WRAPPER__ bbank[i].xo = 40 - ( IGraphics->TextLength( &mainbm.rp, bbank[i].name, strlen( bbank[i].name ) ) >> 1 ); #else int w, h; TTF_SizeText(mainbm.font, bbank[i].name, &w, &h); bbank[i].xo = 40 - (w/2); #endif } } } void gui_render_bbank_texts( struct buttonbank *bbnk, int32 nb ) { int32 i; set_font(&mainbm, FONT_PRP, FALSE); set_fpen(&mainbm, PAL_BTNSHADOW); for( i=0; i= 26 ) { tabh = 26; l = PN_TRACKER; if( curtune ) l = curtune->at_curpanel; switch( l ) { case PN_INSED: put_bitmap( BM_BG_INSED, 0, 120, 800, 2 ); break; default: put_bitmap( BM_BG_TRACKER, 0, 120, 800, 2 ); break; } } for( i=0; i<8; i++ ) { ttab[i].tune = NULL; gui_zap_zone( &zones[0], &numzones, MAX_ZONES, &ttab[i].zone ); } IExec->ObtainSemaphore( rp_list_ss ); ntabs = 0; at = (struct ahx_tune *)IExec->GetHead(rp_tunelist); while( at ) { ntabs++; at = (struct ahx_tune *)IExec->GetSucc(&at->at_ln); } if( ntabs == 0 ) { curtune = NULL; IExec->ReleaseSemaphore( rp_list_ss ); return; } if( curtune == NULL ) curtune = (struct ahx_tune *)IExec->GetHead(rp_tunelist); // Ensure phantom tab! rtabs = ntabs; if( rtabs < 2 ) rtabs = 2; if( rtabs > 8 ) rtabs = 8; // Space per tab tabsw = (800<<8) / rtabs; // Actual tab width tabw = (tabsw>>8)&~15; if( tabw == (tabsw>>8) ) tabw -= 8; // Tab offset k = ((tabsw>>1)-(tabw<<7)); set_font(&mainbm, FONT_PRP, FALSE); // Draw them! at = (struct ahx_tune *)IExec->GetHead(rp_tunelist); for( i=0, j=k; i= ntabs ) break; ttab[i].tune = at; ttab[i].zone = gui_add_zone( &zones[0], &numzones, MAX_ZONES, (j>>8), 96, tabw, 24 ); if( at == curtune ) { l = BM_TAB_LEFT; o = BM_TAB_TEXT; m = tabh; } else { l = BM_ITAB_LEFT; o = BM_ITAB_TEXT; m = 24; } tname = at->at_Name[0] ? at->at_Name : "[Untitled]"; #ifndef __SDL_WRAPPER__ n = IGraphics->TextLength( &mainbm.rp, tname, strlen( tname ) ); #else TTF_SizeText(mainbm.font, tname, &n, &k); #endif if( !tabtextback ) o = l+1; put_bitmap( l+0, j>>8, 96, 20, m ); for( k=0; k<(tabw-36); k+=32 ) { put_bitmap( o, (j>>8)+20+k, 96, (tabw-36)-k < 32 ? (tabw-36)-k : 32, m ); if( k > n ) o = l+1; } put_bitmap( l+2, (j>>8)+tabw-16, 96, 16, m ); k = textfit(&mainbm, tname, tabw-32); if( tabtextshad ) { set_fpen(&mainbm, PAL_TABSHADOW); printstrlen(&mainbm, tname, k, (j>>8)+25, 100); } set_fpen(&mainbm, PAL_TABTEXT); printstrlen(&mainbm, tname, k, (j>>8)+24, 99); at = (struct ahx_tune *)IExec->GetSucc(&at->at_ln); } IExec->ReleaseSemaphore( rp_list_ss ); } void gui_render_wavemeter( void ) { int32 i, v, off, delta; int16 *ba; set_fpen(&bitmaps[BM_WAVEMETERS], PAL_BACK); fillrect_xy(&bitmaps[BM_WAVEMETERS], 9, 7, 181+9, 37+ 7); fillrect_xy(&bitmaps[BM_WAVEMETERS], 9, 49, 181+9, 37+49); set_fpen(&bitmaps[BM_WAVEMETERS], PAL_WAVEMETER); #ifndef __SDL_WRAPPER__ ba = (int16 *)rp_audiobuffer[0]; #else ba = rp_audiobuffer; #endif delta = rp_audiobuflen / 728; if( delta == 0 ) delta = 1; delta <<= 1; for( i=9, off=0; i<(9+182); i++, off += delta ) { v = (ba[off] >> 10) + 16 + 10; if( v < (0+10) ) v = 0+10; if( v > (31+10) ) v = 31+10; #ifndef __SDL_WRAPPER__ if( i == 9 ) IGraphics->Move( &bitmaps[BM_WAVEMETERS].rp, 9, v ); else IGraphics->Draw( &bitmaps[BM_WAVEMETERS].rp, i, v ); #else fillrect_xy(&bitmaps[BM_WAVEMETERS], i, v, i, v); #endif } ba++; for( i=9, off=0; i<(9+182); i++, off += delta ) { v = (ba[off] >> 10) + 16 + 52; if( v < (0+52) ) v = 0+52; if( v > (31+52) ) v = 31+52; #ifndef __SDL_WRAPPER__ if( i == 9 ) IGraphics->Move( &bitmaps[BM_WAVEMETERS].rp, 9, v ); else IGraphics->Draw( &bitmaps[BM_WAVEMETERS].rp, i, v ); #else fillrect_xy(&bitmaps[BM_WAVEMETERS], i, v, i, v); #endif } put_bitmap( BM_WAVEMETERS, 576, 0, 200, 96 ); } void gui_render_vumeters( void ) { int32 lch, dch, v, i; if( curtune == NULL ) return; if( curtune->at_curpanel != PN_TRACKER ) return; dch = curtune->at_Channels; if( dch > 6 ) dch = 6; lch = curtune->at_curlch; for( i=0; iat_Voices[i+lch].vc_VUMeter >> 7; if( v > 64 ) v = 64; if( ( vum_needclr ) && ( v < vum_last[i+lch] ) && ( v < 64 ) ) { #ifndef __SDL_WRAPPER__ IGraphics->BltBitMapRastPort( bitmaps[BM_TRACKED].bm, 64+i*120, 64, mainwin->RPort, TRACKED_X+64+i*120, TRACKED_Y+64, 32, 64-v, 0x0C0 ); #else SDL_Rect srect = { .x = 64+i*120, .y = 64, .w = 32, .h = 64-v }; SDL_Rect drect = { .x = TRACKED_X+64+i*120, .y = TRACKED_Y+64, .w = 32, .h = 64-v }; SDL_BlitSurface( bitmaps[BM_TRACKED].srf, &srect, ssrf, &drect ); needaflip = TRUE; #endif } if( v > 0 ) put_bitmap( BM_VUMETER, TRACKED_X+64+i*120, (TRACKED_Y+128)-v, 32, v ); vum_last[i+lch] = v; } vum_needclr = TRUE; } void gui_render_tracked( BOOL force ) { int16 posnr=0, notenr=0, trklen=0, trkmax=0; struct ahx_tune *at; struct ahx_step *stp; int32 i, j, k, l, bw, lch, dch, ech; BOOL needrestore, hrestore; TEXT pbuf[256]; i = PN_TRACKER; if( curtune ) i = curtune->at_curpanel; if( i != PN_TRACKER ) return; bw = TRACKED_W; lch = dch = 0; at = curtune; if( at ) { if( at->at_cbmarktrack != -1 ) { if( ( at->at_doing == D_EDITING ) || ( at->at_doing == D_IDLE ) ) { // Do the full-on draw force = TRUE; at->at_cbmarkcurnote = at->at_NoteNr; at->at_cbmarkcurx = at->at_tracked_curs%9; if( at->at_cbmarkmarknote > at->at_cbmarkcurnote ) { at->at_cbmarkstartnote = at->at_cbmarkcurnote; at->at_cbmarkendnote = at->at_cbmarkmarknote; } else { at->at_cbmarkstartnote = at->at_cbmarkmarknote; at->at_cbmarkendnote = at->at_cbmarkcurnote; } i = at->at_tracked_curs/9+at->at_curlch; j = at->at_Positions[at->at_PosNr].pos_Track[i]; if( j == at->at_cbmarktrack ) { if( at->at_cbmarkmarkx > at->at_cbmarkcurx ) { at->at_cbmarkleftx = at->at_cbmarkcurx; at->at_cbmarkrightx = at->at_cbmarkmarkx; } else { at->at_cbmarkleftx = at->at_cbmarkmarkx; at->at_cbmarkrightx = at->at_cbmarkcurx; } } at->at_cbmarkbits = 0; if( at->at_cbmarkleftx < 3 ) at->at_cbmarkbits |= CBF_NOTES; if( ( at->at_cbmarkleftx < 6 ) && ( at->at_cbmarkrightx > 2 ) ) at->at_cbmarkbits |= CBF_CMD1; if( at->at_cbmarkrightx > 5 ) at->at_cbmarkbits |= CBF_CMD2; } else { // Cancel if not editing or idle at->at_cbmarktrack = -1; } } posnr = at->at_PosNr; notenr = at->at_NoteNr; trklen = at->at_TrackLength; trkmax = trklen-1; dch = at->at_Channels; if( dch > 6 ) dch = 6; lch = at->at_curlch; } if( force == FALSE ) { if( ( at == trked_lasttune ) && ( posnr == trked_lastposnr ) && ( notenr == trked_lastnotenr ) && ( lch == trked_lastlchan ) && ( dch == trked_lastchans ) ) return; } ech = dch + lch; // Optimise for scrolling? if( ( at == trked_lasttune ) && ( posnr == trked_lastposnr ) && ( notenr == trked_lastnotenr+1 ) && ( lch == trked_lastlchan ) && ( dch == trked_lastchans ) && ( force == FALSE ) && ( at != NULL ) ) { bw = (3*8)+(dch*15*8)-1; if( bw > TRACKED_W ) bw = TRACKED_W; #ifndef __SDL_WRAPPER__ IGraphics->ScrollRaster( &bitmaps[BM_TRACKED].rp, 0, 16, 0, 0, bw, TRACKED_MY ); #else { SDL_Rect srect = { .x = 0, .y = 16, .w = bw, .h = TRACKED_H-16 }; SDL_Rect drect = { .x = 0, .y = 0, .w = bw, .h = TRACKED_H-16 }; SDL_BlitSurface(bitmaps[BM_TRACKED].srf, &srect, bitmaps[BM_TRACKED].srf, &drect); } #endif set_fpen(&bitmaps[BM_TRACKED], PAL_BACK); fillrect_xy(&bitmaps[BM_TRACKED], 0, 256, TRACKED_MX, TRACKED_MY ); fillrect_xy(&bitmaps[BM_TRACKED], 0, 7*16-2, TRACKED_MX, 7*16-1 ); // IGraphics->Move( &bitmaps[BM_TRACKED].rp, 0, 7*16-1 ); // IGraphics->Draw( &bitmaps[BM_TRACKED].rp, bw, 7*16-1 ); hrestore = FALSE; set_fpen(&bitmaps[BM_TRACKED], PAL_TEXT); for( i=notenr-1, j=(7*16); iat_rempos[0] ) || ( i == at->at_rempos[1] ) || ( i == at->at_rempos[2] ) || ( i == at->at_rempos[3] ) || ( i == at->at_rempos[4] ) ) { set_fpen(&bitmaps[BM_TRACKED], PAL_FKEYHIGHLIGHT); hrestore = TRUE; } else { if( hrestore ) { set_fpen(&bitmaps[BM_TRACKED], PAL_TEXT); hrestore = FALSE; } } if( ( i >= 0 ) && ( i < trklen ) ) { sprintf( pbuf, "%02d ", (int)i ); for( k=lch, l=0; kat_Tracks[at->at_Positions[posnr].pos_Track[k]&0xff][i]; if( stp->stp_Instrument ) { sprintf( &pbuf[l*15+3], "%s %02d %1X%02X %1X%02X ", notenames[stp->stp_Note], stp->stp_Instrument, stp->stp_FX, stp->stp_FXParam&0xff, stp->stp_FXb, stp->stp_FXbParam&0xff ); } else { sprintf( &pbuf[l*15+3], "%s %1X%02X %1X%02X ", notenames[stp->stp_Note], stp->stp_FX, stp->stp_FXParam&0xff, stp->stp_FXb, stp->stp_FXbParam&0xff ); } if( pref_blankzeros ) { if( ( stp->stp_FX == 0 ) && ( stp->stp_FXParam == 0 ) ) { pbuf[l*15+10] = ' '; pbuf[l*15+11] = ' '; pbuf[l*15+12] = ' '; } if( ( stp->stp_FXb == 0 ) && ( stp->stp_FXbParam == 0 ) ) { pbuf[l*15+14] = ' '; pbuf[l*15+15] = ' '; pbuf[l*15+16] = ' '; } } } printstr(&bitmaps[BM_TRACKED], pbuf, 0, j); } } } else { // Not optimised for scrolling if( ( !at ) || ( force ) ) { set_fpen(&bitmaps[BM_TRACKED], PAL_BACK); fillrect_xy(&bitmaps[BM_TRACKED], 0, 0, TRACKED_MX, TRACKED_MY); set_fpen(&bitmaps[BM_TRACKED], PAL_BARMID); fillrect_xy(&bitmaps[BM_TRACKED], 0, 8*16, TRACKED_MX, 8*16+15); } if( at ) { if( ( force ) || ( lch != trked_lastlchan ) || ( posnr != trked_lastposnr ) ) { set_font(&bitmaps[BM_TRACKBAR], FONT_PRP, FALSE); bm_to_bm(&bitmaps[BM_BG_TRACKER], 20+TRACKED_X, TRACKED_Y-142, &bitmaps[BM_TRACKBAR], 0, 0, 768, 19); for( j=0, k=lch, l=0; l<6; j+=15*8, k++, l++ ) { if( k < ech ) { sprintf( pbuf, "Track %d", (int)k+1 ); set_fpen(&bitmaps[BM_TRACKBAR], PAL_BTNSHADOW); printstr(&bitmaps[BM_TRACKBAR], pbuf, j+1, 2); set_fpen(&bitmaps[BM_TRACKBAR], PAL_BTNTEXT); printstr(&bitmaps[BM_TRACKBAR], pbuf, j, 1); } } set_font(&bitmaps[BM_TRACKBAR], FONT_FIX, TRUE); set_pens(&bitmaps[BM_TRACKBAR], PAL_TEXT, PAL_BACK); for( j=11*8-2, k=lch, l=0; l<6; j+=15*8, k++, l++ ) { if( k < ech ) { if( at->at_Voices[k].vc_SetTrackOn ) bm_to_bm(&bitmaps[BM_CHANMUTE], 0, 0, &bitmaps[BM_TRACKBAR], j-20, 0, 14, 19); else bm_to_bm(&bitmaps[BM_CHANMUTE], 0, 19, &bitmaps[BM_TRACKBAR], j-20, 0, 14, 19); sprintf( pbuf, "%03d", at->at_Positions[posnr].pos_Track[k] ); printstr(&bitmaps[BM_TRACKBAR], pbuf, j, 1); } } put_bitmap( BM_TRACKBAR, 20+TRACKED_X, TRACKED_Y-22, TRACKED_W-24, 19 ); } if( notenr < 8 ) { set_fpen(&bitmaps[BM_TRACKED], PAL_BACK); fillrect_xy(&bitmaps[BM_TRACKED], 0, 0, TRACKED_MX, ((8-notenr)*16)-1); } if( notenr > trkmax-8 ) { set_fpen(&bitmaps[BM_TRACKED], PAL_BACK); fillrect_xy(&bitmaps[BM_TRACKED], 0, ((trklen-notenr)+8)*16, TRACKED_MX, TRACKED_MY); } hrestore = FALSE; set_fpen(&bitmaps[BM_TRACKED], PAL_TEXT); for( i=notenr-8, j=0; iat_rempos[0] ) || ( i == at->at_rempos[1] ) || ( i == at->at_rempos[2] ) || ( i == at->at_rempos[3] ) || ( i == at->at_rempos[4] ) ) { set_fpen(&bitmaps[BM_TRACKED], PAL_FKEYHIGHLIGHT); hrestore = TRUE; } else { if( hrestore ) { set_fpen(&bitmaps[BM_TRACKED], PAL_TEXT); hrestore = FALSE; } } if( ( i >= 0 ) && ( i < trklen ) ) { sprintf( pbuf, "%02d ", (int)i ); for( k=lch, l=0; kat_Tracks[at->at_Positions[posnr].pos_Track[k]&0xff][i]; if( stp->stp_Instrument ) { sprintf( &pbuf[l*15+3], "%s %02d %1X%02X %1X%02X ", notenames[stp->stp_Note], stp->stp_Instrument, stp->stp_FX, stp->stp_FXParam&0xff, stp->stp_FXb, stp->stp_FXbParam&0xff ); } else { sprintf( &pbuf[l*15+3], "%s %1X%02X %1X%02X ", notenames[stp->stp_Note], stp->stp_FX, stp->stp_FXParam&0xff, stp->stp_FXb, stp->stp_FXbParam&0xff ); } if( pref_blankzeros ) { if( ( stp->stp_FX == 0 ) && ( stp->stp_FXParam == 0 ) ) { pbuf[l*15+10] = ' '; pbuf[l*15+11] = ' '; pbuf[l*15+12] = ' '; } if( ( stp->stp_FXb == 0 ) && ( stp->stp_FXbParam == 0 ) ) { pbuf[l*15+14] = ' '; pbuf[l*15+15] = ' '; pbuf[l*15+16] = ' '; } } } printstr(&bitmaps[BM_TRACKED], pbuf, 0, j); if( at->at_cbmarktrack != -1 ) { needrestore = FALSE; l = at->at_tracked_curs/9; k = l+lch; // Render any marked blocks if( ( k >= lch ) && ( k= at->at_cbmarkstartnote ) && ( i <= at->at_cbmarkendnote ) && ( at->at_Positions[posnr].pos_Track[k] == at->at_cbmarktrack ) ) { stp = &at->at_Tracks[at->at_Positions[posnr].pos_Track[k]&0xff][i]; if( needrestore == FALSE ) { set_pens(&bitmaps[BM_TRACKED], PAL_BACK, PAL_TEXT); needrestore = TRUE; } if( stp->stp_Instrument ) { sprintf( pbuf, "%s %02d %1X%02X %1X%02X", notenames[stp->stp_Note], stp->stp_Instrument, stp->stp_FX, stp->stp_FXParam&0xff, stp->stp_FXb, stp->stp_FXbParam&0xff ); } else { sprintf( pbuf, "%s %1X%02X %1X%02X", notenames[stp->stp_Note], stp->stp_FX, stp->stp_FXParam&0xff, stp->stp_FXb, stp->stp_FXbParam&0xff ); } switch( at->at_cbmarkbits ) { case CBF_NOTES: printstrlen(&bitmaps[BM_TRACKED], pbuf, 6, 24+(l*120), j ); break; case CBF_NOTES|CBF_CMD1: printstrlen(&bitmaps[BM_TRACKED], pbuf, 10, 24+(l*120), j ); break; case CBF_NOTES|CBF_CMD1|CBF_CMD2: // everything printstr(&bitmaps[BM_TRACKED], pbuf, 24+(l*120), j ); break; case CBF_CMD1: printstrlen(&bitmaps[BM_TRACKED], &pbuf[7], 3, 24+(7*8)+(l*120), j ); break; case CBF_CMD1|CBF_CMD2: printstrlen(&bitmaps[BM_TRACKED], &pbuf[7], 7, 24+(7*8)+(l*120), j ); break; case CBF_CMD2: printstrlen(&bitmaps[BM_TRACKED], &pbuf[11], 3, 24+(11*8)+(l*120), j ); break; } } } if( needrestore ) { set_pens(&bitmaps[BM_TRACKED], PAL_TEXT, PAL_BACK); } } } } } } trked_lasttune = at; trked_lastposnr = posnr; trked_lastnotenr = notenr; trked_lastchans = dch; trked_lastlchan = lch; set_fpen(&bitmaps[BM_TRACKED], PAL_BARMID); for( i=0; i<6*120; i+=120 ) { fillrect_xy(&bitmaps[BM_TRACKED], i+24+28, 0, i+24+28, TRACKED_MY); fillrect_xy(&bitmaps[BM_TRACKED], i+24+52, 0, i+24+52, TRACKED_MY); fillrect_xy(&bitmaps[BM_TRACKED], i+24+84, 0, i+24+84, TRACKED_MY); } set_fpen(&bitmaps[BM_TRACKED], PAL_BARLIGHT); fillrect_xy(&bitmaps[BM_TRACKED], 0, 8*16-1, TRACKED_MX, 8*16-1); set_fpen(&bitmaps[BM_TRACKED], PAL_BARDARK); fillrect_xy(&bitmaps[BM_TRACKED], 0, 9*16-1, TRACKED_MX, 9*16-1); set_fpen(&bitmaps[BM_TRACKED], PAL_TEXT); fillrect_xy(&bitmaps[BM_TRACKED], 20, 0, 20, TRACKED_MY); for( i=120; i<6*120; i+=120 ) { fillrect_xy(&bitmaps[BM_TRACKED], 20+i, 0, 20+i, TRACKED_MY); } if( at->at_editing == E_TRACK ) { int32 cx, cy, mx, my; if( at->at_doing == D_EDITING ) set_fpen(&bitmaps[BM_TRACKED], PAL_CURSEDIT); else set_fpen(&bitmaps[BM_TRACKED], PAL_CURSNORM); cx = tracked_xoff[at->at_tracked_curs]-2; cy = 8*16-2; mx = cx+8+3; my = cy+16+3; if( ( at->at_tracked_curs % 9 ) == 0 ) mx = cx+24+3; fillrect_xy(&bitmaps[BM_TRACKED], cx, cy, mx, cy+1); fillrect_xy(&bitmaps[BM_TRACKED], cx, my-1, mx, my); fillrect_xy(&bitmaps[BM_TRACKED], cx, cy, cx+1, my); fillrect_xy(&bitmaps[BM_TRACKED], mx-1, cy, mx, my); } put_bitmap( BM_TRACKED, TRACKED_X, TRACKED_Y, bw, TRACKED_H ); vum_needclr = FALSE; } void gui_render_inslist( BOOL force ) { struct ahx_tune *at; struct ahx_instrument *in; int16 curi; int32 i, j, k; TEXT tmp[32]; curi = 0; at = curtune; if( at ) { if( at->at_curpanel != PN_TRACKER ) return; curi = at->at_curins; } if( ( insls_lastcuri == curi ) && ( insls_lasttune == at ) && ( insls_lasttopi == at->at_topins ) && ( force == FALSE ) ) return; set_fpen(&bitmaps[BM_INSLIST], PAL_BACK); fillrect_xy(&bitmaps[BM_INSLIST], 0, 0, INSLIST_MX, INSLIST_MY); if( at ) { if( curi < at->at_topins ) at->at_topins = curi; if( curi > (at->at_topins+8) ) at->at_topins = curi-8; if( at->at_topins < 1 ) at->at_topins = 1; set_pens(&bitmaps[BM_INSLIST], PAL_TEXT, PAL_BACK); for( i=0, j=at->at_topins; i<9; i++, j++ ) { sprintf( tmp, "%02d ", (int)j ); in = &at->at_Instruments[j]; for( k=0; k<21; k++ ) { if( in->ins_Name[k] == 0 ) break; tmp[k+3] = in->ins_Name[k]; } if( j == curi ) set_bpen(&bitmaps[BM_INSLIST], PAL_BARMID); if( j == curi+1 ) set_bpen(&bitmaps[BM_INSLIST], PAL_BACK); printstrlen(&bitmaps[BM_INSLIST], tmp, 24, 0, i*14+5); } if( curi >= at->at_topins ) { k = (curi-at->at_topins)*14+5; set_fpen(&bitmaps[BM_INSLIST], PAL_BARLIGHT); fillrect_xy(&bitmaps[BM_INSLIST], 0, k-1, INSLIST_MX, k-1); set_fpen(&bitmaps[BM_INSLIST], PAL_BARDARK); fillrect_xy(&bitmaps[BM_INSLIST], 0, k+14, INSLIST_MX, k+14); } set_fpen(&bitmaps[BM_INSLIST], PAL_TEXT); fillrect_xy(&bitmaps[BM_INSLIST], 17, 0, 17, INSLIST_MY); } put_bitmap( BM_INSLIST, INSLIST_X, INSLIST_Y, INSLIST_W, INSLIST_H ); insls_lastcuri = curi; insls_lasttune = at; insls_lasttopi = at->at_topins; } void gui_render_posed( BOOL force ) { struct ahx_tune *at; int16 posnr=0; int32 i, j, k, l, m, lch=0, dch, ech; TEXT pbuf[256]; at = curtune; dch = 0; if( at ) { if( at->at_curpanel != PN_TRACKER ) return; if( at->at_NextPosNr != -1 ) posnr = at->at_NextPosNr; else posnr = at->at_PosNr; lch = at->at_curlch; dch = at->at_Channels; if( dch > 6 ) dch = 6; } ech = lch + dch; if( force == FALSE ) { if( ( at == posed_lasttune ) && ( posnr == posed_lastposnr ) && ( lch == posed_lastlchan ) && ( dch == posed_lastchans ) ) return; } if( posed_lastposnr != posnr ) { trk_nb[NB_POSNR].cnum = posnr; gui_render_nbox( at, PN_TRACKER, &trk_nb[NB_POSNR] ); } posed_lasttune = at; posed_lastposnr = posnr; posed_lastlchan = lch; posed_lastchans = dch; if( ( !at ) || ( force ) ) { set_fpen(&bitmaps[BM_POSED], PAL_BACK); fillrect_xy(&bitmaps[BM_POSED], 0, 0, POSED_MX, POSED_MY); set_fpen(&bitmaps[BM_POSED], PAL_BARMID); fillrect_xy( &bitmaps[BM_POSED], 0, 3*14, POSED_MX, 3*14+13 ); } if( at ) { if( posnr < 3 ) { set_fpen(&bitmaps[BM_POSED], PAL_BACK); fillrect_xy( &bitmaps[BM_POSED], 0, 0, POSED_MX, ((3-posnr)*14)-1 ); } if( posnr > 996 ) { set_fpen(&bitmaps[BM_POSED], PAL_BACK); fillrect_xy( &bitmaps[BM_POSED], 0, (1003-posnr)*14, POSED_MX, POSED_MY ); } set_fpen(&bitmaps[BM_POSED], PAL_TEXT); for( i=posnr-3, j=0; i= 0 ) && ( i < 1000 ) ) { sprintf( pbuf, "%03d ", (int)i ); for( k=lch, l=0; kat_Positions[i].pos_Track[k]&0xff, at->at_Positions[i].pos_Transpose[k]&0xff ); printstr(&bitmaps[BM_POSED], pbuf, 0, j); } } // Any marked areas? if( at->at_cbpmarkmarklft != -1 ) { BOOL showit; at->at_cbpmarktop = at->at_cbpmarkmarkpos; at->at_cbpmarklft = at->at_cbpmarkmarklft; at->at_cbpmarkbot = at->at_PosNr; at->at_cbpmarkrgt = (((at->at_posed_curs/5)+lch)<<1)|((at->at_posed_curs%5)>2?1:0); if( at->at_cbpmarktop > at->at_cbpmarkbot ) { i = at->at_cbpmarktop; at->at_cbpmarktop = at->at_cbpmarkbot; at->at_cbpmarkbot = i; } if( at->at_cbpmarklft > at->at_cbpmarkrgt ) { i = at->at_cbpmarklft; at->at_cbpmarklft = at->at_cbpmarkrgt; at->at_cbpmarkrgt = i; } at->at_cbpmarklftcol = at->at_cbpmarklft & 1; at->at_cbpmarkrgtcol = at->at_cbpmarkrgt & 1; at->at_cbpmarklft >>= 1; at->at_cbpmarkrgt >>= 1; showit = TRUE; if( ( at->at_cbpmarkrgt < lch ) || ( at->at_cbpmarklft >= ech ) || ( at->at_cbpmarkbot < (posnr-3) ) || ( at->at_cbpmarktop > (posnr+3) ) ) showit = FALSE; if( showit ) { int32 kl, kr, it, ib; kl = at->at_cbpmarklft>lch ? at->at_cbpmarklft : lch; kr = at->at_cbpmarkrgt<(ech-1) ? at->at_cbpmarkrgt : (ech-1); it = at->at_cbpmarktop>(posnr-3) ? at->at_cbpmarktop : (posnr-3); ib = at->at_cbpmarkbot<(posnr+3) ? at->at_cbpmarkbot : (posnr+3); set_pens(&bitmaps[BM_POSED], PAL_BACK, PAL_TEXT); pbuf[0]=0; // Nice! or... more accurately Confusing! for( i=it, j=14*(it-(posnr-3)); i<=ib; i++, j+=14 ) { for( m=0, k=kl, l=0; k<=kr; k++, l++ ) { if( ( k>kl ) || ( at->at_cbpmarklftcol == 0 ) ) { sprintf( &pbuf[m], "%03d ", at->at_Positions[i].pos_Track[k]&0xff ); m+=4; } if( ( kat_cbpmarkrgtcol != 0 ) ) { sprintf( &pbuf[m], "%02X ", at->at_Positions[i].pos_Transpose[k]&0xff ); m+=3; } } pbuf[m-1] = 0; printstr(&bitmaps[BM_POSED], pbuf, ((kl-lch)*49)+(at->at_cbpmarklftcol*28)+28, j); } } } set_pens(&bitmaps[BM_POSED], PAL_POSEDCHIND, PAL_BARMID); for( k=lch, l=0; kat_editing == E_POS ) { int32 cx, cy, mx, my; cx = posed_xoff[at->at_posed_curs]-2; cy = 3*14-2; mx = cx+7+3; my = cy+14+3; fillrect_xy(&bitmaps[BM_POSED], cx, cy, mx, cy+1 ); fillrect_xy(&bitmaps[BM_POSED], cx, my-1, mx, my ); fillrect_xy(&bitmaps[BM_POSED], cx, cy, cx+1, my ); fillrect_xy(&bitmaps[BM_POSED], mx-1, cy, mx, my ); } put_bitmap( BM_POSED, POSED_X, POSED_Y, POSED_W, POSED_H ); } void gui_render_tracker( BOOL force ) { int32 panel, i; if( curtune == NULL ) panel = PN_TRACKER; else panel = curtune->at_curpanel; if( panel != PN_TRACKER ) return; gui_render_posed( force ); gui_render_tracked( force ); gui_render_inslist( force ); gui_set_various_things( curtune ); for( i=0; iat_curpanel != PN_TRACKER ) return; if( !force ) { if( ( tmr_lasttune == curtune ) && ( tmr_lasttime == ((curtune->at_hours<<16)|(curtune->at_mins<<8)|curtune->at_secs) ) ) return; } tmr_lasttune = curtune; tmr_lasttime = (curtune->at_hours<<16)|(curtune->at_mins<<8)|curtune->at_secs; sprintf( tbuf, "%02d:%02d:%02d", curtune->at_hours, curtune->at_mins, curtune->at_secs ); set_font(&mainbm, FONT_FIX, FALSE); set_fpen(&mainbm, PAL_BACK); fillrect_xy(&mainbm, 546, 132, 546+65, 132+15 ); set_fpen(&mainbm, PAL_TEXT); printstr(&mainbm, tbuf, 546, 132); } void gui_render_tunepanel( BOOL force ) { int32 panel, i; if( curtune == NULL ) panel = PN_TRACKER; else panel = curtune->at_curpanel; switch( panel ) { case PN_TRACKER: if( force ) { put_bitmap( BM_BG_TRACKER, 0, 120, 800, 480 ); gui_render_track_buttonbank(); gui_render_tabs(); for( i=0; iat_Instruments[curtune->at_curins] : NULL, force ); for( i=0; iat_rempos[0] = 0; at->at_rempos[1] = at->at_TrackLength>>2; at->at_rempos[2] = at->at_TrackLength>>1; at->at_rempos[3] = (at->at_TrackLength*3)/4; at->at_rempos[4] = at->at_TrackLength-1; } void gui_update_from_nbox( int32 panel, struct numberbox *bp, int32 i ) { struct ahx_tune *at; struct ahx_instrument *ip; int32 j; if( bp[i].cnum < bp[i].min ) bp[i].cnum = bp[i].min; if( bp[i].cnum > bp[i].max ) bp[i].cnum = bp[i].max; at = curtune; if( at == NULL ) return; switch( at->at_curpanel ) { case PN_TRACKER: switch( i ) { case NB_POSNR: if( at->at_doing == D_PLAYING ) at->at_NextPosNr = bp[i].cnum; else at->at_PosNr = bp[i].cnum; gui_render_tracker( FALSE ); return; case NB_LENGTH: modify_tune_w( at, UNT_POSITIONNR, bp[i].cnum ); break; case NB_RESPOS: modify_tune_w( at, UNT_RESTART, bp[i].cnum ); break; case NB_TRKLEN: j = bp[NB_TRKLEN].cnum; if( j < 1 ) j = 1; modify_tune_b( at, UNT_TRACKLEN, j ); if( at->at_NoteNr >= at->at_TrackLength ) at->at_NoteNr = at->at_TrackLength-1; gui_set_rempos( curtune ); trked_lastposnr = 8000; gui_render_tracker( FALSE ); break; case NB_SSNR: modify_tune_w( at, UNT_SUBSONGS, bp[NB_SSNR].cnum ); gui_set_various_things( at ); break; case NB_CURSS: at->at_curss = bp[NB_CURSS].cnum; rp_init_subsong( at, bp[NB_CURSS].cnum ); gui_set_various_things( at ); gui_render_tracker( FALSE ); break; case NB_SSPOS: if( at->at_curss > 0 ) modify_sspos( at, bp[NB_SSPOS].cnum ); break; case NB_CHANS: rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); at->at_doing = D_IDLE; j = bp[NB_CHANS].cnum; if( j < 4 ) j = 4; if( j > MAX_CHANNELS ) j = MAX_CHANNELS; modify_tune_b( at, UNT_CHANNELS, j ); at->at_curlch = 0; at->at_tracked_curs = 0; at->at_posed_curs = 0; gui_render_tracker( TRUE ); break; case NB_MIXG: j = bp[NB_MIXG].cnum; if( j < 0 ) j = 0; if( j > 200 ) j = 200; modify_tune_b( at, UNT_MIXGAIN, j ); at->at_mixgain = (j*256)/100; break; case NB_SPEEDMULT: j = bp[NB_SPEEDMULT].cnum; if( j < 1 ) j = 1; if( j > 4 ) j = 4; modify_tune_b( at, UNT_SPMUL, j ); break; case NB_DRUMPADMODE: j = bp[NB_DRUMPADMODE].cnum; if( j < 0 ) j = 0; if( j > 1 ) j = 1; at->at_drumpadmode = j; break; } gui_render_nbox( at, PN_TRACKER, &bp[i] ); break; case PN_INSED: ip = &at->at_Instruments[at->at_curins]; switch( i ) { case INB_INS: at->at_curins = bp[i].cnum; gui_render_nbox( at, PN_INSED, &bp[i] ); gui_set_various_things( at ); return; case INB_VOL: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_VOLUME, bp[i].cnum ); break; case INB_WAVELEN: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_WAVELENGTH, bp[i].cnum ); switch( ip->ins_WaveLength ) { case 0: j = 0x20; break; case 1: j = 0x10; break; case 2: j = 0x08; break; case 3: j = 0x04; break; case 4: j = 0x02; break; default: j = 0x01; break; } ins_nb[INB_SQRLOWER].min = j; ins_nb[INB_SQRUPPER].min = j; if( ip->ins_SquareLowerLimit < j ) ip->ins_SquareLowerLimit = j; if( ip->ins_SquareUpperLimit < j ) ip->ins_SquareUpperLimit = j; ins_nb[INB_SQRLOWER].cnum = ip->ins_SquareLowerLimit; ins_nb[INB_SQRUPPER].cnum = ip->ins_SquareUpperLimit; gui_render_nbox( at, PN_INSED, &bp[INB_SQRLOWER] ); gui_render_nbox( at, PN_INSED, &bp[INB_SQRUPPER] ); break; case INB_ATTACK: if( ip == NULL ) break; modify_env_w( at, &ip->ins_Envelope, UNT_ENV_AFRAMES, bp[i].cnum ); break; case INB_AVOL: if( ip == NULL ) break; modify_env_w( at, &ip->ins_Envelope, UNT_ENV_AVOLUME, bp[i].cnum ); break; case INB_DECAY: if( ip == NULL ) break; modify_env_w( at, &ip->ins_Envelope, UNT_ENV_DFRAMES, bp[i].cnum ); break; case INB_DVOL: if( ip == NULL ) break; modify_env_w( at, &ip->ins_Envelope, UNT_ENV_DVOLUME, bp[i].cnum ); break; case INB_SUSTAIN: if( ip == NULL ) break; modify_env_w( at, &ip->ins_Envelope, UNT_ENV_SFRAMES, bp[i].cnum ); break; case INB_RELEASE: if( ip == NULL ) break; modify_env_w( at, &ip->ins_Envelope, UNT_ENV_RFRAMES, bp[i].cnum ); break; case INB_RVOL: if( ip == NULL ) break; modify_env_w( at, &ip->ins_Envelope, UNT_ENV_RVOLUME, bp[i].cnum ); break; case INB_VIBDELAY: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_VIBRATODELAY, bp[i].cnum ); break; case INB_VIBDEPTH: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_VIBRATODEPTH, bp[i].cnum ); break; case INB_VIBSPEED: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_VIBRATOSPEED, bp[i].cnum ); break; case INB_SQRLOWER: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_SQUARELOWERLIMIT, bp[i].cnum ); break; case INB_SQRUPPER: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_SQUAREUPPERLIMIT, bp[i].cnum ); break; case INB_SQRSPEED: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_SQUARESPEED, bp[i].cnum ); break; case INB_FLTLOWER: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_FILTERLOWERLIMIT, bp[i].cnum ); break; case INB_FLTUPPER: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_FILTERUPPERLIMIT, bp[i].cnum ); break; case INB_FLTSPEED: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_FILTERSPEED, bp[i].cnum ); break; case INB_PERFSPEED: if( ip == NULL ) break; modify_pls_w( at, &ip->ins_PList, UNT_PLS_SPEED, bp[i].cnum ); break; case INB_PERFLEN: if( ip == NULL ) break; modify_pls_w( at, &ip->ins_PList, UNT_PLS_LENGTH, bp[i].cnum ); gui_render_perf( curtune, ip, FALSE ); break; case INB_HARDCUT: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_HARDCUTRELEASEFRAMES, bp[i].cnum ); break; case INB_RELCUT: if( ip == NULL ) break; modify_ins_b( at, ip, UNT_INS_HARDCUTRELEASE, bp[i].cnum ); break; } gui_render_nbox( at, PN_INSED, &bp[i] ); break; } } void gui_set_popup( int32 pn, int16 x, int16 y ) { pp[pn].x = x; pp[pn].y = y; } void gui_render_popup( int32 pn, BOOL pressed ) { if( pressed ) put_ppartbitmap( BM_DIRPOPUP, 0, 18, pp[pn].x, pp[pn].y, 20, 18 ); else put_pbitmap( BM_DIRPOPUP, pp[pn].x, pp[pn].y, 20, 18 ); } void gui_set_pcycle( int32 cn, int16 x, int16 y, int32 cur, TEXT **opts ) { int32 i; for( i=0; opts[i] != NULL; i++ ) ; if( cur >= i ) cur = 0; pcyc[cn].x = x; pcyc[cn].y = y; pcyc[cn].numopts = i; pcyc[cn].copt = cur; pcyc[cn].options = opts; pcyc[cn].zone = gui_add_zone( &pzones[0], &numpzones, MAX_PZONES, x, y, 160, 24 ); } void gui_render_pcycle( int32 cn, BOOL pressed ) { int w, tx; const TEXT *ctxt; if( pressed ) put_ppartbitmap( BM_PRF_CYCLE, 0, 24, pcyc[cn].x, pcyc[cn].y, 158, 24 ); else put_pbitmap( BM_PRF_CYCLE, pcyc[cn].x, pcyc[cn].y, 158, 24 ); ctxt = pcyc[cn].options[pcyc[cn].copt]; if( ctxt == NULL ) return; set_font(&prefbm, FONT_PRP, FALSE); #ifndef __SDL_WRAPPER__ w = IGraphics->TextLength( &prefbm.rp, ctxt, strlen( ctxt ) ); #else TTF_SizeText(prefbm.font, ctxt, &w, &tx); #endif tx = pcyc[cn].x+pw_bl+(79-(w>>1)); set_fpen(&prefbm, PAL_BTNSHADOW); printstr(&prefbm, ctxt, tx+1, pcyc[cn].y+pw_bt+5); set_fpen(&prefbm, PAL_BTNTEXT); printstr(&prefbm, ctxt, tx, pcyc[cn].y+pw_bt+4); #if defined(WIN32) || defined(__APPLE__) if (cn == 0) { set_fpen(&prefbm, PAL_BACK); for (w=0; w<24; w+=3) fillrect_xy(&prefbm, pcyc[0].x, pcyc[0].y+w, pcyc[0].x+157, pcyc[0].y+w); } #endif } void gui_render_prefs( void ) { int32 i; bm_to_bm( &bitmaps[BM_PRF_BG], 0, 0, &prefbm, pw_bl, pw_bt, 400, 300); for( i=0; iLeftEdge + 150; if( pw_y == -1 ) pw_y = mainwin->TopEdge + 100; prefwin = IIntuition->OpenWindowTags( NULL, WA_Left, pw_x, WA_Top, pw_y, WA_InnerWidth, 400, WA_InnerHeight, 300, WA_Title, "Preferences", WA_ScreenTitle, "HivelyTracker (c)2013 IRIS & Up Rough! - http://www.uprough.net - http://www.hivelytracker.co.uk", WA_RMBTrap, TRUE, WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_MOUSEBUTTONS|IDCMP_RAWKEY|IDCMP_GADGETUP|IDCMP_EXTENDEDMOUSE, WA_Activate, TRUE, WA_DragBar, TRUE, WA_CloseGadget, TRUE, WA_DepthGadget, TRUE, fullscr ? WA_CustomScreen : TAG_IGNORE, scr, TAG_DONE ); if( !prefwin ) { printf( "Unable to open prefs window!\n" ); prefwin_sig = 0; return; } memset(&prefbm, 0, sizeof(struct rawbm)); memcpy(&prefbm.rp, prefwin->RPort, sizeof(struct RastPort)); prefbm.bm = prefwin->RPort->BitMap; pw_bl = prefwin->BorderLeft; pw_bt = prefwin->BorderTop; #else pw_bl = 0; pw_bt = 0; #endif set_font(&prefbm, FONT_PRP, FALSE); gui_set_pcycle( PC_WMODE, 220, 8 , pref_fullscr ? 1 : 0, pc_wmode_opts ); gui_set_pcycle( PC_DEFSTEREO, 220, 8+24*1, pref_defstereo, pc_defst_opts ); gui_set_pcycle( PC_BLANKZERO, 220, 8+24*2, pref_blankzeros, pc_bzero_opts ); gui_set_tbox( &ptb[PTB_SONGDIR], 220+pw_bl, 8+24*3+4+pw_bt, 136, songdir, 511, TBF_ENABLED|TBF_VISIBLE, 0 ); gui_set_tbox( &ptb[PTB_INSTDIR], 220+pw_bl, 8+24*4+4+pw_bt, 136, instdir, 511, TBF_ENABLED|TBF_VISIBLE, 0 ); gui_set_tbox( &ptb[PTB_SKINDIR], 220+pw_bl, 8+24*5+4+pw_bt, 136, skindir, 511, TBF_ENABLED|TBF_VISIBLE, 0 ); gui_set_popup( PP_SONGDIR, 360, 8+24*3+3 ); gui_set_popup( PP_INSTDIR, 360, 8+24*4+3 ); gui_set_popup( PP_SKINDIR, 360, 8+24*5+3 ); gui_set_pcycle( PC_MAXUNDOBUF, 220, 8+24*6, pref_maxundobuf, pc_mundo_opts ); gui_render_prefs(); #ifndef __SDL_WRAPPER__ prefwin_sig = (1L<UserPort->mp_SigBit); gui_sigs |= prefwin_sig; #else prefwin_open = TRUE; #endif } void gui_close_prefs( void ) { int32 i; for( i=0; iLeftEdge; pw_y = prefwin->TopEdge; IIntuition->CloseWindow( prefwin ); } prefwin = NULL; gui_sigs &= ~prefwin_sig; prefwin_sig = 0; memset(&prefbm.rp, 0, sizeof(struct RastPort)); prefbm.bm = NULL; for( i=0; iFreeBitMap( ptb[i].bm.bm ); ptb[i].bm.bm = NULL; } #else prefwin_open = FALSE; aboutwin_open = FALSE; for( i=0; iAddPart( tmp, "Settings_os4", 1024 ); #else strncat( tmp, "/Settings_os4", 1024 ); #endif f = fopen( tmp, "r" ); if( !f ) return; pen = 0; while( fgets(tmp, 1024, f) ) { if( gui_decode_pstr( "skinext", tmp ) ) { strncpy( skinext, tmp, 32 ); continue; } if( gui_decode_pstr( "font1", tmp ) ) { // strcpy( prpfontname, tmp ); continue; } if( gui_decode_pstr( "font2", tmp ) ) { // strcpy( fixfontname, tmp ); continue; } if( gui_decode_pstr( "font3", tmp ) ) { // strcpy( sfxfontname, tmp ); continue; } if( gui_decode_pstr( "tabtextback", tmp ) ) { if( strcasecmp( tmp, "Yes" ) == 0 ) tabtextback = TRUE; else tabtextback = FALSE; continue; } if( gui_decode_pstr( "tabtextshad", tmp ) ) { if( strcasecmp( tmp, "Yes" ) == 0 ) tabtextshad = TRUE; else tabtextshad = FALSE; continue; } col = 0; if( ishex( tmp[0] ) ) { // No more pens if( pen >= PAL_END ) continue; i=0; while( ishex( tmp[i] ) ) { col <<= 4; if(( tmp[i] >= '0' ) && ( tmp[i] <= '9' )) col += tmp[i]-'0'; if(( tmp[i] >= 'A' ) && ( tmp[i] <= 'F' )) col += tmp[i]-('A'-10); if(( tmp[i] >= 'a' ) && ( tmp[i] <= 'f' )) col += tmp[i]-('a'-10); i++; } pal[pen] = col; #ifdef __SDL_WRAPPER__ #ifdef __APPLE__ // Work-around for SDL bug on OSX mappal[pen] = SDL_MapRGB(ssrf->format, col&0xff, (col>>8)&0xff, (col>>16)&0xff) >> 8; #else mappal[pen] = SDL_MapRGB(ssrf->format, (col>>16)&0xff, (col>>8)&0xff, col&0xff); #endif #endif // Just in case the skin doesn't specify // tab colours if( pen == PAL_BTNTEXT ) pal[PAL_TABTEXT] = col; if( pen == PAL_BTNSHADOW ) pal[PAL_TABSHADOW] = col; pen++; } } fclose(f); } void gui_load_prefs( void ) { FILE *f; int32 i; TEXT tmp[256]; #ifdef __APPLE__ f = fopen( osxGetPrefsPath(), "r"); #else f = fopen( "ht.prefs", "r" ); #endif if( !f ) return; while( fgets(tmp, 256, f) ) { #ifdef __SDL_WRAPPER__ if( gui_decode_num( "rctrlplaypos", tmp, &i ) ) { #ifdef __WIN32__ /* On Win32 RALT generates phantom RCTRL presses that stop this being workable */ pref_rctrlplaypos = FALSE; #else pref_rctrlplaypos = (i!=0); #endif continue; } #endif if( gui_decode_num( "display", tmp, &i ) ) { pref_fullscr = (i!=0); continue; } if( gui_decode_num( "defstereo", tmp, &i ) ) { if( i < 0 ) i = 0; if( i > 4 ) i = 4; pref_defstereo = i; continue; } if( gui_decode_num( "blankzero", tmp, &i ) ) { pref_blankzeros = (i!=0); continue; } if( gui_decode_num( "maxundobf", tmp, &i ) ) { if( i < 0 ) i = 0; if( i > 5 ) i = 5; pref_maxundobuf = i; continue; } if( gui_decode_pstr( "songdir", tmp ) ) { strcpy( songdir, tmp ); continue; } if( gui_decode_pstr( "instdir", tmp ) ) { strcpy( instdir, tmp ); continue; } if( gui_decode_pstr( "skindir", tmp ) ) { strcpy( skindir, tmp ); continue; } if( gui_decode_num( "posedadvance", tmp, &i ) ) { posedadvance = (i!=0); continue; } if( gui_decode_num( "notejump", tmp, &i ) ) { if( i < 0 ) i = 0; if( i > 9 ) i = 9; defnotejump = i; continue; } if( gui_decode_num( "inotejump", tmp, &i ) ) { if( i < 0 ) i = 0; if( i > 9 ) i = 9; definotejump = i; continue; } } fclose(f); #if defined(WIN32) || defined(APPLE) pref_fullscr = FALSE; #endif } void gui_pre_init( void ) { uint32 i; #ifndef __SDL_WRAPPER__ for( i=0; ip96BestModeIDTags( P96BIDTAG_NominalWidth, 800, P96BIDTAG_NominalHeight, 600, P96BIDTAG_FormatsAllowed, RGBFF_R8G8B8| RGBFF_B8G8R8| RGBFF_A8R8G8B8| RGBFF_A8B8G8R8| RGBFF_R8G8B8A8| RGBFF_B8G8R8A8| RGBFF_R5G6B5| RGBFF_R5G6B5PC| RGBFF_R5G5B5| RGBFF_R5G5B5PC| RGBFF_B5G6R5PC| RGBFF_B5G5R5PC, TAG_DONE ); if( modeid == INVALID_ID ) { printf( "Unable to get a suitable screenmode. Falling back to windowed mode...\n" ); fullscr = FALSE; } else { depth = IP96->p96GetModeIDAttr( modeid, P96IDA_DEPTH ); scr = IIntuition->OpenScreenTags( NULL, SA_Width, 800, SA_Height, 600, SA_Depth, depth, SA_Title, "HivelyTracker", SA_ShowTitle, FALSE, SA_DisplayID, modeid, SA_Pens, -1, TAG_DONE ); if( !scr ) { printf( "Unable to open screen. Falling back to windowed mode...\n" ); fullscr = FALSE; } } } mainwin = IIntuition->OpenWindowTags( NULL, WA_Width, 800, WA_Height, 600, WA_Borderless, TRUE, WA_ScreenTitle, "HivelyTracker (c)2013 IRIS & Up Rough! - http://www.uprough.net - http://www.hivelytracker.co.uk", WA_RMBTrap, TRUE, WA_IDCMP, IDCMP_MOUSEBUTTONS|IDCMP_RAWKEY|IDCMP_GADGETUP|IDCMP_EXTENDEDMOUSE, WA_Activate, TRUE, WA_Gadgets, fullscr ? NULL : &gad_drag, fullscr ? WA_CustomScreen : TAG_IGNORE, scr, WA_Backdrop, fullscr, TAG_DONE ); if( !mainwin ) { printf( "Unable to open main window!\n" ); return FALSE; } #endif // __SDL_WRAPPER__ memset(&mainbm, 0, sizeof(mainbm)); mainbm.w = 800; mainbm.h = 600; #ifndef __SDL_WRAPPER__ memcpy(&mainbm.rp, mainwin->RPort, sizeof(struct RastPort)); mainbm.bm = mainwin->RPort->BitMap; #else mainbm.srf = ssrf; #endif prefbm.w = 400; prefbm.h = 300; if( !gui_open_skin_images() ) { printf( "Error loading skin. Reverting to SIDMonster-Light...\n" ); strcpy( skindir, "Skins/SIDMonster-Light" ); if( !gui_open_skin_images() ) return FALSE; } if( !make_image( &bitmaps[BM_POSED], POSED_W+8, POSED_H ) ) return FALSE; if( !make_image( &bitmaps[BM_TRACKED], TRACKED_W+8, TRACKED_H ) ) return FALSE; if( !make_image( &bitmaps[BM_TRACKBAR], 768, 20 ) ) return FALSE; if( !make_image( &bitmaps[BM_PERF], PERF_W, PERF_H ) ) return FALSE; if( !make_image( &bitmaps[BM_INSLIST], INSLIST_W, INSLIST_H ) ) return FALSE; if( !make_image( &bitmaps[BM_INSLISTB], INSLSTB_W, INSLSTB_H ) ) return FALSE; #ifdef __SDL_WRAPPER__ if( !make_image( &prefbm, 408, 308 ) ) return FALSE; prefbm.offx = 4; prefbm.offy = 4; #endif #ifndef __SDL_WRAPPER__ fixfont = IDiskfont->OpenDiskFont( &fixfontattr ); if( !fixfont ) { printf( "Unable to open '%s'\n", fixfontattr.ta_Name ); return FALSE; } sfxfont = IDiskfont->OpenDiskFont( &sfxfontattr ); if( !sfxfont ) { printf( "Unable to open '%s'\n", sfxfontattr.ta_Name ); return FALSE; } prpfont = IDiskfont->OpenDiskFont( &prpfontattr ); if( !prpfont ) { printf( "Unable to open '%s'\n", prpfontattr.ta_Name ); return FALSE; } #else fixttf = TTF_OpenFont(fixfontname, 14); if( !fixttf ) { printf( "Unable to open '%s'\n", fixfontname ); return FALSE; } sfxttf = TTF_OpenFont(sfxfontname, 12); if( !sfxttf ) { printf( "Unable to open '%s'\n", sfxfontname ); return FALSE; } prpttf = TTF_OpenFont(prpfontname, 14); if( !prpttf ) { printf( "Unable to open '%s'\n", prpfontname ); return FALSE; } #endif set_font(&bitmaps[BM_POSED], FONT_SFX, TRUE); set_pens(&bitmaps[BM_POSED], PAL_BACK, PAL_BACK); fillrect_xy(&bitmaps[BM_POSED], 0, 0, POSED_MX, POSED_MY); set_font(&bitmaps[BM_TRACKED], FONT_FIX, TRUE); set_pens(&bitmaps[BM_TRACKED], PAL_BACK, PAL_BACK); fillrect_xy(&bitmaps[BM_TRACKED], 0, 0, TRACKED_MX, TRACKED_MY); set_font(&bitmaps[BM_PERF], FONT_FIX, TRUE); set_pens(&bitmaps[BM_PERF], PAL_BACK, PAL_BACK); set_font(&bitmaps[BM_INSLIST], FONT_FIX, TRUE); set_pens(&bitmaps[BM_INSLIST], PAL_BACK, PAL_BACK); fillrect_xy(&bitmaps[BM_INSLIST], 0, 0, INSLIST_MX, TRACKED_MY); set_font(&bitmaps[BM_INSLISTB], FONT_FIX, TRUE); set_pens(&bitmaps[BM_INSLISTB], PAL_BACK, PAL_BACK); fillrect_xy(&bitmaps[BM_INSLISTB], 0, 0, INSLIST_MX, TRACKED_MY); for( i=0; i<8; i++ ) { ttab[i].tune = NULL; ttab[i].zone = -1; } zn_close = gui_add_zone( &zones[0], &numzones, MAX_ZONES, 0, 0, 24, 24 ); if( fullscr ) zn_scrdep = gui_add_zone( &zones[0], &numzones, MAX_ZONES, 776, 0, 24, 24 ); for( i=0; i<6; i++ ) zn_mute[i] = gui_add_zone( &zones[0], &numzones, MAX_ZONES, TRACKED_X+20+66+i*120, TRACKED_Y-22, 14, 19 ); #ifndef __SDL_WRAPPER__ mainwin_sig = (1L<UserPort->mp_SigBit); gui_sigs = mainwin_sig | gui_tick_sig; #endif for( i=0; i<16; i++ ) bbank[i].zone = gui_add_zone( &zones[0], &numzones, MAX_ZONES, (i&3)*80+256, (i>>2)*24, 80, 24 ); for( i=0; i<12; i++ ) tbank[i].zone = gui_add_zone( &zones[0], &numzones, MAX_ZONES, 744, 120+200+i*23, 52, 23 ); posed_lasttune = NULL; posed_lastposnr = -1; gui_set_nbox( &trk_nb[NB_POSNR], 75, 132, 58, 16, 0, 999, 0, "%03d", NBF_ENABLED|NBF_UDDURINGPLAY ); gui_set_nbox( &trk_nb[NB_LENGTH], 75, 132+20*1, 58, 16, 1, 999, 0, "%03d", NBF_ENABLED|NBF_UDDURINGPLAY ); gui_set_nbox( &trk_nb[NB_RESPOS], 75, 132+20*2, 58, 16, 0, 999, 0, "%03d", NBF_ENABLED|NBF_UDDURINGPLAY ); gui_set_nbox( &trk_nb[NB_TRKLEN], 75, 132+20*3, 58, 16, 1, 64, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &trk_nb[NB_SSNR], 75, 132+20*4, 58, 16, 0, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &trk_nb[NB_CURSS], 75, 132+20*5, 58, 16, 0, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &trk_nb[NB_SSPOS], 75, 132+20*6, 58, 16, 0, 999, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &trk_nb[NB_CHANS], 219, 132, 58, 16, 4, MAX_CHANNELS, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &trk_nb[NB_MIXG], 219, 132+20*1, 58, 16, 0, 200, 0, "%03d", NBF_ENABLED|NBF_UDDURINGPLAY ); gui_set_nbox( &trk_nb[NB_SPEEDMULT], 219, 132+20*2, 58, 16, 1, 4, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &trk_nb[NB_DRUMPADMODE], 219, 132+20*3, 58, 16, 0, 1, 0, "", NBF_ENABLED|NBF_ONOFF|NBF_UDDURINGPLAY ); gui_set_nbox( &ins_nb[INB_INS], 9, 128, 58, 16, 0, 63, 0, " %02d", NBF_ENABLED|NBF_UDDURINGPLAY ); gui_set_nbox( &ins_nb[INB_VOL], 72, 142+20*1, 58, 16, 0, 64, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_WAVELEN], 72, 142+20*2, 58, 16, 0, 5, 0, " %02X", NBF_ENABLED|NBF_WAVELEN ); gui_set_nbox( &ins_nb[INB_ATTACK], 72, 160+20*3, 58, 16, 1, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_AVOL], 72, 160+20*4, 58, 16, 0, 64, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_DECAY], 72, 160+20*5, 58, 16, 1, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_DVOL], 72, 160+20*6, 58, 16, 0, 64, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_SUSTAIN], 72, 160+20*7, 58, 16, 1, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_RELEASE], 72, 160+20*8, 58, 16, 1, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_RVOL], 72, 160+20*9, 58, 16, 0, 64, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_VIBDELAY],72, 178+20*10, 58, 16, 0, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_VIBDEPTH],72, 178+20*11, 58, 16, 0, 15, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_VIBSPEED],72, 178+20*12, 58, 16, 0, 63, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_SQRLOWER],72, 196+20*13, 58, 16, 1, 63, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_SQRUPPER],72, 196+20*14, 58, 16, 1, 63, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_SQRSPEED],72, 196+20*15, 58, 16, 0, 127, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_FLTLOWER],72, 214+20*16, 58, 16, 1, 63, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_FLTUPPER],72, 214+20*17, 58, 16, 1, 63, 0, " %02d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_FLTSPEED],72, 214+20*18, 58, 16, 0, 127, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_PERFSPEED], 208, 142+20*1, 58, 16, 0, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_PERFLEN], 208, 142+20*2, 58, 16, 0, 255, 0, "%03d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_HARDCUT], 208, 142+20*3, 58, 16, 0, 7, 0, " %01d", NBF_ENABLED ); gui_set_nbox( &ins_nb[INB_RELCUT], 208, 142+20*4, 58, 16, 0, 1, 0, " %01d", NBF_ENABLED|NBF_ONOFF ); gui_set_tbox( &tbx[TB_SONGNAME], 298, 152, POSED_W, NULL, 127, TBF_ENABLED|TBF_VISIBLE, PN_TRACKER ); gui_set_tbox( &tbx[TB_INSNAME], 115, 128, 148, NULL, 127, TBF_ENABLED|TBF_VISIBLE, PN_INSED ); gui_set_tbox( &tbx[TB_INSNAME2], INSLIST_X+17, INSLIST_Y, INSLIST_W-17, NULL, 127, TBF_ENABLED, PN_TRACKER ); gui_setup_trkbbank( 0 ); gui_setup_buttonbank( 0 ); if( IExec->GetHead(rp_tunelist) ) gui_set_various_things( (struct ahx_tune *)IExec->GetHead(rp_tunelist) ); gui_render_everything(); gui_render_tunepanel( TRUE ); cz_lmb = -1; cz_rmb = -1; return TRUE; } BOOL gui_init( void ) { gui_savethem = FALSE; #ifndef __SDL_WRAPPER__ iev.ie_Class = IECLASS_RAWKEY; iev.ie_SubClass = 0; strcpy( fixfontname, "Bitstream Vera Sans Mono.font" ); strcpy( sfxfontname, "Bitstream Vera Sans Mono.font" ); strcpy( prpfontname, "Bitstream Vera Sans.font" ); #else #ifdef __APPLE__ osxGetResourcesPath(fixfontname, "DejaVuSansMono.ttf"); osxGetResourcesPath(sfxfontname, "DejaVuSansMono.ttf"); osxGetResourcesPath(prpfontname, "DejaVuSans.ttf"); #elif defined(__HAIKU__) strcpy( fixfontname, "/boot/system/data/fonts/ttfonts/DejaVuSansMono.ttf" ); strcpy( sfxfontname, "/boot/system/data/fonts/ttfonts/DejaVuSansMono.ttf" ); strcpy( prpfontname, "/boot/system/data/fonts/ttfonts/DejaVuSans.ttf" ); #else strcpy( fixfontname, "ttf/DejaVuSansMono.ttf" ); strcpy( sfxfontname, "ttf/DejaVuSansMono.ttf" ); strcpy( prpfontname, "ttf/DejaVuSans.ttf" ); #endif #endif strcpy( skinext, ".png" ); #ifndef __SDL_WRAPPER__ if( !getLibIFace( &IntuitionBase, "intuition.library", 51, &IIntuition ) ) return FALSE; if( !getLibIFace( &GfxBase, "graphics.library", 51, &IGraphics ) ) return FALSE; if( !getLibIFace( &P96Base, "Picasso96API.library", 2, &IP96) ) return FALSE; if( !getLibIFace( &DataTypesBase, "datatypes.library", 51, &IDataTypes ) ) return FALSE; if( !getLibIFace( &DiskfontBase, "diskfont.library", 51, &IDiskfont ) ) return FALSE; if( !getLibIFace( &AslBase, "asl.library", 51, &IAsl) ) return FALSE; if( !getLibIFace( &KeymapBase, "keymap.library", 51, &IKeymap) ) return FALSE; if( !getLibIFace( &RequesterBase, "requester.class", 51, &IRequester ) ) return FALSE; gui_tick_signum = IExec->AllocSignal( -1 ); if( gui_tick_signum == -1 ) { printf( "Unable to allocate signal\n" ); return FALSE; } gui_tick_sig = (1L<AllocAslRequestTags( ASL_FileRequest, ASLFR_TitleText, "Load Instrument", ASLFR_InitialDrawer, instdir, ASLFR_DoSaveMode, FALSE, ASLFR_RejectIcons, TRUE, TAG_DONE ); if( !ins_lreq ) { printf( "Error allocating Asl request\n" ); return FALSE; } ins_sreq = IAsl->AllocAslRequestTags( ASL_FileRequest, ASLFR_TitleText, "Save Instrument", ASLFR_InitialDrawer, instdir, ASLFR_DoSaveMode, TRUE, ASLFR_RejectIcons, TRUE, TAG_DONE ); if( !ins_sreq ) { printf( "Error allocating Asl request\n" ); return FALSE; } sng_lreq = IAsl->AllocAslRequestTags( ASL_FileRequest, ASLFR_TitleText, "Load Song", ASLFR_InitialDrawer, songdir, ASLFR_DoSaveMode, FALSE, ASLFR_RejectIcons, TRUE, TAG_DONE ); if( !sng_lreq ) { printf( "Error allocating Asl request\n" ); return FALSE; } sng_sreq = IAsl->AllocAslRequestTags( ASL_FileRequest, ASLFR_TitleText, songdir, ASLFR_InitialDrawer, "Songs", ASLFR_DoSaveMode, TRUE, ASLFR_RejectIcons, TRUE, TAG_DONE ); if( !sng_sreq ) { printf( "Error allocating Asl request\n" ); return FALSE; } dir_req = IAsl->AllocAslRequestTags( ASL_FileRequest, ASLFR_DoSaveMode, FALSE, ASLFR_DrawersOnly, TRUE, TAG_DONE ); if( !ins_lreq ) { printf( "Error allocating Asl request\n" ); return FALSE; } #endif if( !gui_open() ) return FALSE; gui_savethem = TRUE; return TRUE; } void gui_press_bbank( struct buttonbank *bbnk, int32 nb, int32 z, int32 button ) { int32 i; for( i=0; i>2)*24, (i&3)*80+256, (i>>2)*24, 80, 24 ); if( bbnk == &tbank[0] ) put_partbitmap( BM_TRKBANKP, 0, i*23, 744, 120+200+i*23, 52, 23 ); if( bbnk[i].name == NULL ) return; set_font(&mainbm, FONT_PRP, FALSE); set_fpen(&mainbm, PAL_BTNSHADOW); printstr(&mainbm, bbnk[i].name, bbnk[i].x+bbnk[i].xo+1, bbnk[i].y+5); set_fpen(&mainbm, PAL_BTNTEXT); printstr(&mainbm, bbnk[i].name, bbnk[i].x+bbnk[i].xo, bbnk[i].y+4); } void gui_release_bbank( struct buttonbank *bbnk, int32 nb, int32 z, int32 button ) { int32 i; for( i=0; i>2)*24, (i&3)*80+256, (i>>2)*24, 80, 24 ); if( bbnk == &tbank[0] ) put_partbitmap( BM_TRKBANKR, 0, i*23, 744, 120+200+i*23, 52, 23 ); if( bbnk[i].name == NULL ) return; set_font(&mainbm, FONT_PRP, FALSE); set_fpen(&mainbm, PAL_BTNSHADOW); printstr(&mainbm, bbnk[i].name, bbnk[i].x+bbnk[i].xo+1, bbnk[i].y+5); set_fpen(&mainbm, PAL_BTNTEXT); printstr(&mainbm, bbnk[i].name, bbnk[i].x+bbnk[i].xo, bbnk[i].y+4); } void gui_copyposregion( struct ahx_tune *at, BOOL cutting ) { int32 i, j, size; uint8 *ptr; // Calculate the size of the region to copy size = ((at->at_cbpmarkrgt-at->at_cbpmarklft)+1) * /* Channels */ ((at->at_cbpmarkbot-at->at_cbpmarktop)+1) * /* Rows */ 2; if( cbpbuf == NULL ) { cbpbuf = IExec->AllocVecTags( size, TAG_DONE ); if( cbpbuf == NULL ) return; cbpblen = size; } if( cbpblen < size ) { IExec->FreeVec( cbpbuf ); cbpblen = 0; cbpbuf = IExec->AllocVecTags( size, TAG_DONE ); if( cbpbuf == NULL ) return; cbpblen = size; } cbplcol = at->at_cbpmarklftcol; cbprcol = at->at_cbpmarkrgtcol; cbpchns = (at->at_cbpmarkrgt-at->at_cbpmarklft)+1; cbprows = (at->at_cbpmarkbot-at->at_cbpmarktop)+1; if( cutting ) setbefore_posregion( at, at->at_cbpmarklft, at->at_cbpmarktop, cbpchns, cbprows ); ptr = cbpbuf; for( i=at->at_cbpmarktop; i<=at->at_cbpmarkbot; i++ ) { for( j=at->at_cbpmarklft; j<=at->at_cbpmarkrgt; j++ ) { *(ptr++) = at->at_Positions[i].pos_Track[j]; *(ptr++) = at->at_Positions[i].pos_Transpose[j]; if( cutting ) { if(( j>at->at_cbpmarklft ) || ( cbplcol == 0 )) at->at_Positions[i].pos_Track[j] = 0; if(( jat_cbpmarkrgt ) || ( cbprcol != 0 )) at->at_Positions[i].pos_Transpose[j] = 0; } } } if( cutting ) setafter_posregion( at, at->at_cbpmarklft, at->at_cbpmarktop, cbpchns, cbprows ); } void gui_pasteposregion( struct ahx_tune *at ) { int32 i, j, pos, lch; uint8 *ptr; if( cbpbuf == NULL ) return; if( ( cbpchns == 0 ) || ( cbprows == 0 ) ) return; pos = at->at_PosNr; lch = at->at_posed_curs/5 + at->at_curlch; setbefore_posregion( at, lch, pos, cbpchns, cbprows ); ptr = cbpbuf; for( i=0; i 999 ) break; for( j=0; j= at->at_Channels ) break; if( ( j > 0 ) || ( cbplcol == 0 ) ) at->at_Positions[pos+i].pos_Track[lch+j] = *(ptr++); else ptr++; if( ( j < (cbpchns-1) ) || ( cbprcol == 1 ) ) at->at_Positions[pos+i].pos_Transpose[lch+j] = *(ptr++); else ptr++; } } setafter_posregion( at, lch, pos, cbpchns, cbprows ); at->at_modified = TRUE; } void gui_copyregion( int16 track, int16 start, int16 end, int16 toclip, BOOL cutting ) { int32 i; for( i=start; i<=end; i++ ) { if( toclip & CBF_NOTES ) { cb_content[i-start].stp_Note = curtune->at_Tracks[track][i].stp_Note; cb_content[i-start].stp_Instrument = curtune->at_Tracks[track][i].stp_Instrument; if( cutting ) { curtune->at_Tracks[track][i].stp_Note = 0; curtune->at_Tracks[track][i].stp_Instrument = 0; } } if( toclip & CBF_CMD1 ) { cb_content[i-start].stp_FX = curtune->at_Tracks[track][i].stp_FX; cb_content[i-start].stp_FXParam = curtune->at_Tracks[track][i].stp_FXParam; if( cutting ) { curtune->at_Tracks[track][i].stp_FX = 0; curtune->at_Tracks[track][i].stp_FXParam = 0; } } if( toclip & CBF_CMD2 ) { cb_content[i-start].stp_FXb = curtune->at_Tracks[track][i].stp_FXb; cb_content[i-start].stp_FXbParam = curtune->at_Tracks[track][i].stp_FXbParam; if( cutting ) { curtune->at_Tracks[track][i].stp_FXb = 0; curtune->at_Tracks[track][i].stp_FXbParam = 0; } } } cb_contains = toclip; cb_length = (end-start)+1; } void gui_paste( struct ahx_tune *at, int16 track, int16 pastemask ) { int32 i; if( at == NULL ) return; if( track == -1 ) return; if( cb_length == 0 ) return; pastemask &= cb_contains; // Only the bits in both if( pastemask == 0 ) return; for( i=0; iat_NoteNr) >= at->at_TrackLength ) break; if( pastemask & CBF_NOTES ) { at->at_Tracks[track][i+at->at_NoteNr].stp_Note = cb_content[i].stp_Note; at->at_Tracks[track][i+at->at_NoteNr].stp_Instrument = cb_content[i].stp_Instrument; } if( pastemask & CBF_CMD1 ) { at->at_Tracks[track][i+at->at_NoteNr].stp_FX = cb_content[i].stp_FX; at->at_Tracks[track][i+at->at_NoteNr].stp_FXParam = cb_content[i].stp_FXParam; } if( pastemask & CBF_CMD2 ) { at->at_Tracks[track][i+at->at_NoteNr].stp_FXb = cb_content[i].stp_FXb; at->at_Tracks[track][i+at->at_NoteNr].stp_FXbParam = cb_content[i].stp_FXbParam; } } at->at_NoteNr = (at->at_NoteNr+i)%at->at_TrackLength; at->at_modified = TRUE; } BOOL optimise( struct ahx_tune *from, struct ahx_tune *to, BOOL optimise_more ) { int32 op_pos, op_chan, op_track, op_trans; int32 thistrans, i, j, k; int32 imap[64], icare[64], ins, topins; BOOL usedzero, carenote; for( i=0; i<63; i++ ) imap[i] = 0; IExec->CopyMem( from->at_Name, to->at_Name, 128 ); strncat( to->at_Name, "[opt]", 128 ); to->at_Name[127] = 0; to->at_Restart = from->at_Restart; to->at_PositionNr = from->at_PositionNr; to->at_SpeedMultiplier = from->at_SpeedMultiplier; to->at_TrackLength = from->at_TrackLength; to->at_SubsongNr = from->at_SubsongNr; to->at_Stereo = from->at_Stereo; IExec->CopyMem( from->at_Subsongs, to->at_Subsongs, 256*2 ); to->at_Channels = from->at_Channels; to->at_mixgain = from->at_mixgain; to->at_mixgainP = from->at_mixgainP; topins = 1; usedzero = FALSE; carenote = TRUE; // Loop through all the positions for( op_pos=0; op_posat_PositionNr; op_pos++ ) { // Loop through all the channels for( op_chan=0; op_chanat_Channels; op_chan++ ) { op_track = from->at_Positions[op_pos].pos_Track[op_chan]; op_trans = from->at_Positions[op_pos].pos_Transpose[op_chan]; // Find a track already present that matches // this one for( i=0; iat_TrackNr; i++ ) { thistrans = optimise_more ? 10000 : 0; for( j=0; jat_TrackLength; j++ ) { // Map the instrument ins = from->at_Tracks[op_track][j].stp_Instrument; if( ins > 0 ) { if( imap[ins] == 0 ) { to->at_Instruments[topins] = from->at_Instruments[ins]; imap[ins] = topins++; icare[ins] = FALSE; for( k=0; kat_Instruments[ins].ins_PList.pls_Length; k++ ) { if( ( from->at_Instruments[ins].ins_PList.pls_Entries[k].ple_Note != 0 ) && ( from->at_Instruments[ins].ins_PList.pls_Entries[k].ple_Fixed == 0 ) ) { icare[ins] = TRUE; break; } } } carenote = icare[ins]; ins = imap[ins]; } // Compare it if( to->at_Tracks[i][j].stp_Instrument != ins ) break; if( to->at_Tracks[i][j].stp_FX != from->at_Tracks[op_track][j].stp_FX ) break; if( to->at_Tracks[i][j].stp_FXParam != from->at_Tracks[op_track][j].stp_FXParam ) break; if( to->at_Tracks[i][j].stp_FXb != from->at_Tracks[op_track][j].stp_FXb ) break; if( to->at_Tracks[i][j].stp_FXbParam != from->at_Tracks[op_track][j].stp_FXbParam ) break; if( from->at_Tracks[op_track][j].stp_Note != 0 ) { if( to->at_Tracks[i][j].stp_Note == 0 ) break; if( ( ins == 0 ) || ( carenote ) ) { if( thistrans != 10000 ) { if( (to->at_Tracks[i][j].stp_Note-from->at_Tracks[op_track][j].stp_Note) != thistrans ) break; } else { thistrans = to->at_Tracks[i][j].stp_Note - from->at_Tracks[op_track][j].stp_Note; if( ( (thistrans+op_trans) < -128 ) || ( (thistrans+op_trans) > 127 ) ) break; } } } else { if( to->at_Tracks[i][j].stp_Note != 0 ) break; } } // Found a match? if( j == from->at_TrackLength ) break; } if( i == to->at_TrackNr ) { for( j=0; jat_TrackLength; j++ ) { // Map the instrument ins = from->at_Tracks[op_track][j].stp_Instrument; if( ins > 0 ) { if( imap[ins] == 0 ) { to->at_Instruments[topins] = from->at_Instruments[ins]; imap[ins] = topins++; icare[ins] = FALSE; for( k=0; kat_Instruments[ins].ins_PList.pls_Length; k++ ) { if( ( from->at_Instruments[ins].ins_PList.pls_Entries[k].ple_Note != 0 ) && ( from->at_Instruments[ins].ins_PList.pls_Entries[k].ple_Fixed == 0 ) ) { icare[ins] = TRUE; break; } } } ins = imap[ins]; } to->at_Tracks[i][j].stp_Instrument = ins; to->at_Tracks[i][j].stp_Note = from->at_Tracks[op_track][j].stp_Note; to->at_Tracks[i][j].stp_FX = from->at_Tracks[op_track][j].stp_FX; to->at_Tracks[i][j].stp_FXParam = from->at_Tracks[op_track][j].stp_FXParam; to->at_Tracks[i][j].stp_FXb = from->at_Tracks[op_track][j].stp_FXb; to->at_Tracks[i][j].stp_FXbParam = from->at_Tracks[op_track][j].stp_FXbParam; } to->at_TrackNr++; thistrans = 0; } if( thistrans == 10000 ) thistrans = 0; to->at_Positions[op_pos].pos_Track[op_chan] = i; to->at_Positions[op_pos].pos_Transpose[op_chan] = op_trans-thistrans; if( i == 0 ) usedzero = TRUE; } } return usedzero; } BOOL gui_check_bbank( struct buttonbank *bbnk, int32 nb, int32 z, int32 button ) { int32 i, j, b, l; int32 chan, track; #ifndef __SDL_WRAPPER__ BPTR lock, olddir; #else char *gfname; #endif BOOL ok, optimise_more; struct ahx_tune *at; struct ahx_step tstp; TEXT tmp[256]; BOOL cutting; int32 toclip; for( i=0; iat_tracked_curs/9)+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; } cutting = FALSE; toclip = 0; optimise_more = FALSE; switch( b ) { case BBA_ABOUT: about_toggle(); break; case BBA_ZAP: #ifndef __SDL_WRAPPER__ switch( gui_req( REQIMAGE_QUESTION, "Zap!", "What would you like to Zap?", "Everything|Song|Tracks|Positions|Instruments|Oops! Nothing!" ) ) { case 1: // Everything rp_clear_tune( curtune ); gui_render_tunepanel( TRUE ); break; case 2: // Song rp_zap_tracks( curtune ); rp_zap_positions( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); break; case 3: // Tracks rp_zap_tracks( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); break; case 4: // Positions rp_zap_positions( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); break; case 5: // Instruments rp_zap_instruments( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); break; } #else gui_setup_buttonbank( 1 ); gui_render_main_buttonbank(); #endif break; case BBA_ZAP_SONG: rp_zap_tracks( curtune ); rp_zap_positions( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); gui_setup_buttonbank( 0 ); gui_render_main_buttonbank(); break; case BBA_ZAP_TRACKS: rp_zap_tracks( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); gui_setup_buttonbank( 0 ); gui_render_main_buttonbank(); break; case BBA_ZAP_POSNS: rp_zap_positions( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); gui_setup_buttonbank( 0 ); gui_render_main_buttonbank(); break; case BBA_ZAP_INSTRS: rp_zap_instruments( curtune ); free_undolists( curtune ); gui_render_tunepanel( TRUE ); gui_setup_buttonbank( 0 ); gui_render_main_buttonbank(); break; case BBA_ZAP_ALL: rp_clear_tune( curtune ); gui_render_tunepanel( TRUE ); gui_setup_buttonbank( 0 ); gui_render_main_buttonbank(); break; case BBA_BACK: gui_setup_buttonbank( 0 ); gui_render_main_buttonbank(); break; case BBA_UNDO: rp_stop(); curtune->at_doing = D_IDLE; undo( curtune ); gui_render_wavemeter(); break; case BBA_REDO: rp_stop(); curtune->at_doing = D_IDLE; redo( curtune ); gui_render_wavemeter(); break; case BBA_AUTOGAIN: rp_stop(); gui_render_wavemeter(); curtune->at_doing = D_IDLE; for( j=0; jat_Channels; j++ ) { curtune->at_Voices[j].vc_SetTrackOn = 1; curtune->at_Voices[j].vc_TrackOn = 1; } bbank[i].name = "Wait..."; gui_render_main_buttonbank(); l = rp_find_loudest( curtune ); if( l > 0 ) l = 3276700/l; else l = 100; if( l > 200 ) l = 200; modify_tune_b( curtune, UNT_MIXGAIN, l ); curtune->at_mixgain = (l*256)/100; rp_init_subsong( curtune, 0 ); gui_set_various_things( curtune ); gui_render_posed( FALSE ); gui_render_tracked( TRUE ); bbank[i].name = "Autogain"; gui_render_main_buttonbank(); break; case BBA_INSEDIT: curtune->at_idoing = D_IDLE; if( curtune->at_curpanel == PN_TRACKER ) curtune->at_curpanel = PN_INSED; else curtune->at_curpanel = PN_TRACKER; gui_render_tunepanel( TRUE ); break; case BBA_NEWTAB: at = rp_new_tune( TRUE ); if( at ) { curtune = at; gui_set_various_things( at ); gui_render_tabs(); gui_render_tunepanel( TRUE ); } break; case BBA_OPTIMISE_MORE: optimise_more = TRUE; case BBA_OPTIMISE: at = rp_new_tune( TRUE ); if( at ) { at->at_TrackNr = 1; // Try and use track 0 as the empty one if( !optimise( curtune, at, optimise_more ) ) { rp_clear_tune( at ); at->at_TrackNr = 0; optimise( curtune, at, optimise_more ); } at->at_doing = D_IDLE; at->at_editing = E_TRACK; at->at_idoing = D_IDLE; curtune = at; gui_set_various_things( at ); gui_render_tabs(); gui_render_tunepanel( TRUE ); } break; case BBA_CLONETAB: at = rp_new_tune( TRUE ); if( at ) { IExec->CopyMem( &curtune->at_Name[0], &at->at_Name[0], sizeof( struct ahx_tune ) - offsetof( struct ahx_tune, at_Name ) ); at->at_doing = D_IDLE; at->at_editing = E_TRACK; at->at_idoing = D_IDLE; curtune = at; gui_set_various_things( at ); gui_render_tabs(); gui_render_tunepanel( TRUE ); } break; case BBA_LOADINS: if( curtune == NULL ) break; if( curtune->at_curins == 0 ) curtune->at_curins = 1; rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); curtune->at_doing = D_IDLE; #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( ins_lreq, ASLFR_Window, mainwin, ASLFR_SleepWindow, TRUE, TAG_DONE ); if( !ok ) break; lock = IDOS->Lock( ins_lreq->fr_Drawer, ACCESS_READ ); olddir = IDOS->CurrentDir( lock ); rp_load_ins( ins_lreq->fr_File, curtune, curtune->at_curins ); IDOS->CurrentDir( olddir ); IDOS->UnLock( lock ); #else if (!(gfname = filerequester("Load instrument", reminstdir, "", FR_INSLOAD))) break; setpathpart(reminstdir, gfname); rp_load_ins( gfname, curtune, curtune->at_curins ); free(gfname); #endif gui_set_various_things( curtune ); if( curtune->at_curpanel == PN_TRACKER ) { gui_render_inslist( TRUE ); } else { gui_render_perf( curtune, &curtune->at_Instruments[curtune->at_curins], TRUE ); gui_render_tbox( &mainbm, &tbx[TB_INSNAME] ); gui_render_inslistb( TRUE ); } break; case BBA_SAVEAHX: if( curtune == NULL ) break; rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); curtune->at_doing = D_IDLE; i = rp_ahx_test( curtune ); if( i != 0 ) { TEXT wtxt[4096]; sprintf( wtxt, "'%s' uses the following HivelyTracker specific features:\n\n", curtune->at_Name ); if( i & SWF_MANYCHANS ) strcat( wtxt, "* More than 4 channels\n" ); if( i & SWF_DOUBLECMD ) strcat( wtxt, "* Notes with 2 commands\n" ); if( i & SWF_NEWINSCMD ) strcat( wtxt, "* Instruments with commands other than 1,2,3,4,5,C & F\n" ); if( i & SWF_PANCMD ) strcat( wtxt, "* 7xx panning command\n" ); if( i & SWF_EFXCMD ) strcat( wtxt, "* EFx command\n" ); strcat( wtxt, "\nSaving as AHX would strip these out. Are you sure?" ); if( gui_req( REQIMAGE_WARNING, "HivelyTracker", wtxt, "_OK|_Noooo!" ) == 0 ) break; } strcpy( tmp, "AHX." ); strcat( tmp, curtune->at_Name ); #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( sng_sreq, ASLFR_Window, mainwin, ASLFR_InitialFile, tmp, ASLFR_SleepWindow, TRUE, TAG_DONE ); if( !ok ) break; lock = IDOS->Lock( sng_sreq->fr_Drawer, ACCESS_READ ); olddir = IDOS->CurrentDir( lock ); rp_save_ahx( sng_sreq->fr_File, curtune ); if( i == 0 ) curtune->at_modified = FALSE; IDOS->CurrentDir( olddir ); IDOS->UnLock( lock ); #else if (!(gfname = filerequester("Save AHX module", remsongdir, "", FR_AHXSAVE))) break; setpathpart(remsongdir, gfname); rp_save_ahx( gfname, curtune ); free(gfname); if( i == 0 ) curtune->at_modified = FALSE; #endif break; case BBA_SAVEHVL: if( curtune == NULL ) break; rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); curtune->at_doing = D_IDLE; strcpy( tmp, "HVL." ); strcat( tmp, curtune->at_Name ); #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( sng_sreq, ASLFR_Window, mainwin, ASLFR_InitialFile, tmp, ASLFR_SleepWindow, TRUE, TAG_DONE ); if( !ok ) break; lock = IDOS->Lock( sng_sreq->fr_Drawer, ACCESS_READ ); olddir = IDOS->CurrentDir( lock ); rp_save_hvl( sng_sreq->fr_File, curtune ); curtune->at_modified = FALSE; IDOS->CurrentDir( olddir ); IDOS->UnLock( lock ); #else if (!(gfname = filerequester("Save HVL module", remsongdir, "", FR_HVLSAVE))) break; setpathpart(remsongdir, gfname); rp_save_hvl( gfname, curtune ); free(gfname); curtune->at_modified = FALSE; #endif break; case BBA_SAVEINS: if( curtune == NULL ) break; rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); curtune->at_doing = D_IDLE; strcpy( tmp, "INS." ); strcat( tmp, curtune->at_Instruments[curtune->at_curins].ins_Name ); #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( ins_sreq, ASLFR_Window, mainwin, ASLFR_InitialFile, tmp, ASLFR_SleepWindow, TRUE, TAG_DONE ); if( !ok ) break; lock = IDOS->Lock( ins_sreq->fr_Drawer, ACCESS_READ ); olddir = IDOS->CurrentDir( lock ); rp_save_ins( ins_sreq->fr_File, curtune, curtune->at_curins ); gui_set_various_things( curtune ); IDOS->CurrentDir( olddir ); IDOS->UnLock( lock ); #else if (!(gfname = filerequester("Save instrument", reminstdir, "", FR_INSSAVE))) break; setpathpart(reminstdir, gfname); rp_save_ins( gfname, curtune, curtune->at_curins ); free(gfname); gui_set_various_things( curtune ); #endif break; case BBA_LOADTUNE: if( ( curtune != NULL ) && ( curtune->at_modified ) ) if( gui_req( REQIMAGE_QUESTION, "HivelyTracker", "This song has been modified. Continue?", "_Yep!|Arrgh.. _No!" ) == 0 ) break; #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( sng_lreq, ASLFR_Window, mainwin, ASLFR_SleepWindow, TRUE, TAG_DONE ); if( !ok ) break; #else if (!(gfname = filerequester("Load song", remsongdir, "", FR_MODLOAD))) break; setpathpart(remsongdir, gfname); #endif rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); if( curtune ) curtune->at_doing = D_IDLE; #ifndef __SDL_WRAPPER__ lock = IDOS->Lock( sng_lreq->fr_Drawer, ACCESS_READ ); olddir = IDOS->CurrentDir( lock ); at = rp_load_tune( sng_lreq->fr_File, curtune ); if( at ) curtune = at; IDOS->CurrentDir( olddir ); IDOS->UnLock( lock ); #else at = rp_load_tune( gfname, curtune ); free( gfname ); if( at ) curtune = at; #endif if( at ) gui_set_rempos( at ); gui_render_tabs(); gui_render_tunepanel( TRUE ); break; case BBA_CONTPOS: case BBA_PLAYPOS: if( curtune == NULL ) break; curtune->at_doing = D_IDLE; if( rp_play_pos( curtune, b == BBA_CONTPOS ? TRUE : FALSE ) ) curtune->at_doing = D_PLAYING; break; case BBA_CONTSONG: case BBA_PLAYSONG: if( curtune == NULL ) break; curtune->at_doing = D_IDLE; if( rp_play_song( curtune, curtune->at_curss, b == BBA_CONTSONG ? TRUE : FALSE ) ) curtune->at_doing = D_PLAYING; break; case BBA_STOP: rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); curtune->at_doing = D_IDLE; break; case BBA_PREFS: #ifndef __SDL_WRAPPER__ if( prefwin ) gui_close_prefs(); else gui_open_prefs(); #else gui_open_prefs(); #endif break; case BBA_CUTTRK: cutting = TRUE; case BBA_COPYTRK: if( curtune->at_cbmarktrack != -1 ) { gui_copyregion( curtune->at_cbmarktrack, curtune->at_cbmarkstartnote, curtune->at_cbmarkendnote, curtune->at_cbmarkbits, cutting ); curtune->at_cbmarktrack = -1; if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; } if( track == -1 ) break; if( button == 1 ) { toclip = CBF_CMD1|CBF_CMD2; } else { if( qual & IEQUALIFIER_CONTROL ) toclip = CBF_NOTES; else toclip = CBF_NOTES|CBF_CMD1|CBF_CMD2; } gui_copyregion( track, 0, curtune->at_TrackLength-1, toclip, cutting ); if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; case BBA_PASTE: if( curtune->at_cbmarktrack != -1 ) curtune->at_cbmarktrack = -1; if( track == -1 ) break; setbefore_track( curtune, track ); gui_paste( curtune, track, CBF_NOTES|CBF_CMD1|CBF_CMD2 ); setafter_track( curtune, track ); if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; case BBA_NOTEUP: if( track == -1 ) break; setbefore_track( curtune, track ); for( i=0; iat_TrackLength; i++ ) { if( ( curtune->at_Tracks[track][i].stp_Note > 0 ) && ( curtune->at_Tracks[track][i].stp_Note < 60 ) ) curtune->at_Tracks[track][i].stp_Note++; } setafter_track( curtune, track ); if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; case BBA_NOTEDN: if( track == -1 ) break; setbefore_track( curtune, track ); for( i=0; iat_TrackLength; i++ ) { if( curtune->at_Tracks[track][i].stp_Note > 1 ) curtune->at_Tracks[track][i].stp_Note--; } setafter_track( curtune, track ); if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; case BBA_REVERSE: if( track == -1 ) break; setbefore_track( curtune, track ); l = curtune->at_TrackLength>>1; b = curtune->at_TrackLength-1; for( i=0; iat_Tracks[track][i]; curtune->at_Tracks[track][i] = curtune->at_Tracks[track][b-i]; curtune->at_Tracks[track][b-i] = tstp; } setafter_track( curtune, track ); if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; case BBA_SCRLUP: if( track == -1 ) break; setbefore_track( curtune, track ); tstp = curtune->at_Tracks[track][0]; for( i=1; iat_TrackLength; i++ ) curtune->at_Tracks[track][i-1] = curtune->at_Tracks[track][i]; curtune->at_Tracks[track][curtune->at_TrackLength-1] = tstp; setafter_track( curtune, track ); if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; case BBA_SCRLDN: if( track == -1 ) break; setbefore_track( curtune, track ); tstp = curtune->at_Tracks[track][curtune->at_TrackLength-1]; for( i=curtune->at_TrackLength-1; i>=0; i-- ) curtune->at_Tracks[track][i] = curtune->at_Tracks[track][i-1]; curtune->at_Tracks[track][0] = tstp; setafter_track( curtune, track ); if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); break; } return TRUE; } void gui_press_any_bbank( int32 z, int32 button ) { gui_press_bbank( &bbank[0], 16, z, button ); if (curtune->at_curpanel == PN_TRACKER) gui_press_bbank( &tbank[0], 12, z, button ); } void gui_release_any_bbank( int32 z, int32 button ) { gui_release_bbank( &bbank[0], 16, z, button ); if (curtune->at_curpanel == PN_TRACKER) gui_release_bbank( &tbank[0], 12, z, button ); } BOOL gui_check_any_bbank( int32 z, int32 button ) { if( gui_check_bbank( &bbank[0], 16, z, button ) ) return TRUE; if (curtune->at_curpanel == PN_TRACKER) if( gui_check_bbank( &tbank[0], 12, z, button ) ) return TRUE; return FALSE; } BOOL gui_check_nbox_butpress( int16 x, int16 y ) { int32 i, num; struct numberbox *nbp = NULL; if( curtune == NULL ) return FALSE; switch( curtune->at_curpanel ) { case PN_TRACKER: nbp = &trk_nb[0]; num = NB_END; break; case PN_INSED: nbp = &ins_nb[0]; num = INB_END; break; } if( nbp == NULL ) return FALSE; for( i=0; i= nbp[i].x+nbp[i].w-28 ) && ( x < nbp[i].x+nbp[i].w-14 ) && ( y >= nbp[i].y ) && ( y < nbp[i].y+nbp[i].h ) ) { if( nbp[i].flags & NBF_ENABLED ) { if( ( ( nbp[i].flags & NBF_UDDURINGPLAY ) == 0 ) && ( curtune->at_doing == D_PLAYING ) ) return FALSE; put_partbitmap( BM_PLUSMINUS, 0, 19, nbp[i].x+nbp[i].w-28, nbp[i].y-1, 14, 19 ); nbp[i].pressed = 1; } return TRUE; } if( ( x >= nbp[i].x+nbp[i].w-14 ) && ( x < nbp[i].x+nbp[i].w ) && ( y >= nbp[i].y ) && ( y < nbp[i].y+nbp[i].h ) ) { if( nbp[i].flags & NBF_ENABLED ) { if( ( ( nbp[i].flags & NBF_UDDURINGPLAY ) == 0 ) && ( curtune->at_doing == D_PLAYING ) ) return FALSE; put_partbitmap( BM_PLUSMINUS, 14, 19, nbp[i].x+nbp[i].w-14, nbp[i].y-1, 14, 19 ); nbp[i].pressed = 2; } return TRUE; } } return FALSE; } BOOL gui_check_nbox_butrelease( int16 x, int16 y ) { int32 i, num; BOOL ret; struct numberbox *nbp = NULL; if( curtune == NULL ) return FALSE; ret = FALSE; switch( curtune->at_curpanel ) { case PN_TRACKER: nbp = &trk_nb[0]; num = NB_END; break; case PN_INSED: nbp = &ins_nb[0]; num = INB_END; break; } if( nbp == NULL ) return FALSE; for( i=0; i= nbp[i].x+nbp[i].w-28 ) && ( x < nbp[i].x+nbp[i].w-14 ) && ( y >= nbp[i].y ) && ( y < nbp[i].y+nbp[i].h ) ) { if( nbp[i].pressed == 1 ) { if( qual & IEQUALIFIER_RBUTTON ) nbp[i].cnum+=10; else nbp[i].cnum++; gui_update_from_nbox( curtune->at_curpanel, nbp, i ); ret = TRUE; } } if( ( x >= nbp[i].x+nbp[i].w-14 ) && ( x < nbp[i].x+nbp[i].w ) && ( y >= nbp[i].y ) && ( y < nbp[i].y+nbp[i].h ) ) { if( nbp[i].pressed == 2 ) { if( qual & IEQUALIFIER_RBUTTON ) nbp[i].cnum-=10; else nbp[i].cnum--; gui_update_from_nbox( curtune->at_curpanel, nbp, i ); ret = TRUE; } } if( nbp[i].pressed != 0 ) { nbp[i].pressed = 0; put_partbitmap( BM_PLUSMINUS, 0, 0, nbp[i].x+nbp[i].w-28, nbp[i].y-1, 28, 19 ); } } return ret; } BOOL gui_check_ptbox_press( int16 x, int16 y ) { int32 i; #ifndef __SDL_WRAPPER__ // *sigh* i suck, i know i suck. x += pw_bl; y += pw_bt; #else if (!prefwin_open) return FALSE; #endif for( i=0; i= ptb[i].x ) && ( x < ptb[i].x+ptb[i].w ) && ( y >= ptb[i].y ) && ( y < ptb[i].y+16 ) && ( ptb[i].flags & TBF_ENABLED ) && ( ptb[i].flags & TBF_VISIBLE ) && ( ptb[i].content != NULL ) ) { if( ( ptbx != NULL ) && ( ptbx != &ptb[i] ) ) { ptbx->flags &= ~TBF_ACTIVE; gui_render_tbox( &prefbm, ptbx ); } ptbx = &ptb[i]; #ifdef __SDL_WRAPPER__ SDL_EnableUNICODE(SDL_TRUE); #endif ptbx->flags |= TBF_ACTIVE; ptbx->cpos = ((x-ptbx->x)>>3)+ptbx->spos; gui_render_tbox( &prefbm, ptbx ); #ifdef __SDL_WRAPPER__ bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); #endif return TRUE; } } return FALSE; } void gui_ptbox_finish_editing( void ) { if( ptbx == NULL ) return; ptbx = NULL; #ifdef __SDL_WRAPPER__ SDL_EnableUNICODE(SDL_FALSE); #endif } void gui_tbox_finish_editing( void ) { if( etbx == NULL ) return; if( etbx == &tbx[TB_SONGNAME] ) { setafter_string( UNT_STRING_SONGNAME, curtune, etbx->content ); gui_render_tabs(); } if( etbx == &tbx[TB_INSNAME] ) { setafter_string( UNT_STRING_INSNAME, curtune, etbx->content ); gui_render_inslistb( TRUE ); } if( etbx == &tbx[TB_INSNAME2] ) { setafter_string( UNT_STRING_INSNAME2, curtune, etbx->content ); tbx[TB_INSNAME2].flags &= ~TBF_VISIBLE; tbx[TB_INSNAME2].content = NULL; gui_render_inslist( TRUE ); } #ifdef __SDL_WRAPPER__ SDL_EnableUNICODE(SDL_FALSE); #endif etbx = NULL; } BOOL gui_check_tbox_press( int16 x, int16 y ) { int32 i; int16 panel; panel = PN_TRACKER; if( curtune ) panel = curtune->at_curpanel; for( i=0; i= tbx[i].x ) && ( x < tbx[i].x+tbx[i].w ) && ( y >= tbx[i].y ) && ( y < tbx[i].y+16 ) && ( tbx[i].flags & TBF_ENABLED ) && ( tbx[i].flags & TBF_VISIBLE ) && ( tbx[i].content != NULL ) ) { if( ( etbx != NULL ) && ( etbx != &tbx[i] ) ) { etbx->flags &= ~TBF_ACTIVE; gui_render_tbox( &mainbm, etbx ); gui_tbox_finish_editing(); } setbefore_string( curtune, tbx[i].content ); etbx = &tbx[i]; etbx->flags |= TBF_ACTIVE; etbx->cpos = ((x-etbx->x)>>3)+etbx->spos; gui_render_tbox( &mainbm, etbx ); #ifdef __SDL_WRAPPER__ SDL_EnableUNICODE(SDL_TRUE); #endif return TRUE; } } } return FALSE; } void gui_press_mute( int32 zone ) { int32 i, mch; if( curtune == NULL ) return; if( curtune->at_curpanel != PN_TRACKER ) return; mch=0; for( i=0; i<6; i++ ) if( zone == zn_mute[i] ) break; if( i == 6 ) return; mch = i+curtune->at_curlch; if( curtune->at_Voices[mch].vc_SetTrackOn ) put_partbitmap( BM_CHANMUTE, 0, 19, zones[zone].x, zones[zone].y, 14, 19 ); else put_partbitmap( BM_CHANMUTE, 0, 0, zones[zone].x, zones[zone].y, 14, 19 ); } void gui_release_mute( int32 zone ) { int32 i, mch; if( curtune == NULL ) return; if( curtune->at_curpanel != PN_TRACKER ) return; mch=0; for( i=0; i<6; i++ ) if( zone == zn_mute[i] ) break; if( i == 6 ) return; mch = i+curtune->at_curlch; if( curtune->at_Voices[mch].vc_SetTrackOn ) put_partbitmap( BM_CHANMUTE, 0, 0, zones[zone].x, zones[zone].y, 14, 19 ); else put_partbitmap( BM_CHANMUTE, 0, 19, zones[zone].x, zones[zone].y, 14, 19 ); } BOOL gui_check_mute( int32 zone ) { int32 i, mch; if( curtune == NULL ) return FALSE; if( curtune->at_curpanel != PN_TRACKER ) return FALSE; mch=0; for( i=0; i<6; i++ ) if( zone == zn_mute[i] ) break; if( i == 6 ) return FALSE; mch = i+curtune->at_curlch; curtune->at_Voices[mch].vc_SetTrackOn ^= 1; curtune->at_Voices[mch].vc_TrackOn = curtune->at_Voices[mch].vc_SetTrackOn; if( curtune->at_Voices[mch].vc_SetTrackOn ) put_partbitmap( BM_CHANMUTE, 0, 0, zones[zone].x, zones[zone].y, 14, 19 ); else put_partbitmap( BM_CHANMUTE, 0, 19, zones[zone].x, zones[zone].y, 14, 19 ); return TRUE; } BOOL gui_check_rmb_mute( int32 zone ) { int32 i, mch; if( curtune == NULL ) return FALSE; if( curtune->at_curpanel != PN_TRACKER ) return FALSE; mch=0; for( i=0; i<6; i++ ) if( zone == zn_mute[i] ) break; if( i == 6 ) return FALSE; mch = i+curtune->at_curlch; // Already solo'd? for( i=0; iat_Channels; i++ ) { if( i == mch ) { if( curtune->at_Voices[i].vc_SetTrackOn == 0 ) break; } else { if( curtune->at_Voices[i].vc_SetTrackOn != 0 ) break; } } if( i == curtune->at_Channels ) { // Already solo'd, so set all tracks on for( i=0; iat_Channels; i++ ) curtune->at_Voices[i].vc_SetTrackOn = 1; } else { // Not solo'd, so solo it for( i=0; iat_Channels; i++ ) curtune->at_Voices[i].vc_SetTrackOn = i==mch ? 1 : 0; } for( i=0; iat_Channels; i++ ) { curtune->at_Voices[i].vc_TrackOn = curtune->at_Voices[i].vc_SetTrackOn; if( ( i >= curtune->at_curlch ) && ( i < (curtune->at_curlch+6) ) ) { zone = zn_mute[i-curtune->at_curlch]; if( curtune->at_Voices[i].vc_SetTrackOn ) put_partbitmap( BM_CHANMUTE, 0, 0, zones[zone].x, zones[zone].y, 14, 19 ); else put_partbitmap( BM_CHANMUTE, 0, 19, zones[zone].x, zones[zone].y, 14, 19 ); } } return TRUE; } BOOL gui_maybe_quit( void ) { struct ahx_tune *at; BOOL anymod; anymod = FALSE; at = (struct ahx_tune *)IExec->GetHead(rp_tunelist); while( at ) { if( at->at_modified ) { anymod = TRUE; break; } at = (struct ahx_tune *)IExec->GetSucc(&at->at_ln); } if( anymod ) return gui_req( REQIMAGE_QUESTION, "HivelyTracker", "One or more songs have been modified. Really quit?", "_Yep!|Arrgh.. _No!" ); return gui_req( REQIMAGE_QUESTION, "HivelyTracker", "Are you sure you want to quit?", "_Yep!|Arrgh.. _No!" ); } void gui_wheelybin( int16 mousex, int16 mousey, int16 wheeldir ) { int32 i, chan; struct ahx_plsentry *cple; struct ahx_instrument *cins; if( wheeldir == 0 ) return; if( !curtune ) return; switch( curtune->at_curpanel ) { case PN_TRACKER: // Check numberboxes for( i=0; i= trk_nb[i].x ) && ( mousex < (trk_nb[i].x+trk_nb[i].w ) ) && ( mousey >= trk_nb[i].y ) && ( mousey < (trk_nb[i].y+trk_nb[i].h ) ) ) break; } if( i < NB_END ) { if( trk_nb[i].flags & NBF_ENABLED ) { if( ( ( trk_nb[i].flags & NBF_UDDURINGPLAY ) == 0 ) && ( curtune->at_doing == D_PLAYING ) ) return; trk_nb[i].cnum -= wheeldir; gui_update_from_nbox( curtune->at_curpanel, &trk_nb[0], i ); } return; } if( ( mousey >= (TRACKED_Y-22) ) && ( mousey < TRACKED_Y ) && ( mousex >= (TRACKED_X+24) ) && ( mousex < (TRACKED_X+TRACKED_W) ) ) { i = (mousex-(TRACKED_X+24)) % 120; chan = (mousex-(TRACKED_X+24)) / 120 + curtune->at_curlch; if( i < 78 ) return; if( chan >= curtune->at_Channels ) return; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, curtune->at_Positions[curtune->at_PosNr].pos_Track[chan] - wheeldir ); gui_render_posed( TRUE ); gui_render_tracked( TRUE ); return; } if( ( mousex >= TRACKED_X ) && ( mousex < (TRACKED_X+TRACKED_W) ) && ( mousey >= TRACKED_Y ) && ( mousey < (TRACKED_Y+TRACKED_H) ) ) { if( curtune->at_doing == D_PLAYING ) return; i = curtune->at_NoteNr + wheeldir; if( i <= 0 ) i = 0; if( i >= curtune->at_TrackLength ) i = curtune->at_TrackLength-1; curtune->at_NoteNr = i; gui_render_tracker( FALSE ); return; } if( ( mousex >= POSED_X ) && ( mousex < POSED_X+POSED_W ) && ( mousey >= POSED_Y ) && ( mousey < POSED_Y+POSED_H ) ) { if( curtune->at_doing == D_PLAYING ) { int16 next; if( curtune->at_NextPosNr == -1 ) next = curtune->at_PosNr + wheeldir; else next = curtune->at_NextPosNr + wheeldir; if( next < 0 ) next = 0; if( next > 999 ) next = 999; curtune->at_NextPosNr = next; } else { curtune->at_PosNr += wheeldir; if( curtune->at_PosNr < 0 ) curtune->at_PosNr = 0; if( curtune->at_PosNr > 999 ) curtune->at_PosNr = 999; } gui_render_tracker( FALSE ); return; } if( ( mousex >= INSLIST_X ) && ( mousex < (INSLIST_X+INSLIST_W) ) && ( mousey >= INSLIST_Y ) && ( mousey < (INSLIST_Y+INSLIST_H) ) ) { curtune->at_curins += wheeldir; if( curtune->at_curins < 1 ) curtune->at_curins = 1; if( curtune->at_curins > 63 ) curtune->at_curins = 63; gui_render_inslist( FALSE ); gui_set_various_things( curtune ); } break; case PN_INSED: // Check numberboxes for( i=0; i= ins_nb[i].x ) && ( mousex < (ins_nb[i].x+ins_nb[i].w ) ) && ( mousey >= ins_nb[i].y ) && ( mousey < (ins_nb[i].y+ins_nb[i].h ) ) ) break; } if( i < INB_END ) { if( ins_nb[i].flags & NBF_ENABLED ) { if( ( ( ins_nb[i].flags & NBF_UDDURINGPLAY ) == 0 ) && ( curtune->at_doing == D_PLAYING ) ) return; ins_nb[i].cnum -= wheeldir; gui_update_from_nbox( curtune->at_curpanel, &ins_nb[0], i ); } return; } cins = NULL; cple = NULL; if( curtune ) { cins = &curtune->at_Instruments[curtune->at_curins]; cple = &cins->ins_PList.pls_Entries[cins->ins_pcury]; } if( ( cins != NULL ) && ( mousex >= PERF_X ) && ( mousex < (PERF_X+PERF_W) ) && ( mousey >= PERF_Y ) && ( mousey < (PERF_Y+PERF_H) ) ) { cins->ins_pcury += wheeldir; gui_render_perf( curtune, cins, FALSE ); return; } if( ( curtune != NULL ) && ( mousex >= INSLSTB_X ) && ( mousex < (INSLSTB_X+INSLSTB_W) ) && ( mousey >= INSLSTB_Y ) && ( mousey < (INSLSTB_Y+INSLSTB_H) ) ) { curtune->at_curins += wheeldir; if( curtune->at_curins < 0 ) curtune->at_curins = 0; if( curtune->at_curins > 63 ) curtune->at_curins = 63; cins = &curtune->at_Instruments[curtune->at_curins]; cple = &cins->ins_PList.pls_Entries[cins->ins_pcury]; gui_set_various_things( curtune ); } break; } } void gui_mouse_handler( int16 x, int16 y, uint32 code ) { int32 i, j; switch( code ) { case SELECTDOWN: if( gui_check_tbox_press( x, y ) ) return; if( etbx ) { etbx->flags &= ~TBF_ACTIVE; gui_render_tbox( &mainbm, etbx ); gui_tbox_finish_editing(); return; } if( ( curtune ) && ( curtune->at_curpanel == PN_TRACKER ) ) { if( ( x >= TRACKED_X ) && ( x < 744 ) && ( y >= TRACKED_Y ) && ( y < TRACKED_Y+TRACKED_H ) ) { curtune->at_editing = E_TRACK; if( ( curtune ) && ( x > (TRACKED_X+23) ) ) { i = (x-(TRACKED_X+24)) >> 3; j = i % 15; i /= 15; switch( j ) { case 0: case 1: case 2: case 3: j = 0; break; case 4: j = 1; break; case 5: case 6: j = 2; break; case 7: j = 3; break; case 8: j = 4; break; case 9: case 10: j = 5; break; case 11: j = 6; break; case 12: j = 7; break; case 13: case 14: j = 8; break; } if( i < curtune->at_Channels ) curtune->at_tracked_curs = i*9+j; } gui_render_tracked( TRUE ); gui_render_posed( TRUE ); break; } if( ( x >= POSED_X ) && ( x < POSED_X+POSED_W ) && ( y >= POSED_Y ) && ( y < POSED_Y+POSED_H ) ) { curtune->at_editing = E_POS; if( ( curtune ) && ( x > (POSED_X+27) ) ) { i = (x-(POSED_X+28)) / 7; j = i % 7; i /= 7; switch( j ) { case 3: j = 2; break; case 4: j = 3; break; case 5: case 6: j = 4; break; } if( i < curtune->at_Channels ) curtune->at_posed_curs = i*5+j; } gui_render_tracked( TRUE ); gui_render_posed( TRUE ); break; } } if( gui_check_nbox_butpress( x, y ) ) break; cz_lmb = gui_get_zone( &zones[0], numzones, x, y ); if( cz_lmb != -1 ) { gui_press_mute( cz_lmb ); gui_press_any_bbank( cz_lmb, 0 ); } break; case SELECTUP: if( etbx != NULL ) break; if( gui_check_nbox_butrelease( x, y ) ) break; if( cz_lmb != -1 ) { gui_release_any_bbank( cz_lmb, 0 ); gui_press_mute( cz_lmb ); } if( ( cz_lmb != -1 ) && ( cz_lmb == gui_get_zone( &zones[0], numzones, x, y ) ) ) { if( gui_check_mute( cz_lmb ) ) { cz_lmb = -1; break; } if( gui_check_any_bbank( cz_lmb, 0 ) ) { cz_lmb = -1; if( quitting ) return; break; } if( cz_lmb == zn_close ) { // Close gadget quitting = gui_maybe_quit(); } else if( cz_lmb == zn_scrdep ) { #ifndef __SDL_WRAPPER__ if( scr ) IIntuition->ScreenToBack( scr ); #endif } else { // Check for tab zones for( i=0; i<8; i++ ) { if( cz_lmb == ttab[i].zone ) { BOOL dorend; dorend = FALSE; if( x < (zones[ttab[i].zone].x+20) ) { if( ttab[i].tune->at_modified ) if( gui_req( REQIMAGE_QUESTION, "HivelyTracker", "This song has been modified. Continue?", "_Yep!|Arrgh.. _No!" ) == 0 ) break; // Only one tune? IExec->ObtainSemaphore( rp_list_ss ); if( IExec->GetSucc(IExec->GetHead(rp_tunelist)) == NULL ) { rp_clear_tune( ttab[i].tune ); gui_set_various_things( ttab[i].tune ); dorend = TRUE; } else { rp_free_tune( ttab[i].tune ); if( curtune == ttab[i].tune ) { curtune = (struct ahx_tune *)IExec->GetHead(rp_tunelist); gui_set_various_things(curtune); } dorend = TRUE; } IExec->ReleaseSemaphore( rp_list_ss ); } else { if( ttab[i].tune != curtune ) { curtune = ttab[i].tune; dorend = TRUE; } } if( dorend ) { gui_render_tabs(); gui_render_tunepanel( TRUE ); } break; } } } cz_lmb = -1; break; } if( ( curtune ) && ( curtune->at_curpanel == PN_TRACKER ) ) { if( ( x >= INSLIST_X ) && ( x < (INSLIST_X+INSLIST_W) ) && ( y >= INSLIST_Y ) && ( y < (INSLIST_Y+INSLIST_H) ) ) { int32 i; i = (y-(INSLIST_Y+5))/14; if( i < 0 ) i = 0; curtune->at_curins = i+curtune->at_topins; gui_render_inslist( FALSE ); gui_set_various_things( curtune ); } } if( ( curtune ) && ( curtune->at_curpanel == PN_INSED ) ) { if( ( x >= INSLSTB_X ) && ( x < (INSLIST_X+INSLSTB_W) ) && ( y >= INSLSTB_Y ) && ( y < (INSLIST_Y+INSLSTB_H) ) ) { int32 i; i = (y-(INSLSTB_Y+4))>>4; if( i < 0 ) i = 0; curtune->at_curins = i+curtune->at_topinsb; gui_set_various_things( curtune ); } if( ( x >= PERF_X ) && ( x < (PERF_X+PERF_W) ) && ( y >= PERF_Y ) && ( y < (PERF_Y+PERF_H) ) ) { int32 i; struct ahx_instrument *cins; cins = &curtune->at_Instruments[curtune->at_curins]; i = (y-(PERF_Y+4))>>4; if( i < 0 ) i = 0; cins->ins_pcury = i+cins->ins_ptop; gui_set_various_things( curtune ); } } break; case MENUDOWN: if( etbx != NULL ) break; cz_rmb = gui_get_zone( &zones[0], numzones, x, y ); if( cz_rmb != -1 ) { gui_press_any_bbank( cz_rmb, 1 ); gui_press_mute( cz_rmb ); } break; case MENUUP: if( etbx != NULL ) break; if( cz_rmb != -1 ) { gui_release_any_bbank( cz_rmb, 1 ); gui_release_mute( cz_rmb ); } if( ( cz_rmb != -1 ) && ( cz_rmb == gui_get_zone( &zones[0], numzones, x, y ) ) ) { if( gui_check_rmb_mute( cz_rmb ) ) { cz_rmb = -1; break; } if( gui_check_any_bbank( cz_rmb, 1 ) ) { cz_rmb = -1; break; } cz_rmb = -1; break; } if( ( curtune ) && ( curtune->at_curpanel == PN_TRACKER ) ) { if( ( x >= INSLIST_X ) && ( x < (INSLIST_X+INSLIST_W) ) && ( y >= INSLIST_Y ) && ( y < (INSLIST_Y+INSLIST_H) ) ) { int32 i, j; i = (y-(INSLIST_Y+5))/14; if( i < 0 ) i = 0; j = i+curtune->at_topins; tbx[TB_INSNAME2].content = curtune->at_Instruments[j].ins_Name; tbx[TB_INSNAME2].y = (INSLIST_Y+5)+(i*14)-1; tbx[TB_INSNAME2].flags |= TBF_VISIBLE|TBF_ACTIVE; tbx[TB_INSNAME2].spos = 0; setbefore_string( curtune, tbx[TB_INSNAME2].content ); etbx = &tbx[TB_INSNAME2]; if( x < etbx->x ) x = etbx->x; etbx->cpos = ((x-etbx->x)>>3)+etbx->spos; gui_render_tbox( &mainbm, etbx ); } } if( ( curtune ) && ( curtune->at_curpanel == PN_INSED ) ) { if( ( x >= INSLSTB_X ) && ( x < (INSLSTB_X+INSLSTB_W) ) && ( y >= INSLSTB_Y ) && ( y < (INSLSTB_Y+INSLSTB_H) ) ) { int32 i, j; BOOL doit = TRUE; i = (y-INSLSTB_Y)>>4; i += curtune->at_topinsb; if( i < 1 ) i = 1; if( i > 63 ) i = 63; j = curtune->at_curins; if( qual & IEQUALIFIER_CONTROL ) { rp_clear_instrument( &curtune->at_Instruments[i] ); curtune->at_curins = i; } else { if( i == j ) break; curtune->at_curins = i; if( curtune->at_Instruments[i].ins_PList.pls_Length != 0 ) { gui_render_inslistb( TRUE ); doit = gui_req( REQIMAGE_QUESTION, "Copy Instrument", "That instrument is not empty.\nDo you want to copy over it?", "_Yes yes!|Whoooooooops! _No!!!" ); } if( doit ) curtune->at_Instruments[i] = curtune->at_Instruments[j]; } if( doit ) { gui_render_tunepanel( TRUE ); curtune->at_modified = TRUE; } } } break; } } void gui_posed_moveright( void ) { curtune->at_posed_curs++; if( (curtune->at_posed_curs/5+curtune->at_curlch) >= curtune->at_Channels ) { curtune->at_posed_curs = 0; curtune->at_curlch = 0; } while( (curtune->at_posed_curs/5) > 5 ) { curtune->at_curlch++; curtune->at_posed_curs -= 5; } } void gui_textbox_keypress( struct rawbm *bm, struct textbox **ttbx, struct IntuiMessage *msg ) { int32 i, j, actual; TEXT kbuf[80]; int32 lqual; if( *ttbx == NULL ) return; lqual = msg->Qualifier; switch( msg->Code ) { case 79: // Left arrow if( lqual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) (*ttbx)->cpos = 0; else if( (*ttbx)->cpos > 0 ) (*ttbx)->cpos--; gui_render_tbox( bm, *ttbx ); break; case 78: // Right arrow if( lqual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) (*ttbx)->cpos = strlen( (*ttbx)->content ); else (*ttbx)->cpos++; gui_render_tbox( bm, *ttbx ); break; case 65: // Backspace if( (*ttbx)->cpos == 0 ) break; j = strlen( (*ttbx)->content ); if( lqual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) { for( i=(*ttbx)->cpos; i<=j; i++ ) (*ttbx)->content[i-(*ttbx)->cpos] = (*ttbx)->content[i]; (*ttbx)->cpos = 0; gui_render_tbox( bm, *ttbx ); break; } for( i=(*ttbx)->cpos-1; icontent[i] = (*ttbx)->content[i+1]; (*ttbx)->cpos--; gui_render_tbox( bm, *ttbx ); break; case 70: // Del if( lqual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) { (*ttbx)->content[(*ttbx)->cpos] = 0; gui_render_tbox( bm, *ttbx ); break; } if( (*ttbx)->cpos >= strlen( (*ttbx)->content ) ) break; j = strlen( (*ttbx)->content ); for( i=(*ttbx)->cpos; icontent[i] = (*ttbx)->content[i+1]; gui_render_tbox( bm, *ttbx ); break; case 68: // Enter case 197: // ESC (*ttbx)->flags &= ~TBF_ACTIVE; gui_render_tbox( bm, *ttbx ); if( bm == &mainbm ) gui_tbox_finish_editing(); else gui_ptbox_finish_editing(); break; default: #ifndef __SDL_WRAPPER__ iev.ie_Code = msg->Code; iev.ie_Qualifier = msg->Qualifier; /* recover dead key codes & qualifiers */ iev.ie_EventAddress = (APTR *) *((ULONG *)msg->IAddress); actual = (IKeymap->MapRawKey( &iev, kbuf, 80, 0 ) == 1); #else actual = (((event.key.keysym.unicode&0xff)!=0) && ((event.key.keysym.unicode&0xff00)==0)); kbuf[0] = event.key.keysym.unicode; #endif if( ( actual ) && ( kbuf[0] > 31 ) && ( kbuf[0] < 128 ) ) { if( strlen( (*ttbx)->content ) >= (*ttbx)->maxlen ) break; j = strlen( (*ttbx)->content ); for( i=j+1; i>(*ttbx)->cpos; i-- ) (*ttbx)->content[i] = (*ttbx)->content[i-1]; (*ttbx)->content[(*ttbx)->cpos] = kbuf[0]; (*ttbx)->cpos++; gui_render_tbox( bm, *ttbx ); // } else { // if( msg->Code < 128 ) // printf( "%d (%d)\n", msg->Code, msg->Code+128 ); } break; } } int32 gui_whichpop( int16 x, int16 y ) { int i; for( i=0; i= pp[i].x ) && ( x < pp[i].x+20 ) && ( y >= pp[i].y ) && ( y < pp[i].y+18 ) ) return i; } return -1; } BOOL gui_checkpop_down( int16 x, int16 y ) { whichpop = gui_whichpop( x, y ); if( whichpop == -1 ) return FALSE; gui_render_popup( whichpop, TRUE ); #ifdef __SDL_WRAPPER__ bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); #endif return TRUE; } BOOL gui_checkpop_up( int16 x, int16 y ) { uint32 ok; if( whichpop != -1 ) { if( gui_whichpop( x, y ) == whichpop ) { switch( whichpop ) { case PP_SONGDIR: #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( dir_req, ASLFR_Window, mainwin, ASLFR_SleepWindow, TRUE, ASLFR_InitialDrawer, songdir, ASLFR_TitleText, "Select default song directory", TAG_DONE ); if( !ok ) break; strncpy( songdir, dir_req->fr_Drawer, 512 ); #else if (directoryrequester("Select default song directory", songdir)) strncpy(remsongdir, songdir, 512); #endif gui_render_tbox( &prefbm, &ptb[PTB_SONGDIR] ); break; case PP_INSTDIR: #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( dir_req, ASLFR_Window, mainwin, ASLFR_SleepWindow, TRUE, ASLFR_InitialDrawer, instdir, ASLFR_TitleText, "Select default instrument directory", TAG_DONE ); if( !ok ) break; strncpy( instdir, dir_req->fr_Drawer, 512 ); #else if (directoryrequester("Select default instrument directory", instdir)) strncpy(reminstdir, instdir, 512); #endif gui_render_tbox( &prefbm, &ptb[PTB_INSTDIR] ); break; case PP_SKINDIR: #ifndef __SDL_WRAPPER__ ok = IAsl->AslRequestTags( dir_req, ASLFR_Window, mainwin, ASLFR_SleepWindow, TRUE, ASLFR_InitialDrawer, skindir, ASLFR_TitleText, "Select skin directory", TAG_DONE ); if( !ok ) break; strncpy( skindir, dir_req->fr_Drawer, 512 ); #else #ifdef __APPLE__ osxGetResourcesPath(skindir, "Skins"); directoryrequester("Select skin directory", skindir); // Convert absolute path to relative path for bundle resources char *resPath = osxGetResourcesPath(NULL, ""); if(strncmp(resPath, skindir, strlen(resPath)) == 0) strcpy(skindir, skindir + strlen(resPath) + 1); #else directoryrequester("Select skin directory", skindir); #endif #endif gui_render_tbox( &prefbm, &ptb[PTB_SKINDIR] ); break; } } gui_render_popup( whichpop, FALSE ); whichpop = -1; } #ifdef __SDL_WRAPPER__ bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); #endif return gui_whichpop( x, y ) != -1; } int32 gui_whichpc( int16 x, int16 y ) { int i; for( i=0; i= pcyc[i].x ) && ( x < pcyc[i].x+160 ) && ( y >= pcyc[i].y ) && ( y < pcyc[i].y+24 ) ) return i; } return -1; } BOOL gui_checkpc_down( int16 x, int16 y ) { whichcyc = gui_whichpc( x, y ); if( whichcyc == -1 ) return FALSE; gui_render_pcycle( whichcyc, TRUE ); #ifdef __SDL_WRAPPER__ bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); #endif return TRUE; } BOOL gui_checkpc_up( int16 x, int16 y ) { if( whichcyc != -1 ) { if( gui_whichpc( x, y ) == whichcyc ) { pcyc[whichcyc].copt = (pcyc[whichcyc].copt+1)%pcyc[whichcyc].numopts; switch( whichcyc ) { case PC_WMODE: pref_fullscr = (pcyc[PC_WMODE].copt == 1); break; case PC_DEFSTEREO: pref_defstereo = pcyc[PC_DEFSTEREO].copt; break; case PC_BLANKZERO: pref_blankzeros = (pcyc[PC_BLANKZERO].copt == 1); if( curtune ) gui_render_tracked( TRUE ); break; case PC_MAXUNDOBUF: pref_maxundobuf = pcyc[PC_MAXUNDOBUF].copt; break; } } gui_render_pcycle( whichcyc, FALSE ); #ifdef __SDL_WRAPPER__ bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); #endif } return gui_whichpc( x, y ) != -1; } int32 gui_findlastpos( struct ahx_tune *at ) { int32 maxpos, i; for( maxpos=999; maxpos>0; maxpos-- ) { for( i=0; iat_Positions[maxpos].pos_Track[i] != 0 ) || ( at->at_Positions[maxpos].pos_Transpose[i] != 0 ) ) return maxpos; } return 0; } void gui_render_everything( void ) { // Do the logo put_bitmap( BM_LOGO, 0, 0, 256, 96 ); put_bitmap( BM_DEPTH, 776, 0, 24, 24 ); put_bitmap( BM_BLANK, 776, 24, 24, 72 ); gui_render_main_buttonbank(); gui_render_tunepanel( TRUE ); gui_render_wavemeter(); #ifdef __SDL_WRAPPER__ if (prefwin_open) gui_render_prefs(); #endif } BOOL gui_restart( void ) { #ifndef __SDL_WRAPPER__ int32 i; BOOL oh_crap = FALSE; // Shutdown just the things we have to shut in order to reopen // with new settings gui_close_prefs(); about_close(); IIntuition->CloseWindow( mainwin ); mainwin = NULL; gui_sigs &= ~mainwin_sig; mainwin_sig = 0; if( scr ) IIntuition->CloseScreen( scr ); scr = NULL; for( i=0; iFreeBitMap( bitmaps[i].bm ); bitmaps[i].bm = NULL; } for( i=0; iFreeBitMap( tbx[i].bm.bm ); tbx[i].bm.bm = NULL; } IGraphics->CloseFont( prpfont ); prpfont = NULL; IGraphics->CloseFont( fixfont ); fixfont = NULL; IGraphics->CloseFont( sfxfont ); sfxfont = NULL; // Set some defaults strcpy( fixfontname, "Bitstream Vera Sans Mono.font" ); strcpy( sfxfontname, "Bitstream Vera Sans Mono.font" ); strcpy( prpfontname, "Bitstream Vera Sans.font" ); strcpy( skinext, ".png" ); numzones = 0; numpzones = 0; if (!oh_crap) { if (!gui_open()) { oh_crap = TRUE; } } if( oh_crap ) { BPTR cdir, lock; int32 i; struct ahx_tune *at; char rname[64]; gui_req( REQIMAGE_ERROR, "Oh crap!", "There was an error re-opening the GUI,\nand Hively has to quit. All open songs\nwill be saved to 'Songs/Rescue'", "OK" ); lock = IDOS->Lock( "Songs/Rescue", ACCESS_READ ); if( !lock ) { lock = IDOS->CreateDirTree( "Songs/Rescue" ); if( !lock ) { lock = IDOS->Lock( "", ACCESS_READ ); if( !lock ) { // Oh fuck. return FALSE; } } } cdir = IDOS->CurrentDir( lock ); IExec->ObtainSemaphore( rp_list_ss ); i=1; at = (struct ahx_tune *)IExec->GetHead(rp_tunelist); while( at ) { sprintf( rname, "HVL.rescuetab%ld", i++ ); rp_save_hvl( rname, at ); at = (struct ahx_tune *)IExec->GetSucc(&at->at_ln); } IDOS->CurrentDir( cdir ); IDOS->UnLock( lock ); IExec->ReleaseSemaphore( rp_list_ss ); return FALSE; } #else // Just reload the skin... SDL_FreeSurface(bitmaps[BM_LOGO].srf ); bitmaps[BM_LOGO].srf = NULL; SDL_FreeSurface(bitmaps[BM_TAB_AREA].srf ); bitmaps[BM_TAB_AREA].srf = NULL; SDL_FreeSurface(bitmaps[BM_TAB_LEFT].srf ); bitmaps[BM_TAB_LEFT].srf = NULL; SDL_FreeSurface(bitmaps[BM_TAB_MID] .srf) ; bitmaps[BM_TAB_MID] .srf = NULL; SDL_FreeSurface(bitmaps[BM_TAB_RIGHT].srf ); bitmaps[BM_TAB_RIGHT].srf = NULL; SDL_FreeSurface(bitmaps[BM_ITAB_LEFT].srf ); bitmaps[BM_ITAB_LEFT].srf = NULL; SDL_FreeSurface(bitmaps[BM_ITAB_MID].srf ); bitmaps[BM_ITAB_MID].srf = NULL; SDL_FreeSurface(bitmaps[BM_ITAB_RIGHT].srf ); bitmaps[BM_ITAB_RIGHT].srf = NULL; SDL_FreeSurface(bitmaps[BM_BUTBANKR].srf ); bitmaps[BM_BUTBANKR].srf = NULL; SDL_FreeSurface(bitmaps[BM_BUTBANKP].srf ); bitmaps[BM_BUTBANKP].srf = NULL; SDL_FreeSurface(bitmaps[BM_BG_TRACKER].srf ); bitmaps[BM_BG_TRACKER].srf = NULL; SDL_FreeSurface(bitmaps[BM_BG_INSED].srf ); bitmaps[BM_BG_INSED].srf = NULL; SDL_FreeSurface(bitmaps[BM_PLUSMINUS].srf ); bitmaps[BM_PLUSMINUS].srf = NULL; SDL_FreeSurface(bitmaps[BM_VUMETER] .srf) ; bitmaps[BM_VUMETER] .srf = NULL; SDL_FreeSurface(bitmaps[BM_DEPTH].srf ) ; bitmaps[BM_DEPTH].srf = NULL; SDL_FreeSurface(bitmaps[BM_BLANK].srf ) ; bitmaps[BM_BLANK].srf = NULL; SDL_FreeSurface(bitmaps[BM_WAVEMETERS].srf ); bitmaps[BM_WAVEMETERS].srf = NULL; SDL_FreeSurface(bitmaps[BM_PRF_BG].srf ); bitmaps[BM_PRF_BG].srf = NULL; SDL_FreeSurface(bitmaps[BM_PRF_CYCLE].srf ); bitmaps[BM_PRF_CYCLE].srf = NULL; SDL_FreeSurface(bitmaps[BM_TRKBANKR].srf ); bitmaps[BM_TRKBANKR].srf = NULL; SDL_FreeSurface(bitmaps[BM_TRKBANKP].srf ); bitmaps[BM_TRKBANKP].srf = NULL; SDL_FreeSurface(bitmaps[BM_CHANMUTE].srf ); bitmaps[BM_CHANMUTE].srf = NULL; SDL_FreeSurface(bitmaps[BM_DIRPOPUP].srf ); bitmaps[BM_DIRPOPUP].srf = NULL; if (bitmaps[BM_ITAB_TEXT].srf) { SDL_FreeSurface(bitmaps[BM_ITAB_TEXT].srf ); bitmaps[BM_ITAB_TEXT].srf = NULL; } if (bitmaps[BM_TAB_TEXT].srf) { SDL_FreeSurface(bitmaps[BM_TAB_TEXT].srf ); bitmaps[BM_TAB_TEXT].srf = NULL; } if( !gui_open_skin_images() ) { printf( "Error loading skin. Reverting to SIDMonster-Light...\n" ); strcpy( skindir, "Skins/SIDMonster-Light" ); if( !gui_open_skin_images() ) return FALSE; } gui_render_everything(); #endif return TRUE; } #ifdef __SDL_WRAPPER__ struct IntuiMessage *translate_sdl_event(void) { static uint32 mkqual = 0; static struct IntuiMessage fakemsg; fakemsg.IAddress = NULL; switch (event.type) { case SDL_MOUSEMOTION: fakemsg.Class = IDCMP_MOUSEMOVE; fakemsg.Qualifier = mkqual; fakemsg.Code = 0; fakemsg.MouseX = event.motion.x; fakemsg.MouseY = event.motion.y; break; case SDL_MOUSEBUTTONDOWN: { static struct IntuiWheelData iwd; if (event.button.button == SDL_BUTTON_WHEELUP) { fakemsg.Class = IDCMP_EXTENDEDMOUSE; fakemsg.Code = IMSGCODE_INTUIWHEELDATA; fakemsg.IAddress = &iwd; fakemsg.MouseX = event.motion.x; fakemsg.MouseY = event.motion.y; iwd.WheelY = -1; break; } if (event.button.button == SDL_BUTTON_WHEELDOWN) { fakemsg.Class = IDCMP_EXTENDEDMOUSE; fakemsg.Code = IMSGCODE_INTUIWHEELDATA; fakemsg.IAddress = &iwd; fakemsg.MouseX = event.motion.x; fakemsg.MouseY = event.motion.y; iwd.WheelY = 1; break; } if (event.button.button == SDL_BUTTON_RIGHT) mkqual |= IEQUALIFIER_RBUTTON; fakemsg.Class = IDCMP_MOUSEBUTTONS; fakemsg.Code = event.button.button; fakemsg.Qualifier = mkqual; fakemsg.MouseX = event.motion.x; fakemsg.MouseY = event.motion.y; break; } case SDL_MOUSEBUTTONUP: if (event.button.button == SDL_BUTTON_RIGHT) mkqual &= ~IEQUALIFIER_RBUTTON; fakemsg.Class = IDCMP_MOUSEBUTTONS; fakemsg.Code = event.button.button | 0x100; fakemsg.Qualifier = mkqual; fakemsg.MouseX = event.motion.x; fakemsg.MouseY = event.motion.y; break; case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_LSHIFT: mkqual |= IEQUALIFIER_LSHIFT; break; case SDLK_RSHIFT: mkqual |= IEQUALIFIER_RSHIFT; break; case SDLK_LCTRL: mkqual |= IEQUALIFIER_CONTROL; break; case SDLK_RCTRL: if (!pref_rctrlplaypos) { mkqual |= IEQUALIFIER_CONTROL; } break; case SDLK_LALT: mkqual |= IEQUALIFIER_LALT; break; case SDLK_LSUPER: mkqual |= IEQUALIFIER_LCOMMAND; break; #ifdef __WIN32__ /* Workaround: On Windows, SDLK_RALT is always prefixed by SDLK_RCTRL */ /* but we want RALT to be treated separately, and since RCTRL+RALT */ /* isn't a key combo we care about, we make pressing RALT cancel out */ /* the dummy RCTRL press. */ case SDLK_RALT: mkqual &= ~IEQUALIFIER_CONTROL; break; #endif } fakemsg.Class = IDCMP_RAWKEY; fakemsg.Code = sdl_keysym_to_amiga_rawkey(event.key.keysym.sym); fakemsg.Qualifier = mkqual; break; case SDL_KEYUP: switch (event.key.keysym.sym) { case SDLK_LSHIFT: mkqual &= ~IEQUALIFIER_LSHIFT; break; case SDLK_RSHIFT: mkqual &= ~IEQUALIFIER_RSHIFT; break; case SDLK_LCTRL: mkqual &= ~IEQUALIFIER_CONTROL; break; case SDLK_RCTRL: if (!pref_rctrlplaypos) { mkqual &= ~IEQUALIFIER_CONTROL; } break; case SDLK_LALT: mkqual &= ~IEQUALIFIER_LALT; break; case SDLK_LSUPER: mkqual &= ~IEQUALIFIER_LCOMMAND; break; } fakemsg.Class = IDCMP_RAWKEY; fakemsg.Code = sdl_keysym_to_amiga_rawkey(event.key.keysym.sym) | 0x80; fakemsg.Qualifier = mkqual; break; } return &fakemsg; } #endif void gui_handler( uint32 gotsigs ) { struct IntuiMessage *msg; const int8 hexkeys[] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 32, 53, 51, 34, 18, 35 }; const int8 inskeys[] = { 15, 29, 30, 31, 45, 46, 47, 61, 62, 63 }; const int8 pianokeys[] = { 49, 33, 50, 34, 51, 52, 36, 53, 37, 54, 38, 55, 16, 2, 17, 3, 18, 19, 5, 20, 6, 21, 7, 22, 23, 9, 24, 10, 25 }; const int8 extrakeys[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 56, 40, 57, 41, 58, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; struct ahx_step *stp, *pstp; #ifndef __SDL_WRAPPER__ if( gotsigs & gui_tick_sig ) #else if( event.type == SDL_USEREVENT ) #endif { if( curtune == rp_curtune ) { gui_render_tunepanel( FALSE ); gui_render_vumeters(); } if( (wm_count++)&1 ) gui_render_wavemeter(); #ifdef __SDL_WRAPPER__ if (prefwin_open) bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); return; #endif } #ifndef __SDL_WRAPPER__ if( gotsigs & prefwin_sig ) #else if( prefwin_open ) #endif { BOOL wantclose; int16 x, y; wantclose = FALSE; #ifndef __SDL_WRAPPER__ while( ( msg = (struct IntuiMessage *)IExec->GetMsg( prefwin->UserPort ) ) ) #else msg = translate_sdl_event(); #endif { switch( msg->Class ) { case IDCMP_RAWKEY: // In a textbox? if( ptbx != NULL ) { gui_textbox_keypress( &prefbm, &ptbx, msg ); #ifdef __SDL_WRAPPER__ bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); #endif break; } switch( msg->Code ) { case 197: case 13: // ESC is intermittently broken on my laptop :-/ wantclose = TRUE; break; } break; case IDCMP_MOUSEBUTTONS: #ifndef __SDL_WRAPPER__ x = msg->MouseX-pw_bl; y = msg->MouseY-pw_bt; #else x = msg->MouseX-200; y = msg->MouseY-150; #endif switch( msg->Code ) { case SELECTDOWN: if( gui_check_ptbox_press( x, y ) ) return; if( ptbx ) { ptbx->flags &= ~TBF_ACTIVE; gui_render_tbox( &prefbm, ptbx ); gui_ptbox_finish_editing(); #ifdef __SDL_WRAPPER__ bm_to_bm(&prefbm, -4, -4, &mainbm, 196, 146, 408, 308); #endif } if( gui_checkpc_down( x, y ) ) break; if( gui_checkpop_down( x, y ) ) break; break; case SELECTUP: if( ptbx != NULL ) break; if( gui_checkpc_up( x, y ) ) break; if( gui_checkpop_up( x, y ) ) break; break; } break; #ifndef __SDL_WRAPPER__ case IDCMP_CLOSEWINDOW: wantclose = TRUE; #endif break; } #ifndef __SDL_WRAPPER__ IExec->ReplyMsg( (struct Message *)msg ); #endif } if( wantclose ) gui_close_prefs(); #ifdef __SDL_WRAPPER__ return; #endif } #ifndef __SDL_WRAPPER__ if( gotsigs & mainwin_sig ) #endif { int32 panel, maxpos; struct ahx_plsentry *cple; struct ahx_instrument *cins; BOOL cutting; int16 next; #ifndef __SDL_WRAPPER__ while( ( msg = (struct IntuiMessage *)IExec->GetMsg( mainwin->UserPort ) ) ) #else msg = translate_sdl_event(); #endif { BOOL donkey; int32 i, j, k, l, chan, track, tran; struct IntuiWheelData *iwd; switch( msg->Class ) { case IDCMP_EXTENDEDMOUSE: switch( msg->Code ) { case IMSGCODE_INTUIWHEELDATA: iwd = (struct IntuiWheelData *)msg->IAddress; gui_wheelybin( msg->MouseX, msg->MouseY, iwd->WheelY ); break; } break; case IDCMP_RAWKEY: qual = msg->Qualifier; donkey = FALSE; // In a textbox? if( etbx != NULL ) { gui_textbox_keypress( &mainbm, &etbx, msg ); break; } // Keys for any mode switch( qual&(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT|IEQUALIFIER_CONTROL|IEQUALIFIER_LALT|IEQUALIFIER_LCOMMAND) ) { case 0: // No qualifiers switch( msg->Code ) { case 13: // \ = drum pad mode if( curtune->at_drumpadmode ) curtune->at_drumpadmode = FALSE; else curtune->at_drumpadmode = TRUE; gui_set_various_things( curtune ); donkey = TRUE; break; case 48: // Blank next to Z (shut up) rp_stop(); gui_render_wavemeter(); gui_render_tracked( TRUE ); // Kill the VUMeters curtune->at_doing = D_IDLE; donkey = TRUE; break; case 11: // Minus posedadvance = !posedadvance; donkey = TRUE; break; case 12: // Equals if( !curtune ) break; if( curtune->at_doing == D_PLAYING ) break; rp_play_row( curtune ); donkey = TRUE; break; case 80: // F1 case 81: // F2 case 82: // F3 case 83: // F4 case 84: // F5 donkey = TRUE; basekey = (msg->Code-80)*12; break; case 197: donkey = TRUE; if( !curtune ) break; if( curtune->at_curpanel == PN_INSED ) curtune->at_curpanel = PN_TRACKER; else curtune->at_curpanel = PN_INSED; gui_render_tunepanel( TRUE ); break; case 103: donkey = TRUE; curtune->at_doing = D_IDLE; if( rp_play_pos( curtune, FALSE ) ) curtune->at_doing = D_PLAYING; break; case 101: donkey = TRUE; curtune->at_doing = D_IDLE; if( rp_play_song( curtune, curtune->at_curss, FALSE ) ) curtune->at_doing = D_PLAYING; break; case 94: donkey = TRUE; if( curtune == NULL ) break; curtune->at_baseins += 10; if( curtune->at_baseins > 60 ) curtune->at_baseins = 60; break; case 74: donkey = TRUE; if( curtune == NULL ) break; curtune->at_baseins -= 10; if( curtune->at_baseins < 0 ) curtune->at_baseins = 0; break; } break; case IEQUALIFIER_LSHIFT: // Shifty case IEQUALIFIER_RSHIFT: // Shifty case IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT: // Shifty shift chan = track = -1; if( curtune ) { chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; } cutting = FALSE; switch( msg->Code ) { case 79: // Shift+Left (pos up) donkey = TRUE; if( !curtune ) break; if( curtune->at_PosNr == 0 ) break; curtune->at_PosNr--; gui_render_posed( TRUE ); gui_render_tracked( TRUE ); break; case 78: // Shift+Right (pos down) donkey = TRUE; if( !curtune ) break; if( curtune->at_PosNr == 999 ) break; curtune->at_PosNr++; gui_render_posed( TRUE ); gui_render_tracked( TRUE ); break; case 82: // Shift+F3 (cut track to buffer) cutting = TRUE; case 83: // Shift+F4 (copy track to buffer) donkey = TRUE; if( track == -1 ) break; if( curtune->at_curpanel != PN_TRACKER ) break; gui_copyregion( track, 0, curtune->at_TrackLength-1, CBF_NOTES|CBF_CMD1|CBF_CMD2, cutting ); gui_render_tracked( TRUE ); break; case 84: // Shift+F5 (paste) donkey = TRUE; if( track == -1 ) break; if( curtune->at_curpanel != PN_TRACKER ) break; setbefore_track( curtune, track ); gui_paste( curtune, track, CBF_NOTES|CBF_CMD1|CBF_CMD2 ); setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case 85: // Shift+F6 (remember pos 0) case 86: // Shift+F7 (remember pos 1) case 87: // Shift+F8 (remember pos 2) case 88: // Shift+F9 (remember pos 3) case 89: // Shift+F10 (remember pos 4) donkey = TRUE; if( curtune == NULL ) break; curtune->at_rempos[msg->Code-85] = curtune->at_NoteNr; gui_render_tracked( TRUE ); break; case 65: // Shift+Backspace (delete row above) donkey = TRUE; if( curtune == NULL ) break; switch( curtune->at_curpanel ) { case PN_INSED: if( curtune->at_idoing != D_EDITING ) break; cins = &curtune->at_Instruments[curtune->at_curins]; cple = &cins->ins_PList.pls_Entries[255]; setbefore_plist( curtune, &cins->ins_PList.pls_Entries[0] ); if( cins->ins_pcury == 0 ) break; cins->ins_pcury--; for( i=cins->ins_pcury; i<254; i++ ) cins->ins_PList.pls_Entries[i] = cins->ins_PList.pls_Entries[i+1]; cple->ple_Note = 0; cple->ple_Fixed = 0; cple->ple_FX[0] = 0; cple->ple_FXParam[0] = 0; cple->ple_FX[1] = 0; cple->ple_FXParam[1] = 0; setafter_plist( curtune, &cins->ins_PList.pls_Entries[0] ); gui_render_perf( curtune, cins, TRUE ); break; case PN_TRACKER: switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing != D_EDITING ) break; chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; if( curtune->at_NoteNr == 0 ) break; setbefore_track( curtune, track ); curtune->at_NoteNr--; for( i=curtune->at_NoteNr; i<(curtune->at_TrackLength-1); i++ ) curtune->at_Tracks[track][i] = curtune->at_Tracks[track][i+1]; curtune->at_Tracks[track][i].stp_Note = 0; curtune->at_Tracks[track][i].stp_Instrument = 0; curtune->at_Tracks[track][i].stp_FX = 0; curtune->at_Tracks[track][i].stp_FXParam = 0; curtune->at_Tracks[track][i].stp_FXb = 0; curtune->at_Tracks[track][i].stp_FXbParam = 0; setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case E_POS: if( curtune->at_PosNr == 0 ) break; curtune->at_PosNr--; maxpos = gui_findlastpos( curtune )+1; if( maxpos <= curtune->at_PosNr ) maxpos = curtune->at_PosNr+1; setbefore_posregion( curtune, 0, curtune->at_PosNr, MAX_CHANNELS, maxpos-curtune->at_PosNr ); for( i=curtune->at_PosNr; i<999; i++ ) curtune->at_Positions[i] = curtune->at_Positions[i+1]; for( j=0; jat_Positions[i].pos_Track[j] = 0; curtune->at_Positions[i].pos_Transpose[j] = 0; } setafter_posregion( curtune, 0, curtune->at_PosNr, MAX_CHANNELS, maxpos-curtune->at_PosNr ); gui_render_posed( TRUE ); break; } break; } break; case 68: // Shift+Enter (insert row) donkey = TRUE; if( curtune == NULL ) break; switch( curtune->at_curpanel ) { case PN_INSED: if( curtune->at_idoing != D_EDITING ) break; cins = &curtune->at_Instruments[curtune->at_curins]; cple = &cins->ins_PList.pls_Entries[cins->ins_pcury]; setbefore_plist( curtune, &cins->ins_PList.pls_Entries[0] ); for( i=255; i>cins->ins_pcury; i-- ) cins->ins_PList.pls_Entries[i] = cins->ins_PList.pls_Entries[i-1]; cple->ple_Note = 0; cple->ple_Fixed = 0; cple->ple_FX[0] = 0; cple->ple_FXParam[0] = 0; cple->ple_FX[1] = 0; cple->ple_FXParam[1] = 0; setafter_plist( curtune, &cins->ins_PList.pls_Entries[0] ); gui_render_perf( curtune, cins, TRUE ); break; case PN_TRACKER: switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing != D_EDITING ) break; chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; setbefore_track( curtune, track ); for( i=(curtune->at_TrackLength-1); i>curtune->at_NoteNr; i-- ) curtune->at_Tracks[track][i] = curtune->at_Tracks[track][i-1]; curtune->at_Tracks[track][i].stp_Note = 0; curtune->at_Tracks[track][i].stp_Instrument = 0; curtune->at_Tracks[track][i].stp_FX = 0; curtune->at_Tracks[track][i].stp_FXParam = 0; curtune->at_Tracks[track][i].stp_FXb = 0; curtune->at_Tracks[track][i].stp_FXbParam = 0; setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case E_POS: maxpos = gui_findlastpos( curtune )+1; if( maxpos <= curtune->at_PosNr ) maxpos = curtune->at_PosNr+1; setbefore_posregion( curtune, 0, curtune->at_PosNr, MAX_CHANNELS, maxpos-curtune->at_PosNr ); for( i=999; i>curtune->at_PosNr; i-- ) curtune->at_Positions[i] = curtune->at_Positions[i-1]; for( j=0; jat_Positions[i].pos_Track[j] = 0; curtune->at_Positions[i].pos_Transpose[j] = 0; } setafter_posregion( curtune, 0, curtune->at_PosNr, MAX_CHANNELS, maxpos-curtune->at_PosNr ); gui_render_posed( TRUE ); break; } break; } break; } break; case IEQUALIFIER_CONTROL: chan = track = -1; if( curtune ) { chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; } cutting = FALSE; switch( msg->Code ) { case 1: // Ctrl+n (set notejump to n) case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: donkey = TRUE; if( !curtune ) break; if( curtune->at_curpanel == PN_INSED ) curtune->at_inotejump = msg->Code%10; else curtune->at_notejump = msg->Code%10; break; case 32: // Ctrl+A (toggle voice) if( !curtune ) break; if( curtune->at_editing == E_POS ) chan = (curtune->at_posed_curs/5)+curtune->at_curlch; else chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; curtune->at_Voices[chan].vc_SetTrackOn ^= 1; gui_render_tracked( TRUE ); break; case 16: // Ctrl+Q (all tracks on) if( !curtune ) break; for( i=0; iat_Voices[i].vc_SetTrackOn = 1; gui_render_tracked( TRUE ); break; case 60: // Ctrl+Keypad . (clear instrument) // Have to use Ctrl because i don't like how easy this was // to do by mistake in PT. if( !curtune ) break; cins = &curtune->at_Instruments[curtune->at_curins]; rp_clear_instrument( cins ); case 79: // Ctrl+Left (instrument up) donkey = TRUE; if( !curtune ) break; if( curtune->at_curins == 0 ) break; curtune->at_curins--; gui_render_inslist( FALSE ); gui_set_various_things( curtune ); break; case 78: // Ctrl+Right (instrument down) donkey = TRUE; if( !curtune ) break; if( curtune->at_curins == 63 ) break; curtune->at_curins++; gui_render_inslist( FALSE ); gui_set_various_things( curtune ); break; case 82: // Ctrl+F3 (cut commands to buffer) cutting = TRUE; case 83: // Ctrl+F4 (copy commands to buffer) donkey = TRUE; if( track == -1 ) break; if( curtune->at_curpanel != PN_TRACKER ) break; gui_copyregion( track, 0, curtune->at_TrackLength-1, CBF_CMD1|CBF_CMD2, cutting ); gui_render_tracked( TRUE ); break; case 84: // Ctrl+F5 (paste commands) donkey = TRUE; if( track == -1 ) break; if( curtune->at_curpanel != PN_TRACKER ) break; setbefore_track( curtune, track ); gui_paste( curtune, track, CBF_CMD1|CBF_CMD2 ); setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case 65: // Ctrl+Backspace (delete row above (commands only)) donkey = TRUE; if( curtune == NULL ) break; switch( curtune->at_curpanel ) { case PN_INSED: if( curtune->at_idoing != D_EDITING ) break; cins = &curtune->at_Instruments[curtune->at_curins]; cple = &cins->ins_PList.pls_Entries[255]; if( cins->ins_pcury == 0 ) break; setbefore_plist( curtune, &cins->ins_PList.pls_Entries[0] ); cins->ins_pcury--; for( i=cins->ins_pcury; i<254; i++ ) { cins->ins_PList.pls_Entries[i].ple_FX[0] = cins->ins_PList.pls_Entries[i+1].ple_FX[0]; cins->ins_PList.pls_Entries[i].ple_FXParam[0] = cins->ins_PList.pls_Entries[i+1].ple_FXParam[0]; cins->ins_PList.pls_Entries[i].ple_FX[1] = cins->ins_PList.pls_Entries[i+1].ple_FX[1]; cins->ins_PList.pls_Entries[i].ple_FXParam[1] = cins->ins_PList.pls_Entries[i+1].ple_FXParam[1]; } cple->ple_FX[0] = 0; cple->ple_FXParam[0] = 0; cple->ple_FX[1] = 0; cple->ple_FXParam[1] = 0; setafter_plist( curtune, &cins->ins_PList.pls_Entries[0] ); gui_render_perf( curtune, cins, TRUE ); break; case PN_TRACKER: switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing != D_EDITING ) break; chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; if( curtune->at_NoteNr == 0 ) break; setbefore_track( curtune, track ); curtune->at_NoteNr--; for( i=curtune->at_NoteNr; i<(curtune->at_TrackLength-1); i++ ) { curtune->at_Tracks[track][i].stp_FX = curtune->at_Tracks[track][i+1].stp_FX; curtune->at_Tracks[track][i].stp_FXParam = curtune->at_Tracks[track][i+1].stp_FXParam; curtune->at_Tracks[track][i].stp_FXb = curtune->at_Tracks[track][i+1].stp_FXb; curtune->at_Tracks[track][i].stp_FXbParam = curtune->at_Tracks[track][i+1].stp_FXbParam; } curtune->at_Tracks[track][i].stp_FX = 0; curtune->at_Tracks[track][i].stp_FXParam = 0; curtune->at_Tracks[track][i].stp_FXb = 0; curtune->at_Tracks[track][i].stp_FXbParam = 0; setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; } break; } break; case 68: // Ctrl+Enter (insert row (commands only)) donkey = TRUE; if( curtune == NULL ) break; switch( curtune->at_curpanel ) { case PN_INSED: if( curtune->at_idoing != D_EDITING ) break; cins = &curtune->at_Instruments[curtune->at_curins]; cple = &cins->ins_PList.pls_Entries[cins->ins_pcury]; setbefore_plist( curtune, &cins->ins_PList.pls_Entries[0] ); for( i=255; i>cins->ins_pcury; i-- ) { cins->ins_PList.pls_Entries[i].ple_FX[0] = cins->ins_PList.pls_Entries[i-1].ple_FX[0]; cins->ins_PList.pls_Entries[i].ple_FXParam[0] = cins->ins_PList.pls_Entries[i-1].ple_FXParam[0]; cins->ins_PList.pls_Entries[i].ple_FX[1] = cins->ins_PList.pls_Entries[i-1].ple_FX[1]; cins->ins_PList.pls_Entries[i].ple_FXParam[1] = cins->ins_PList.pls_Entries[i-1].ple_FXParam[1]; } cple->ple_FX[0] = 0; cple->ple_FXParam[0] = 0; cple->ple_FX[1] = 0; cple->ple_FXParam[1] = 0; setafter_plist( curtune, &cins->ins_PList.pls_Entries[0] ); gui_render_perf( curtune, cins, TRUE ); break; case PN_TRACKER: switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing != D_EDITING ) break; chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; setbefore_track( curtune, track ); for( i=(curtune->at_TrackLength-1); i>curtune->at_NoteNr; i-- ) { curtune->at_Tracks[track][i].stp_FX = curtune->at_Tracks[track][i-1].stp_FX; curtune->at_Tracks[track][i].stp_FXParam = curtune->at_Tracks[track][i-1].stp_FXParam; curtune->at_Tracks[track][i].stp_FXb = curtune->at_Tracks[track][i-1].stp_FXb; curtune->at_Tracks[track][i].stp_FXbParam = curtune->at_Tracks[track][i-1].stp_FXbParam; } curtune->at_Tracks[track][i].stp_FX = 0; curtune->at_Tracks[track][i].stp_FXParam = 0; curtune->at_Tracks[track][i].stp_FXb = 0; curtune->at_Tracks[track][i].stp_FXbParam = 0; setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; } break; } break; } break; case IEQUALIFIER_LALT: switch( msg->Code ) { case 16: // Alt+Q (mute all channels) donkey = TRUE; if( !curtune ) break; for( i=0; iat_Voices[i].vc_SetTrackOn = 0; gui_render_tracked( TRUE ); break; case 32: // Alt+A (mute all but the current channel) donkey = TRUE; if( !curtune ) break; if( curtune->at_editing == E_POS ) chan = curtune->at_posed_curs/5; else chan = curtune->at_tracked_curs/9; for( i=0; iat_Voices[i].vc_SetTrackOn = 1; else curtune->at_Voices[i].vc_SetTrackOn = 0; } gui_render_tracked( TRUE ); break; case 79: // Alt+Left (decrement currently selected track number) donkey = TRUE; if( !curtune ) break; chan = -1; switch( curtune->at_editing ) { case E_TRACK: chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; break; case E_POS: chan = curtune->at_posed_curs/5 + curtune->at_curlch; break; } if( chan != -1 ) { track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]-1; if (track < 0) track = 0; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, track ); gui_render_posed( TRUE ); gui_render_tracked( TRUE ); } break; case 78: // Alt+Right (increment currently selected track number) donkey = TRUE; if( !curtune ) break; chan = -1; switch( curtune->at_editing ) { case E_TRACK: chan = (curtune->at_tracked_curs/9)+curtune->at_curlch; break; case E_POS: chan = curtune->at_posed_curs/5 + curtune->at_curlch; break; } if( chan != -1 ) { track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]+1; if (track > 255) track = 255; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, track ); gui_render_posed( TRUE ); gui_render_tracked( TRUE ); } break; case 85: // Alt+F6 (play from pos 0) case 86: // Alt+F7 (play from pos 1) case 87: // Alt+F8 (play from pos 2) case 88: // Alt+F9 (play from pos 3) case 89: // Alt+F10 (play from pos 4) donkey = TRUE; if( curtune == NULL ) break; rp_stop(); curtune->at_NoteNr = curtune->at_rempos[msg->Code-85]; if( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr = curtune->at_TrackLength-1; if( curtune->at_curpanel == PN_TRACKER ) gui_render_tracked( TRUE ); curtune->at_doing = D_IDLE; if( rp_play_pos( curtune, TRUE ) ) curtune->at_doing = D_PLAYING; break; } break; case IEQUALIFIER_LCOMMAND: break; } if( curtune != NULL ) { for( i=0; i<10; i++ ) { if( msg->Code == inskeys[i] ) { donkey = TRUE; curtune->at_curins = curtune->at_baseins+i; if( curtune->at_curins > 63 ) curtune->at_curins = 63; gui_render_inslist( FALSE ); gui_set_various_things( curtune ); if( curtune->at_drumpadmode ) { chan = curtune->at_tracked_curs/9+curtune->at_curlch; if( curtune->at_curins > 0 ) rp_play_note( curtune, curtune->at_curins, curtune->at_drumpadnote, chan ); if( curtune->at_doing == D_EDITING ) { stp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr]; modify_stp_w( curtune, stp, UNT_STP_NOTEANDINS, (curtune->at_drumpadnote<<8)|curtune->at_curins ); curtune->at_NoteNr += curtune->at_notejump; curtune->at_modified = TRUE; while( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr -= curtune->at_TrackLength; gui_render_tracked( TRUE ); } } break; } } } if( donkey ) break; if( curtune == NULL ) panel = PN_TRACKER; else panel = curtune->at_curpanel; switch( panel ) { case PN_TRACKER: switch( msg->Code ) { case 64: donkey = TRUE; rp_stop(); gui_render_tracked( TRUE ); // Kill the VUMeters gui_render_wavemeter(); if( curtune->at_doing == D_EDITING ) curtune->at_doing = D_IDLE; else curtune->at_doing = D_EDITING; gui_render_tracker( TRUE ); break; case 68: donkey = TRUE; if( curtune == NULL ) break; if( curtune->at_editing == E_POS ) curtune->at_editing = E_TRACK; else curtune->at_editing = E_POS; gui_render_tracker( TRUE ); break; case 79: donkey = TRUE; if( curtune == NULL ) break; switch( curtune->at_editing ) { case E_POS: curtune->at_posed_curs--; if( curtune->at_curlch == 0 ) { if( curtune->at_posed_curs < 0 ) curtune->at_posed_curs = curtune->at_Channels*5-1; while( (curtune->at_posed_curs/5) > 5 ) { curtune->at_posed_curs -= 5; curtune->at_curlch++; } } else { while( curtune->at_posed_curs < 0 ) { curtune->at_curlch--; curtune->at_posed_curs += 5; } } gui_render_tracker( TRUE ); break; case E_TRACK: curtune->at_tracked_curs--; if( curtune->at_curlch == 0 ) { if( curtune->at_tracked_curs < 0 ) curtune->at_tracked_curs = curtune->at_Channels*9-1; while( (curtune->at_tracked_curs/9) > 5 ) { curtune->at_tracked_curs -= 9; curtune->at_curlch++; } } else { while( curtune->at_tracked_curs < 0 ) { curtune->at_curlch--; curtune->at_tracked_curs += 9; } } gui_render_tracker( TRUE ); break; } break; case 78: donkey = TRUE; if( curtune == NULL ) break; switch( curtune->at_editing ) { case E_POS: gui_posed_moveright(); gui_render_tracker( TRUE ); break; case E_TRACK: curtune->at_tracked_curs++; if( (curtune->at_tracked_curs/9+curtune->at_curlch) >= curtune->at_Channels ) { curtune->at_tracked_curs = 0; curtune->at_curlch = 0; } while( (curtune->at_tracked_curs/9) > 5 ) { curtune->at_curlch++; curtune->at_tracked_curs -= 9; } gui_render_tracker( TRUE ); break; } break; case 66: donkey = TRUE; if( curtune == NULL ) break; switch( curtune->at_editing ) { case E_POS: i = 0; switch( qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_CONTROL) ) { case IEQUALIFIER_CONTROL: i = (curtune->at_posed_curs%5) > 2 ? 3 : 0; case 0: curtune->at_posed_curs = ((curtune->at_posed_curs+5)/5)*5; if( (curtune->at_posed_curs/5)+curtune->at_curlch >= curtune->at_Channels ) { curtune->at_posed_curs = 0; curtune->at_curlch = 0; } while( (curtune->at_posed_curs/5) > 5 ) { curtune->at_posed_curs -= 5; curtune->at_curlch++; } curtune->at_posed_curs += i; break; case IEQUALIFIER_LSHIFT|IEQUALIFIER_CONTROL: i = (curtune->at_posed_curs%5) > 2 ? 3 : 0; case IEQUALIFIER_LSHIFT: if( curtune->at_posed_curs < 5 ) { if( curtune->at_curlch > 0 ) { curtune->at_curlch--; curtune->at_posed_curs = 0; } else { curtune->at_posed_curs = 5*5; curtune->at_curlch = curtune->at_Channels-6; if( curtune->at_curlch < 0 ) { curtune->at_curlch = 0; curtune->at_posed_curs = (curtune->at_Channels-1)*5; } } } else { curtune->at_posed_curs = ((curtune->at_posed_curs-5)/5)*5; } curtune->at_posed_curs += i; break; } gui_render_tracker( TRUE ); break; case E_TRACK: i = 0; switch( qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_CONTROL) ) { case IEQUALIFIER_CONTROL: j = curtune->at_tracked_curs%9; if(( j > 0 ) && ( j < 3 )) i = 1; if(( j > 2 ) && ( j < 6 )) i = 3; if( j > 5 ) i = 6; case 0: curtune->at_tracked_curs = ((curtune->at_tracked_curs+9)/9)*9; if( (curtune->at_tracked_curs/9)+curtune->at_curlch >= curtune->at_Channels ) { curtune->at_tracked_curs = 0; curtune->at_curlch = 0; } while( (curtune->at_tracked_curs/9) > 5 ) { curtune->at_tracked_curs -= 9; curtune->at_curlch++; } curtune->at_tracked_curs += i; break; case IEQUALIFIER_LSHIFT|IEQUALIFIER_CONTROL: j = curtune->at_tracked_curs%9; if(( j > 0 ) && ( j < 3 )) i = 1; if(( j > 2 ) && ( j < 6 )) i = 3; if( j > 5 ) i = 6; case IEQUALIFIER_LSHIFT: if( curtune->at_tracked_curs < 9 ) { if( curtune->at_curlch > 0 ) { curtune->at_curlch--; curtune->at_tracked_curs = 0; } else { curtune->at_tracked_curs = 5*9; curtune->at_curlch = curtune->at_Channels-6; if( curtune->at_curlch < 0 ) { curtune->at_curlch = 0; curtune->at_tracked_curs = (curtune->at_Channels-1)*9; } } } else { curtune->at_tracked_curs = ((curtune->at_tracked_curs-9)/9)*9; } curtune->at_tracked_curs += i; break; } gui_render_tracker( TRUE ); break; } break; } switch( curtune->at_doing ) { case D_EDITING: case D_IDLE: if( curtune == NULL ) break; for( i=0; i<16; i++ ) if( msg->Code == hexkeys[i] ) break; if( ( i < 16 ) && ( (qual&IEQUALIFIER_CONTROL) == 0 ) ) { switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing == D_IDLE ) break; if( ( curtune->at_tracked_curs%9 ) == 0 ) break; donkey = TRUE; chan = curtune->at_tracked_curs/9 + curtune->at_curlch; stp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr]; switch( curtune->at_tracked_curs%9 ) { case 1: if( i>9 ) break; curtune->at_modified = TRUE; j = (stp->stp_Instrument%10)+(i*10); if( j > 63 ) j = 63; modify_stp_b( curtune, stp, UNT_STP_INSTRUMENT, j ); stp->stp_Instrument = j; break; case 2: if( i>9 ) break; curtune->at_modified = TRUE; j = (stp->stp_Instrument/10)*10+i; if( j > 63 ) j = 63; modify_stp_b( curtune, stp, UNT_STP_INSTRUMENT, j ); break; case 3: curtune->at_modified = TRUE; modify_stp_b( curtune, stp, UNT_STP_FX, i ); break; case 4: curtune->at_modified = TRUE; modify_stp_b( curtune, stp, UNT_STP_FXPARAM, (stp->stp_FXParam&0xf)|(i<<4) ); break; case 5: curtune->at_modified = TRUE; modify_stp_b( curtune, stp, UNT_STP_FXPARAM, (stp->stp_FXParam&0xf0)|i ); break; case 6: curtune->at_modified = TRUE; modify_stp_b( curtune, stp, UNT_STP_FXB, i ); break; case 7: curtune->at_modified = TRUE; modify_stp_b( curtune, stp, UNT_STP_FXBPARAM, (stp->stp_FXbParam&0xf)|(i<<4) ); break; case 8: curtune->at_modified = TRUE; modify_stp_b( curtune, stp, UNT_STP_FXBPARAM, (stp->stp_FXbParam&0xf0)|i ); break; } curtune->at_NoteNr += curtune->at_notejump; if( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr = 0; gui_render_tracker( TRUE ); break; case E_POS: donkey = TRUE; chan = curtune->at_posed_curs/5 + curtune->at_curlch; track = (curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff); tran = (curtune->at_Positions[curtune->at_PosNr].pos_Transpose[chan]&0xff); switch( curtune->at_posed_curs%5 ) { case 0: if( i > 9 ) break; curtune->at_modified = TRUE; j = (track%100)+(i*100); if( j > 255 ) j = 255; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, j ); break; case 1: if( i > 9 ) break; curtune->at_modified = TRUE; j = ((track/100)*100)+(track%10)+(i*10); if( j > 255 ) j = 255; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, j ); break; case 2: if( i > 9 ) break; curtune->at_modified = TRUE; j = ((track/100)*100)+(((track%100)/10)*10)+i; if( j > 255 ) j = 255; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, j ); break; case 3: j = (tran & 0x0f)|(i<<4); curtune->at_modified = TRUE; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRANS, j ); break; case 4: j = (tran & 0xf0)|i; curtune->at_modified = TRUE; modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRANS, j ); break; } if( posedadvance ) gui_posed_moveright(); gui_render_tracker( TRUE ); break; } } if( donkey ) break; if( ( curtune->at_editing == E_TRACK ) && ( (qual&IEQUALIFIER_CONTROL) == 0 ) && ( (curtune->at_tracked_curs%9) == 0 ) ) { // Piano! for( i=0; i<29; i++ ) { if( ( msg->Code == pianokeys[i] ) || ( ( extrakeys[i] != -1 ) && ( msg->Code == extrakeys[i] ) ) ) { donkey = TRUE; chan = curtune->at_tracked_curs/9+curtune->at_curlch; j = i+basekey+1; if( j > 60 ) break; curtune->at_drumpadnote = j; if( curtune->at_curins > 0 ) rp_play_note( curtune, curtune->at_curins, i+basekey+1, chan ); if( curtune->at_doing == D_EDITING ) { stp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr]; modify_stp_w( curtune, stp, UNT_STP_NOTEANDINS, ((i+basekey+1)<<8)|curtune->at_curins ); curtune->at_NoteNr += curtune->at_notejump; curtune->at_modified = TRUE; while( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr -= curtune->at_TrackLength; gui_render_tracked( TRUE ); } break; } } } if( donkey ) break; if( qual&IEQUALIFIER_CONTROL ) { cutting = FALSE; switch( msg->Code ) { case 19: // Ctrl+R (restore positions) donkey = TRUE; if( curtune ) gui_set_rempos( curtune ); gui_render_tracked( TRUE ); break; case 21: // Ctrl+Y (reverse block) donkey = TRUE; if( !curtune ) break; if( curtune->at_cbmarktrack != -1 ) { struct ahx_step tmpstp; track = curtune->at_cbmarktrack; setbefore_track( curtune, track ); j = ((curtune->at_cbmarkendnote-curtune->at_cbmarkstartnote)+1)>>1; k = curtune->at_cbmarkstartnote; l = curtune->at_cbmarkendnote; for( i=0; iat_cbmarkbits & CBF_NOTES ) { tmpstp.stp_Note = curtune->at_Tracks[track][k].stp_Note; tmpstp.stp_Instrument = curtune->at_Tracks[track][k].stp_Instrument; curtune->at_Tracks[track][k].stp_Note = curtune->at_Tracks[track][l].stp_Note; curtune->at_Tracks[track][k].stp_Instrument = curtune->at_Tracks[track][l].stp_Instrument; curtune->at_Tracks[track][l].stp_Note = tmpstp.stp_Note; curtune->at_Tracks[track][l].stp_Instrument = tmpstp.stp_Instrument; } if( curtune->at_cbmarkbits & CBF_CMD1 ) { tmpstp.stp_FX = curtune->at_Tracks[track][k].stp_FX; tmpstp.stp_FXParam = curtune->at_Tracks[track][k].stp_FXParam; curtune->at_Tracks[track][k].stp_FX = curtune->at_Tracks[track][l].stp_FX; curtune->at_Tracks[track][k].stp_FXParam = curtune->at_Tracks[track][l].stp_FXParam; curtune->at_Tracks[track][l].stp_FX = tmpstp.stp_FX; curtune->at_Tracks[track][l].stp_FXParam = tmpstp.stp_FXParam; } if( curtune->at_cbmarkbits & CBF_CMD2 ) { tmpstp.stp_FXb = curtune->at_Tracks[track][k].stp_FXb; tmpstp.stp_FXbParam = curtune->at_Tracks[track][k].stp_FXbParam; curtune->at_Tracks[track][k].stp_FXb = curtune->at_Tracks[track][l].stp_FXb; curtune->at_Tracks[track][k].stp_FXbParam = curtune->at_Tracks[track][l].stp_FXbParam; curtune->at_Tracks[track][l].stp_FXb = tmpstp.stp_FXb; curtune->at_Tracks[track][l].stp_FXbParam = tmpstp.stp_FXbParam; } k++; l--; } setafter_track( curtune, track ); } else { struct ahx_step tmpstp; chan = curtune->at_tracked_curs/9+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; setbefore_track( curtune, track ); j = curtune->at_TrackLength>>1; k = curtune->at_TrackLength-1; for( i=0; iat_Tracks[track][i]; curtune->at_Tracks[track][i] = curtune->at_Tracks[track][k-i]; curtune->at_Tracks[track][k-i] = tmpstp; } setafter_track( curtune, track ); } gui_render_tracked( TRUE ); break; case 24: // Ctrl+O (squish) if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; if( curtune->at_editing != E_TRACK ) break; if( cb_length == 0 ) break; chan = curtune->at_tracked_curs/9+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; if( i == curtune->at_TrackLength-1 ) break; setbefore_track( curtune, track ); for( i=curtune->at_NoteNr+1; iat_TrackLength; i++ ) { for( j=i; j<(curtune->at_TrackLength-1); j++ ) curtune->at_Tracks[track][j] = curtune->at_Tracks[track][j+1]; curtune->at_Tracks[track][curtune->at_TrackLength-1].stp_Note = 0; curtune->at_Tracks[track][curtune->at_TrackLength-1].stp_Instrument = 0; curtune->at_Tracks[track][curtune->at_TrackLength-1].stp_FX = 0; curtune->at_Tracks[track][curtune->at_TrackLength-1].stp_FXParam = 0; curtune->at_Tracks[track][curtune->at_TrackLength-1].stp_FXb = 0; curtune->at_Tracks[track][curtune->at_TrackLength-1].stp_FXbParam = 0; } setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case 23: // Ctrl+I (Insert block) donkey = TRUE; if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; if( curtune->at_editing != E_TRACK ) break; if( cb_length == 0 ) break; chan = curtune->at_tracked_curs/9+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; setbefore_track( curtune, track ); for( i=curtune->at_TrackLength-1; i>=curtune->at_NoteNr+cb_length; i-- ) { if( cb_contains & CBF_NOTES ) { curtune->at_Tracks[track][i].stp_Note = curtune->at_Tracks[track][i-cb_length].stp_Note; curtune->at_Tracks[track][i].stp_Instrument = curtune->at_Tracks[track][i-cb_length].stp_Instrument; } if( cb_contains & CBF_CMD1 ) { curtune->at_Tracks[track][i].stp_FX = curtune->at_Tracks[track][i-cb_length].stp_FX; curtune->at_Tracks[track][i].stp_FXParam = curtune->at_Tracks[track][i-cb_length].stp_FXParam; } if( cb_contains & CBF_CMD2 ) { curtune->at_Tracks[track][i].stp_FXb = curtune->at_Tracks[track][i-cb_length].stp_FXb; curtune->at_Tracks[track][i].stp_FXbParam = curtune->at_Tracks[track][i-cb_length].stp_FXbParam; } } gui_paste( curtune, track, CBF_NOTES|CBF_CMD1|CBF_CMD2 ); setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case 34: // Ctrl+D (Delete block) if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; if( curtune->at_editing != E_TRACK ) break; if( curtune->at_cbmarktrack == -1 ) break; donkey = TRUE; setbefore_track( curtune, curtune->at_cbmarktrack ); j = (curtune->at_cbmarkendnote - curtune->at_cbmarkstartnote) + 1; for( i=curtune->at_cbmarkstartnote; iat_TrackLength-j; i++ ) { if( curtune->at_cbmarkbits & CBF_NOTES ) { curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Note = curtune->at_Tracks[curtune->at_cbmarktrack][i+j].stp_Note; curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Instrument = curtune->at_Tracks[curtune->at_cbmarktrack][i+j].stp_Instrument; } if( curtune->at_cbmarkbits & CBF_CMD1 ) { curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FX = curtune->at_Tracks[curtune->at_cbmarktrack][i+j].stp_FX; curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FXParam = curtune->at_Tracks[curtune->at_cbmarktrack][i+j].stp_FXParam; } if( curtune->at_cbmarkbits & CBF_CMD2 ) { curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FXb = curtune->at_Tracks[curtune->at_cbmarktrack][i+j].stp_FXb; curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FXbParam = curtune->at_Tracks[curtune->at_cbmarktrack][i+j].stp_FXbParam; } } for( ; iat_TrackLength; i++ ) { if( curtune->at_cbmarkbits & CBF_NOTES ) { curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Note = 0; curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Instrument = 0; } if( curtune->at_cbmarkbits & CBF_CMD1 ) { curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FX = 0; curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FXParam = 0; } if( curtune->at_cbmarkbits & CBF_CMD2 ) { curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FXb = 0; curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_FXbParam = 0; } } setafter_track( curtune, curtune->at_cbmarktrack ); curtune->at_cbmarktrack = -1; gui_render_tracked( TRUE ); break; case 37: // Ctrl+H (transpose block up) if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; if( curtune->at_editing != E_TRACK ) break; if( curtune->at_cbmarktrack == -1 ) break; if( ( curtune->at_cbmarkbits & CBF_NOTES ) == 0 ) break; donkey = TRUE; setbefore_track( curtune, curtune->at_cbmarktrack ); for( i=curtune->at_cbmarkstartnote; i<=curtune->at_cbmarkendnote; i++ ) if( ( curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Note < 60 ) && ( curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Note > 0 ) ) curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Note++; setafter_track( curtune, curtune->at_cbmarktrack ); gui_render_tracked( TRUE ); break; case 40: // Ctrl+L (transpose block down) if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; if( curtune->at_editing != E_TRACK ) break; if( curtune->at_cbmarktrack == -1 ) break; if( ( curtune->at_cbmarkbits & CBF_NOTES ) == 0 ) break; donkey = TRUE; setbefore_track( curtune, curtune->at_cbmarktrack ); for( i=curtune->at_cbmarkstartnote; i<=curtune->at_cbmarkendnote; i++ ) if( curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Note > 1 ) curtune->at_Tracks[curtune->at_cbmarktrack][i].stp_Note--; setafter_track( curtune, curtune->at_cbmarktrack ); gui_render_tracked( TRUE ); break; case 39: // Ctrl+K (kill to end/start of track) chan = curtune->at_tracked_curs/9+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; if( qual & IEQUALIFIER_LSHIFT ) { setbefore_track( curtune, track ); for( i=0; i<=curtune->at_NoteNr; i++ ) { curtune->at_Tracks[track][i].stp_Note = 0; curtune->at_Tracks[track][i].stp_Instrument = 0; curtune->at_Tracks[track][i].stp_FX = 0; curtune->at_Tracks[track][i].stp_FXParam = 0; curtune->at_Tracks[track][i].stp_FXb = 0; curtune->at_Tracks[track][i].stp_FXbParam = 0; } setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; } setbefore_track( curtune, track ); for( i=curtune->at_NoteNr; iat_TrackLength; i++ ) { curtune->at_Tracks[track][i].stp_Note = 0; curtune->at_Tracks[track][i].stp_Instrument = 0; curtune->at_Tracks[track][i].stp_FX = 0; curtune->at_Tracks[track][i].stp_FXParam = 0; curtune->at_Tracks[track][i].stp_FXb = 0; curtune->at_Tracks[track][i].stp_FXbParam = 0; } setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case 22: // Ctrl+U (Undo) rp_stop(); gui_render_wavemeter(); curtune->at_doing = D_IDLE; if( qual & IEQUALIFIER_LSHIFT ) redo( curtune ); else undo( curtune ); break; case 50: // Cut cutting = TRUE; case 51: // Copy donkey = TRUE; if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_cbmarktrack == -1 ) break; if( cutting ) setbefore_track( curtune, curtune->at_cbmarktrack ); gui_copyregion( curtune->at_cbmarktrack, curtune->at_cbmarkstartnote, curtune->at_cbmarkendnote, curtune->at_cbmarkbits, cutting ); if( cutting ) setafter_track( curtune, curtune->at_cbmarktrack ); curtune->at_cbmarktrack = -1; gui_render_tracked( TRUE ); break; case E_POS: if( curtune->at_cbpmarklft == -1 ) break; gui_copyposregion( curtune, cutting ); curtune->at_cbpmarkmarklft = -1; curtune->at_cbpmarklft = -1; gui_render_posed( TRUE ); if( cutting ) gui_render_tracked( TRUE ); break; } break; case 25: // Paste case 52: // V as well as P donkey = TRUE; if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; switch( curtune->at_editing ) { case E_TRACK: chan = curtune->at_tracked_curs/9+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; setbefore_track( curtune, track ); gui_paste( curtune, track, CBF_NOTES|CBF_CMD1|CBF_CMD2 ); setafter_track( curtune, track ); gui_render_tracked( TRUE ); break; case E_POS: gui_pasteposregion( curtune ); gui_render_posed( TRUE ); gui_render_tracked( TRUE ); break; } break; case 53: // Ctrl+B Mark donkey = TRUE; if( ( curtune->at_doing != D_EDITING ) && ( curtune->at_doing != D_IDLE ) ) break; switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_cbmarktrack != -1 ) { curtune->at_cbmarktrack = -1; gui_render_tracked( TRUE ); break; } chan = curtune->at_tracked_curs/9+curtune->at_curlch; track = curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]; curtune->at_cbmarktrack = track; curtune->at_cbmarkmarknote = curtune->at_NoteNr; curtune->at_cbmarkcurnote = curtune->at_NoteNr; curtune->at_cbmarkmarkx = curtune->at_tracked_curs%9; curtune->at_cbmarkcurx = curtune->at_tracked_curs%9; gui_render_tracked( TRUE ); break; case E_POS: if( curtune->at_cbpmarkmarklft != -1 ) { curtune->at_cbpmarkmarklft = -1; gui_render_posed( TRUE ); break; } curtune->at_cbpmarkmarkpos = curtune->at_PosNr; curtune->at_cbpmarkmarklft = (((curtune->at_posed_curs/5)+curtune->at_curlch)<<1)|((curtune->at_posed_curs%5)>2?1:0); gui_render_posed( TRUE ); break; } break; } } if( donkey ) break; switch( msg->Code ) { case 11: switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing != D_EDITING ) break; if( (curtune->at_tracked_curs%9) > 2 ) { donkey = TRUE; chan = curtune->at_tracked_curs/9+curtune->at_curlch; stp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr]; if( curtune->at_NoteNr > 0 ) pstp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr-1]; else pstp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_TrackLength-1]; if( (curtune->at_tracked_curs%9) < 6 ) { modify_stp_w( curtune, stp, UNT_STP_FXANDPARAM, (pstp->stp_FX<<8)|((pstp->stp_FXParam-1)&0xff) ); } else { modify_stp_w( curtune, stp, UNT_STP_FXBANDPARAM, (pstp->stp_FXb<<8)|((pstp->stp_FXbParam-1)&0xff) ); } curtune->at_NoteNr += curtune->at_notejump; curtune->at_modified = TRUE; if( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr = 0; gui_render_tracker( FALSE ); } break; case E_POS: if( curtune->at_PosNr == 0 ) break; chan = curtune->at_posed_curs/5 + curtune->at_curlch; donkey = TRUE; switch( curtune->at_posed_curs%5 ) { case 0: case 1: case 2: modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, (curtune->at_Positions[curtune->at_PosNr-1].pos_Track[chan]-1)&0xff ); curtune->at_PosNr++; if( curtune->at_PosNr == 1000 ) curtune->at_PosNr = 999; gui_render_posed( FALSE ); break; default: modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRANS, (curtune->at_Positions[curtune->at_PosNr-1].pos_Transpose[chan]-1)&0xff ); curtune->at_PosNr++; if( curtune->at_PosNr == 1000 ) curtune->at_PosNr = 999; gui_render_posed( FALSE ); break; } break; } break; case 12: switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing != D_EDITING ) break; if( (curtune->at_tracked_curs%9) > 2 ) { donkey = TRUE; chan = curtune->at_tracked_curs/9+curtune->at_curlch; stp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr]; if( curtune->at_NoteNr > 0 ) pstp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr-1]; else pstp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_TrackLength-1]; if( (curtune->at_tracked_curs%9) < 6 ) { modify_stp_w( curtune, stp, UNT_STP_FXANDPARAM, (pstp->stp_FX<<8)|((pstp->stp_FXParam+1)&0xff) ); } else { modify_stp_w( curtune, stp, UNT_STP_FXBANDPARAM, (pstp->stp_FXb<<8)|((pstp->stp_FXbParam+1)&0xff) ); } curtune->at_NoteNr += curtune->at_notejump; curtune->at_modified = TRUE; if( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr = 0; gui_render_tracker( FALSE ); } break; case E_POS: if( curtune->at_PosNr == 0 ) break; chan = curtune->at_posed_curs/5 + curtune->at_curlch; donkey = TRUE; switch( curtune->at_posed_curs%5 ) { case 0: case 1: case 2: modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, (curtune->at_Positions[curtune->at_PosNr-1].pos_Track[chan]+1)&0xff ); curtune->at_PosNr++; if( curtune->at_PosNr == 1000 ) curtune->at_PosNr = 999; gui_render_posed( FALSE ); break; default: modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRANS, (curtune->at_Positions[curtune->at_PosNr-1].pos_Transpose[chan]+1)&0xff ); curtune->at_PosNr++; if( curtune->at_PosNr == 1000 ) curtune->at_PosNr = 999; gui_render_posed( FALSE ); break; } break; } break; case 13: switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing != D_EDITING ) break; if( (curtune->at_tracked_curs%9) > 2 ) { donkey = TRUE; chan = curtune->at_tracked_curs/9+curtune->at_curlch; stp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr]; if( curtune->at_NoteNr > 0 ) pstp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_NoteNr-1]; else pstp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[chan]&0xff][curtune->at_TrackLength-1]; if( (curtune->at_tracked_curs%9) < 6 ) { modify_stp_w( curtune, stp, UNT_STP_FXANDPARAM, (pstp->stp_FX<<8)|pstp->stp_FXParam ); } else { modify_stp_w( curtune, stp, UNT_STP_FXBANDPARAM, (pstp->stp_FXb<<8)|pstp->stp_FXbParam ); } curtune->at_NoteNr += curtune->at_notejump; curtune->at_modified = TRUE; if( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr = 0; gui_render_tracker( FALSE ); } break; case E_POS: if( curtune->at_PosNr == 0 ) break; chan = curtune->at_posed_curs/5 + curtune->at_curlch; donkey = TRUE; switch( curtune->at_posed_curs%5 ) { case 0: case 1: case 2: modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, curtune->at_Positions[curtune->at_PosNr-1].pos_Track[chan] ); curtune->at_PosNr++; if( curtune->at_PosNr == 1000 ) curtune->at_PosNr = 999; gui_render_posed( FALSE ); break; default: modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRANS, curtune->at_Positions[curtune->at_PosNr-1].pos_Transpose[chan] ); curtune->at_PosNr++; if( curtune->at_PosNr == 1000 ) curtune->at_PosNr = 999; gui_render_posed( FALSE ); break; } break; } break; case 70: donkey = TRUE; switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_doing == D_IDLE ) break; stp = &curtune->at_Tracks[curtune->at_Positions[curtune->at_PosNr].pos_Track[curtune->at_tracked_curs/9+curtune->at_curlch]&0xff][curtune->at_NoteNr]; switch( qual&(IEQUALIFIER_LSHIFT|IEQUALIFIER_LALT) ) { case IEQUALIFIER_LSHIFT: modify_stp_b( curtune, stp, UNT_STP_NOTE, 0 ); break; case IEQUALIFIER_LALT: modify_stp_b( curtune, stp, UNT_STP_INSTRUMENT, 0 ); break; default: modify_stp_w( curtune, stp, UNT_STP_NOTEANDINS, 0 ); } curtune->at_NoteNr += curtune->at_notejump; curtune->at_modified = TRUE; if( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr = 0; gui_render_tracker( FALSE ); break; case E_POS: chan = curtune->at_posed_curs/5 + curtune->at_curlch; if( ( curtune->at_posed_curs%5 ) < 3 ) modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRACK, 0 ); else modify_pos_b( curtune, &curtune->at_Positions[curtune->at_PosNr], chan, UNT_POS_TRANS, 0 ); curtune->at_PosNr++; if( curtune->at_PosNr == 1000 ) curtune->at_PosNr = 999; gui_render_posed( FALSE ); break; } break; case 76: donkey = TRUE; switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_NoteNr > 0 ) curtune->at_NoteNr--; else curtune->at_NoteNr = curtune->at_TrackLength-1; gui_render_tracked( FALSE ); break; case E_POS: if( curtune->at_PosNr > 0 ) curtune->at_PosNr--; else curtune->at_PosNr = 999; gui_render_tracker( FALSE ); break; } break; case 77: donkey = TRUE; switch( curtune->at_editing ) { case E_TRACK: if( curtune->at_NoteNr < curtune->at_TrackLength-1 ) curtune->at_NoteNr++; else curtune->at_NoteNr = 0; gui_render_tracked( FALSE ); break; case E_POS: if( curtune->at_PosNr < 999 ) curtune->at_PosNr++; else curtune->at_PosNr = 0; gui_render_tracker( FALSE ); break; } break; case 85: case 86: case 87: case 88: case 89: donkey = TRUE; curtune->at_NoteNr = curtune->at_rempos[msg->Code-85]; if( curtune->at_NoteNr >= curtune->at_TrackLength ) curtune->at_NoteNr = curtune->at_TrackLength-1; gui_render_tracker( FALSE ); break; } break; case D_PLAYING: switch( msg->Code ) { case 76: if( curtune->at_editing != E_POS ) break; if( curtune->at_NextPosNr == -1 ) next = curtune->at_PosNr-1; else next = curtune->at_NextPosNr-1; if( next < 0 ) next = 999; curtune->at_NextPosNr = next; gui_render_tracker( FALSE ); break; case 77: if( curtune->at_editing != E_POS ) break; if( curtune->at_NextPosNr == -1 ) next = curtune->at_PosNr+1; else next = curtune->at_NextPosNr+1; if( next > 999 ) next = 0; curtune->at_NextPosNr = next; gui_render_tracker( FALSE ); break; } break; case D_RECORDING: break; } break; case PN_INSED: if( curtune == NULL ) break; cins = &curtune->at_Instruments[curtune->at_curins]; cple = &cins->ins_PList.pls_Entries[cins->ins_pcury]; if( ( curtune->at_idoing == D_IDLE ) || ( ( curtune->at_idoing == D_EDITING ) && ( cins != NULL ) && ( cins->ins_pcurx == 0 ) ) ) { for( i=0; i<29; i++ ) { if( ( msg->Code == pianokeys[i] ) || ( ( extrakeys[i] != -1 ) && ( msg->Code == extrakeys[i] ) ) ) { donkey = TRUE; if( curtune->at_idoing == D_IDLE ) { chan = curtune->at_tracked_curs/9+curtune->at_curlch; if( curtune->at_curins > 0 ) rp_play_note( curtune, curtune->at_curins, i+basekey+1, chan ); } else { modify_ple_b( curtune, cins, cple, UNT_PLE_NOTE, i+basekey+1 ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); } break; } } } if( ( curtune->at_idoing == D_EDITING ) && ( cple != NULL ) && ( cins->ins_pcurx > 0 ) ) { for( i=0; i<16; i++ ) if( msg->Code == hexkeys[i] ) break; if( i<16 ) { switch( cins->ins_pcurx ) { case 1: if( i < 5 ) { donkey = TRUE; modify_ple_b( curtune, cins, cple, UNT_PLE_WAVEFORM, i ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); } break; case 2: donkey = TRUE; modify_ple_b( curtune, cins, cple, UNT_PLE_FX0, i ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); break; case 3: donkey = TRUE; modify_ple_b( curtune, cins, cple, UNT_PLE_FXPARAM0, (cple->ple_FXParam[0]&0xf)|(i<<4) ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); break; case 4: donkey = TRUE; modify_ple_b( curtune, cins, cple, UNT_PLE_FXPARAM0, (cple->ple_FXParam[0]&0xf0)|i ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); break; case 5: donkey = TRUE; modify_ple_b( curtune, cins, cple, UNT_PLE_FX1, i ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); break; case 6: donkey = TRUE; modify_ple_b( curtune, cins, cple, UNT_PLE_FXPARAM1, (cple->ple_FXParam[1]&0xf)|(i<<4) ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); break; case 7: donkey = TRUE; modify_ple_b( curtune, cins, cple, UNT_PLE_FXPARAM1, (cple->ple_FXParam[1]&0xf0)|i ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, TRUE ); break; } } } if( donkey ) break; switch( msg->Code ) { case 70: donkey = TRUE; if( cple ) { modify_ple_b( curtune, cins, cple, UNT_PLE_NOTE, 0 ); cins->ins_pcury += curtune->at_inotejump; gui_render_perf( curtune, cins, FALSE ); } break; case 68: donkey = TRUE; if( curtune->at_idoing != D_EDITING ) break; if( cple ) { modify_ple_w( curtune, cins, cple, UNT_PLE_FIXED, cple->ple_Fixed ^ 1 ); gui_render_perf( curtune, cins, TRUE ); } break; case 64: donkey = TRUE; curtune->at_doing = D_IDLE; rp_stop(); gui_render_wavemeter(); if( curtune->at_idoing == D_IDLE ) curtune->at_idoing = D_EDITING; else curtune->at_idoing = D_IDLE; gui_render_perf( curtune, cins, TRUE ); break; case 76: donkey = TRUE; if( cins ) { if( qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) cins->ins_pcury-=(PERF_H>>4); else cins->ins_pcury--; gui_render_perf( curtune, cins, FALSE ); } break; case 77: donkey = TRUE; if( cins ) { if( qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) cins->ins_pcury+=(PERF_H>>4); else cins->ins_pcury++; gui_render_perf( curtune, cins, FALSE ); } break; case 79: donkey = TRUE; if( cins ) { if( qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) cins->ins_pcurx = 0; else cins->ins_pcurx--; gui_render_perf( curtune, cins, FALSE ); } break; case 78: donkey = TRUE; if( cins ) { if( qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT) ) cins->ins_pcurx = 7; else cins->ins_pcurx++; gui_render_perf( curtune, cins, FALSE ); } break; } break; } /* if( donkey == FALSE ) { if( msg->Code < 128 ) printf( "Key: %d (%d)\n", msg->Code, msg->Code|128 ); } */ break; case IDCMP_MOUSEBUTTONS: qual = msg->Qualifier; gui_mouse_handler( msg->MouseX, msg->MouseY, msg->Code ); break; } #ifndef __SDL_WRAPPER__ IExec->ReplyMsg( (struct Message *)msg ); #endif } } #ifndef __SDL_WRAPPER__ if( pref_dorestart ) { if( !gui_restart() ) quitting = TRUE; pref_dorestart = FALSE; } #endif } void gui_shutdown( void ) { uint32 i; gui_close_prefs(); if( gui_savethem ) gui_save_prefs(); #ifndef __SDL_WRAPPER__ if( mainwin ) IIntuition->CloseWindow( mainwin ); if( scr ) IIntuition->CloseScreen( scr ); #endif if( cbpbuf ) IExec->FreeVec( cbpbuf ); #ifndef __SDL_WRAPPER__ if( ins_lreq ) IAsl->FreeAslRequest( ins_lreq ); if( ins_sreq ) IAsl->FreeAslRequest( ins_sreq ); if( sng_lreq ) IAsl->FreeAslRequest( sng_lreq ); if( sng_sreq ) IAsl->FreeAslRequest( sng_sreq ); if( dir_req ) IAsl->FreeAslRequest( dir_req ); for( i=0; iFreeBitMap( bitmaps[i].bm ); for( i=0; iFreeBitMap( tbx[i].bm.bm ); if( prpfont ) IGraphics->CloseFont( prpfont ); if( fixfont ) IGraphics->CloseFont( fixfont ); if( sfxfont ) IGraphics->CloseFont( sfxfont ); if( gui_tick_signum != -1 ) IExec->FreeSignal( gui_tick_signum ); if( IRequester ) IExec->DropInterface( (struct Interface *)IRequester ); if( RequesterBase ) IExec->CloseLibrary( RequesterBase ); if( IKeymap ) IExec->DropInterface( (struct Interface *)IKeymap ); if( KeymapBase ) IExec->CloseLibrary( KeymapBase ); if( IDiskfont ) IExec->DropInterface( (struct Interface *)IDiskfont ); if( DiskfontBase ) IExec->CloseLibrary( DiskfontBase ); if( IDataTypes ) IExec->DropInterface( (struct Interface *)IDataTypes ); if( DataTypesBase ) IExec->CloseLibrary( DataTypesBase ); if( IP96) IExec->DropInterface( (struct Interface *)IP96 ); if( GfxBase ) IExec->CloseLibrary( P96Base ); if( IGraphics ) IExec->DropInterface( (struct Interface *)IGraphics ); if( GfxBase ) IExec->CloseLibrary( GfxBase ); if( IIntuition ) IExec->DropInterface( (struct Interface *)IIntuition ); if( IntuitionBase ) IExec->CloseLibrary( IntuitionBase ); #else for( i=0; i HivelyTracker Documentation

Command Reference

Track commands

CommandDescription
0Pos jump hi
0 sets the high digit of the "jump to position" command (B)
1Portamento Up
Portamento up will slide the sample pitch up. The higher the parameter value, the faster it slides. You can't slide higher than B-5! Note: The portamento will be called as many times as the speed of the song per note. This means that you'll sometimes have trouble sliding accurately. If you change the speed without changing the slide rate, it could sound wrong.
2Portamento down
Portamento down will slide the sample pitch down. The higher the parameter value, the faster it slides. You cannot slide lower than C-1! The same limitations apply as portamento up.
3Tone portamento
Tone portamento automatically slides from the old note to the new note. You don't have to worry about which direction to slide, you only need to set the slide speed. To keep on sliding at a previously set rate, just use "300".
4Set filter
If the parameter is 00 to 3F, it overrides the filter value in the instrument performancelist. So, "420" would make the instrument set filter to "20" once the "0xx" command is reached in the performance list instead of "xx". 40-7F sets the filter immediately to parameter-40. So "452" immediately sets the filter to 12.
5Tone portamento and volume slideThis command continues the current tone portamento slide, and slides the volume at the same time. The volume slide is the same as command "A".
6Not used
7Stereo panning (HivelyTracker only)
This sets the stereo panning position of the current channel from 80 (hard left) through 0 (centre) to 7F (hard right). It is a signed byte, so 80 is -128, and 7F is 127.
8External timing - UNUSED IN HIVELYTRACKER
9Set square relation
This command will set the square relation (set in performance list with 3xx) immediately and will disable the first 3xx command in the performance list of this instrument.
AVolume slide
This command will slide the volume up or down. If the high nibble is non-zero it slides the volume up at that speed. If the low nibble is non-zero it slides it down at that speed. e.g. A05 slides the volume down at speed 5, and A10 slides the volume up at speed 1.
BPosition Jump
This command jumps to the specified position. Unlike most commands, the perameter is in decimal, not hex. If you want to jump to a position past "99", use the 0 command to set the "hundreds" column of the number
CSet volume
This sets the volume from 0 (silent) to 40 (full volume). Parameter is in hex. A value of 00 to 40 sets the volume for the current instrument. 50 to 90 sets the volume to "paremeter-50" for all channels. A0 to E0 sets the volume to "paremeter-A0" for the current channel
DPosition break
This jumps to the next position in the song. The parameter is the note number to jump to.
EMultiple
The E command is split into sub commands using the high nibble of the effect parameter, so "E1x" is a different command to "E2x"
E0Not used
E1Fineslide up
This is like the "1xx" command, but the sliding is much finer.
E2Fineslide down
This is like the "2xx" command, but the sliding is much finer.
E3Not used
E4Vibrato control
This command overrides the vibrato depth set in the instrument editor
E5Not used
E6Not used
E7Not used
E8Not used
E9Not used
EAFine volume up
This is similar to the "Ax0" command except the volume slide is much finer
EBFine volume down
This is similar to the "A0x" command except the volume slide is much finer
ECNote cut
This command will cut the note at the selected tick, creating extremely short notes
EDNote delay
This command will delay the note to the selected tick
EENot used
EFMiscellaneous flags:

EF1 = Preserve transpose value until another note is played in the channel (overrides default AHX behaviour) (v1.5+).
FSet tempo
The higher the parameter, the slower the tracks are parsed.

Instrument performance list commands

CommandDescription
0Set filter
A parameter of 01 to 1F sets the low pass filter, where 01 is heavy filtering and 1f is light filtering.
A parameter of 20 turns of all filtering.
A parameter of 21 to 3F sets the high pass filter, where 21 is light, and 3F is heavy filtering
1Portamento up (same as track cmd)
2Portamento down (same as track cmd)
3Set square relation value
4Toggle modulation
The upper nibble toggles filter modulation.
The lower nibble toggles square modulation
A value of 1 sets positive start direction, and F sets negative start direction.
5Jump to performance list position specified by the parameter
7Ring modulate instrument with a triangle wave. The parameter sets the note of the triangle oscillator, where 01 to 3B is C-1 to B-5 (fixed), and 81 to BB is C-1 to B-5 (relative). 00 or 80 turns off the modulation.
8Ring modulate instrument with a sawtooth wave. The parameter sets the note of the sawtooth oscillator, where 01 to 3B is C-1 to B-5 (fixed), and 81 to BB is C-1 to B-5 (relative). 00 or 80 turns off the modulation.
9Stereo panning (HivelyTracker only)
This sets the stereo panning for the channel the instrument is played in. This is limited to the instrument itself; the next time an instrument is played in the channel, it returns to the last stereo position set with the "7xx" track command (see table above).
CSet volume
00-40 sets the volume for the current waveform
50-90 sets the master volume for the instrument to parameter-50
a0 to f0 sets the track master volume
FChanges the speed of the performance list

Back to index hivelytracker-0+git20180223/Docs/index.html0000775001024000102400000000212613042730153015265 0ustar HivelyTracker Documentation

HivelyTracker v1.8 from IRIS & Up Rough!

Credits

HivelyTracker was coded by Xeron/IRIS (pete@petergordon.org.uk)
68k and AROS ports done by BuZz (buzz@exotica.org.uk)
OS X port done by Deltafire
Haiku port done by Pulkomandy
GUI design and skins by Spot/Up Rough
The included tunes were created by Xeron/IRIS, Varthall/Up Rough, Oxide/Sonik and Syphus
Many thanks to pieknyman, passing_by and kode54 for helping to improve the replayer accuracy! You all rule!
HivelyTracker is based upon the AHX module format by Dexter/Abyss and Pink/Abyss
Large parts of "ahxplay.library" by Ilkka Lehtoranta and Per Johansson were referenced for the replayer

Contents

hivelytracker-0+git20180223/Docs/images/0000775001024000102400000000000013042730153014531 5ustar hivelytracker-0+git20180223/Docs/images/wave02.png0000775001024000102400000000654313042730153016356 0ustar PNG  IHDRhҫU/tEXtSoftwareSGrab. http://www.stephan-rupprecht.devB IDATx!|ZS@TDT @TDL *"*WD\Q11Q1QqEbbQ@D &"* D+Ͳ4_!@~*vS֮pλ)W O@P(b1@ Fq8Q(b1@ Fq8Q(b1@ Fq8QĶTځV|, >5Mus'Mӕo$Җ#_nR;]Mx;/.>Fp;' msi@ǬsoݓnƱ:Mn"˲VԢ59R*uJ%lR#_I01Msk{}[VfOK9 MC>m̗8 pΝK`_N]\]gOK9 MiʫސO~C*:~9N{u_] ⓾H]ӞI~h_|4X6l~T7|{:1ozO~_oߋ3[_}/ Fq8Q(UmF>[{i_. Fq8Q(b-$/=67(b1@ Fq8Q(ھWjO틾O~8@ Fq8Q(b1@بO~u8nQ(b1U7_(b1@ő?9x?6 58&N(xEd2)1O~48>O|7 ?$őUzoųő?v-{X( Mԏ%?Y O^yn*/ϻYlbZgR* G(6X:y..Mq8i*|©q̿ͻ']p8t3&^4my6__&UpǁjF/?h@> _1@ Fq8Q(b](?n@> JmЀ|c@Y(b1@ Fq8Q(b;R|%&oߎm8Q(b1@ Fq۝hZ"S?'i8 Fq8Q(b*|bc{|h@(b1@l׊w'v8V^7ӖC;ЊagisiZO>C]?3;I$WRqOyڬu!gιS ֩̓dX58l$һ?M.{:G<ϕReYiWf. O~)sMdYVk۶GQeC:۶-Z?U]!&oA'R]}^94FGJq'<9/Tr]XwE8z߈̶,kRW4 õ?Tq+O~39۰PG/sU3O&uq/Ǟ]˭2Msy<1:CkmiırqQ|җWCz<ݿ.Οr˲ƟݓO[M49϶=FQT xq My8Q(bG{zy^EoHY6ER׽^ y4Mc/_ݷ c۶C86&I+zY;nxOiEՆ5Dʲi-\\M49TwN꿯E߅$Ib8Om4|bXn~kִ}֩}f+›p*zv 8Of)KT}0ض8NuyFql+z mq$dފgqk.{̮>] [.|K/t:98*D(JS_KŅs`Z嶆z5o׈8~Ckϗڡ?rܤ^ERbv@?K xՍȟdt!K%x\ Bq|)?5kz|>/朲,3 #˲*>Qck.˲qtnwt9*2U_Vݓn_]דoI稣JR0A|WkT*9{КMlcAWJ)]]םNKKfI/nyTtPE_jFޣˑU$G9t:nжs??iJ")sij8PVny> qy|K_rTrRj4*n(&'QdžqlTvkuF1@ Fq8Q(b1@ Fq8vܦIYla;tl:m|ǎ۴/ItzjoaG4ű›($Irw<|.,y yD$;.\<´:R8&y) e/HGN|ޟ;x=,,,[A$YQ@" }B0Q0MDq8X8fL2w{PC\aA P`P.s@N`A8 ĝL!cY0iDÉQ:U$d9'(Yi ` 1k#@ ;2F``` +׍^@a PF1NVunDB\<2"KΓFu0`D p(QAEH$I(Vk  ɷ5W@" KgLE" z&Sm[^@Ο{i4n_sfƞ8zrjj2$-4RlWz羻vnґFmsw = H 2$UnLO/}&rv~LŇxiBpѼU^A^y@ġjXY7"sPU?_xJ1-}@U +U%4C~ cY z}5A2Bb?j0h]=//D(OB Nl;ghP/P/BGBȀJƒʀ@ '^> =^RV"a9cfqWHI"R 4s(PZa  hD(D0dayw8i];<z\Xn 5?@+3HO xa E* puj~?k5Mwާ@41~E~U~e~Eh4lwezyE+k\e,KӁTU44M3F9l6zei=~WI+٠>>M`}H"HX盛i ,O޼Y7뻲ift K,4^jjͫOϚz=^5QAj6zѨSLWdz] Q*ji}-@TWnYWD'Ɔ#cd[[ϯutmmgY}uɷzrݸ9|uG͌:@ei}WZL5!"NDd>M3DfFN_zc?+YǾqvqyѬ0]K=F IdfrydskkO透X & ~C~OM;6nOGGک3ϭkj gV:5=%L ?!vnƂuo?f#{'OͯN021913Y;S_:2{{i-=5 &ol/{f≿<=2Q5pd0T4P2\oBJ-Q p|qs}csk}DekGO/'5/ $+x9P ^ PuN@@"7QD!N`9 :% N}]eA DAN|MDH&Nh%$}vX:aCu$Pu*{8*aּdx A?CX]wO"YS>sfyM_D6T-S._{vglqiidx,Lz^n0)65c/>ps]u;fcm}s {BК.`w?y;sKԳ{6R)͌F T՚_?n祭{:'>xy?yf~Ǐy.,{nٛ667FV̹ߜ?yf6R$"R 򉦃i3$rԹm]m݅vVP#|;te}geF:ߜ?r,sXYi?OJ"K5!,Uj2uo!u%NE:' @")i"HOR nXagjd*? $hYq9g0852x4(B$<d @@@"wcN#0F+Gc0U-j04/$$J2*Y)E8F QDqB FrrZx,XJcn϶:/mvzOWS[[Kkwh[gg&>?ef;kGmm9N-u"f{K=+L|gB(Px{d4L93 zmi0;=qǾ1e yqlܾ߫hM?Г߹;[WO?S+3 ˓#>St{uٮ4Gzbʴw_<{F "guvVcϯ𵭅%z~t}҉GײO4.98r]yEhƹN>rni 酵ﮦ^'nm^٨g XAiߜ_R?66'p!Kk;zrꆱyzumY[pxc#iMWϟ_WC"ı3g:21>h"] $ JPQz[f6|gV?R7HG|3rMߜ[u7lډo7lBl]]/>ȓ'w>n,âhMx5Do*P@fQWFshhHDzMDDI(4 D] h8A'F6Ci(h & jj(E^jv'q@uZ.Kt2WZ0ʉ10y#3mbčUvhYNF{8۠h*@Ba QE HR\B 01tQ=?3+欞EdfrRBm74!w4qZ(;uШtnټ$mhhh5$>QiGǑ٩)AiIk5-D!`blT\)[ā4M3J"p^E^}u-q^V{$O0th!־ڋ@H:+XPE^Ekb⚦ދ@Ik:~ݰ$D5HéCq; $n6hNk>QMJ-PT.NxvSHNաI3")0Ue@eeܻy!@^:.Q*U!^l(ŽH~  IbT*dtCQ@"/pm=I ,U[ef >á#J2`$ {*ICP+ iy^BCQA%۶HnbR h?\đmL!8(7n#'/ ᫆} H#AXs@FsDќBt1ALdŸݻQz)x^A+E!褴&H5">;㌁r.FuUsv<%xl;i4#1*ԥt DD`ty,/ -*=4o« EUzj6Hi,@,PތKm&gPh}M>MmX(\d˥B#F'$j(6i4RS"fQ=m(jO$乑EItPjف r c0â8th,BHJ"GA8)=ȬEWl]YTjri,gy-ޥǟ8ҿ0FЃ℡HCLy/8::0BGqx^8m^8'N:U*ټӡ@vʥW+gUH&壽 N5%[/mQ o@yj&O*$뿙DaO~O^~ {+kGז|~38X!{Cw*c|wϧ>Oo<|χn_>O?oG߼'{}$&)BF޵qMbȂ"ݩp<XxI w{d.#*O.~v<>/>sc͋6??v5{F?cc'Y(5J{ @]'f%媌X֫lry~?8ӚøxZAj&tr݇o?ptWYQQ^4 OU@Y\2-tF)׎k'uz]}UDR'gKR>O#RmJR%PMܳX,XRWUrnA 2;UYjv^4F RՇxJW4K ^[]]Zxn5J|{O>p/⪉[_?c#?|s[W̘zP/vv}/Yw_~ėx;Kv0ZmpսcaG>Zo[ֶY.QpH|tWݫiad5!%5;5ؑϱL IDAT3S qday {TA\ZqBeBPlkb٥n֧\ Dh)+ `s]_ $0`k>RqR2N^jI,:"=p?*G073L7sKpQmʒޥNYQQE}}9A(B@㛛daDLPC*s`q"Њ(oeܛ 1b)zAy(L)GӟݟO}KktO>ՕeY_]>O~ޚ}mNhٍ7ZU^@guck{q/\ Fcr7xOTkJl;?"Fic0 }3Ǿ>p~z::۹ھ:=|5CC͡&@fj=u?ڍE[)PQg8dll0KRwj],lne{| z6 j= KDҩ4;[BMUduOR$%}oI|cПyMdfbx/˫OZ0tec#íFo{܉S s[}};ەiMH{$K<w?w9,>(^LZ6oЙNP)4H0j' .7uX'I($ JdcJ HpHuꯞ(|M_䅍^l絷V\<߸ks)Ї7C^;Hݽ(Wu47Lњ{Е,ͬ$P|*2q}?o~Χ(H:vg:4?EDҚ¡w oigl_?6~]svr쁯<Z3>6"1"P'̂hBֽ?2~P}҅{&y>ȇ?xuvxXofjS_x}M{rlhjbvd9vakaMրI1K%M3qhqvlOGZWO8<9>'?j_;13=5.2{y󫪾֧>U8 -*̘oM3D{yLU)C9EpYms>u~WngrC_9Jc'?>P3AM }ɚdoC"Qya(/T0vF C~_[o#:~ׇ^5^{NP߸@ ?t;o}_~7'zW~yqOJo6ƣ#ĩQ)" (AyV/lz=`(g;l,Jo(uSo"FC\O^]?źsS%ޚW߿['JYrx'>vWTi]W" {fߨm{z <W c߳_Z-R`gk?\|*i*NwWGFvk疖 #o5:ڮ覆Yb::йϯuGG^qɹݳHcbwkqb{ *" w8?mya/o>u-Է'}ӓ~Dkhbt=nvz]K>^OTd5z:;[[/u7{4QUU<=Rȟijt晅剱aC@ۛiTkz^ b*g`$jB&J;2*HUX>hh߹z*Y{ "*КuáWkvӟ._~\^q^Ub}EE~k'㱼3 .~S]1v~@Ǯ_.zǮ5w%cmln}*N6!`I^ 5/S8bu嵕յ`:11'7:fw.n7QM _Ώ/wpę7oD{M} vi&pּ$"ѕ,]=BjgW[CG~M9?q:o[ZY5߾uj~eYZ=_WkaMn7!.<-=EGn ӣC>}7HĩVП]9^ߕ _UyM{+C} $M3v9/{W(PeQ oO?I 5k*XO?NUҖ ^JӚ2' "zk<1J +޷:{M྽s TU`!6h"Xcr!$@0MTETŜF*i(<.NxhqtCLzĄp@o'͛ l o5{h/*$RZAud0bWm0z}[_?€1QƳ "[gX N@jkͫS_\$k 1V_Ef(thHU$" dHՇM#hNCD&Ee2V z1yo  4$Иu0-iJ2N+.Nk>4^CVo?}gopw}C!(`*`?M]O<~LSO y8Gb-FY9D%P.FG)2]5}3C΂2?h좠zFKU d|L3,Pd$`!A\4cMn)L^_9g zb++2fŠb2":\ΰy%$(`@1 "1RH*f…gH1XaCڎ4 -ʀxSR-7Ѵ&Ȃ2 @LB)l:ܾD@e>%Ǎ/\?lyUG80+14_xr@^lp(7J`U#2 t=}=>i*0<ךW+Pzp3Tx'2 8c "0zq `i/,H@5 $:R(?1D$u@2-/blH hGrDb3DU$-D%h%2V W8ԳW0y^?zbC$. ChUJժFb2X̊Y)W8F6GO[hj8J y|kh(D00# J_f'U,pREb'+*nn~ng%h1B5qaU ht*nnG@qNiqV )he>T-X/jk5c3D%S[-"\Np|I< 轏ˣNА zt.$N iU`CGK`@$"6 QHEA|_n<0+K{9w;Ur>ȋUDZ/r.CKU /K5"E <#كfyn,qWTDCE  @ V r`K9pr[#-5ؖJ,Nt?q +a܁_[.덡0(Wi(T֫Q?055c⛨b\y㰭 J"ZUxUQH(1MH)ȊBE$ଌ!>s0 ,G܌"W>^%('D^Q")IyDLͷ >} *dz(Xa"IU<Qx({C`[2WGFQ T *o҄$īPR8iE.-mC/Z<җ*y@uj#9)' d} IBr9 v}O>|~ Mƃ?`)%Y Eg=>}g%)_V2ab 4I2H M/0ƐCHhv D'ujFL+U3"hy$;D)` ERmP@DRKTJf8Z `s@ "@g]yOkӁW A"dTbH4ZPSjʂ'.=#`KqNID2DX.7jivo=c`kb90Q)b+I?QH!ƗŭuZg4XiP$\ 3J'b`oE(S0z/a(\R'$e¢7a'H|]Raeu914:xF]Ye"$,MVV$ #@ O?}lnz:ss$Nة-7NwAL4N/`sFe֚{3s>~dWn૟-ggޱ7Oeyz{n?[U=π;,_er~fs@VO/ﻥ}zay-ٴR)7T@In ^5b;4iyz5Ѭ7I%&7W7h[@ >!C?(_lWּzUoVoĬ{ԓZzT9Bp( 2&\fu""5 QZ>MSt0-qX ]/=<)0vTR&uiwRFVXmC՗BHS/Ƞ=[':TJPaSg]wӏ>qvk #Jd 'Q4 HI|f&t@==h/ MNL5o[o in"=U";:[ ,ҭPZeo5IHԸ̎f^\VAմ ST=Q )ZfP\i.}j?l=ʳ "rH@t+@ HhTĜhe8t3gϷ[rJӷO>ܹ&g?93Mw|8x|߅KSOpBP\)JqcTrO4BQB 2O++ zՑSjdd:k`@͘*3F+ ,=3IHD"iR_LJ֋WwG?@65zƹ˵SSͣ3O?Go?* 2k#,&QÊysZ8zѧؖd:] ' KeF r1$T),E'ɣO=67ylnO}wx?:;oxm둇N/,2O_|7GǟaɆCH|+NلJ6G#{́'zrC}{k3 ާ*d(K i F+ļQ ,(.A .SDUfNR.1UK`J2#c& ""䚺9A=-=z|@%M˞LPUSzrC˫OsB7noW+8PF Uk7V  "" `'2\0,z}Kd$&*T9Z$W2U!%$D\u1 G&CN(+9R=Z89v(m` {hbF8lJǣS>3;cuk?pUFrj/צlPp.8PD=% izK/ܡʓGOY|`}ڨnw'7:s?X_?YUtfioJ|~ YI64t QEhѻ CUWTdf%oSxv (w&A~"zhՂӍ׶Tѣxms-̨lxMiu9h8p)A3E$8 i9a **RfOU9ᄅ~Hk./B $GOY(j 儘K)31 ,j>$bs0di~4E@ڬIêRZ8>PAr^Xn:(9L %B(V& nͮImRḢ;Cu b )!} PA%S.AذIU-n[S8"ʚh*A$yLA"ą[GE QD\UODTU<{bQBZJ#f(tKeX e|NXsҠ8EwaSD@=r[V3UvTTթ8=b*)׆ yvD$RJ@e'h1 iE bYh,0E0*I3J#L% aɅm\ZZSVu j|DN4 @b/hɩ|`BE 3TDa#SZxaVZs0<@QS`duVE'm7A@}4əO=2:;&AdO+w(jY$ZɎ 4ϠT);pHˁ@<xYD$l'ʐM(vIw^D ;aSpp`X4z&*&G#"(A)XK%'f&XPy|[UYv Af hE Z\bN!戆ILLl'LyXM܊i1|H`QQ&f<";&rKh*T,> U=Fn *AM`y- H{>> yb/XOlˌU5Ga+_Q#&U2:S{c'<{ԉOPBE0YAPb˜[ʼ;ty߻(*(ALzzݟ,F$C`g=}Ҽı V۬B! 0@!wDVB$ : c).1O DzSr@%8ې`K!0Tr+`1m?)lG0nv@H#:u@ aX fze sBqiç9h.HHT 91$C⭎Z(ZeryOL PrH![4CUfukj5}=D@GB. vLk^-~?xl`NJCvbeW죋0FB{hלD㏵ۍNBs@*;]GG ,'c\Sa c0&alGBoVK{bM8)AYLQlP}3HF}zbǚx*<0"f&nHH9+~̳#+t8`Yw6,Shre#8V9&:B %;0'%43N3끈'rE0IF bGԐM!#e At@K[BY,"BLA~,7ID@ V#S O~\:X27 `TPR8{Iw4q̹ '?vO{uc5R&&PDe@.p\PK8zP5XsUJ(X "8Us| :8J]H <ɐT5hޅ{~ԩOH,DPXnYHZW22P U ^]eґݧ=Jd@?:"4aJ2f\g9eL(M' D=O%"GŹe "I|D "8%WɅ,A= 0̲٩6U^"d D9H#j GA`O1fqA&e}1b0]Lݩy1o[\t2^^Z/>MA gj-nН( Ð꘡ɠ.a *2OVx,4-q FfGf'Wȑ}`N ;^9V{)3\bJXDf4NS&fsO m,0:L:_ɩ&ĶjbC1I<3xI|R?[|L];uU%XSЙ;2Wx}u)G1 A9)U.C}Ϭ)a`IRq6xN#I"0 tGȑw"D4-+ dj  ( Z!3ME]|ils};C=KN̩ "HEU=k&#HEH`JDDD҄@ VJG`VHkBDP K*\By<߭;)iZ$BH@٩0#UP%;lG$,WJ<|t>hPkn/.LL# $'s;s/ywD(jI(,WyW̴0q=GfW翿4u[siep_d NFnx21%[(<sH sK&Jp`F,&"U DNG :̆Y{x`#,wdP e#0NP0qfFPPr;:ADhmwʙ Kj\kacp7#Ws /l>ǿu၏Ξ-n &je,һV جT*ZBQ+׾5{('.ԙzVf8VO>wԱ)^d!rU)s奵g:Ŀ0;95s{j}secO ki;TCGųV<9=5sGMye+z`s{2o,{frU*SSx9WGǏNO|;Jđ[k[= 0k6[:ylW뫟;]jVyPAAɗP L[%UŤИWvjz> }oaie~c:{f7<|grF;@%Z~ bGFK)3nLLLS.yfoy24!l!g65@-tJfQsE>EM: QuQt '4٬uM~핵յW_^cpfya~aAVQ$kkk^OvֶZ7WL"VJztޠORBjME@gdOBLOS1J^ZZ ~ngUٰ`V6{ ltLѨW2{k,otghҹ7*7V<{yu}}Kks3S +Cѭn-u~ʆG'T7]1Cs.d\bJ(xnԪ[[3k?K/lTuvwl\`/y8?s:4}lnvu<=~\;[}w~fjzA \;dz]tLK<~OOMW67srW^'-Ntn./کh̯c/tEcʂ9L֊}2zUr|?}nT[ϟG~L``cGa\IʁRN1rʜ1H/8MwgOS*O2^.˕rRxZ/g0 83XK% "KdPQi"Yf}1 @xv  3UEʳϯS4npgWK˫jTe66 _ڭu{osfum{Bop@@PrqEu0=F60rM5zOr- {eXf,.-Wook-VNSNhɥ,Gp{/(e>͍j羻ڽ2XlZ(kOsjj0  ?^(O ?YoO#X;q[O=%?^\^cfiyyvf҅4-CQV!J}9*yoH zÇ^\?0shӟ8zљAZ'9+tW^Yo֫ZRolOAݚukssotgՇ'>2&Y@4McyW_YuK:f=V敗>cro=vsB^X{N\x]v̩x`m+K}J#3@nV'6uK}X~iV!r ;Fڵf62ZFD u@qK9Uo37ZDqAs4tu(fMK \@P50A"@`C=EETٱZ2as sT@v7M Ys%#FjhC-ĵkP̃Pl}+ncim\ nEJ}\idNz^fk$){1 w7wC62;{XBec:|{^|c=[pᙻ)Թ*Gz+n>Xo4j^M-|} .;2755W/~gⶖg#wpDT/z֛NW{xad:رuniyuujbً+6K'o\v{j( WP,55Aj:{y饵 ֭cs_O~{˭VKW&6^{~Nf&/Sؗ~~%~_ˌ-"0Hct#@ |Ꙙ@7Vc-&( 6 B`vS2u *@ 0YUu/}W+뫫 S9 >˄(z.\Z<ÝOvVZsW_ުUnxɮ #wMܝS{ϹKP}4n b82uᇫ+`}ܿ'* y(S92)P*z?\m$A1%}k_ا}zohW`^ٖVȝioHrLvB@R}g;d UO_4n-lj_~aSp-j_zaR.WT>+^XK˫榫S&omEjA9  1" K_LlMb=B` Tн" fnP.qKČtBֹW:|;WF×¥ENn2'4y:k|v;(c@d @abN "\\i "f s>H;qCO~VijZK>I{ѩ[xuNkx3!K{Gˏg'Zʁr@:=ٜh7;P([ ؿ/Qq%vO+vGEy|5{{Lw/u xp5 nsyتtX+[~޿qZ^yqzc:\W/m=x=+/otzå=ljfwn㘲\8P ^!&Z]0pZC. im>gg/G_?qp0,k>Ѽ'nkt^__xyl̉!19_{̬md9!@ 9U Gb G8_WzQZ *VC[>͟}ڧ}ڧ}ڧȍ>?o&k-@hKa@gx/8rPD5(Ahb*5}({C{ȸR kgFcWOɍ51;!dBLvWТW((@ j8 j3 9 uiD $0P[klkEc Q{cp@]"iݥQP+}Vk :Z(@= p`0jnDlR`Sh#"BH.1x6rKgˡ|5 S[$נLg*̯_(Ta }=~Yї \d(&Qx_i*{Ncb" k IlB̢0~d 042t"%fFР{JVQBi$\^좠3>Ӝby; Y<[O&."Xps6u3[Ld'?rl-3>hO=zy,6"#`3wA'R"@U*4*p  Ѳ >))`G[wrŨ}AAin4H B\ m;dG)٢Q NJ*"LDx0Q1N˂=u/A$Q4ڕl! ຑ]l\f4a6d|LkjW~MOM#!"P$"{jtSBP(4%⨁IeA]@0L:zPB P($!p Jo ڍUYafO'[SǦM\X \D2:h0>c J BExbk E|{U qugM2nHBqXAQVDIPMG[(dD^XgCGEA*"ƿm*`}*y"90`q/i gK_B Uh)]akkHHwTrfLB ً=4*wVz#Rx^ѿsZP5R7 H8!(tCU J{i6ZP=*APu Gp@qM@ԅ5[zwdP/!F(tGؤկ~Y?}ɏ8̃88RĞXl)36,`~#U!Ha6< g G z%PMxt@NS "rg׿{wLyN#z ֚s)#Dj-|@uqȚPP 'l'Ș80 w3x~ZkP_|=:ڈep` ]9"sSѕPtY̞D@T;54cQkǵ+Baw|]*!0tD^?cFL̻V 81cpuO?V{hMDP:N8)R=ׂV[8`k^t U(wd@D XW>̬ºޒ#u]R1XtjPC㴹@@c`Hւs4P BZ ƈQn "(32vZK^Lr-=\ j{ADHsh9Q*8qL=wʣ3rPD#w͖x8E]QogTl&Ԑ< 2`TUk'v}OTw6 VGШ[Sa9kC갼֌b!(b#`v}x- uH67_]9v״-@$z77}׷7,%Lq>+; lQ݀B1 h =9)` !&Gg'5"CE6W׎5- ܅*Ui&Wz qAD ®- %@<c$B.b(3Ɔ sՉ5 4\}\bE;]A?:#f0C427=Z(h؍u@a䷄++ cAtP7 hqm wQ88DQbVOR Ʉh 2QTQ7b &nB ¨C [0F'0Fʚ!@PA-*;)愲;FmFGjP8a#I\. ap8iKD !-y>Q_xF'>fn>K[~h_ۿ&F.g~vg_X*jٺWJ_dU]w{W_|iviBQ34 0xXx<PʛueSެ+5:T&;f].븴C<+NhhYʲܴۍ,+M-M>w8}!k'Szǽ{9Ő&͇|SVO^~o-Y-@] . :o.qUfVwڟu}u]x lۺuDtʶ|(߶ukksKD٥ѱDظb#͝1GwlQ[;/nt;~|~U.}_o5~޹K-׍x+ Vvnn޹AKT>N;~#o._˺#Zeё+!&*VQ}BfՕ NJ+íP0"4q+͵a!(M"*rl8݀\vSyu ,A2H7]v[sֆDTHJJkuMddg32uX?K E d,sQQRB80E)W'j"DZ+$Bhl,כ''= MkȐ62"A)6JO$ȸ j6yg{o۳:[iC痱\x?ގVcg=yî<;ݶv_#p KSACWWWy;>+v "{ϟ^["(R22>-"86(|%"G!ojuɈ*F.<;ov_9s./ͻ>}`ΥewnV y3vE/WXrmic_gK~Y>pZi45{ࣿ4xݣ~m_3'۵=ʪ޹ PFznnGwjT E!DZmLϜjw{W;!=R?w?V*|h'XUjQ5{5# `ʋv1:zV \::*stcŦ|;<{/gLNH~ٟ9Dz/9d?Dr罿m5;߽/[&גOݐdrcy7L8 *rj%0p7Ou Gg=\QVzGc$끔({}oمվn㭓\y#_:m-vz{q兕 d+boElk/-Lg_Zݼmㅭ̞Z][J_sDΔk{&̵\^l-osK˫9Ot%[ gU~?Dxp?2C7 斪.4HwAi+#?u |-d IDATR$Hr̞YQmy<Z;~ˮG<8h,tnώi"sfO-hf2ikB5(JnD @R~@hl.. 1@yAu5$GJtl!f%/)"Ʉ׸*FJHn8A$m{tz>0<޾  _=k*KD]NL?1mﯙN \XM"ؽ{K'?j}6 H4B؇m*|r999VuVͰ Pt濳;} W V\Hw[YGFF\mqب'K~ʪT$s)eynD}l>$ZKi"O/LY\FE]9V=466NŕɭϯM+;ЀZMnNmӾO ?N0iS&~u|R=~0hՠq&oy]+ |uCk|ogIȽw5űjr;}򬶤D{V\r1'ZT`*V *6L8K/B Hi`@T;"hpJZ ~#upnp`S6Fq C}mMf̬? isp{o=e 3׼}N槷804"L<$ =}{֣\V<,hf6 m@fB裹fKfd ((`Lݫ8? pಊWT /Bj8P;/tͣy3ӯ|R Յp7G Kur$ `5So@~DSST4iZϝ^`ypGg,xKGgy``Gl1TL=ytg h2HЩGfN-L=qt~kԁÀA/A\|3wG"TԵi @((AdsIt/8@kVTA|Y@Li(B2 8;DH{{Z, 0~d昧9ʗ e])R:B7|1'E7N>3CfppJC W}'1q*TfvCsSǿ67NM %ѵ"{wkӈ{}u6]`R;~Y} YM$^=DX!LPvKo{GNhZPgx4t08߱4i |+:?Kn\ё9WʲQ 3LP61>${IH?}J* HBBNW_5q׭-AkV ™kwn׬:F9~4:2Ш" K¸lkɕ-N ATt!4J3,#⩴%X!}k,_X?s۷Z@i?$keUJ =_&QZe H\LC[fDj[Wi<3QPw)f0=tKGl+'U] S53_ 2Ё 6$rBi}G,$ŐI4l0$hl5~0Hxu~ h%M փfK ֩a "]߁ʲ}+d/ވ>}4w k $m1p `L|if[EЍmhiIH- (6؆ycyv;&[|\Ȝtψ9qF}bˑ#m)B,{U fUe>5%P )@m2=UDj$)7Dly䡅ï܍>z^֯-o\$kzNkTK}MoZ̞ϼ5w}#n[\ZY 1+Vfn9WNu]-2@QvZ6rFȝ;1Jx G 0nwǿ MЏt̪|U{ykmt7kNs"#N̞i[f)Q͹cHO !"R$[i) H2 V9^_Ik09992ڝ-912wɛɥ9jywݺ/}v1y]R3R>RpVL6پG ݧwZ{K#cyÝb'&\˺K;KKw̵lYp-xQ^Y03cĺ) t`+W{쎏O 1Y h< pf8'F/fmM-3ηy)fmLE$SQUsoU$Ka:"7bA76vHAdQt\ksjnWM/mȀ+G@/X(z/s/< w[_IoG"C*yCZ AdX)]y.HnoP[ۮzW/" `;ZWDRjY/Q kkbKҬ{ywzn~ns*έAbaD"PB5ۀܚXVPDb,w]šBڛWO{wdznx3r\8`;%X9G gyUë/}tji2^<}lrkrouI h~: G/>6$tMŅy!QWm HU 9ɹ:er ng|]-BTЅ\N7O[291H֘>G=ݕѱ1Q;NJ?YHC 1ADrrO$RhN/.OL:JwV4Q,n#r%A "'OH ;ٞ.r=Խb+b=1CVm#iEMUBA^dy..,O}Y`=%ɆEjRU( π%ξPΜZXyay|bHhА D5,KW  `BT;WU59XWijwDG#*=aQ p`L(Y&&@~<˥OO7vRm3q|䶫]>{G=bA&y)KZeIC^ccf} AP KX"&'>5L"՗_z/{9- "Rl*dHE!=SI&ę 0 owI u9NTuDkb_JLD! k.bXt!NR@vLJ1R!m2m"s"xUXA$$++9@$x2JwWu xK͙0ĦBH4K HQ> qo xZ0&&!%)F#@Z`:LIu?QD%E $oU LHwV 盏@2EjQzǎxCN$4&Y4OwMaƬXI@RF&V&]rU)yUDј"<* "~ nm%_sbsgvjAƪGgM/FVIH&*U5!Kؚ)</rU=?nBfxГz:BU5&'ݴפ6|۶oVHFX1W>xs*9S,D sE8XH 撣"I a,B&QX@b<)bS{`$@xj4fy8B2`MhH0x<3wMgX u9QҌg" x 毆Il8˘uf#1iF[IV3N㕨W!%QĄǰ^ˁ*9X!xS \,gPFZ3DH4k1>='/4}o׶#'~XA~" )Az>&d}|@ʣHORiR Eҳ5h#/o/nܒ&AIN1kG DFD1U!e_ !=D*h J aLڮEƥ3' }ě$:3DDl՜u%FGØi OBXPIy=yt @=Kh G8X!qU~A!BedlCxN"̚HcvE5#l<(ÏhbDh[SRʃx5[Hl^/ #);ܚ~ @J z^=Qk5Sͤd*K[ק<șuU.QYnMV%" kF@4AH*ߚ.L1R@($֬8h@ XmITUQAo)ArO&^D<}h05AC\I"$(hDc gBέ"s |q%DtЀL S";$^s񉉑+F:۝KO۵Zc MVgxxSy;b gWg&LZ{Hb%mě&BF8^EQxz~ + A3% l(HW pW>AHl+puRD"DEVW{̠'VgZ v6Zv07d/ȀO[o~{|rvN!}k&sK#gVduyȋ^ghxf7FZ[,"ɹ3#]R2} hf^؀~uv #·_v;!乒,jIt""͔AZl<ϋV;Uhyqhz~hjHcdԨ+k\%Er5~*3Fn5U.NTr$D*4DU"M^UEU3 sKj}{P73y䉣3sӳS_9 ߔ"uH< BDek߲< FD 5w\5~?ڵ#X۠\bWs yv~zL3blj[o:p8]I4:`RN 7<{fԙ<=I( ) O" L TJ>9+{kHbU"f0/Ā\I|yؼi' pKK=cO?'|2jtp̙pnE믞B;8/boyzsk-3M*濷r[ڧ9ˡܒAVCQAL_ۜ gV"c3痌bsؔ8\H FL%@O/ hI٤ FjP2065#؃?t& @":(=g޺J[]yqxiA.LLTT20sjЉyO2u7re]CO?ywljZ} w;ucA @k)w#_!>`ɝ&{50<3ϯ'81cGF6cдED* "C4h$h1C HD3leM?h^)4Pi$3(A Jf/Ec KϝRHEE -*r'HrʑT`1-_M&=qj-5^ZNw+ M,~b&[K 鮪ŦfgZM̝mdMHbKLɳ"o4iܭl>}kdM^cN3Thu7Ek*4SPԷ $6 IDAT-W({hIkfK0–ƇS~{!澵Z[+$a@̩L=y:p: L=q~g1"r[S5jp7'ea#O~4"H"f=ƛ1EҦ?dB F@@qi<+9(( ؒdUqq#4"~TL[3Ćk(6ž2 U,+pKHJN Pic0I&݇dhlFҬ@a5mHӐK\W˝L2R" r Ju7Px]3 @Ŀ'ۛP4p; )dn?2C9@Dhd;}n(bb4BD`!U/GhE$ɂted6Ffk! ǑBBd se*28/ju &R!И @7% x<^G 1kVQDW C V!D_]';%Gfp%䇏jQ^٘a>{$WwA!诠09&m:Ͼ/4dz$9@;?&Yv/0vX{>DE~4EIHW +A R',zjLdbLѦ@Ult/| a,^a ,W:No׹CzG^gVMyd'o |qC{Q@ kAD煌^{XuoU557 DZ eDVZEV Lܧ> n89;ebbMP/g{gGٝ=vxOn;gjQAELO͍OW0:?|UbD"_Q޶sWut~SpMj>?_MA~?r8]ל_>6ktVWV~I*dz'DE*"XYlȹ2քP/hZA)di#Hz@hƪzH#[tRu9kAhW#߾zw™ś}Xs cلG"IG_R jZLQ xs#U0̙Te`ILB 5l ÄʌRV"AeG1wxA \ ۜ c@7'@LPo49.(#&&C}n5$cal&,UքLA 7Aٯo\$FNZ6w_~sWz U;h 7ms?pr [ryG7Ͽ'tb{{gu/Ǟ|~sW;¹[ז>ngggƅd󅭾psa*^5 ,,=r$6ܶmN9͙[3ZCkJgu 5:t{>VHdžAE]ݮ{$ڹg9 hMw{~ތ2?(+|ouA xtbJg5;~ܰYBTYZlOwZ8'N7:_}3?0ϟeum>9]\z v_OY ;Ll,ϙ5l\7<~&Cdp-0zdDܽvNmw]:{|% 9kȡ {ckVHItyx'"l,!΋;;t} dLl3 {ouHZX޻?[śR{䃬䩓K[t>ciȦ7^>q֯`흣o=mkw7?*/lm݀oӝ{,mp;~7->yd7綶*tF-_מi ՙ3g_5i۶3MF' XDW1a)@z|f)FC N ^-Tֈ/B#\(&6m.=C0;oIX/NXÏW:!kh 33Mnh'79$=yMFNW>~Μd[>M]w:*E 7-G~_}2ߴ{c0,r9WV@P5Wvo%^o",1W7K2Yb{F[$Tj5"P0b:.Y"\4(e|Sm-L^;HwLE]wns͙Eh.d39("CEȃnQ ,OVOMM&̡ jv9(}׻3;%m@$GjqA0!I$3k)DN۽wVjѪe.J52ч1(z ƔŨG+q/fe&G:]*}2ߢ$!_Lx' ":O ) $hbK88# a"Pxg"eZn<r\Nlhf6+!(C#P"ҘIE‡HRNKUdEa `XP#v ""{R_G7t*>~PTVcߢ둬!@59V`LDq$XèX B+G)Cy$/s2Tv@ 4 Ls IOYʜ#lJȰ.7y炀7Swqu w@O>_%\캝vk;0̄q" QmQ)R ##<69>P: DI4dG.)qL#!X|d  Akl "7 \mwWn ͮJV?xzիOXÝoypG0kD7dSqҠHiI ̶x3fhw4&PI^4 q'퉏?zϒ}o}iNvGMMXε#403#RQ YQ+9F%#&])e]p(t=)О-*V4`MMj@bʏYmLBwNH;ꤾ-,U^WV?xzapl Sǟ{"S"ɸ3"9HqZ;}@{[zfXT ;Npw,>s8ҽ~g{'WF3@|pB9$"JrrTiB4rXۣR"ut⡅{n?8Novzh akkYnt"hҶ>wC77^YY}nYEǏq 7v~y޴yeq`+k+k b2ϵťte`+4 |jcq,*a QED7ŊiT$^F3V (y1|TI{H9-/ݲ/HX9|d-+/,g~][6g?3ݿɖ'3 ڛnoy-!b=#کZ;Hxۏf{ŅE8gY 04xj{[%moo3~}Z<ՍgpnmuNݞs~&Kt# AC ~F-&ŝn^캋]wLWػ5ܽn]wFqb̒YC#ͲLU(swy'8"(@UK|CPeS|ֹ|Ev 7.`G&[b?]:xk( 4C?έy_ӟZ lŶBhii!\?z~Fj6/N8u  A>{9I;Y}$ȅ@194AQTuf5@]܆e +].BPic~n.mo+͵6YdqW vbP!0j`@ՈdT+YMoh'o-drGx`R`X^^ym k^œ]HdK~W|[^Aoy͐2Y"F3iwϜqǎ=x]ӹ@}7>LFAG jVZI8CW-pWްճ(@Q&3fol9zO^bl}~o?x+?-ĵG$^k_*vS}ps|&AqVW0|x i9tjܭhV.)\~ŮN_}ZEcS3-tWէsM3#AI}#@)(9ɐP Ӕ' IDAT4@4`͙0 y觩%#2\2 傩% O 69V 3`Kulm:׳n9~{t 隡HhP&z{}~j}\TH4AC~p^A"1']q-Y}D'4AOn_0ni;-&ٌsDnR349HVDIT 79 DrQYQ0Ed5}eU_4:HPQW,Z9rEV"h$|W.3k#я懮jWWz]Z?I h?_5)g$z2TLFVN4NKS ɱpP9"򭠿 :& 7~` 9A E龼]ww$mФm'4c" @HPHC d1BH <Q`րBAVW0`5ME Qj/s3 !y AȤڻw_}>@z;wz>0ny$铳Β˶헹}wlKkxH4Z`5/oִHc6<*"r9?JRF#m̧< DvƸk=irDYFbdMT]y;u ;}_9}GzݭNv\O< Zwur‰CC%ED:Dia}+(DG{15S1kӞDԅrjhK5w.]H fUsG{;oSe[çg?>^qo-gv(t`?|ϪK5!U0&ۘ28T<6DA+$\7  %#k?H0?gŻ,dij/1CwTa-<>ˣPe(Y!D#N\[O٧>'v.$Vd;H9t+˖16lY%#hL H'a/H$ 5X瘳Y5ƜQ1H0}]nT\Hc*'nt/޸e[-wu:x˫>-qufS5 T`ko߸5M^qS}vkEL7L=qbo_'6v,麍WUQU4Byu+++˫I^]Z\e y,//.,N'Ad+*ԕQeYÜTz% ӭ /@RW=`@R5ݻjdTF㲝χo^\y O'C| >o?|sxϏ=;?>-7NxG·^~[^Aoy%=&CX[%ZD/ϯ#_ݸ;?"\sK''/D]ڿz[ >ͭC^Q5N ўotPlҝo:B$vvvtwU9A?j?@ 8ᓩVQ+%HC+ S#LJE6!QK2 `}5|SpS!@ZG}/`涿أS'>_PpޏWm9~{t ڡ"/0 a o?ͅZšH $`ؐ omkk7Om.t;?.x(^g>>A{R͝6,("0WZx1hbcrkY&su w=ul׻sÖF8`HZn-6gh& ūCx 72YPPffsͬAkXo胾u^3P켸cngj_/T"E!amw:>94ܚoϜ_8?=wϝ\[[+ϝl}M+[/ѣG{g?wls3slq.צ_=`?=7ya{I "T`YaJGFo)  @KE ^{4С=)xxkd``1  &Fay=M6"__^Y}OzٽMcf=}kwH E c; ab6-.r .Bm`ZLXq1SHx9iMfnm~scU(ne$ #[lDVhy.6mγ9[k͈r;ך1-jgc·G?_yTE졿-xoyUtϴG5̓]'P *~G#6ÂC9@km#ź Qk v#M9 ks6c@ιLCl S(0 JˉeJ崡a J@7l.fu@HQG5{1v($w_ tuιyz{%kCp !'%'?s7Pa@k 0 $|w;>ie ^rttܛ; ͅL4#W U? ٘-$J -5G@9BHQ %z$ɘ˶`5c5VJB$>Qt(ppLF۶$"'~ww'|w--/?/~-O{z[*/-@tդ%0H$Y@7X>qkUy*3,( 劊(@ǿWSĉ/_b/(W!t0ɭ.1mqmGSSCPK+ Vd\Q*5 D~2cL Z*5 AjIT(1 E(4f@`pr[Dc?W#'~q&^kpHHB&Ba a A{_TU5; F9K6(`@PE($!JzmOChs]L^%LdHE"ՠ#ВX-!̀1HŒ2$\} $dhHN" j'a bbŚk2",Lڸ A 58k\X';;;ۛ?j}\DcsS6KfAŋ[zJkP`_JdQM0.% hH@G_HB1"RBbL΁F BE58P$ ΢H`#6U., Ys1' 3^WS&3&G <l Cx8!}Sjll hG?s?uptt@mśrn'mk{I{L3%$׭3*G5)2cz %UM eM<0ncKa 2 mܼp47d'  (U$ ` Y  D&jAgQ;N@mX?] sŅ~lۼ8Y^hF\߿]va\Cvx=c}{W:"e `:]!i30,謥bYk**@r Td$GqgE%yq/w}׵޻Icc$ZomdlLa/32ޟcPns۾\ Nu:®) V+ !#2\B dɏ3 b!];fb/]kdkWB/lr33Z6HRB)`8]/f96C˄3aM>ו=R5ip#PPz{Mw/1m!GaL|$D"iMn,gֶ[nhh,05< 5`kj@"r] nv}}лRMlr@d?U 5}M=.lzaso>9m+>oyUtI %E(_xӿW:!#Yc ';;;66ye8Fe&W\Rd8*>as)=$ )~Bjj* Cj^Ӫatb@ֵ3g6Pp8{3h9A  2r̘&d4x_`o)\aGJ}̞@J&A ;5}k_^?C( '܋;>/eqe: v1!"{Z'^ĆHmO (|ƁRnzQ>TTdLڹ2)*)=t<BEzP\s }j/"~,Pra+ 1P8䣱 Yxɽp"H#2'Q3  $QCЬxo h`){wL-BԐ;1*/voo_kpHeC e"L~n\Y2ucx0)UD06²gW@YT] CމVm)d} ^-H$$ZcGJ) qH tWj,Ϸ9sح,#h($|jJ6 *sFĩCb[͘,W/#^]e C&|PD2p$BvDv aaWTI(J~d'Dњ Kβ栉"8Ό<5@hUKUMc>*gٙ_'C;ux7[~>TYEZP&1"WLi}_xHC=R }Q6j~gЏ0{o $@D>$d,Qy[댑Ip>Z'F($H_0V4} |]5ҋ @U80 $\dlx_x!Ƙ J((ՐOD-O}e<"iq"cDK=pط7]3"*ȫKh9n6 ZI`6Ξ6nW݅mUb-@=UCI̥~){d"eR@!EfQFM E@'CDU͇XJe@ᯜv>.ݸX `eϞb *7ڭ7,u=EUi: ^ɡvHp $^|HT@ j|mooe؉O#Iuu`ςeK  , 'B$163;"18 cD0 +v7Ar(@4X6GGZ@YP$ O]xlT& I ;N0|3;ju٪5f5|oeaQXW6{=x(|I$G!0D "nt첌3¨e!>*c2^43>騟_B m C5o$}&9J#=yB fFUc<]0LٳTZ">2 $ ܕ I!Ф$10Dt}{ fA~NR@CK @df I+׹LVc d 0ApyGP9ޢݵ1Y`#9g$;BG%[m .C9S k#X(u0#`F$@7VG78$<;B3L(&(>x.@l%+㟱A!c@/WY`7|G4* \ElC 1U(b~=۾ ebU3ʬRP|dP,]D~q8*g\&xLtb!:AUJq ~Rn$~ܐ\JGx.[UIOwR v*.`,OuٌWBl㯷`Qe\Q 5ܵh }W(hi5"}HOջKKR tzI<(_hOоblǐe)ԲPّq@whB42ozQCcvO2>^joe8C" Z׏[gmH9g+"=rC$L }cǾxJ=3Uޣ ]{ԩuF6CZ7PP]CqE` $O`X t%J`E506(kc3X,$P$iz \KIH@N[w4qnB9Qܔ# Ղi.KCB~54vO>K1u*8}*1lR{$<*{A$E֤(R++>jP{ƾ~vK.h @!vnQj-Db{T>iM#iO/Ǒ58 ,Xl)'k D1Q$C$9HF~|)=FڕXxirP##4SD]Alj zb4,pտ}YqX3~ֺWQ~^lu@a~/RʛQTqkLMHQWxc  b 0DQV"T{6%O2.\=oJT$` \/DAF< 0 R#=bHBb 7}Q&A-j!$ C IDAT$FݸCFV#2rD$$s l^!ĉX9#f2YW]$egAEif6704їG`\B65Kş`s PK&y\S3J5rHL/}%h+:3}!U>E:B6#ͭBVqE|Jj8u1Z/*##B ꍓOW='(9i,R0mU61O5"Hx! Tq ჿ>^po_?r8'>hM`w>'CǨ_@?+ t娊q֏D xɘ?ds7N:~ f$쥥 ok>] 5p(VL}$0}sj{Rҍzʯv[囗HJ@\6QdG##rt{?;^^YHC#@ qԑ2J_߶m;GG0m K7-r.8q5lqzտbо{fwK[ h,i$mA a͉tz} g_Wťzξ.Y* Lz_~>-V$*@ćU4\fK ^6qlBb@t P bko}|aAA4VTqv\~Wo^.8{W_aA_?0=">+lp )Qwڷw%l3(m^ΠS"޲p4X t@i4 7zd %S T8V gdos:iS >I``vq{f@ˌNl.x0d$n{{ϟ^~2 9 J/ Aͯ_]]B{1~[|~+_̤p]3f8O=??&9 [>?Co5 #cOSK7/d#_lV9~fuyh)1A -Sꆞ ,-l@c&iQ;t37N @6///HsՆ#oO= (ha<Ơ%cw¬< ۼ6 @A(Lץ\(NY! X_j@jnbQ<6%2h\=eQV%VgK+R%l&޴zQS=x{Z!QэA6gdo<Z[ϕ>Oͷ{o?ȑgo瞃-ďǟ:Nvc~=qE,޴xxez#gn^?xn;onD Dع8]/7..꘳-+PvO:>z_8u䍫OWw:sf~tmeowo'~tc“vbG|`ЄKƼxKoh-n6sgGW߲'yr6wMVxOMM9Ա -kUM-v-X84w_:K7/f(839И'7t9L gG}]\KxAs쓿|)GL08ikpPBEQ|Q\5q]uq\2ܔRG)et2&7/Jr\r q5GU((U8zSϬuO{>ZESqk$I8DA.dB6 *UT=Xl~ :s"i[ 1㤵,EwTLUz̊Bd׶2G1@&#S/v&ß{@מزqӆޞxNMSw~} '-qZ∙nUhaf@_vDDZ"gR D֮[-_ڶ6+v30Gwv<ogO+3nÇ&rq);nvCݝR O+Yu~Y:;xdSӳˆpz྇MMԏFι} NMD,b`rs0ʔRھY. T{?lOwutDbt㺎ᮎ}ק@z-3o< B>6)$KRy\=ϝ~ƣ8\V?^)={hV<2>vx @I'xfn3]W`gi3XT Ѽktn:R33B>Hc9>=vIB'OgwQY>35 ̅jzִ6GuT풿~"ϻb}juW/<>R _ȓ&MX%7NI-<;82@ZWoP}F7.cg-J)l͝1q|]G~7ݫu- ٣Gx[DyVQ4w/J~R$ȭ;QRi\WKfnX`/Vߘ9prWww/<֭<'q>> >}+0f 0ޘXjVB,Ԃ!Qyوdtq,;u7|{ᾚ4֠Ut&z03f% T`ͰYOU\z8L2p}ZU2p%r4M4C\lϛqn'7L-7Y1@YD.Eahpg2[\3xi-#C8mo[{i@wI$ޤj4,1 W .)7*0g*d3km~v W.Zw8*D0WlҘ%=183$P޵r;78;ծc/ս &Eb6M"l,y&C`Uٰ'0?Yn%I*5ĥK_eLP| m׮72&&D T-P{Sׯ0(fyW[ʻ:sy_+L%r.3 pd8RU/V-"aP"a\r2daQ "c "} T5! &_[Mgt\R#˫̋ IGV4jC[h';XAu\j1_F[ w\$^&}@Vٙ,˲YTGSxVv TB', }SUj3[~@ޙw"#ҝ dUuLRoP֖ "%Uu6#Uew.7 QݞE3ֈ\/r u \ebVLiҶr,6?pk< 4fuq_]!KƱ' d 'DթUgԔ#|aHPP+od٢Ӝ'I]\ZRtY*C. r jA:l9cww[*>M`@m[udrZ_εDpkْr킺7y Wi!RK 5+(ވ"H\:Cr6"~U[;Bk6+v&Y5O丢Az'>cppaqGQQ* Z,tpNơjK&ZJ\g(o6QvVSK{97T?v5N=̿ g3$!7ԸxCPG8U9,(Ddu+ -.5@jwKc?;BJD#KYVޯnlr #,5|bRdπ8_}_+y}߿:) 6O5Հpwʄ3/1(8bPwoK No%_$rT}69Ak1Ej+g64',?$4[]wSZ{RXCm/2SߢȊܪ[dԵ 9|WTβPUX]V0)o^Ő+< -`]ŭ۶Mpn܊}%()P` ٥S5IZn:m~-}3w9_44_xL#A41jK;{>ۮfgϮ^׷mwwnm||gߵo޷ugߵ;׻kvv=U:Ѯ ,_ Mb"g0r$|8yfڭl:|q5v9I5gKM䬎q =-,_N-ݿu#_}.Kήށa6}GO=y\1\R%=@$I>""P2M$~30<84c3/Aw#n]*@ K>pNq&JZZ%cWֶVkm>[V / G\M32D î5~Vr8߻+=0?۷?bғLD@+|ZFa(7N84"9PV#6Zf]#q%%NqDd\meW\+W@`uqFҥww;#F?3:^~Hp2[j> Ԓ_1@ GHOgm] @$̈́ "*V:PVa55m rv@ /AU.v j>]*ԪZdjD$[ܦ d5ʗITH? [O,{I m`l|?XTcC:6 Iȷz333j +(f[bjI^94kʆף4!^K VL Cq?wVFylwY/OdZ(+V@ J#RKKIo~צP)i[4M_g}q F6`Z>v=GH2' "5U l@.13W63;3Pj}?io >R+pbdM޿8APV hDıc7n胢\Jےʥ嬆 kJKR>cI'v9f4EQ+7ed0ݡ#7~t ygȞM:w {g٦սO=_2YZCL $,˲lQ4fM veqMr(19]]k; CLD(@ J"BU@}uTP Wu3YW\INRU5B>@ (01K-`Qd)WvD +  YtuvlݼQ in1Tj.*b<7XVOdBk"5eLZ@ 6l:6>ɆŪ2z}>d0y>--u5::ZCt (sKLa +6ju||SDV`z*0@Ϡ IDATt-AU\y+:U"f Bp$9my5g^{oDB[$fPlݲΆɂ- Ղ 58wnԖnc/p݇v_>Y1=yG?>00|u;KI̳\xMbU5ꢪ  CIHwSϟ+C'b6 ;F_2ݵ{?]qlB+2d@D!昢 ξ6_a{w2_U}Oo]^TZE9ҐRJ@ PI5bH*,zuyIk2Svlާ{m UZU2U,DU VCVLv:Ͼp"%X_89$JXG5W`*as X}(=]]KY!2Tь\W82 WppD 0B@ o|0nQDMTKV" jVU+G1G"Us*cxU,.b>S |dRSg{iZ&,Y*E X]xop`,` QY@@yzggՙu_DY'~N1q *BUXșTD2ѬVuwt;$[{{(:\w@߹3aHխLa +6("T&G7mLy綍wג_}2C,/)j?#{ҥv&F Q'7⺸T084eDp܉:6_>\%PU2 J\@ v!RU+xo=qk7o hKmK.k޵mJ[EA%8iIB]@ i89 5o͢vj:5/X>t*>'bS|g&VQM3XJHV2uN~g066Nf@;Tg E855ԁLTjNT@ FkӍ`! њ&&_)/묔@ ff(LD!_C X],f>҈Sq#'*x6@ n3kX@t^ѱ_np/.ZhM]&j󒅀_RS4@-X$ťM[!7P Ԡ!PR(kTj5SSSDUg&9δNndb-,uEGZaP>2۷nVѱq(^ 8%КML>JJ ʓA-1L4 ZKʻ~e}"}rnzp\|Ֆʓ>p}uEG;T.X-Ă  |pqqQG kTJZ8J8c0ޏe&vU'UaD*DoC5{V㈓8qw2ɚ2$Nm}ŁT~Y&@ZBN@ pd#z]wޑ0ITz"b(9nafJ09zۃ>XOԪ+FR :g_oF':*兌IVZU70زy @ JkJ5J[; 6m/'r[%e`"Z~S7tu7}[v߰R@#'?wco4no|QK\/$c/<%뮿TnWZ^=;|ĀY:޿޻U??:܍7<^gΤD13=Ύܻ1oFWi0 ]w˝]=K+ٵWvwox-g= Yvx텹ٟ;jo|㛁@S{FFG~O?~KS%՟NLPEܾ FD, Lv:?7;IM@ Tz|O.~-<:Y%yC㛁f7j4*ڴoVSӳwG_D2;G3;P~[C1RTdi-d"CD f˗`õ;nSoMqW~AsQsZvi>Зokɞ8;_ 8\W >|?|xx|߾8xW ᭖/̽:7|J^:ZyKq ~;AWoŨ9T\!mݝ0gAuJ.\b+0rcJd྇MMaõ;v.|~4r |57_ Wo|'3ZΨh&kJ|Հ(g@!#]W[oB/$c/<Q-ot]V vZԐ.&GdZTS'r@bb5ˎQ CV'&Oz >L,! s3E.I ;;g_|w7%%wMr&}_|; |57_ Wo|(!SDvu "(`Bm [疰zg|㛁f7j475ţ'*%R3Ed@vU2;e^?zs&h1j42DTVhkt}iWo\Z'>;;+&)_\JJ2GռYL2q^l<52N NHOO?\_{f]gNYZmgc Ke"P@Ё@  q"q&V`Wb TZO2quva,rD ]Bu\ 8*j[`!ari_`6rwDGV*zg|㛁f7j4X}q2,,E>RS4D ;JM3Ň'~@O lovo|㛁f7>TȠ_\܍UR5|Q؞:EUp-vn .FMМ7bh6ԒmTt]vEw=]=Wt^~eO{*Wvutttvu=k;{vut==Wuv_ѽv'"Ue [whsrEgE@ X5 #FjFכ"LͲ(_A ˪`1( f 6Pf(2~¸֬Q"Rd&tean[>_9mo|㛁f7>dHDJz&K(CyqyîyE 87pvF‚ `sOnM=&w7jo|㛁@Q%2PYI"h>E)Tm}K% %(&(Fʇu=<|k4xj5Z@)u^yy{G{GG{JGQ)WU+*w\V麼ή+;:긬 d"g6o{7\cõ;M4&hvj5 08"E+a/jUU PU 'LW CDaqvZ&o|㛁f7>l'ǧ'uGL3f;Z; |57_ Wo|lٶwKff"P+,,Ϫ;l1;#p>?M>6pIb3삥*^|iN݊|lca.Հ 8Rb#`s3X2iA" $KSFإj|?<w7jo|㛁@Q LDER :)~܀ra_ R,,,Pof7jo|͆#RE[98}<;kV2DV@IRՔD|XyhGnm< s<5_%ϸPX,y apT"&ˡ DrK5;` YJQ2\fQsG1"Q1zR'-E5v{rV˰òv[9+ Y|3Wo|3 |5ZL& 0y fD}6-@DK`&YUo>񛀯w7jo|㛁@QU*$Ű:Cܝ!vDbETQ`k+<(Bj͍ J&C`,NٲQ@ X sGy8߬8:eF aYqKƭYM_::| t|3Wo|3 |5J[TյRE/ ȇAwXhh)`W^n~fw_o|3 |57_ &ĥ}&^f cA` u(5k‥>EyM'Y/8G'G_^9SoŨ9lgWO>>0zb`pGG_Ĕtu~ @Fp&hvj5 1$1'q fpL"$FݠM AZr 3?7s7_ Wo|3h.V9KKqLTl 9@\5:ʥɥsHF mNz!ߊw7jo|㛁@s1qb Q$[34(CM <",g2yTb E"R 6@Ҡ8O|^a,+F $@`Xj Hv&H HeX2eJjBv&SԄ{n GԱo<Ͼf7jo|E#2DTԩ[-s.Rb+XV e7s'٭3Wo|3 |5 4nM:.력<ȮγA>tD G(_0ܫ#مT/FMМ7bh2 `hhdzvfjvfrzfrzf|er&21ѱѱa8wV*q\{:Jמ |/F́BfNM̜8_ 0W;f7jo|MEnܼ_ 5EFTjh Uk SU™eUZuDv&@-:if0<@,PS8 ϧ$`Y a`2GEMR==:xb f7jo|͆`0WIDAT"dM9aEvglY4r5Q]a#ٳn|3Wo|3 |5 `&!6V[;KAD@CCo*ɰ "w7e s<5_ 2 bEH•xf0BV\;(PZa)+~pC_9fQs5%YifTDDLLIEI%=e{6M4TEdY*5EU6zXfoٙC;w7jo|㛁@S!7b]HI#b7fm" ,("6ID ڼ=XcΠA#0o|㛁f7>\ `-rYei4^˪`@WJ_jԳj@ ZXT9Lx`<]\#Cٙ*`rL5ˢya|XE@9mzϡS`iiŶcoLV2<7<,!=Ir(od̛tg4s*&Dj|ɦR$hKdhѐ,S,oؿՎ;{AD 6\;>>ϞEsqŠKuop*,Y{J @Imx Uo%lÿPLQ+၆:g͛FGǫ.B /Mglb˵[I#vK~郈;Lb^t[2P .D<<@ (Xj}Ϻ8 OEB8eۮ5ˮP[: 3A"&`jkpo a@ Zj"k#jq 3ܴb߅:оm)ץ?OBc3ָQߓ{$j㢲`b u"8isDU?R?+WxUD%]{WSRfVa' 7߸yZK(1\L!h^{@yL j 5U-e )ϴYnY]U4!t@*1ͭyȠQk(7qKm (/j]omiPd鞪Xxc@?V/IENDB`hivelytracker-0+git20180223/Docs/images/wave05.png0000775001024000102400000000652513042730153016361 0ustar PNG  IHDRhҫU/tEXtSoftwareSGrab. http://www.stephan-rupprecht.devB IDATx!Xs"@DLD * DWT "WDLDLDLT *&WTLDLT " DDED"@EXJMA9 -x,Ho`P(b1@ Fq8Q(b1@ Fq8Q(b1@ Fq8QVTڞV~= >5Mk3'˲_ ъx,^nR;]x9o\~ .jM=myUע(G0 Ccm]Ge _Ģ5[(T|{`y| uTYqDQ8N-0M`/enug[OU pΜ[`ϟ ..竧#yq //q8~9B];'uXѫ* O.R}Vksq?n-;[˱u8k˱6@ Fq8Q(b1@ Fq8Q(b1@ Fq8Q(b1@ CSO_mh'IBg|w*_Pj-rorPv8 79v8@Wl|F;]N>}(b-WN +Y"&-Fq8Q(6_-M׎?Aq8Q(6}a|볋űK䓿vXPx,Ru8M҆~n.Mq8gi*OsqQ[)YEOUD=M= ^~TP-䓿h*%qo*8Q(bP8PoK;TBq8QĶ8~p+OےJ1@l{JV'+=`e(b*M|4o*6@_*Gq۶;|H>M_m++űk|MtsS<|_mtz`l Q(b1@ Fq8Q(b1@ Fq8Q(b1@ Fq88=z>}jk:h9gNex$nI ъx,^n8]x9x׳N$M4,w[Ax>C:hE4-sMƥjҔ5:,mۃ >^ m^ӈG|z4MTzvN:]j84SKqAOE8:18-}rr; pa>\#jr2\^ =筡2Myd,Ey/8Lxm\<e GW7'nl~Q;zXДq 1@ Q<:hyW_^C*ˮERո^/K/eYfub[4^"\_~,ZW< i4M3MsyPyDQeơz}ŧ3:m Հk֬-+ mW׊7)药u^[0K!MQ<4)a}|O&D)幞A<%V}˰s|׬.^*iZ9ynFuQs"\/:;bPNd>HQW{:h)4ߟ A~{͚mꩊydNn&W>>XA8gRJuu'iҵjw::ǟ9y]fKNmu:Nkvucj8RιcmmfP T ZJQEơa5u`6u`(b1@ Fq8Q(b1@ Fqle[`;t,_6h>c-ۗ$:|e+Gq:,#IydsƱp|u~y)\[, GmM ø,˩J^7YQZ/$9(]Tft:eGq<<σ6L&]iyOv|Of[U.L@sp0AE畗>OI'ԏ:݃k{Z^IXW0 uݮaënev1844&9q:wΙfiΙ3to\ud[aOdwY.WjC#:Uɲe;,L@s0 s3˲Ayle]{ë9wf1 ~?EqnyX8a(tʩZCDzhcvuG_܎ձOm]vX?iZǖG&98U9~4M׭k f;g/ݘqc@ Fq8Q(b1@ Fq8Q(bpB_SuIENDB`hivelytracker-0+git20180223/Docs/images/wave01.png0000775001024000102400000000715413042730153016354 0ustar PNG  IHDRhҫU/tEXtSoftwareSGrab. http://www.stephan-rupprecht.devB IDATx!XHAD " D'*NT "'"VDXQXDŊD"Q@D@D "OueH:=+ɱwۙiQ=TdGu8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8H8HPq;FkbkFo{EQ9-f|y{K =re< 'Untg.JgޱmB;T'Nlj}!x<!h033{irk!l^lU ǓEW]afzuZ=TLNFֺ<:Z I;ltv8h M L h!Ҕu"i`0Fm(}oSi[٬7MgZ͒$I7;{9 =re< 'Umyynd&홽ƿEmG뺞8_ 7y6:nO8&U=EҸ8yBG}zкƎQVeiR=Ta>'dYвE\V(~_k[eloG@w0xq{^^y{uחZ>kYEe|r,:?{ p`i:4g=欘qyq zUCj:ވ&EMkK` V`G0L Ԡ<BjaTEԹkå`0z;q\+RY}eP`Q@/+"6 >L1%=H@`v!{ ]l}^<1=h,A0qrP P+? pE`#W?RшGgJed` ]|G=Ac&AnX ā)&W-TpLT!X)ZjL[}cnz>)"1XQ V[r M([6hf0 52.}t͍UU2y`? SJblɴ x1WH<0h[e 9tC$zE"2tq ~= Dq=5 Wç}r1_ӄ HNUXÉ0b;p}uz3QIon֫ul FB>4FLM%G!ŌΞî\Ljvt""$y5 X@uL!"h՟-r:iY| g~!%N ~^]ӱı^Xs1wė67}m5у;cF?2k_6/J{8a?D3Fl 1?U '~F6ԁq( Q,$q\VU_{sIv)ao`aygf)D؎4v%ׁJ+9x\I׌j01;@mz#3Iv9dh s-lڸ= dFan,ld@in{ 2Iov,}!sR^C|s"݉p5k4I[L^9[/%iU"P է$QD+!h}!^&k )0Xƀ hHie.ֱL10+#[DBS`5zx%z5ގfnD৾ V *d@Lj6u$TV?VF] ή[CL98A$,ZIPݰB׋i$I䄼S-1n#DRv8fj[a'&Kg}B34 "{cA3FzP9dSLuFɢ&I8 2Q*1&lg$TJ $&g; t]qBΈLaECХ&Ni m5o0oAS(Ynv.DOzШ;sIX?vL;<(zl}) mH纒nZ^oc @mu-BT'yTģp Hfs 0z"F>'e󴧡*Òp;&l%6NzkɱuOq 큠 n{OdWn:!lv״X܀YgnFĴ0`m0ڂz+N3{X~cS" >1nȵ16y=[n0՚əUBN2nz*XY+]Ih\sٶ iaդ9]An0J8HƚK4;iT9i<&l;PC5`bQ!4_u]T:$|-,^mXݼy`+w?H}IݿSG{YlAX 4X[dUYVS}.ĬU ψ bfkp F,XYPK at=D˶c!§nij V̝;q ҳT/@!jE쒕ae{{<]5ҷ^b(0&Ȗmc,>;ccG 刺wУe3 !(u (47hgb]Xg`m,[k*x~YEY&-ۺҭz1oHm.Ocݡm+n3 dfN&pySNT9&!4֬. R Pi 94q$'*gR0t= zG*E̪] M6N;;lar pcJkhٴMOǛ_i6- L(w~vi Ltujnޭ>,PlM伅`rZd@n` M$pYDZWp;4)g0k\[Epբ-p>QE'^^`S,Zs9\рTƽynAx4=A.fl# k%vgu}fE#j,y8\2xJ+DowS9<],h0Jʺ'0&Դ  ]ZgPn6 y;E'^׫ 4\޵ zB -DRv};R ȲL$9W6DqFc4a0"2fQ?U\LҎ`Y%Fd>Dy/ ݔۈQ;WgR VjbM&Ĉ @Q59+ a*gdJ]%($10_|L$oSU]W)Md~<]y2+fiyZu̐`90DZ~?>><=OU-'@Vz[s[5mϣ :NiZ}b" u*ou'93V7CwNJіw ?vOϧcީ$|3lyӴ HwLbߖMteiH6| 6ʪ&Anܴmw{]wO yuWmcm$lWu=6Aȳo~]~: mZ"M~Vlo7+O0P7 P?Bg3"}:?y<iBqdUU)iNJYUDYOUyuWmgE͓]ۭk*n|m 92/.×̊`zUhl폋_Y2czl?WD?,'̧b6v!!ʲ\dBoB!2;)z'foi>I„im>L3/{3ȇM^_lZ~]ye 桛O㩚M"ϮAa 䌲,J}Ksɷx]I"G6iW[9{/bmvY" Mr Z,3.06tǡ%L^j"!&$$Sn:cy,?<|Gũ`N@pX),^GG]./ҏk;\EiQVŦJ %n.k-k_94P>eA HB[@/a]Pt*H[n̆73m$I690wCVwZ+jwovŻvY<+q4!JTr&ߤg%wuuW̚v>uc6+i=9絓_OޔD4AY:eHDeʺ"X,;|9NxUi лo!ʏ_NmM'CЩӷjQY nnwy CCOt>v#(?|9ME)f#@Uߪ~y`)Q>uUwcv"}:\NLnL ̪V"+PM B߸4?}䄀ixNMZ-f/'墼]9HPע[鯶h"Z"}D*$(=O7ɰ-E%gTӫ*3:U&Iiڦ(2"4meiuϧ"il]j +%tcqaQ@ r v"+^.{aX}q;10)%Ff14[*q`+ǰ[3ʄz gs0CؽgS%PBbd1Xy?<З $b]d ݅^mX$4!+]+t̙p+[u[v;^JQ˜myCL!p8DJfj;eHԹ'7k{4 y2-4[{BI<m H s襯3XX!9E l6;~C4 An\dgi8ˮp库<ˈHNHŝEb)lڶ I]U@kiof|9/_m:Ҳyb~?ͦy3eնH"ϖ׷/'}$q{8US ٝ":2^eu&g\O4y"V2D.R@kz4`wooltҧ雯;0gi}߮Ϸ|V~Le3[]#K3E>ZnwG]=F] Gfp Nؠ &8p#QW_F[ۋ?zց8ɀ5L(C>uGQ';cx>-j97f%=8oiS(`%?n7{gGLo/6AN\;7>@l9T~+YB^D @uWNs8}+]EiT] ^/'}wav,+PS}ofEzrLd꾞M7-U-%-EyWk BUUn߶$T׵<$5r} DP<|)TDT5t\=P(Tu$MdkurfC;ʶ}zUͬ>$X 8_i"'>U 8Ur.ʺYon, _[(w ~e_jzn~CPU7fiJ/GE82cYgiTey=;|yY/ k|-,\N_ $RBXVY^/gəlñ,zz1ӫ1RPKW[$xg-Mx*ENDnx0[o|@Sq-SMrU?r"}:]-_Nez;~n~]7;&TyvZ$gmcuW&-[A6-on8b+0` Y{WC5[ ek:s@A+x",䙔id̊/t=9LCn7$Mz/w2W(21qI+](6-| _oć!x5簓>1_A[#z$GU6bC pKaXDBBJ iޗplv'z- CyV쪻:)eq:ܷm7{1cWw\ 7G~heuZSGy8HݡbFvWrk8$'6~n6k\Kϓ2cF}Oefp0e}׾iZu@$@La$ۃp2ӓ.|Ϡ͔}XJSK.bfd_s}ߔߪ-J_\5㩪V v:U}X U]?Df.PuO] ` ݧ[$RXV]M77_,؜[lڮcMuSmNCˊD9[]o.@BlUu}dm 1) '1{1]we+ +1^$X؈EO!^v=iۤB"fDM9cjno`ɍQ?Pp~,@t6 W&BO?Æɺ>Bn CXnUQVu!;=Xx. d5ΜBl}l G"#t|C55@y 汣 Ip4߿I`c@s$%l/F46OCn:g Y:_޴m}Na}}Iu}c=ކWB]&,3cU(AD4/cW8Ut}RգK|F: z5WaslUYS%NrBta=vMypRɳf1 cp8'o B>YؤJ8PzjuLDwo]5ߎvNI _O2+Lg3BrTu @^Lƨچ,1k2m["ٴp, !kѷ8I%3(ޏ=`y7OUԝ1YDp`2Y[oLeGk-䤢#&}tm}l [/u}рwƲ)~lsuZ!_j-7|P 0? x#DC(Ak/\/Jy%+@o9 A7`~#lSv2?¥b/sAW#ۏH"0b5s WM0&csRBX+ж"'5c,@00_N=1BCdc!->L$Nde)f">\x<11Wq !dPG'/j13fAHg 3Anӝ5[*:!@X }aꮴn3O9̢ls;BfP 2 =   inzZN7p`X눻B~>?X1Ӿrwf:2K^3E %ڨa{Q5Dfi+KCL}n%Q"S4ʠ v<4K3zĨmP@S=h/ql܌ u ^d Fz'J[ wH֖V$;DWYt\xVDخB`~&BDt7" X& f0CbSr -z {BtSH)`ͫHVsWxn& U *CW04t AAz]kfo]JWSq&,fZ6N3"Uul+-YD"h|okj2Ʊ忭)]Kdd]%hr=nl+{&)Xy=Ϲׄ{h;j %c< "*6gA| hJ) 0 G{%,qCSg¶A'åH@^`K _eB4>7O@^1aSTA,p8[u[}d0`~F!$eAE=Q]RzIYXejO/g]5NZ`?cOD1k)y(Xо3&WxAS@?cBjilHOw}kYGkw" 3k bIY:)X.9yMިWS0\&SmFDs: ); 82=Jn)V+# c'}{H+47߃}j% aoUeOԬȽW;) x׬Q[[5[?0U0݊,8 8;:#nO1yG7xq{~Zn!ʲkfPL,?5DyXh\Ҏ CUkEoF8 pZMmjkX"vy~yPWBn#g-x!irP_\ TeU7up9 cxaeV#E 4Rb)lbҖx.~WA񈛳O {IȚ#W#D(X]nqt!`l &Wr(L :E|9 {?M+ӏ.{ܩ^o;i`7>]OCgwAc7'ջQR[~C@~ĢslKy!O/KH[YxD`D鶳5al3Mu]p*9*1~kԱk(n&`  o* pR5?9ٚxE6HP}6: L+^ư;uzj]Ynm u:kEa[501O ֱi×S Բd;A$ GwF4Cy[qm@ π0f|]1w:<d:fvIJ6vz v?\  `!E4$+u6:Gߛίx{ `<@vgŜMSx1}O |2 L3*3kxbƚ|mӰg.20Yћ<|ȋ7= 1u' ز)PYGhtr71"3zW0¦s{bѺ,Ӯw~7"NS0{m>X`QǪSw>fwb/җov'dn_a=؍G(ٜ>7o~(@5x'eQwO]_ۛD~P_l{Wo&DwtF*e>9|I3s M^yJLHf2`_5{&F)@4ϓ~3>~A^f`Vw0]aѻ~ uUx@Avh/Ci xQbp𫇰y/F}>:{dl"(eknٯ+9 t߻0MѲWx\." wG:f ؄A(j|8U\$}Iy"S!$py?0LanBK,K#)`7%%{w_s+c 0CӄPBvՔ=0r`lS_ױzE[ y"ܹmy(p3`^3hb Jv,7ˎ3'ߑ8A%o4ߪ;6![Ng+SP(R-/C0l T [[9p`ҒO! VWR;BQ'VY뛹g(Y\H&[ CY $>):;qH٘g@P`댒2a$"&y3<4߱#4}sH@a+f]Hr7Μ8?ޤ?D qQzA P&DdG :!`ph[2^hެWM(V4!(:֦3j~1 B^P}-T A 7U6fJǥQ {w,5>NS/`0!BpWXm`T̝/K""a>/A9 жFV܇"= +5܋J < g HHq+xA4ʮ&b3-H]sOGElneb E[1XG+="Z -[}~kaX߇XhzyI]sBPGh{d9`JYLeOMv_:s5%|gR;` 0* ~hcC;qd?~`w̲NL7>M:n d}R1?} X㗋ɭtsDcB6ì@`.&"Xj)52,15]+_ "=H$5e:/}̪w]t~9TWxqT[`vE֞e~U=qzF $4 =݆ؠQ(&"]Щ r]l($F*x¼_9iz ^scwEcDX?+䚏23D3DRN/'{8lՂ78`BSfs9[ߙRСF=J/+VLDl|JP47(A5Jܑ=Ži! ;uIw# z`~Y ^+@_B,($R r^7fXX}iBK'x.h @9HB#SܡnYfuloc ,K?,!ia ő#opTlj@+Z"zB`Lkbg0ziw{nXa|[A{Y#Xw[OLJi+?A5a*n34gG839nWz\g[k(VyW@gt D%)Ufߗ` C·M$==y8(a0\1 l:..ve]a  @E:0ꘃ$0'0uk )݈zlZ\/FXfw_?)3K3w[XGf7~^l/7bh@0/߇2uu@!^M˺c\ aVpVЏI`,ψ\:D t~/@׶aѥIőn1:O`2ZȀI *@7f3}?3ķQnV̏us~rJz&O MZ!u.G&U /9 ʌLh7Mbut e꛿Y(@)GzϑȻ:`f_R39u8k, x۰)xG[SgBiFƮdX?'LFvM"zk iG1 vX7bzsZMm 䨳 ЫY7:fЄt&uE6"#4{noL]s?Bc dkp8j=fiY.3őQ酶/$ [t$#hnrpUsS.ZJfor}8>S5@87dE$wP:~6%{|qG;MfX2_u=x2%E^pJXd ?r |W05 @.E4x3}Rl uL`4k( æz*4$>a$$ȬD<{Q|n`Ѓo5_"ŝi`5~1ly.'"9`܉"~kVɔg2M@ruj"B~!3v\*iB>b6-B{BY5IE"'`Ft̓ǁq3/F# qL^ldi%+9!f.ۇn1a IDATL i\V +,f/qo D4$KhS@+ӼXUg)Oj}:?Id'rB̨:^yhUHRʲ̝כYIN2 qTEqcYw 4Enr{e 5fvNW+DͦY&Pպ4fۿ3= ^"m6 .2""y)@ߨӝ[^. P7viv*&`ZdeFuPu-nܧi2LvO|*jF`cK(Y]dl/0hKxWG`4|ģ!nYne t7P'}҅326|:Zn/aKX>_a M=\+S# r.85#Bz3Fu]ӄHEq|:ppa2{3wMެ:nV\-ϧv|a'Dzɘ1p<7+MWx۴kվ,_ixJYͷn$s^p$kIf&Ǧm˻z9}>>i/UrjY1"+75 ,֛nnK8Uu9{l7Lo͗rnoGi=ij& ފ6jO#W8=4\9(k﫻&g9_lwOG9×S&qS =-Xty>v3-n{iNn;4N4K;= !oWx*_]d{`H80ɝp5FT4Mק*M "u~<>2A 3XS9}[k/phaWAD=rs_;zۦR6MC| +gRp+u&gԴ-/Ӯk;및iӤ ~ZyAaw8v]hJ S!ڇi yW(؄G@ŭI9*/hk9 Vz3D[-ߤXfE'BV! 7tx V!l&6+2nlh}VHdqU "q~MT֛}cEBrFeQ\A>fk4@U774Um]I"*$|5)Mߦr6;neiڴZvrV䷟NYL/ 6t&4x:̉4 Y8}ko:r{;&Ds89:O2#"D Oaf6R\~T!HT6}&UU7Y(A 75nf$̳,*ei"MR#l,td 9IK$Be.we2>vv\v>8bD"Jd>Z(:/:Bcy.H/WwMvHN؏T 'AP,$I"LJif+0S-WU7Fv{d5 AiXWxZw=˓+8uQ)e$"=`$ eKPخU~Q97 |\ MɄd9:2 WƳb]ZH 9婪"u=Jc$ia^S(JlSI-HTMm.k=%MfӼyĴ(4!ai 3ڿvUxeJ?`/Ӳn#i5]4uSk)j:/x7{Ӡ 00X`` ,0 `@@@ATUwNλwI䙞[giﻵ_*lUC6ۦHճjjrW|i+ك]aVḟxo[:Q5E#s$hs[U} ({bFz9?|;̦ɋnwJМ^j.Nf9G;=[͗y>|A I~Nn&8xܶMub_ZU示r1KoнY_v;hT-̯_6盾Snh6M2ޠڱ]Zwvb]OP9p;Y@)w*JyLyK }7,߶5r;$=_Bv|t.f:sYkifNt v" v@۵=Xsq5t\Pٟz%]sz6:Dfu U"f2[Uoۿ|6C}98R߯Ѩ:|;^^oC$'~ rGV[.Bs ِ D4~\\ cM>lL&c(жnv\uOA%~_yJS5>x@0KKhK @]=N~^Umg/'K۶r4ՍVUW-F$ iyh'x̒gJ, #;I ex+fÿQ`z:/;wNDlQGO5HrֆBaQ9u&Bh樉񍸸]v6VL Gnm>|w/59\vDSTZ^O5=PnM4ǻcUU73M6i4|pHgV_D4fghDjDys?53"Woڵ_\TɘQ F=U/vMcuRfSyeϨX\d(-ajb2N L z^\KzyMN-JX-](6嗋mzP^ŚCig!m2h(l.1Z/՟#M{ݵ3ŸCq:py$gp˹h]w `c 2YIGJ uy}d7'Y9/6[s{O،v_ו8&EF76m6k8 gs!`<cSZ:BޣYɀS{L/NO74Vk(Q )@w95 wwVs+9Cb$uqLc3&xAtqz'. T!tLE'T&c~έ > Px͠X3yV" VRg8J$4'5[2I-]ulb:'7vHCf:;`x) XX2(}81w^}#S;Pc$S+~;o"BⱘyEN B@pE6RhC)n ɵ"z@Ltz2qᴞ,r'EY/L枔3'xA@S ir}7^4=69~̅ӭ _##M‡gocM+1r:DgFH2N/V!d'l I]lq9E̸RDUzO vp$8'PaP2$ |>GΘ D}3!;eΧ` eCGhtg˵B#s8ƛd|(Pt8ߨH>[EUvS$["93bj,1&TN3߮P?a P_q*vCft'ՇlSccj(26an[oZU: @& Ѽ!eu}hǻb>Ŭ%oCJk&GoFV/j+$6\'\mdJ罏uo{_ (-0 :ltuk`N2=d ;79ŷ\|j?^i8Y{I%ڌТN3=x̄4$Hq#ʛeOMH?nR޼O,Xة;*,™dx'?6tp" CpfyDݔnׁ2sɲ!p~DƢk' A0~7&]bˁlݕ{uTrU *r3^yY1Eޞȑt$).-EX_I)3 fX@ig-!,[}K,{=ZѝL{i6e'/Z;Q!f5u tBgWۡDG ԆqvMc$nh:N B]mz8z`=2H2a^BbH$G#2)WuǶmi \*5AAJgnl#'.b.Y `lwu穑<7:+) NXZnOՅHWy%9߱dA$1.iQ&_rN*j&5U/Apl N |@,f~HcG :p_%eDt'ϫDzBNwW~a3 G,0pd~rn`yL59%*w tڙ}BcBWh~4V8nrV   R,sR[JxCdDp R+Ù@(T@4RH@J?НJ@ly-*Hs&X1OYL=* mEh.:x.HCG ZL?쩎zH=aserrQ:L}TTY%&os buYBEy-S#9#uV(X<0=z Vo>9({_''A?Xs:l}C=ǚ7Rn'e;׊ISkb.:PI1+ܣjnCBxh͢rw}JJ˂H&c$ԯNA($jC"G A-(\.N?&T]% &ɲdwͤwEKԂXMInI2E\Ef}mzf)Ay=1'rB2(J!M/`jRz%ÂJ}gtY^*)(4ܓQ|%r%>(\@'!?bw}Š~&D@M<]JX43UY`]]R]\p*se àHyD  ~\3GYM*ڊE- 7ߵ̓~6gpɳT$2xW7)Ox;1.wD E&N2$:GD;\9yHZ[%daO HNx(޿HbpV:{Ź,%E@S_W~AaK42b*FqLNB8R{UJMA-sSe @Z05\% “Cl@7C,[ɾ~ XekJ“ i$+Y D':MϜ;Tubo6J+CSt⑤+bؗ;b{pP:e`WE:!OS<>GPDA3 @ Lc2qV; P (I'{dhSIzH̀;΍K#gt[ ZXVh',4R{:.gcL$gӶyðHW)MuO}(^$IjXބg56´M3ۦ`ͦ;[3W'ۖk[cM IDAT +זvznk*ݐ 淸KG/}99pG)(›kpm6B;\hӼl/.k j,WLuQ)t^P} ׃=gW8%X%c'UPA;)މtyZ30fى NF8q2Lzԡrfr8y}<$ 4!<={ `~u;VX6vmu.cexv:y;UNa`HS'=XV̙7_H OtRV:V̻"#Z!;I-]H WQO7,CdHD& p~}H[W%΢;bʺ |gB2ND7(]"wl>2@R8[WV}JL^%%;jL@P#47IgMD.Apm-wS"IKǀ|tg)rY[GE]]b)K;rHba)ywg(@?N=cGwә֐{Gu/$nD]΋/]) f;&$6*[9rEU1#8(](v(ޓdvBР@gNV ݃@5 xytCU";cdG`pV=<ʗ[IG)+zzI :.J)-soOG'x (3P2*ONON@ɌM@qZ)jsʩNhe=ˉo״i-r(m]qZnnn|j ui}uF?qXfVW(Gn Y["2%W-v{O>\-7Eb<W876c@ B®jTsfT_x-{jqGNL! EՇ'6: H #ߪ \[!M' pNtt.$/"4C?BD+@bV+m+ROOsQ%?ܓmG)[JL)6BJg{i3 V/,Pl2d vkYEh"1KcH:`D?2r>5j"^L>O/n`0vm؇OQBQ<.ޓЕC=$w? zP0CN 8}79@"V^{D|ʖyecY[!Wb>c;}9ђ2Ps;ġNʾG302Rd {Obn)tc]1/! GǓLwUi~CC(.R 蚑jS BHat8z#hxFo (Аo;f0<Gu+[mfnTsH"#fȮL.+#<.#zzh1"·^#KM{/xU 'P3+ꇦ)=DGߗ;>( R V'&$ ,R=#׷lgGz?90Ao~l$[Bv 3ȣ][ܿgZ!!V6@Ņq wT<*,0Rx"%9FTMSw%fݓ+뷳@ݴf7~>Bxsv^_rj%M߯OW||>P)b}?7|^_|r|t[ 09?n]/_.؍)[!XMGuSjwLDm۪uRW]sL{Ud*;"ZmA=~6m{ÁB۶d5y5qIA=ZQpϚ9|UG:o壶A/\Mb1(`[7ⷛ3 >Φg_}X&~9ZUnn W7׳$/h˧_=Q:T<T<abԤ NKyq%O>:Et`%!AU@{=+a?GV srlMTIʜ-EJ`|h6"Pufw| Yd6yњk 3~ZXmw׫t|vq g/usٮ1zyJ^\ONa27g5z0~FP fv`6A:#Ga5Nw%LKiܕJ;ZmtkFh2WD܎t:bu!t]+\ZeJÞl+ǺVDxr6xrB'4y1g^Xgu߼U^M|3vn~篴H*c=}iHo3h<0٘?Ӟ`OCݣ2^[.O:$orw_$ۘO E@ڪTsr?J3 HQ(ĀBtQt]񷝍۽Pk;=_\O"rxWiHK8-7,#nkA_R¥dՒ<+l |a^ OBwRWmjziH c{h{Oa|v\դL_N߻p@f%#O÷d8VUс 7NPjlL|A]s/Yh sAGAh !X:|9i=":|i/y)BSPLOU{jlV*lSbLxG÷Trz鼴T!Lui_lW7f ճ*"}lfp@UUmG@f^Y=Y.mߔ.Yy'L#وHû M՛nbo`T^d5vFNPq޲zFZ +GFbŰzkwQ_u?I^ӣ;V^Exioۿ! zN zX.1 ,hnv6)8[lӣlffRصf#"ٌV' "m~Nk ba*rfigt:@D3pĨf'nח/{9U.ff3ӉFީhbRXD"&/'HWM)u<\}&ҫ7o-H!%_sM^No@)4|zV3R~yZ#ZJM5jT0鄚ը"$c,)O%}{gjv'̢~%h:~>N#Oy4&"K {S$_ rh4~>>бMyt {OJ.€A(d͎bc ́(2dTPA%{0'M2)+8H$v!1"x'c܃0 `'awQ@i"ɪ~?_U[=thAD%P%axZ\J! .PMϣ4:d5@(Vz{Җi՟_i.L'?wۿ@T]9@py̛͚@u{:o{x:"@fvy&/Fz~wuwG:nT{~~Kʍ:I3'b3wGH <,ƘKNJNHpЉ#򩶎0rѯBh5FU'wLeջ\]nwE QCc)qq'7D?mNt@*?K)'l=fW78u9|=cz@ѳj=`*fnӋ˞)¢{wy+_Ntg#yh'<R<|$)=-J9%@>*l"d6N\E~9 ddU-bSo>xZU<߯Tfleջu}w#uA w @#)`p{D5` x\F X.fWN7bΏr1O]͉)Ts|GКS-CN( qP"lԷ!GqEF備jnd#O_N -EWhj<*XΦXTId2LR^"ګv{N7ZI QwH&fd!}@ž<꼕YF_q9v&Ez`#=d"3 \42ZpWEͰވy3{xI[j(ꈧpֺ$|W 6mIplG `ej| `9GT'=ٺϦ&mDFt2J P`xw#"" 4:=>@̌Sg9Pɀ(8BF_1U/ Xf [gg_]`uF}P9u`,w^Tf߹"+H_a\3S)@#,Om)MDRD7 _]󮔬STB,J= V= i\PDR' 9] z},6Ṋd<0@J FSd#p<*f8ǃ UNKߔ(S ēt3~ѳJ!jkEd$T.g sD>,W+y &Jlv_J`4TKqsLGɐ~;/T*~P2`^][ϼ.=|RDd] ߚ"6TGdd W'U?3 yT(KDNHo=3e^ p" Ix 8KDO:M3ϒh:TnL^ ="PY\SY"OvB#^8酲Ugp o}*%̭i))tqM N";^ :)6Ua>"Я_;+dlM3ڃU-ZYtO9+hgo !YB4A":~ 9p NZۢ٩J"NDrs[j8b|,oj Ipm%];4F.ڡV@R RLRF!dz? O١ 攓5R&(}T< IʨyOFE;;}Hϑ3$EIDIV RGRD:dCs4oFp?G? 1"8q#<[/\^ ZE^RR) ي߼ʄ t PGiN[EdD@(~)B0LUg 'rtEX2 "`&(*b<\v %_; IDATO({j ,fٵ"iQ`XsVR{YL*wߢCyt~U0} DVCVQ%I6`[Q U\ЧXΜlLBhY㗙B2Oeٖpn"_;(cP1gsƣ꜂+ ,1Sl3zF|: ~<+B`!}{>_:T|Nj[emC8^)rWA:7RQfN^83 LEzhPKU6@ˤJrzHk |NnB^P. s|b7i*LtYW }ۿ͟"(d,46 `3RI3u5tq;`5؎AO wKsf2]0 >TX84  'f9x(jru2ނLX͙X$eup9jAJZ+u'x!H J^Y ;60n5n._;j֗-}™]ؓ *Vw$e=3l{Ok`XHHW\f)˿WÎQ]HNa=(*8E*&tN["@3WnʭQJ4ʊ? B`՚,B!}4 GP(fQ0C|p(yҧ՗Gιג!Q<)՜(|,glKAPn Je2:Q xTYOgdHxZ~Ĝ@1cNnQQX%Ay\ɍqvՠaAjelփ5Sp wh:៪!Tth S 1fˀPX Mԋ٩o;O\?:>wٰ%wrH "|&¥ܺ7PԜݑ9.^6`q RPQmRי;!۴ӭOwDOoP\%P\YhĚ ^0ܷ~'jKLg靤Y6m]^h4 }T7ߐ:@=\`mU9kBP;9ㇽ__:Pb *P [jff%=eA†@)~&®#8|^+Tt#P6PÕ BP%M)YZ-UNl?Փ#O3ysvTXV;n, f׃tzQHxȟ``WV՞<\HjD!%q\,̝*(iAm6áF`U2cr]wǕ;b0e:yOOΐf^ D,kRM1c+.g.Fͤ#/z<U^8O :RRPr=p:Te32)"2ulk^Kci6CoE*QDg'Rbn-)y` JT'I(U(r#l:?$C1}9?m!%~A T45`qȕqt-!+PV41YopIj1IsJr֮ýx>ݦ~m7}.E6Baȕ9֜Gxw+|MJ'zƄL;5v7fFRL[#ø( YrR$Qݱ(rz${B@`y<@:5 KΡKa_:LM!ItG&eG2]VS^xcHlOo5rK~;m n3(|n7%E;IЌc*!]Js!]dX%RDdJ:-&݈Frr[YnZ?)2:!瑹]. UQme.g@UY";`COw#x T#@Dū_N{luz! &ZDGG)T{93Ms r',d-FGzS&`w|腚 E*\+V.(R3;J-}l#[w EWb6fAx#Υ{L1VG_ qle$Y:=@.ck**ʵV4$-7'xgMLYbea_& ,>]Q8KS NݨmIPYB r 7k[k(8 (n-o[ w<&3iu5wDɹ(X^㈌hOB!恐}$N'ZXފ6QgN|#\&"CDSb`fZrqDeW]pxH{=S{/"^ϦqU3~WC!s\D PED}5H,gfk?ֆoRB\_J*3/vQW]s"m`洊xԣ"BZ\:b>{9*b~i\u٫AE{H KD_ d2[|/sKeKTʢtls 2C̾ty `L΍Bcnz ?,*nkav(ߌŸߌoXoXoތ7o6 ,"&pnzJeflNxǛT:}7tfDtfg V3S}7nbYN9f>]NwYNx<ONNW33ȧ3k+jh# oDHL8z.l-ul=hF9,dERUtWѫti̕  ͻ.tc;59uxtK>2MtaEhyԻ:Qb b콁"DUbD淛O9^6-3Gc,A8#=5z `}v1~.778\,2R$2J3DrgM~Iȑpg %"@f@nj[@j%~}40Qbm |]V0Px @~;" N#'xa3UsR)'Փ(}]|f|m7նpWy;{`ZgVg7cYLvzɣݮ,&hZtf^]\ljON歶p}j&ɯ?YLY_^>6Zp2=OzVV*}WjjX[jmYk+!x7 + IHήKk,"Y@߫_76 M `yzI\<տ,4^NւտQ՟ T Vu2KٺuS={59ҥl=>{?[|٫p_yYqlog; o;ޝ@4T^^\RO5tkB,A *t:?e^]\0|_nޯ%}rF8|v+5DXh2rUQu:7f|xr< VI0}1"Ol:?~9L_o`ގdB;tZ EIIŇ3DowKIN&UUͧѳ >X' hlkŇ3;y0{5<^rGո:!fM~1?/QĐ`Nj_:DWx9-<CL^7ghnI'Čq9ço `z:4_TxӼS@?lwMn+Λ \dh<W6Mc2 IarҪ.8eqO:蠖#:5@XR6z1ƇpLݷdٞ c>'"Xc 8J'-BhPnf"W;Imެo~YڶRb}݋ve-!j\M}ם!"}MAI[ bqRM3K{n.63.ژvSsMƻcWק)wSfU^^uc6RnSt67poD7ot5k>Ր3^(t5Bw.5l._\ZܦF')z3Ay\AF5@6mo7c͗p䳟磺nWo1bvίofjZ8b>kb6x\|~=\ofhԟr>b>=qW`t PrzܴÑ鄚!" Kh1\/ /s@=,nۿÝf[azzy?X_oCGS͗6 \/"5-՟~Zvغ+(Kxv6ms8xL'hVCDsimӊm jj}}]y?-Mk`zl>e˼,-337zūeFD-3EhW".tv-3Klk̋zH{#2f`M^OP"EPg9/c0s>ɋ̲)J8N'8K%ʳaf2) 1 3]Jz{=0}]f)v>OW]Q"j\/=?=SIҔ[EQ4*,^Ӧ0/^PΦy&yv?&S"^$ً<)ϲn;)e@fD\<ˋj,͚olR 1;Mm=P'i|$!|qd6tV<95Ex;I+Zʗ]IU7ñ?nCͺi|o@޶mu_ϧi64mxAԲ!!)˲Uq2t(2sg<߷],?ͯ뺩wtfa~8U]No5\ Crn֟ɳji:)ڶmŬ,MwvWY2c̲n]d;wugF58QPӊ7` .b_[$1AQ5AjquWw&ܵ񳸮*Df|6Gz/XS]l7vqUsOd)5mZoݷnTW1 )`w 9lXGkqA(bT޵eYN&\tTt0Lʺ=J(;Q]*`Еw\-?t**+ojZxQ儩UKUM nt[#kٳn0f;sY$LG`n+*ƦUuS'DT7eYG%@,ˢ(ʺaNEyY5CWV]Tc]7F;M jDT7 <]ܣ鲮˲4AyW[׏P0_y_W_urLPTuUe*0[\\˺*Vū*Ky]EQU\V~&WxU!$(Qew8,,Nm_7 v=/2*~:yN=CO9Xn&~ۯ/G,rK6E<˹G˜}z@@i|$1QecDDzst9-y:r?MCir"y E=B0Q*<ϔ /Pչr-iy^.AQ[8G'B uDZZ"kɉHZ_XV11yL3U\L)^}&`([tE9ex\@dF+ DW40Ч Qk]satYhxm5Dʶ?0X&lkO &ʘ>$$70fRevޞ‡X>=Qw,oqL<_t)z;Gjy:V˒ĺzR^4K~}ѿ6E/*jΟOK&KNSC4yTr?9N(uV5^t|,Kb6#IvdubVDUnw*}EڇqbD3ugjl>̺nND=񬸝Ɋ_l#1 JL6{+}R⑃t@> 55Da )i:A3L(BɃ+m$U6˪4eƓ5@8I )EnQVɍ 0/=ꚊJhhSvѰPpf}Yzx*-P$;Hzkri N6I9 ˞)BzD"n۲;9pb,H4mpb Ԫ@<&W\fb{rӄ=l.ʲk= = j̫lNu Λ,!DEAUuՐ! өH1G`hbtc#hB>3tBhMG U_i̺.%3űwC KEps{2CɇB ebp;ӫg4m 4qDF1d޳ ILQ%> ^k]=Q |`l*eo},QI݀ /^l3TI/čy64rL k%uIDP[jU80Sbj?-=aFP@B~? E X/+~bnL{džxX 6qgخQ,>YI2s)QSk | eq>'F&P(O[{3.~mlSY6f1HV=Hu~.EG 5_ T#kka8Ll=724=ܦ檤@ţ]ٿʃ`|4(qn'i42bwPL.5 [ |)Нv0`@nz gv` =>Mto4`ZE\0G삟rF~laGW߅owh]u?*$F@cuAϻCr  q8Wb\pOE'j}t}sjHp\saZ'_eђ1"~FSYࠀ{*L<f,:k O 1t2im.>\cZ}Q~ly?qRn=+L~#"2W`dk3n3+/W.5./%!ʌc]p70`0ڼfɫ+Jbqa GbΪX+l>XJ^ͧo cmo/Waߖ׹ .~ JQt~ . EX/0{$~fQh5q/mOe&ELOo*Km֝/w}i,Ff5Oŋ;_qwrM'6׿t\_#xB|АR ̫Ǻ ~F (bzin]WBm)J/ps,Lv7E `:__ʯDVBl^Vvw,R7%;%ȳT5xҢ㩚Nry:_\pφ+ϳI.sɳP6`^[}UiwY$yv鶞M'iL,KuӞ(]$ϒg1"mw,+E,Mg4]]-"O)=uON_eJD]ǧUiū,{.*`w:Db9)q8~) ͇yu69"O 3`98Yw^=&c _&?O`WeO " A 4mw . 1`u7 "8mm3{ޜ8y3PUuɳ88=twf7[l~!9}<]omI)r?_'@ijaHW]|jt,?nkLf@q<{sUTVDĽ)_CM=v×r:/5[N~]plw\泩ͦ79>BY*H)"BfLGHlǰ? g8X\pO qsj]?ϳ,͒g4{ಬfo'iLlۮ{r>i5𥜾._H'cgb>]̦vfo'yNl}onn=fcfjkЫ.I/SeYZl_&uL_٫iVᶜO'9L (K,M8γtxO^ 8ٛ\)^f25$W[OM)  IDAT নAzC '| .qg !zo({]W}\pO-eUULDecg`Q4@uW3s,Q D@[>l芺x*'i;X̨I4muw& lr^ZWuhmtO'Y1{|kp-/TUu$l^mEWݤi=Vwv0?꾙),8Mӹ3L}ߪn3>aQUײ87ٛ /CoOz@^Vĺ,dЋ< .'`E9ED z"m#V&5˲,Mgf,{(z'qfyF8&ffE:[Nb}v=Y `~}h`3f/f$[]_p?'<K"LIʻ-ޕCYM^fUwzWh7"Mm?lGOW麗=u&wY~_SYN^#*rQOֵ gYe#]np\`Z {@b %.(G?fXwO_$?}r.Džx?EhW .gDhuj$?Z5w0"صd X`I8mA$.ͅ?pvC x' .`uSwaVG[l0#w(-q@&Lم?.#\#t=:;> Dg1bQ#3UCZ pU?O צz:]w+tct{A ^'ul4Y8z\t;>5麮s7N'$l%gMY̚d`zMqLY%>(%*W{T:1tEQrbfkM[Q(auY[᏾ɢ35T\zcdl%b `o0Zl%GH$ +8X Y/ rYCTp w@3WdR bneg` #y Z;:Φ8Ca\̚o D[QoY.)!-*3~`DT vVl <=g i-v/iD"J ;+M׶ڗ 'lU+,5fKD! :buَ(Y4lo4x&Mt"&IKV)S?򤙍$19ӄV)EiJ0Lj&h?W[YUm^4M"䛼k]# -ј @R>l)"00+pX{`zd` :QȘ 2va܂+Q KCeb̻80 3/:%AҢ16JحF%8@n_L߭;_RlA{1@!<5Oy80?2jQ퓐WIiىc&p1B "ȘQD'H[Xd z|h [)&*XY22l-BrF z#gdp|-]?GdFh&gL(c *.8r9EͲMx]@t'*U~rpGuMd֚qYhWsiܚcTiFg 6w/ @]hwX_ʘ{y{uwr ˥_0@ bz\ؙ\"Ny_VHlK N %~ %\(t'җjYl]Yk>vjUF0UUd^JkYv$I:22D#{A}+`Y𤤗O@5Jpb@7G`:gll}5Gn<z7B[]?2vIc=O'O* tm(Ѱls|Zȫ5tE6b@`+ef#aYFa.q4>2a,a$SU  3a=g(vW7s.cA#H w`krj\%#5jDFApA=biA9Ynս7? bx407bP"@2GYVՖH?*p Ģֵ +BR"q `E9o!7 X>Fu)yG$jl3b!?v.+?;;po$D}D)mcΘcWJH,[6n<W8ӫcr%Ket _P($c"uc  ;iZ DroawylZkf#'O eҔBǮ,loMm7{Ziw5@ [i`@Q;+ uKN2BVg̫w&*F,֬( |z菜'*`Ҧ7Mzsc7SihѥSS9'iJ*/0prvN L[P߸PM-~?>/p–ЬƥC5tmC=sM0ٌ;OAtT<[0KT1tmz/}5"ҐXZ;I1=͋+T^1-+ӷP]\ƎkkaGP;HY0ؼa HII9{ ctP9#! nE+ qZ8vz83:>F!nGKz ,G=7-e✟y%:#hTzw~=kF_dO9>QN ? ɝA6gq=&PADuS)_vd[e) +mq$c𤝍9ư{S@TvOO#T=G(*VB}[_u0NC÷\n,~߮5@X~؝5̝LpV @ǝym6hpacI9o7oOÌ ŘK뵢RQn7oF fˍ.52>=>`~6|AqkD8|>FSuu8_ `fs:)Ӈ6K@kzLʞ)1`=B7w ƤAsD3E?.c ]`TOXo!2B bt Ә2~vmstQB {W?Ni+׫(zw0>|>ؼ yd#"zl;RK+Vˢ+rgyqԵ&o/|nO1VnVfG׶fe18MLoA \=:b,"Qs)&|+.cFFǣ)>| Xewј `f2Tdݴ~GPpR>1)b?3zb媙qw*OZz ;Пܜ7 @j6 ByhdcφOKDj3VG~ĝW`q)F# =A}+`B^ :K I;1<ǎ.4*7R"@s-zaG6|€  U{eAcNd:[,J_t@F}8NSF>%eǠQ(C`h)mˢJXS`寧8ST'Kwv0<˿n͞='"2gx;<23|S,ۣw@wi}h(TEl+Ptq hiZ~q_A^YpD|s]\#V@ů*ŴztmkEكjļxz MClh!jwUM=$=_0:Q|Eϣ?lD:?l=!M]{DT:3 ޸A+ r2XT~e>yRDs |Mc.-ֿ,ѐ{B̀r蕯cNrWYS`(<ޓd!Q@]^;>/Z͇h+mXz,3|j@4s̗MSW_,G=*E?Wd~(){mѻK1V<"R'fZ$]|&"j4ըm0o6 ''^SAonkd(} l0g2FQq7GP9"(WJ?Gz3+gk6#:o*:\w\Qs_C@faEۮǿn$/sK0dwɽppc/mݱR7_r^=na_{E6iώ9=@]LYO^d׶J(4A<]]d O ? Aȧkb9PP|V)zp2g÷CŇ S`1 ;$Yk7-)sv|s _' v D?lBv>A:֝0fl~|2֜:#[M`uG׹ '$f7 k 1g""5臷/}h͒LA6Nؤ31aFwԻ: ;UANU#k%eEp:ӓ4][[gqPܸ FE(-.~Bx&cz} d =鋌ı*Zkz]@ȳfv3F{*_'dC}ɸ5 &wGudg*H|jxnunOˏ8^/Tj}SŐm+=lH&ģ|^0FVq}8I=[mx-t]1v{yJ??}o%HgO /'d"vOdUP)z$xs>FU/" ?Lcn 'fX/Nw/֑yI R2Ȇ}oZ%Ük^O#CoP;βI=ЅYq?~ڤYi/V,w=jǺu@Y}Ðq;\7H0ќ guU"EVǨ+m]V戈:9Nft߻)*)}5͇F'yu{dM,?K6דz4Ň?*vɧrT4m]-Abu:-1] D$m=A̦½9[rKpb~Oy?^jH?g]e@# m;.#?t,Ág(Y3gV60wT5wZF2<n [h D%T;gCV`'sVϽjc]Kf`MMxQ4gL)sU`#X01+2S7L`6C=%_sĩ5W ]2>,qm+i)`)2I?ˁxIڵam_+ ^sg1̡uZ](!1QJq=a^7*. x4/n>oy\ow۵M}\.~[}3y5}'ۮWapg4}6_nWavz1kcm6}/ `` p@AAAAAAAAAAAAAAAAAAAAA@`cV>v{ҧ5ʫ~ΎV.ft<,6{ha}kyvb6e!wZΧbzje܊|geC.1eiFxt-Sb~kOfo93E_9 ݁,l\M9~ H}>KZڭb8KbANdWӸ _8ןm6<Adug᛹UW:TM.uÖ*Hfh%|Pz%}<cM(` KUL=u}muԇ8?2Qkz+C|pe*_1&='\M6Q͊6Tw5YhLgw(Nv_ND;*"ȂN2]0",X9AxYt߽_-難7Օ꿂7he:_Kw vo/)Gfd$/QpQ:g{یNy'UTB$\դUrߏɪƳz9^~xEXZ5 p$aiGr U)87|s:``Y.-V+o(LيUi-%EAN0(HdՏZ1,P6INLzPBdNhhl o鯋/9)VYcQ[Lpk#"]2JBiCYz/m$ {2cB}WwVPRzo>.J{ Wxn&>_'{[9¨=qKMZVS'R%@ogբ"J2QkڌKMfHJD3۵#𭩨Zr'V{RtE*jT@p/AnYn'* +Pz=h1ȳv͍RFn$Ozm7ectys=o% ƃe\%J{?БԊ]JL-ď帗krniWgu2$sS~Fmr/rI@$昆ȧrz2>Dᱰ'5QdD[7Xu7`fP22O2ND<u#h+K)#M:d2^fyLHb+ 5kh3"o֜Ů~B:@eJ3 kv AYE*,TVqZ4hmچFҝF`bp6oU^ap@p^$Gdpcz:m~n')W+o?OY9F쿔[ X6lκ eoA WNȖRvK~y={N6~TíMQQ28X5\؞95Pqٺ"T];N 2Jޭxlۮ}?LM^M~vJl"fa=Ou|o^Njc@g#nE#cI{6Ig61ÒbXꎒUy8VpjMYE:cɺ\Od5aCTF8UuuN@]V+VhnaԷĤ6jte.<\ί;:l]H)7 !'5%[*S hS’JsXlV}]5V\j,#CdTL՛!wMrЫrb-anA3exL-5ʠJHQ`だ|Cc0O*lbdQ7؉d]^`*JGq"s r,ȥ$~jD48od4P; h2F;=)egyJ?h*@.Oo`+Huwُ0v#{JuXhK dcH=U D=ԵQP Zht>l |Xoё2w_rAP}%1zB55Pb2vd!Ow8Udu;^sLb1V" pA鏶rey-1%PDѿ:K%0"EאC# JC\8ӗtCDIJHaE$]BKD류.Q "ASB60qJ!(-Ҿoja!oՕ UZĕnҞR]0 Q+H]KomFzLs̴Lelk4"yiu$JeS%o/_lLtBعZ:~&d)[J2ڡl?i҈L㒄MLfBnVOx#5Ou30jԃUE7"`l\͕J2ļV`ZZ_e<m_Yj yP~M&"}˿{1m@>kÙSlV`%siO`kQU/װb(j/KYyJҔвiPK"x*CaYl=@PS^ }TcDu.Ŋ ~ʞ#:20Ȩ>ݥD5hd԰ mhyΗfTr#^:8gtj[!7Nll9(>l&V37.%[ Hvݔ 9F6\xsڦ4U>=]m~ ̬-9$p$d4iYKbk;xۤ?ç[]o;;WZy\ķq7Qt|X8!hx-6 fPQPJ,lVtS3UDVTsPf [L@E\m-_ +0VDۏXʺ^!d<{^*I]o[1 `_4cA Pw;5#ZsJY貰he~SCt 'U7붘6cĕ  V%8֩\`{2f,,<(6-]7U$  ɓMEis56خ׈@DɓN?n8ץ(Ͳ[q: B&7"BNc7MiW~?ϔO57VjrʾymJ='OZTE~Jd&IOE's:̈tCiy:L=;+OI9&g[ب\mRmEuU)-k[O4L5콋ηEF=5*mAd̹;Ju3Ê]G_/1x< ܀8'*'ipڪ[ IDATVi7_P|ϧKv~} OI`N|/2lc_nn%7va-G+7qd\;]}>%1yCN,o޹?p8JEx5g iVP9:K%S ]` &k̡Z}T 'TJ{u*ZRtp>1*a:-OK޴w@ϵ28No;WX-e"B_l` NТ)wq$O:4]ۮǢ684QYPByIB} ymcQvN,So0^,tCtC|nsnIr{xv}9gʿDI}oq9Y!>$=y?lx8d?Xɓ~5|HJ^"xyU㺒_J\V"}-BԊ&@Os$+ʋ `+Ks{>PQU'q:L =s}ysZЅOT'JPjݴ0tahΕ4Կ2+;Bخk)M2&q"̓`*!(uO2<}^;C6Wϲ&_Eyd˵Rߕ}YZ XID]S*0[B[ݻX䗮5mc_.<n(Hx;Egd}dPbml#p^ \~+@I9%O:=:+RM]qm9Eit:q.kuUlߛ B1Y/NB1ڕ(dF.'gMU3RdRt=:IO'wkp湭pXGӈNM`^\KlGFGlJ1jeخ.gاq+AT3: ] NUPBAdGyU-2!$/kk \졂EJ%6x[K7y_ %Q`z2o&y)=*Oػzh!ύ@eg6*|S\Z’"^HPEy M#צ_IRXxT\$I$z; \׶M7pdKU 5G3{$|xmc];OtPJy~894?rqC|уjs ߦ#t:ݝyp23@Fσ]oo rի/kŇ0* f/$Bƕ&.qkQ ~Plj;@PTw@o0BT_dol˧Z,FC½W,[vFt$4=l2lSN)%w)XyY7&o:V6#K,զ)Φb{;LBF$ `f}W kMmڑ> a0,{ "_۪h/.5P K=*vˑ&pjsdC;ޞ'מ>(Z G@鏿{ өfqQPwL8M-np4"};}9Egi.o^k_]Em:ZccbӼ(C y'ۆ6ο@qlFQiE5Cّ\qjg/"tS1=t<Ew/G0KkuYz8"߇E^9Ik.rt멛fO{ rYix ]i32`5)fvmZ 0wxՐ=Du0չT(M%ڭ6^5N-_-۩?WYR§KSPj)ڎw(kYsfJ@i=B[ʲ&\ `rZԠ;nMQqg4KCI[H *st@{ ߥRhO@X+/+W݆M`DKOQOw[ShYƓp%j۬Fm]8-TfQ?Wl\&ԍtB,Jx a^?һL/xاY/KӐ4=q>z?eݕvr(^ "NC_4z/'uVǢQXl@AA+Fۀ?4d*8LV*i;QgР/pyh.["`]_D UUf^[h)JD-T$ f&"~71EfҐ:rxh`f𽻃t{7U+1۽[d =;. P! xɮ(4X*{bpM7~ڒ"t|iՏ+Հ*0\̥<ָdH:%3"p2Y[ Od <jw8eU1V2Yt3f󁙻:L8J24oH 4@kcܓy~&Iһ8D"n~X\=a}op (,%ݫQ/+"L"ǭ[Њ"UܭPE}AMs*bje`őqDAac(ïSJ w7s Q~(as S 3Fd0]dԟțf$\ zq0Vo(!`Y54a`;,< gGD~_5 nHVd mދ0[ *ލmEE2%B oM|p xT wBi\(zõ!7РQCW"N:0bn;HBʇ+)DjfM-_7wJ"6&W?cW)Nl7cE\>?|W K|* Fc(2`bh|^M뉢?O )-.Ln"UvlD喵c,X5YYLϫ*2O#޾2&r[ 8"pA1(vOV@[%}'0%a=4EU-?j_qqվv}Y˺DR'(jZPkSY$. Z>T Èc" G/b"mxXS[U_Jܣ}y| qDJPD֭(.ZE@Vl-o8FBg} CAxKh-m cVy83g˭Uߤڬ +ń gwFTUɅUIFOYl+m.my7k5K@v,kr͇pa'!ϗ@Z!}TZkhfZ EZ rk~Tk|"|N9,p(4X9ȧgp8{!{IPrRߝziwsLonR9Ecq M =>nٙ5FPK-P u?G#("U^Igzetb'|oDl?/YrA`fqeq>\j' 0?L`ҕ (~+)$u=Q^29z`4o;E~ڼ[]\.܈.)]Hr}ߍH5YmtȺš!/cY6!Gm\K-_s57{.nv8 F f2Q +CM"|N_"@قJrd,8TF i&4x@'9#![yrD p7A0*K_3E\(#좨%/D̳vV;%ObqfE׊rsp =e&w{WbǡMx =|؃}sӑOwVJ^tgF|f:.lƋyN櫢(m[:]̬:.(E n`œ!+^?C#6`a%bCJK?Xh֗1ELV GNZ&Y8\崺p ,mEYiSF֥ ɨ̋,AO.5y5W0JQ0̯g&SFEQavA@/L"|.Kvߊ05ȿlns0w"(O^υkm ٠[[ r۬g~:.7ȷ6e('/=c1uual U~ufu8P#6o\$ϫ@^Q:Zv:!㙻*F!7gMmmQ|ַ"tonbQbfXL l9I@/Me:mncʺnEȞe>޳71*s/b!__{i ^x_}k=39eHzzt\3iW"/{qtzxr,ƃ*%e( =|>qW$$tzXMft^gƥnm֧wu_lޫ)"f{(}P$k!<5O/3g= HɱgD2E=uq̖jZ̘N7\b`Uܴ_~+fzQt~^̊oy| Fɯϣ?AK̩:737uX<J[(k!SZdjݬWM}p[9ݶғź~h`4ټӭ9rAzx/r . f@ftH;eQ r !F ȽHBљ5S *4 K$ٿC ه&eO*y2XN fG(^2naTH֯[ã|5}!ǰ;O` j0S?<tnχtt6]ƽb `X?lf}Շ#94Eub4.-6olohWB-LX8INLt:y>rs~pNo ϑ*D-;"R^DIi& shʐޟk)w!]FmZWͺuѷvfimp w 'U6_IbT]/z$I/ջKIǘDxtziz8#"1zioqP/eéi5شHYW CG̼O(yudr~xݱ" r6rL9uY]Pn[^e9W~?Sdޖxk kkdnⲲ>ݏkD4pdUt}OM\g{F| eg3kD֋CTܴLiaM x-hou(Tr(ZQ"Q{YȦ!Aqe̐=;% 'oJbxnkXJ9m9 d}al,J3@L &[n%I-@ߊΓ.I:Jy;:%@Dccqie_%=FQp}l Y Y`Qڻ!Z&a^fw)A%-_t/ĕ `x4M..WVY7tDN>v;ɲ4S_̞~=. M7v1X䎯m@45$7Иe]6{Q#tvIsq\T(ӻ9qYe?!!K'9V `6O[fDd3uwy3z/ݭ/JPˣf9vq+?w=mg=ƥ!FT2 cS"@w%FPfFv2[?z'd\m{[W+LD$vH)|&X"UhE,#8}&`DzjL%J5a؊Pѝ`+QXѹ`vEl)P.m/&9V%3O~j-6c~|ExZQx,dŨrՕw• bZRꎇX7tt<(au8K&2wH k% 3=v`nn.2%W;-RgowȧO[)jU1mycЮ&\'6Ǔ2N:E~ڬіe+ȅSc\TQ&Y]\^ kaOޯ 3n3D\Su-%x]ׂupe0DPq'T*"G u~S?6 `bGQ'N pgy+l%Q(aˀ~@"BiO51Z{{~e.:Ohr&Pv<|q`ԕ%w"OFftyؼ]T3 S%NSC |.4-{A㱐"-H#Π.\~7oү *@nO0 f0?(. Cp!ChؒE_](S*e@L$,56L{@:ٿ4kf&rz#a<s(`-eeOK"ȀRE3Y5.l_Wc]'+/TMղ2䎳$,k>ZsffdCaJ|cx\C]N) H`?%1 l Do_ GXo~%Jpp%b>H2elنDx$ @($%ҎOO>K6Ӈc߄Ah EkD}.=/LBIt3av^VjV?DZi$H⹫h|X[dT46LQwvԶ#jU 0n1xɪ< %|Vd:*rg;TR:ls;tf^^1ɽ!r.<|tμu6= )s-4x>w1[.區D~GPjb=\JyUYf8wԾԍT#oQǥ>+V,JJ"-=ߺ$Bf(2%Tfi'@d'@F$D\jAtuX a2i\2ɞ(Oz tRNːk]6zHrI+HW6]p44+9GX7 %&;WG}7qI: c kJlֿMYQ f6dž &Bez-0aʤD&xVsd%j g%P[LPPlX-Dl.\C;P]l6˙Xe#yzּ: @>TP*i8RUa8_؃~ U:mÍwlxDp$"7L{@F+ JUM7k87rsqMMhD$V e`őqDAaB~8$y) fL{{}.Hy͊$60 _oly#9m"vp#n QtA`@k\e%+|',gL9k;R C4L {?V5%l5+ qw`6$ۿE~[$Xm3X!8щsKwua3 4MցFZEuZvyE˦@B`]! bi*#X{R.)L;C5{~"i^m+ cIw`{Xjyq復 ۪ fYWjjޫi=Qqxj4zjqc@]7SPm>2!elԟ.7A.#@){'.)VnG`Ҩҿxoܺ?7 FSͻnnKeḛft_O9Gm:ދxX&;Y$Kd"o 90i%;9h5dv[5Xn^Ҭgڵ۬.о\99%Ԉ_͹_rD.Qz3gXv"_*Nz**0Ep`^PӷUX@m͑ Љ P#]MnO&UQ& p 6 aT04㾢٨7_L'!`%MfU,{Td Ci 8cW%x,T<"gp/6kq2vަ0ʛ;OX1o]^Ua#(cK<[n+"}<ʸ5g%1w|!lm2ƋXWEQvv^6-m2M&]_Xp<=oiK{}g`)@H l& 3\;${Iob9~?pY?1t'.(6pnN;y>! $V8M*طUg'<멓tN @'N_ORI8IyeN$No/նADUl೛Ճ*jJ?g+\,m(KUҐSewv7-,)ΰ% 2Q\,ƌA+`4{7|?(ud<[,>_n"6&_LX_ۗS$Uz}&lWx@ZL/m׃"Qw=#֮kMoFIz3[9%[ d79?fy+3{=o7ŷ@-_>KS_߯|tSI_m?/?WOk|D8^qXNW~r{\}FyFc @Ŧ# .d\6iQM8~wKVYKnN^f)ݐ6.9e4͌tUoS(x8rL/ˬA}ޘl7]e{χUt ݖi:ttO60plUmUY![̥?"Gc6KckfX+1!;k)Q%XR`ֺ֚*S -pJܩ- ئ&W:,Ͳ&!J|xw(vfC!BV&SwSoRsyKc7(1 ii,6eTHzQddOi"d w~Kvgえ[}בH5CKQ|O^f{Z}N)SO P4M$ٿ75F9W;`ks 1OmD`?_y4_$QK 뾳%J^d0:} %ю ʻmܖ>{=pyepc\FQed g&tOo3Y?G8>X M~z}Ň?||"{F5TJ|4L"79ZigaTͻTDYE=a3_-fﳦRSq:f^j>4y:T볘o☋"{1vע"Ҟ:&Olaӑt9١#\Jy+@MFTXGv޸kUPGx,'wBn/~y 5!;t=Y@[ }b&&QLd`^;$|f~>'mEVGj6_):fiƽ4뿞sɃ~O|IC$7DߛM_/^o8/}E?ptnQEW-֑_r-L^qe6F,O?_xPyS71}!"C5﫳.b5nhvMGDSh Mnf[^b'ݟ1ta.cVA[?Dqdc\,sSŸ"既q%Cq_MfL:DͰh:q+ޅkӯ¹_Cm9[~;m# ϗo4F6` 2L4an S$dy?ƣI̫T,lyM4A|,Pbي8Nt{>춽fM/@cտ L7Ƣrh&]<6|X YC1X.1UN$7%x'۠e=EahePĪ5[GOz^:JLU"S)H4cDFun5 ] Mlw(4Mv$TW,=%5'Rm8}iحf {eR^y^Ȳ,k)(ŮFVMo<~>Ȓ:R?LQf`KL)PnPYzOgHKd. 3C%2M'jLcmFW8w-BK1.J{Dk}N7e;qF/n@.;}9It{$y_h(x` A p8x X, A p8, (hxIIOMOOwuUuuufÌ]VK/MoC[uu,Tgkq |,L %WELBb˜pX<Mrcey5LTe9uju_7=D$`4X` E \2-j] Y3({8ͼ+"HwfK IDATb0fff(ھ-l_݊jN'yo0*L9>^n ݞe _dmX_N#W$Ro\,,-ƿ/Aw϶Mz( })ɻxo=657кJL3FS\x%H(Ӣq9ڥ3Լ2Bbl3eM}) Fb9/{x:_̛ .4')-PbHI l7BBmޭ6ap4>O~GQvۗtbkK]/G竻/;Oۿ>Qy6<{|;.۟PWݟΜ=qyY71Xfё_])"٩y5 Sb<('(mdl":a ޥDsi~6g_8sl "1;٩W`XF{MY6-8˹5v߇zGvWw3I4r>M7:/?V]=k^~<ʍ8ʳ~k{6eK< monβ|:_ey^Ů65SFc #в.?3DzdLΒ"QA5Vq\  (/9p?I[W+܅[&@}cǑPU~',*{`08 f`0j7lx0W&&]7p0?Ns0_6<0Awx?8҉9(ޝ;;~8ܾn.KAm ?XiͫáN:a} (t~`e٫jBośMbP6e3{95K1ڰd>{ RǦIwDmG#.uZv=$OSCpX?`zBwz8^ljzޥfiib֏(,˂&)+BXûo:gN {YTbv$[|yq4/~,~ȯ*}Qɼn{UV?h+;6EÚ`Db_'Ʌ3E)i !"j\DDn0yIrFd31J!{4 J_b0M4}_iZP3X)f P4sI櫝QX%d[B`Mfo625fowNWM,v5TR J p\$<[垚Y>pF '#/|3X8K~r۴vPyOA7Nk615H8ݮ&cX4eh^b[p 4#i0-:lD. ]5Z%g?kf%V\791h*r*-s)3QL6D3Wdy{ Z:A4t'ciXt7 xAVc\8=?Ϧ-(D }Cj\o Y Pde Ob`Xn9\MXw3q0RG(ԡJKY'xD3Pv}%&'˯eM2 }1Yamj<5 f9Ei-""slF"+:1DreW.evLe^OQ20 (oW1Z8_GwTB)頋(Y M[E!5/.[=qFHhHfڽ(H%ee~*k;"gjІb>4xd|Ѐ5$i~ۜ^^{q*DТ|GSQQ z&xshU?rSl xZIh l6^?Yz3OŬWsm+9{ƅ%yak^'9Q7̊؞2g$ Xv3c!S#Q%?v(!{}I}ƌ:e=AI~j8{WB?d+Jj/ޛeZI@ֶa* wEIڅ$!7f[\'"|o;9Vg)3 Ľ@'6y,3RDkebi(sy>]!ςFa]EpzSc1+ft2]+50g3x%3b_@0&)RAK&9%a lDM64l\3ھXjE?fW:p˱3@3fVbo}~-_ϳ,MlTj(8 C!6&l y2MSh9 LuNVq3eɾ#5QM˸SS4i IJ\{zwk!A1L-!ԢkXq뻲f-"T8I~>lI#BdzS-%KX?x= `C~ӹbBbw=eH8zM/MsX{HJ.R*4FpjsvӢod(B43 @&7i[Jj@.S?]@I(&=|o+.m3;م D#)ά,rgMmtP~C?J躃\Qlbomu)m4/|[(ԯ"D,R+jp4NUo**r]Z+!b;3ջx87'z[oc"Og DGC44CN#he$[%;1ΰ |_povR}*Ysg[*D,ׁVA}JDŽ=!灰Intдv8_8pũv:6<hvݦ *8s\ ִzo M+Er ʼͤmpL=qfeԆy6Iۇ,ΠRƿ>$3Q$۸+Yl,m*YN 1WX3-.x~hK/MS2>A?d!駰W_hMsQuY@K 0 e$UJr*?VyeQQ6iXӸy{ebd!+b1Fmd ц NO3;E2cӓ'kUoJ}ķ̗zmC}We df.\ ls3J33dZlΤF3/JJkjPt{p\=]w*??Jψ_Jiȟ &$,aO`E0;A&:@v_Χ_}YTUY-5a%sOG"{)E12O]͢DJ"Mn^̦2tw?]Hv`xb~Q\ջrt{͟˛gMwu7۬gA4z6P 7!hrhD*%OJwp` Lnp1IcFX w_)4gc5& I `SU{)x?.Omu'>ғ 5>~iہjU&^ dƁ&HsUekEOAbVxI@BS'vlcK{{ِ']fXN"\7p) G5=MP~0).U'S!Q~ٷszpx,P34LQ 2r&D;ۍ_mgќ:nXTƣ _L:7o}~n'ۉ9 _POg)Uyfͼ,- fގm,;ox̓ߗ_Z 2VZxnI̭q[T#Ss ٟ 32[D0Lh{6 ԙ ʬR{eI}3~]  ;xh,Ie2yғ-o)o%0|:)0p-9|0WzejzdWK ("6]+k`,?@ƠE%MO)Jz6QB-h{ӄ/mӡ۴(Fp!x =UnI?]J[ ao>m4uIBhB,K"Vr7dh:[ΦPeW:t?\UowŠ/|;mXh \΋r#Ϳ@u_n+,\./&.~g.;YӕzϝQ'zRB2᪪:yiW2Ϋbu~/}_}I@~W>cZ4(#NDi-۠l ŵ|Rs5CSHOAC?0p'wr-XMDA=9q`:_; fz~84[I:v4o/5W+`iɲR zp<eAE{LQx "*ycy*b\ŋ;^cOd l NQÄ0'~uHCBa"R]Չw)mj ,zH S6".W̟M =a~z8nېEH $}WPUoʪ ϥ ވ3uk j,Eخjeρy,:7MQY:0'*ϥp)تԈǴAlbYJ4Bdq($Klzv.ʟDn ܜRP$^DN__Kx~qr:+7㑆o8#DbuR0*LG`VHSS ]Diw>]qЂG|nILcI?2}6NlL^r0~~6}<"QV`r(1y_9j mk*:m]O}`E_D@xrs@ 30*rӒ6;pNϡ4 s*Ki vbD6m+"noFoT^yvr2_p.fQ+UmBv&+y;>-_ JOKFZ,hd6pVig>-lt[6c`S gt0Q`0yT)yx7;xtZl,ݞP* %~D9qUʊۃbzZNHG_Ji_7L> H)̔0,9$CB"=/Gb9/P ?ӺOg(oEFt;xV]/G竻/;Owz#vK4zY4_@DP,Cs SK2̘,XG,79 rwTOpif'P/pYUʭ&0T]ٗdz@i{b疌u12W5*GW[/soNEv@ #A ''ΰubP@ɰZSIݤeMHGCnF5Oڑ 󙂑"\fG3x?+qJX(&sͳꔒyR>nj,Yefh2ggU:Ӟ̴ry0-\̻? .4导Nr[ԝq)N,Yީz9OauW_ϲ%M|bZ}<./ cG&՛iռlnY/xj=?"O^?m0:.p^4fVFo6}:40(Z"rSЂ2h\ rnO0% d.l4j{ ("D/9M[kf(lPj!}:X-pj͔GPJpP6B U\I*-}xKg/ԡdślfБo6>]4 N\̻)NHn'~,koz^c{x8|8TaJ _G{ޅLc^& W KM,mP?.˯jEp" Ӗ x@[o4j&`c1$ɬe;l\n\$q_ S}AдN^93/^-w,6(%eN~.N^?'/P1AMvTYa&hU5#R};&.Ђ㱪ȹt3"9+UWhged˸dBW NXjڸXb,z21Up2[7&uqX140̸[U7Q<\MHL%`ܺ8b]՚,j >$INK5VW$b `Q_Tmy_%x:Q{=7!$ޙDQx[{܌0msZޢR&? ;ƖX¨)ftתg )a9m̏gB7v| ;#w+^jQ E1m$sd~iiicf $k{Ǐ"I8XC NS a-aHlz,"w覱*B*l-cyV 01 wi+dPBN*B3,'l cw&Ŀ+Cmtԟ-S E yVZ2mkW)tPV`{0BfYPvto:GY1tQ2kN&NWGw ff`PH&>d:*mz*]d:s֞ݾgNp{3~Xc6?Te`P)C5EpXτ AIo2)ֈmeyf:FHPefF]o,/ )Bas #AR zL1]eO}-QWmU(DVH4cV.;!ڦ$E>vy4m}.@;qFZճSfT3\&:h3 Eok7ij{d(4 |D`|dqIMUW#Q DI4'D|Ugk& $1ˎO{UٚnAzŷ._'N;42=Y̱V(/OUp1}^Ulr[}^~<+K7~ g!. bDP**(JrXdsIQҺFa2WSmgG5MVֻRB8|$J_β DpgdWf.;ʅ*!a0Q?Uٖnޒ@3V ?/ m[G)4,9E1'*ݟod@[{yp;.'xXyW+?FofL҉h4z1Q܎ሮhfQLl3.-D>]İ?g-tٕ׀rbٹ|{itx\{eeU%,/2R^IiyT@&)VpdNk+ZܥS#.TȺF%_zG(D֘db$m_'*ptwl=a`Cf7tMn"y5zBdye]UͲ(:s R` 8Emc~h3䨊]!Oh1; ~R*mWYQ 'pkSci#4a;<֐I(,5Ote6gAVrIBVխP5 4!>mZO=}59?\Di"Y#h(ki_Ւ8|;3E6EEv ΀KJ!j;@v8r׆0~//ß^oOYu(v~ O+f\xZ'mOs:])qeiK闡.4 s::?;hXlqֹEq> VOUᤉpX1]}tJ_&S-x3HMEB;t«H=f'Q4Pτ3  d7ЎРeed;Z;ݰaE/M{ðRC9X(M6LWDE"KLHrS ,y~^8hX ݾ[Y+l9>0-`(lHzL,;5"I$atVRmuoӧ>5)_ rS:3>"T~;ftj|gL5wNf}Cf8w\Uu>Mw'~xvNf.}Y&lۗKm׷/Me{s~^^WwEu_vv/&W#w? Yy"t>mHa~Q&7Y=G95/Oѩ7I'8q:k7ɚ!XEYBuZ)o酦V-Wu x/D2)pT,hz HZjVr92kq u^9few+Jf)-L}%vVyGnc'9xj+?؉dv[jGk ϧk`f8r BmJv(W%=8˃nK^pi/?#11CYuI~ ,0Ch?`^ގvڬhlp;UVݗv6_̦b>|1>wEtXYz1&{5/F2[nVwir>M7:/?VofWw,^"|؃TWDb]NY4-EE^a$UCUkXﰖep^Ft1<)E oiOUI k)bo%eI"R$1nf!0}x=}[ 8fjSnf/oҜ^gDD)nc1H.Z2iD}wGwh3eYNG),#EtE| [VلiJ&DAuah[hibO0<iFN8Ϣ #t|l@|f]?A6Wg?¥qb+/glhM̳VYfwf wmE8~eIf5X/mAcۿV'47]?58 RS@mn et&)cOԺ2X!ɏ `JoV"X=:p2Dx(2N$1iDn yz]e έC mUͮھmvͮ24$"s-]evPMw4 bi*)Mo)oOF?Tf2bqJz=(&nSyͳꔒC+L]g?W%Ś=rToΊWT'ȭ)ìnκjs}A|-\uaf0X!SEHF #0kP$"h1Ӆj| мi`M`\,V#Rx֥NFgc$qk3/FVb tYtبAzn+MnE0Hփj*#fe߉~o2G3O`wn#""@d zyywzݬ72'(2cm;l>=zon;c%4n|z;wvnongIysJ.p&Hж̉]g !;Nts7^/Տ=?>0HRG ݻ]Y0f"h&OS~ΟT.ќF\bbP&Cs4Ff#yY~[qoI9 ֯mGGSCL7fЯa!Zj ϴ{D>M039Pz8췳7>-<9f1ɪ `T@~7 -+h'n5eU=EPojdhvw&Kn/29,e48#Y=`%+.Jq Vy7H`mHNeپmޙ=M'4V{bS_lb`:zJ 8T{)06Nk]-BT  g7D͔kB-rPso ŋ#`mڳGlr 3BJ;?$ϰ5U=aҾ`&Ml{f!B.@g8(TڅK [ "j;Ѐ úr&0A7 G l6 vDfQ۠g~fnڮf>\O!`Ӡ[}[Nex# L3?=b.3u*ίHN%E1rFE5ٖUB :J2Opx@("L˼"|C (gz.l8RQY;[l.sKO+>^XZ%[8~b%7k:Il-3뻅*Eaưv#fhb`^uLLraOjؘ(DKbr;34&AՏF}xovIM+@8ڮajnKbM_ >lIƄҬ#@~T `KsͳnvEP2'b-uu_ @{-K7 ^]%]&ͻ\gFuR7x 4eu&@~EBM=r!wJ(g7K5ͳ:ۭ˒5 R`]E2aͳpXyes/lr:O)`Ong8~Ϣ2*ye_W:˩Q . )u2|V~. |Pt CdJ!cHD[l:DžXp#dcRx:iBS,N2r8H^~ŖDn7A?u x!ElCv:<0/3n }~֗;b2>`.Ӗ~1~8n^}UwGs>bSWd~>[ IDAT.Rś k.ln+XRi t[]Tɼsz%-sxݮQd #` % +w#[[0*'[;;,;Ʉ§>U"fĹ ƐUҍ;qۿ@Yg" y&3 '1.Z`*EnגH:ua$u5u4+LbӋSk9&Behnlcknsl6 fB~ >bv{+~<{u_5[n7oq"IBNbÑmwtԎsw]PLĚW”O#JN;VB]~Uk% (Ȃb< xB*1y~^7}L~}U„rua7iDl]D A*O~RILO(|ϣ4`, STD&NQGb#$.,6n]"Ü=[eӻ|r;wk:+Kҋ& ^[p9<=6\qlj_VIunN7Q&H% :"ֻiAN.ʒ-R̬0ȿ PD +r9c+R4Gh9ɫۖQ238lIwtTdQ۝(l-)@ YYȈim|-Hr8~b řZۗ(5|,ǖUqOʶt|'7?T5 b-O3hPA0DAh[';OW cOG"{)E1wCDm׋TvyWv[P'8أ2A2!-򴹝:ϺYiQvp;ΟTtXOv+iDYH-hTArl ђ/kFXw~(MQ۬cY u!CIzOJ 93T3`Jm-oX5N%AՂ6ƐQbKcmƒ "E86XC/:"A̕qUU`4 `wmf x,h4[;=1#s6ݥ<уy:_ [O+bC8cZٵ`dysP4{r,+{cPEOz6"#G|-W_b451g"",^(y 7VV(նNg;[m W@,FD?,^ *otV鹝D҅x:,LlV O AuZcyҘΖ?W[hy6wV[}~E19~g틩3c+R5 2ajʚ;܂Hե1Yuw;h+vM"ګk]r.ZmDdid6*5"Uwե/(`S  bxf q Y0Dg\YW™V2)+"<$}$Tci`s;'UiSLѶb4,zkY hb L&'t4Iqy7_Jiz.dF:%{ tKM !ѼJDY}YFcI1['b,1~6<LG)r^̖1vxJYU3qQyXYx]U::t%EieIwQӛX(FG:eJ1\J ӄӝn\xvWz5 CMaJ/R"pfAho',GK/E5@>0Nh9'0awDM' ,o]~*Kkq[`b4é=mmY,2V9 ;T:Yf^&૲\>]_]MYWVpUeI6fxݮW7_W .*,ty9NٲSE };R e^uY*te7i5&yɲuM9?^D V!bdS5L݂0*ǃQWrӒ~9K/å忴^07@I`10 2%R_DR]=~k,76Vahf ~l" 9Q~88]h/gU7m6;1&(=ĖBR/#ôAivxkށk9ay} T[:=qyęRFOnߞk_O< 38 &8b(@L% d޺cz5)?fJeRۗF/0~Ϛoq{jvqx#(;>ܬlr<|b"`6`y!uK k 2s};k]:PV^JQNeJ-|5yI-Cuo1ǃ귀/H^$apJԁ="9Ć͇yE|?3i)A̰):>՗~$mXaM~,` _nVjQOoD7Ųmor"'k÷ЖU[}nVAopZj$@C~N8bA}5]FIQPe6O.D/TgYr^,+Q5[lP>s̙8Mt Кad>Qy}iŅ$Z?yd/RڤGPzGTC4Q= AybOozUWe b̶}8Ѡ]Zk@2?'R!PmTsۈü$i0&P"0lolcLQnp` NO|2{b #68I lL3H3 ӀLwFI~7eE`?p]xngY,u ~o\HņnZ@BTVxpO>:cu*Pϩ"Al]}$8MɚMM ; X `Ax 0(0(8p @@AAAAA~`guzwy=jh$da޺Iغ0#Hl&.몔6 8&zdfCȌbȝ"UKs&"&PfbY@?Jl})H&rp[_fO'y:$k', gyٸ$`mM!gU7Ř?ACp=3?x&ITu>,iE=z82uuR7 _i y?\0?h: n[@ npnj"-ѣNVlIRtm[a t|w*ry.Nnlhvfz<%i|T5R[ǃ麝q8wr}{|踷f*2<5OU+Ui~oa[+Pw!e,,O')qMH8Xe"He*|^$ruEB OwM!CK>cr љ O@`a$} Df)(P]EwR+mrD\6/af]!{Nɣm @/φV; Ef&/K WŏЌF!z2ߪRW/+C_P}X`nSK77V/?Nn}`d m.~Z4M}ws ,qJcd8-f?W6䗩4|>M)9)GφY!]ss(-o-2.-ɍMgҍ?_y߳:x7 qAx:݀D_N9JtZU8-~]/~VoWի ]+jײXǘƁFj<9_T-2p{E+)֞GO]3=) M mp{}1sUC VY]VWVmchX8G|rkOȮ*VVvP΍UWc_',W3]kʥfY2XQ)x:L "ۺA(Ta93\81L@AAneԒ 4Q[pk{W:L(aa<2͊Aaԯx F_ Ȋ=}DOhVb/FEQHD>z?&p}%=9!h|`b4~>0z1ơdc>]Ѩr~2w:oPFYiͻ.v*NbOƆK-:DirY-f@4|>$dóA9Pz. a~Y>;q8`RӆU9P9$C_wq]tCt6IIWcMñ?:KxM"D{Gliq:ho jfj\O_j9sP!5}g3%FGeX03ʢl9HgY[ƹM`"d+ڛ t~vw7X ӎҌ>`EI?oXV 6fhtC4ˎ-!V_?6NlePAcQzC&JZ t5Ju4]fW7/Bs#'FeA^Kbcv'Jiڒ 'Yj`WbYYtbc{I>%B4ZzǔD!aAd 2ΩLju=i͚oM`2.$``l8|60z>>ؒSHֳa|8z6 w8t]/n?Ԓ~^ nxPF/ƣkwǻMduW7&Hڣm4l O, =S#1G{pw3+Q]k'T'8I!b;2*HSlt(DSӰ"*ħrx }ñ7x,}`ja.)l.r-D0x6}܄w7ӟWHq1[޼ :ͩU' r`8b-|8Iuǐ,eZs2Fjs)ZE!iA"IQlX emeIjD8=FZKTNmSR4gog0ې}k[w~`y^U\z~da&X8*r .JZ\!'& [3ɑLĚn޴O5%>V;G #; S\`ڨ!,?IAu{OY }gEr(. 3bgwp"ZT19kV?CNl;}q3GaQ/ì'{Q FZF4}NKlz@yO-ч.f{x$GY*˥nI) DwRPSBGte^27q*FrФ~ߘ䄐Jxא"o N)6 |߈Cj:_"o<qqZʌT#i%L\O4 œ}ת٥-Fآ(LŒk&RC E!_A^is% NvHFIV &QŊ/0**tq-)i P锛 a9<$%C(\p4Ei: g\GgCjInv9YXI[0=R:-oNGpKpLbE@.%qsG;+K\ZoFK9r a|8#3NJ{T>;=AIǦ:>ϡ GJv!c(Lyeҋ/Xn8o?ɕዱ.M[` ("GSq_qgw{ ں "G{ur,ӘIEG XC L</R\Y Դ6h]ibY  vesp:Bf*JzT h߶72y17SJSWU!e&3Ӣ,KˇJfzO8j~) fkir/װZ\S?A29YR+\-+{)E:sU[0eJ$lKD!6҅_wt62[HJy/&C_?6OFu7 %KEAfr)0'l6?t w/gZf^>h Ư&G4fr5n{oWly^E<8[ WzB/||Nc Mf,, xfUn$@ u_׺<)joT.N¢YuEB05}B.KnkPjeTl>hri'&D՘DӤ9>xeO6$$8m rD h*EY<,-=^iM @8qQx$%[& ը1 ;_9_YdĤ . Qk2Nxmstƀv]Cnc@gK4T4K9g55= E=wWcM{Ф99%~7lRQ̖WE9h*L!-o~]tv1yY^9\-yevQ9^/g_Ӳuy ֏lQP" vϊֻ(j|Cs`p hOZ/eIuߐw =;⨳ԘL$z!q9HNΛr\UNЈ@\ZJkl{(/׀hqv_U8~3^ 񷓋fP1Z_DCR`] \4&랿; ǖg50{G{k_K^"yn "]]좝xo{VNO#J^nc#{FƶDG(6S==%УI}$ ~|@&Lmaīgwr5?r]#s@j*yt[U))EM+w^!BVhb{E)iYq}:@9e+2?B=A` > *؛XvV*VPHkw(sң|`ȏ:dZWȚN ʳR=$+dyV#4df}2IrԴUeJeN=d 7PG?ǿ %珼 ]>H:i r_fN{O^?2 +Nnj< w'أ>uLz¿ X$)OPJDMR :|,U5E Tq$@~ x7@ʋzqNy@UnNNΤz)#tC+a\y*m>;3Ra I5YcDU`-F7f|9e!/ r q !W̑סr;(8t0A^ye;0K . e+k*XBkZ+O YeT'w*'Gee'n"?Di0#ZDBzLR|Z"i!%p+62-R5l;{.˞ E%+)cĵm&I84[BCbZIgFۿ)D.h(ZS5k^(vRK,;*NA8xQ;k5sw3vJT,&'7kyO-#pQD`8VO'- Df{dA4KC^9ban{ ^<8pܞxMXw !Fo΀n=(ߖHwU?ޔe% E5#{ڪewTlk@7$KfRGQ*giU2X,?N)CFJN M,I;u~y~9-NMm9Bظa"r|6ׂgY>)E.m.TT,k%h6%(<`Y/9/>`/rW*pp2|G= >X:俌B)t@P>_>6l?;D{Klzͧ/ݺg8 ֊$Hⴼi>O$fy|ک_ 񷐡iKEcvh{H x4-]pвLJ.PUUGjG"XP[#vWrڥ&Jdr4ʹe@/[ Bɧm!=ejr~9]ͧ>ly4v}ח}GTe{郔C+rr}IHe 'cd6,CL^6=q* Og=˴6D \W Z..Rj8c&%:^t%KkvїD,RԲy GQR9ß2/=džXcӿAlR`$Y\,PD q~Pr$&fVir;e Xr]l4ot F|bpEZLAMk9Fy߮$Oo9ۋ ff+AGc'}t_~ltMݗ<褄=7UGaX?;F%|%#~Iqg$ȝ9^Hp1=n7oCӭ: Rh2è_`wSNq h VIlV{~hɔpBqcWypІyz)kئYʙ}F ,:O.ūVgϖ_NvbzU-~[8YTúuUUn~]BV˫_$UG6(P lclՁɀFREQJ=vvϻfzS@!J"5>?jhq[Y$LBIqIUEc}m;]J!=iR`ʹ҈K:wXaE%ƞdәbߥq6:@gC*G?8$ rflt t$CBݙ#p+6.V2y~Fo%rM$-OJ;CڋN(eX TwcuhxB:(c5S" ,"?\ҟφifFg#!^B " uYޮ>:U|B4M6⤐ r>K?Z gi߷DCnJAF9 JpXEDL!3!ktpG1$ ,svVuD^BDh1{'7H_DH$zX +x#Tdaa;aIK82k|x?6s(c!|`D0XɼO"]ޞ_ݧm&RGkT;3P7!H2~)D#d]a?%K:N&]b2 \ym  GIE_QX u:ff RȻΙVb5tjn4~輚Hd "hSiEQLz}'\v7ΫZ-߮&&۝ ܞu(Q oz9@'n95\>jq=媙6&K7;"PIAg]]j~c7_س{ˆZg)!H4I#s;CYu!ز2D۟k}:BPسYRnG g_Ǧ˟z'CP3Iw 6 3_~$Q0>4]\5]\^_ %w,2 Y{TŘ/ =TPZav@R8ztat (}ۆ LD.d N-pX5h,WJcu⤀q-N2ppFCM &vC `u]ϧ3୐|/ƣ#])B"iuIEy7tb,vO_'T IUXܛբh2ы%pn 1h|X]ȶ.MB\̰u~pVeYu2/ZQ-S}#:?;c9RL@tR"1<]NĶ`IQE~zt6BZtlq8X#Mñ_sT>J@:]\ǎ%r\ypSDjC,M?`!;;Ba{"OGT+2_ee,v{nD=QuZp9m[7>xB U̬jy_VbuXz^'&vd[UUZ\-;&UUU6!m ,lZܜz8~=$jiUחϻkӦ 6H,}]Y/uh՛ y)/I(zU? wӇޕtp~HYBFa ^\[ؖ.`3huQd@0Vx `4 وl5? ՗D(tiC?o?g\J1ҕ;]+݀Hϻw]փOGDHcP:i yIz]^F:gBH5CQ4hZY=ۀbaMڐvx$Y/1쁾zUӫW,H'.lG_o#cieb*ASoW2U/j9a=bxIF}Z\:XyP,dcX>ȍg t,+dH9s Of|߈. 1cmn$@?,n8=}qsK_ЄWSLkj6;z6mw./߽/qǦأ=sU7l Xҝ=IˋbyM1 IDATZcY1 Aȷn0txٸ%{sI&pxtOcsENPa.7տUPX 2%JFs (ل@fKҁ,RTNӳE54_}C>.^lB䱔.DZ`]MΎc\Mf&tstiIerϹ50vb/­C2j*&3:F_MkN>Ow*Vl]ľ80n5 >ݓΟ7m}Շ#?0W̟d#JPN|~ r9%8GnDp v;6> "Hr}HlOn9`xKƿ @4 #]\9{Bknm-'C[NR^Jd`V4BAyn?V9~r^HI y((5\)yKe\=_ t=)!9Tlr/#c`È]ϸ߼S B; |"O'`Degw-]}>,e6`­co\EqB!@^2QR-Ru7n '# 0n~Ƚ>bL_oĖ9݂r6oLàd2y8/Ry|08-ʲ$|{[&뭱ԼE3j&8#FW+BHyL%)B/$=óv̘~1sFb'KK*$N%r:eøF ,ߞ烕-.)Ϊ\ˀ X3qlO.; IJK圢D:Dw;]I`%r kSKޓ$*ʴٟ,ijɽ?݆MX i ℼ+;95htz׿d2ܺj4J&g^7ʼnc"}GR_Į%"YeObf[W1!N#ĕ*W ~ԪQUQNUwi݈ ېUB->aG&ͩ3In<(Jfh}P?%'ec ]$$p'rST:.fڸmraW7?"41q$?nшN/]lO\ hIxhz@xBݷ`Q(kHLJEdw5&8z3G ĴcLw_nzZu=<-dIAD~0h ": „ ?/(YiP i ~ql yCH V+Ņ0uNu\>qjZA3auzȀƗ6>k6]Eif %BJF$tgxjb,RNLqŭIV]:{2]r0zqe|%u ,?D=bfsڳe(o2ML Kn7xZxu#nUO3iHH՛+t(ݶssVv탌Pu菵~$% B$gc+1 QmPl2 3};@{8{HVw+\b_X<Gc\eefBg=I>P@w˃_?dj X|w ?(@>29Rb v,`\~ 6&/9Eq&]"Ww 4EwP> ~! g3[)lRT ³Fc (;'YC0 ӆ}2$k_$uT^?Sn|9 \xpTeb˲,UlPY `ح,`Ol<$,@hBpsUAaK.z#q0U1!M$Ĭ:>RF~eexC4,G# (>]h3QY}WtpŰBxX 7ФR B 2NFR|"N= eP31Pr⤼l|k3gwz>oi>/."2[O^"dAb@RGm B{pĨZj)de1DPD}湽mMN@.~ uE8nl8`f}$ߖ SeBڀWv#F Uw j9mJ2P!^|L=oz:j7iY-LQU]9CCE,:"C ;|J7DgC/^ߋ:>{Q$J\ Opi'Eny:<Z b\SQ4_w7Ww]< IR*4%ϐƟ%!4Ii )@Wїwexeja.v cNLY3wԲ:k24uZ`.MG-7tEQlޭAXqSa'h2]GJ GkА ?OxI לQ!%V3( -[ F)%gI;ܢHE6IPd2񸑗՛Ґ΋ >=A(0%CaY1٘-i0׼[er@1^wE{Kl7_InhVk(0&|*+ϖMlwUSo6wY< %{ X452@2?E'{hk@sc|!H=ڗeAUsuϊzKH'ڍCT[0:XQOx2k( _}UOB> zr xvlȮAĨ>%3;V6KK11,ގ|: [OK2ike6>+hc.߫.3 qԪZ).JHƗt >YR'.:/[:pSM0,]1j }?ogcwT? =78~{Y%jmw|tW2_tA oVF:Ngt؃n},| g'Gfr0-t5b#v",7eQ;;Y|Y JW2#+u`ON[0sw$$a]åϜ@Z7FF?ZKPyZ?LTr-2Zo#m0 y@$R ģC/ iZ. nrv:o;yN$]½ YVNo)^e c)ԧIl.l2G Wlfw.*HS$*pd=nt1 7G^ȁ-` vݺt1H =i1@0TeqR 'EqRI!8iua u]N^<rì]y (Xz&go?u 5 [^W3+#95RT)gO qTxpP ߷כzy:Wۅ} Q}x<%tf¬/I%lk:`T|<|^=0fY}W!m}#gscRl`0֒X6]dճ O U^uMy3AD"Az9<[=ߢ(bt^<(yzbnB_qF ,QY'u;]-N qS]ΗUY^ΗEYBD|&8|^r8̎"V4 Pj>ceRQ _㎵n(<-ʳ`0,e9r0,iVvL@CbA?(֏1w5Χm~, ?aٛͻ.Iz r7qr[\݅c|6dquCr [pK<=Ƴ{ ʳE}t~^]^te3E}<%߳"pΛhYUr#JBs챿-__խ#x [֡/.~:10eq@-,_WY.櫛 &/on`9<AaNF`p)MpX_fԖD'P3QAԮZV_KuybσfyzW]!(BLFkh:)^U\-?-wT~wp45hD$s=2d0̾@ڊRrDտeB9 ⴤYEP}uRUJ"bDwl)]c3 }3yYL뺞8p^ Lr1pT/+xTq`1ܮw~Ooly˫/?N$hڌ|:Zo?->ⴘ0^ېgX6Ц P8C~K1^wIa ja=[k;K2Ԗ ^0R$kgl-'n6?lQ=l>nuJѩՓ[zH+VqRQ ۇ[Ђ\H!TG)M+fݥq?Tv~yIǦ:>σK#- Ij/ҧ*7Ǯp4~^c~3'W?\n? ‡?/ Qe"d;5Nr~.35^ ,K*g,Ke? 5BXiw(OOC0^#m>n uҍwl?V/”@n$DjvդӶ<-j-Rrj+OK^MRv^UՠATj+b9|t_b~X/o`Mywvfq5/ql6KE+{>vw<HI`hvɚ艹QHjڅi9;n^VJX GyH &HIgf}JTp^n`Y/f˛%@1`r77*kH{x7q5#ú[P,`A+;:~v6Io:7t6zqcoI۰h85-lyU^;Mq6nye'+"@tжN_g<)7q.hZM8 ( ad0p :]5.ebHnҎ9ܴeP{ w׿LŁ4{m{iNϫv-Nj-B vvdwdS9_ yeA Kv.0rM_Wo0ruzhv '$~2H/Wd6p( `PVK|1Y*PDRCAEbi Ϊ+g0T|aSUck`|>#W7BnO:7 pQBi77 (<7sM]{=qE2P)5X}3 O&Ea@aH@xCn_uѳ;-KT@{z8އcIgp6g'*ǀ>UukӢ n/ԁG\ VQvU++LS =ȏz+z [Xxي4Pmp#|NaBKGa<¬"FnƓ PRk iq_1\o7w;!yp0$WkPφ+q7w;QQusXxb|1ŋ@yBِTXZMF9z0-r4FGuYА_S2 I#2M2!N0.vWUf`xux&cSvC~tU%ג%=pN_ &.^NY_dnl FTow}wFª=Va$~sk1["cg!@ĔlBPr8*$Hŕ =&bӥ?`UA,".N˔^[֏+LY- oxLsr8hqSAVF?ϗ_\tq_L'bZ~1POWK:{|ݖy5mi) j 6f]t~jrXV,ofSw7"iY[* XW{KIX;}9|}6mJۏ(beCxy_z^9Bl 7̰sFXfń0~YML4X?ՍwZF汢N\z}ٹWD Y|uw on6Wj~9(._BP%toxW*9 0E&v0֜!Ku 7TҜe2s]Š$L&BqOowl+ -`M38-d@5ܓDVnL+LtJ¼'u܍ݟCbꈥEu"") ?>Pcfb`C;޹iK14yUIx`ADD0X\ݱz[ax*8& o;Ex`MqRW7ـۛ-|f'lu#iuR蠴 XQ`(5eX7ZTK{OpFJrp]=ԭ6_I -A.Hese$uLzlL%X-qZ@sZn6΋&_W7rG.{IoK<^ ?`t~_ 08~W!8Ɋr{flq~|٥d'"`Xm#` :͠`}n $8d՝ZaC贫Bjߒz|ˏ)"Z]M} IDATǭc랲]'Ki} :K?d:T அtvRK=[`)Yxx($͚{{ AQSLB*#V_C&vZ۴ 4Ef$l토?)ooscL  1'&VZ#mG=}ʄ0tֆ* ~AmFmJ t3#uXU yPht}#Us}3hI]M4qXl DCWuIn6*Z2s 5I\]ס/+ 67I[WΑT00{ssW WîP[I+raR]-39R4MO8$;2 ^F۟kDXPʪvzYȌTb; !LBT(RǃNg%v,P "Z:9]mo IlUr:099w+3,@h#mC =ݱO}L ~ 6f& m#TP qiQ%C8WD5C qs,`OQpJ= /̷_`<J*<+Ũg1F13CMBpmơݫ8J39}I {t =?>83E&6z?-Op_@0:tX`PiQ$:Z rd2!}h<-bxWHcL>HtH^a:5fۧכZ XB] U0FҙE(ta[&4BDv2[W+\RM|;հ@A^+/5ypH[ms^ qG az uS8[Hk鱔I$D܈3GV\쉞  e;ox6w?'1R%G_%; o7L[΋E5oz$d)6Fng2gjA Zգ%g?NGoFYu6F!k{25Yuݣ<-d1 tie qjZ)2LCIN9%áETy b 0(ǷKy 2uC;a Dq/lY4>5pýFӉrȟ7,+?e4u_: FnRo{cM' g+nU7n@&v`_NW>[^7M#k>QrOL)ny,@|T>ȇ!ldyQFZ&@j]%VDRyYO8dy$|~@z._MБ)`K d~"5I5Mtܮ1UJxKrnj vBP:-UHQ]MS?JpR.Qbs12tZ\#-+[(ĚteDj0g MURD~Io+3 "rЮR$QpzN>=o逿lw 逛7 ,~C?moK9N LtP@IX2I6kI^~^'?L  rQF6J0IAqBn! U 4AFગX&"=)N_' 'O1rY#f8C^jv K<geyV?Jri!Ϥ<O,WL&"ɏ/`{=lUG&'X(4S Obn6num XP`P`P`P`p@ AAA@AAA/0(0((0(08`P `h׻|>Z;Jh$Yf Y1Ks/}xl1nNTZt|vA)>c572Q"3yٿkgXB|c =qHCZaCW?6 k]F2yd)rFDqRHtv3ݾM =׸?,C` yz3ٖ1@ΦaQg~O+6b[; "#Ĩ%bi~:;H"U8؁dx58N(wpqs}?G3K FB+ji59^9ɨ?SvÞP.S'ǑMI&SxtW Y aFkȹ~w}bpZ+ y+ŰmΩ..ZǣѾu/̨uP m5i~Fwqt {vAu.l}wH3_71>ӡ{m C4ΏM? ]RdJttE/!ڡyX04=p~\Dk~,E4rf.Vp!E{hJUs1[AFo!ꔎ\'T1y+G Kһi{zhKLn NW׷z<_]^]/.tuyz˭Ԉ<~?Z7}Wz m[_,})j\Dt"s`whYKj<^^'kr>yXZ4:,q@0毄Rr `l ɴ\Ъ` ӟbVUrO١Riķ-۶XIo[.Uһpl>/)nPI1X}<Ft['ע Q1h*Jt'whڢ˻|:jc u$bF6kRz"LX u^Jl6vЦ<nZh~uD@v1NGˏO+.Ň ǣ`q[kyVq+WjF뫋mČhmΨvN.2awqJɹ1xRL^6{=(h|MJɡI9x/PB8)jO&zy=/t-XKC J#hwS\PIA4;Xz]d;>Z,h]\PыtM,C{Mu W##⳺hE${X}+Q BJ,ˡ]GKu5Ac@,]ey @M5 bu$ ?Sbk6P\kPWوRw "BZtY{]<4vZDVt҃̃l "'ijSnf~ۓJl/?'=l8?{7|Z9F7eRWVW_ُ,{0Od20c-i5A7>qݻed1zqCQr4=_G˖R{) c%3ӗ- nAz=C)P'\4<K/!E-Jo/X*c>˰Fb*ӂ/ @&r- @&#;H#6z]6Ȥ"ͪ`JOg.mᄆ2M1($c:uZ^ӌhcs,0$n. lxئQ) ڧ܃E2j..Oj Ao`q^\~܎C5`˅\`l&wa.2aw'<S j/P_p0=g1 1|v"bO8V@d)%&XڱnN?x>=y_[\3kn^Rf%rN >(&G!/,$Aa2 db:KnH{(f v.D96~? D@6P%c0Uqz 1D=m=e%t3|Qqi%rk]f-ɍm"4UR%bJ!",7:!*N rkuJ}.'=pcy{R KZPz ُ򳸦꿧W׷B`~Qg`Os 6BM}P@Y5ʮ]d~2{'$keTpiQ~X|^%h(k,킠6.tcN,Ig[vd&`Z^OmUEbudӸ S\^s{Ą 1Yo$F #WrQ\pBC:lvsb178у bxdQux'CEYE@ƑQ'ip1u5aZ=Z%drD*Q,M eYWՅ$gKb֟7iOwMWKWtug-~>+K!o>K1Y5$Ax3ϛi.2aw#$s `~)W/۾I `Ƭ,NhuKOeYvf%4.HFΎF#oG66rhz: 2@ MKa LջeQ(-BŒZH2f7%T3z1֞f#H La2Lj4(gpMG?g)~ 8(1$15}+z<߅"u8Xn=7I QemF 87#4t`mƎُS&gи\R:4]%+>?eH7}\}\IS}-M 8Wۏ+LT:3Wqts+פ2t"s`whĈ68&󅰅iR"rxZ%lT2)V]BcCf| ]eF/F2( m1PkO_'_OBLRu$ sH)kt}6gv)B~ I"$(T*^34D>"[o[Ij+juK%hC|\sJϖ^`dhG[Nw#P~1wi)b5N=-\H3n6)K@}<)Ecnz\{1Oo|$sYX9B)0;#M*?w53I]80=Q9_7=Г>w U8]Ǧ9 IDAT mbsq {v>-c ,Û0 ڛ> {K.~Ju@ oMjVO0>;O&Qkp~~7 :ztO8ZCpb2{ S$~@ :nڲFԹ 9/_=~ /4Mfd(^Ϧ f :'K d4,1hG*sKu!o)ښmԂ)x ɊVM̝(R+M4ֽ\xr04)wjY[>j )_d!X ǖ.a6iIHlK ߆.߆Z h_v# YR̖47KP \Z7ZvEQ@Lejȴ@ nȽ{H^W+@ mUhi8HEi1keƄVsL7 [9('l>o67s<ÀdEZ҈nk%X~X=@ɑLB8*!✔Z 9'h˾L%ԍ[{c7ӻW1PwtgQehU5Ty~ne3Nz魢q?]8N:68[.cӻpl>+K4cMFw)~}r&ZZ@M_=߉`jGMC$#G[$vǎɁZ+\dKϛv2⤠L:f|jtzvloj UFp)VuqU ք^*˽2IZ"b<Ϭ鹪rz7ߺ0m$WsEQ~h=9JkyF1Gb̸__Lf7wG/q*ݘ]ȵI|g>ipgd&^ȷd@nV5%HdNry 5ܓy#+鐷kj8!Յc*[.cJi:Tu 1mꊼk JRgxC~ISY9 X:kGiN n4N*=k~1hDE :AD0gmѠVB|HiD=G~? j"g6ng4Q[ҧI{ j{'.#LV7{ h8`pf7V`OkىjMϱ,WnQMK n۠֕8[6_~'#ѫX ^׺GD~SY ׹{ i,ڌ!B6 ٥?z!QvfUHcbY\)[T٦>FZlr~n sXV1+4 mzIďK/D^^͕]A-"N7'}OPÃ}NJ˹t:_> :\/GgPab,~ ٫ɛBOVWB%M<Ko֟z!' un7 sG ŧPóZCԵG U܊ҁ#A^=E=SҸu3ZBa- MG)bT^[RI@Dd PlZWS Snk_$j,g*9IH$:-!t4}uY3tJpX8ףƅ"\HGj"|DG?:EI2U!lnb:t9 7#QTJL,|ט"dEg|/^Nw"7ULL=.4qI[&8~ IjfZ;Gg=u=z1cF{j(`z[}%CU&T56Mn铏@ +)D/!GNd@4B2 Hδ mϚ:c4h2Y~Kglmy[\ߧNVHa73aOlȶ^3Gl9FY &dUQQ.F# H& ...'nj8>//1je.^}:l]=RǾ,Dm\ n@K/Fڦy&R\^=P0yX8|2[ r z9Z}ZEè&8;\=hji>db9'eyK?3 σL@+Z_M~8s#c8 IPUv(`֌#PDI=O/_6gfoV]?Oo,WoIb2ys=ulGNY@3[xv1u B܍ӷ Ī4MW+D?jb<{u>-pgz? 4V5L*4w,bXmrnRcDFl3D mbkrkajq%y~Frhn׶xPWEgqrUw,EJFqbSLhSp̸."] [򟗴3ra/!6l/Q `U]E8|>G᳁`plp~:pyub7J? nկk!ys gÛf^],7.Ά.__~s9{u. x!K_̧̯//7ۮ&nmw~8F T<)&/Gk{ <vERrhRz@AQk{}X;@W;ӂ̜<Hc|(E >=H%EjL)L'N2HPg'%Dg,Jk~(+*BQ?d4A[VZR*²,Aqp64ҫeEqx<[~>r=~1>?l6O *ǘ/MYVؾx~}=͍bOJ6 ^NOn,ZUM9gyg2v_ח=ăedp^_(zx9/^_\_Y#Ao-w_wġ#Q`2@ |)^.v:-sS"0{@IT*F{L?\#"TJ;Lf7酅&dR2nՌiجVBuSuN+ IRc[Yl~wi1bc :"EH17ƽGQ):63"s :2@WsJX QG</2wF'0C[|}BW^94'P$ޥS8!ǒۗWר͛vwyq)2x6> g4vC [`8 /n`(_X{K0?8A6YXI!{^Lg7)틓@6mjV~) '<S j~Ag{D;o ?hLH#(Jm &L&e:6ZS)%L."S9IlhvZiΐxmf f A yw5sJ3f6'%d"p|E܍'} (3߶eg㻎?6 ӕ; i 9lf11p"@#Pv3"ObQ9iū/9̲>\YfM{BbUgC.KpHltD@(~KY 6/of莔z/)rn.O-W;+^^Q+׫tpA\2)Sz\ݞN ̦ D\b=Nh'5~z:\ZkHg/ύ_J"O77 @y"u(̼юx}hNE\R@!_bl:h4uOFqԜx2H@U#O;ff8.7o)$tV } 1ITlC5ʘ Ic <ѡjƆ9>e2ۮzmKAZZ$O`HFVbvz[&$NXczQeJ)}ߏG#m)qc?` L⮸b 9bUY/\nz9L4X|(S 9a GOsH잮tnw3O1X*ªJr(53A1A0xBhm> "kRg~/Hvj<67œbv3{u~L.Vx򶼙]SoWr|.h 4 , eH}s1j@vq]n|EvqYƸ#$s `^'q._ݷ}׍qQmnht\~lw$|jo RYa~M+dOgkdƅ*Ӂ B2^IS*NwA`RY@? D\GQ];<c.Ow-/Q?4 } rEԕIF/N7}T~P_dA$r@7Ld,6stK.d[-n7rD~a>XʺI$r&잣*OAG$^K3RL|CgSBD ?tL1lH>;z31\\RQLfEm7@bDn|=1$NZ){LP}]+[bD,PF$Ojav'){regV6d8p)mА)j2Q0CۃV pei\UZ[C?3Uq–]?6 s( J5 +%Y5g|ruCByhԿO,f݅j,(keX,}K0ME?QdtdH^abndnl.ɔL+/ZF^@kB'8+ i hQ1Md^h-c c.`r 'YS R @V|.?_ ǧ=ďAs͊J`r ɨ_ՔQ%܉j7{V+VTQE=eJ;,BHn8[ڐH:|Q@zw j -ڀ%X]ث;u̴,$NݷtRjd[4U$Y 8\~,ڢ%#=ܨ.1Zn~Q+*1َ6C!lBXԩ1U@ {)W cIwQ5=ތe'~.cӻpl>1DZhJ!°bT$VeRi$O7׻oN?C/jiBMŲƜOVc5i$$*7"Bȋ]G+}Qí8Ij4m ^,̠\"rYUµ:;҂A[Gl?t:2QAw fIdL5r> '#tBv&Q-Žz\gNc_|Y6)Se uoWuf׽b]Ri|WVŶ@jG 5~+mtn7=?6 s b3;PVN<=Qغ[TlGꟉV)WtqWT'uB$Ms=0*Nv} Q:M :d:@1tШd;!Ś9D6{(E y `Sȱ<޺t_kLO̹%' V+9.WɛqQ͍}:bVbխ+jÚw\Aoȇgm2 ʋ(Q,z9:0\&rs tq D3k@NӳY-nU/ߞhC#=[5I/F[1P{JfHUUuG,HiVqfɢYS }Φ[j[Fq{{KTn XHH;bbP{J3YLՃ+fLWSCM"苢tD.HWz5ñ 6v^xsd O]Pdzt7DU,(]]˦kFgЫl4SaIja:jfMBŌ\2X*RdZG h5iq,"AI UF=rȿb !4Yd30F@qe2N܊S抩mKTlF?eUoy@чrVZ,NcSt<>:Wr`"n>B$+E xtRl̦"T zq=L)% zl/Tbl2`@t.9cV΄&٫sRbDHp]|"9Y[ػk\] zN qFh:-Lj1t R b\w' ު{ekp#,XYFz|LqgG.`8֌]i>ӛwIELY oFlbrQ.g}iWoV"YVJZ4$ȗu@ >L8Z oT?LbTM_Mq^1-, 6|w08Y OыQ-(ˀ> uLԯXez+O/&on5ZxFI5YJU]]B5Z x/eD&rK"W3-:!fYvkCq'VT (vNN+!ĸ'uUk(J s)YP (lf>{=EP#>fnhӨ7 @Ԝ" 2at#Y-M5,f¨ ֿ?ArVLß^7m&㿴CN0& obzdXE5 ЖU K@in1z-Ɯ"*F}!eVH_2CPю"eHbBOƈ[L$X#oRO"kk⩲eE#YWhiv+C8i=D)UٵIGh>ƌjHqE1h#D ד/L{MtSV~WL$,`fWN2y;@[ɸBDr$z2psT@...'nj8nppJf0K4®B/ &B:6LDK U!5"_H]5}Bf/O/j Lf7f *bb Z1L^0kZ$e[P&IŦXy5V8TS!ş\iObfV"nhrdIAO8)-Fvt|Ǧw|ߔg'J3!׉Y4wg GјNz\2N)䘐!qۚDXjI u`J4o;8mޔ]abEon5^md|vv~D6QM9od&zz6!*DX77BI4MY-.mKCu?g|9Yd8ϫ?2ACOr>~ '~Yvg[mٞGPV'd@n> _?O3 ^,\# e1k,9}Gtz9GL Z.)KRG&jtj5Vm,u|Dw\Z7/k=g#X,_\1Qo@o?6OʟWQU@2c׶CtuŮ23K޾Y̯vœdNĿ J}F&dQ>$g(ք{sEc@-Xa'inџ=$nOOijSOUqb<J@ ڍeƲAʲ.s@? \^]t}u1/>p0|6LGlgtc|8L_̧̯//7ۮtsPI1y9_&]Ѥ6)OA'ͭq j`C4$0JQZԬnы/ Q?4Bbd3-8ʘe.p/L%+&d PZ湯TDaj y@\'}놞P/b)gJQu?!JMenB9vxWL8GQ32pH]7owW7pLSeVS\agUЎXf'lRf@$q*ZIb+c0:r69\_1|.'s e'cP~ET?_䋯on/FP7܌%:[kUf6(]t [K C` *oKjw*N 9b:Mn_ґ{lS*@h|@N)Q ԃ'zI+40-6qrMO5[>3 kU;>nN)=Ï*]UA6uv0y.k)2 vԸ CԽTe FOuSAѣC.W58M±? oN^;ߩ\*@dTd'{Qܼn0#3T&|+e{ff1P:mf5E3G8MG*ls/i8>]B9]`ZPVYjZER+&_by&Ul68̃\E_7,mWmtJjaXBn7J<v54/e3=|MIHPwB/%˫W/\OOݷ}¿Q ͗ruy H(z ӄ-ԃ,c;eʯ.J3U@nƦ| u~xQ7Z=%쑣a^JBpFhqrt_VJ[SQ8M±_`M{r !Avq}Uzg_ELՂ *b  wOChs$RZ{-+ 74Y8)3 8z?ypr(ֳb@f %>C8xkǒZ[773쾔hV~~=ԕwBE@ـ\XU [o/='4x/b4ʧHK FnWU^{OVuf6mj KnYEWBgC<6IA@]# ^ꎊE\L+T=r=Nۛe$^&8`Bx2ܖۼ~> t\ZCP&rE-7\̯1_Jª.?UW ^]lo&%ͯFYI VWn~^{ءcILfv41rHj;+,Nd)@,jꟍ+j\dh!U,/~= =c0XE)V!oאjqBExWwgpwugODOG^vFf;[]wYR #r;.zGʉ5Fb0Cq- օaE[␲idڷv1FJM!PًٛŻypp1X=: sT/Ʈ.`:_>)f=q wK]uU)d;uOMO SӷjeV87M%'㒊b2.znRt|=1$vd>VJDIUqX3oԞHVh=59|z1}0W=[BRC5NRC:%&(cTlSb@} y+-ڦbY 7r>u|YE.ӛUEO X\ȴbDnq~_]F~/HE*S uDdTSu n/!MQeWkzrľ޲6֡lcw'ciɊmHwQധ%*6OtR'a:0c9%G[)-tXOH];`1ۓſjA}FYMk R lr\U")1Vd&Qzrm}^,hvRKHԼ#{ܖK|DFPЛ~isͩwB֊3@u>ۖm%oZNS[ Չ.cӻpl>ɤV8JE*NMϵE 寫U3PNg2+Vr`ʂS MƁg2 =pƃEq_'i lDţF[kk=;ÔM(о*8J[fKtܟ"fK)\/I# 9o9}k餬9bG F._O.XӿWRa^^>H%O+Q?GрnuqyoHt@vqvxa+{ Fz^V0@ 7q!{CYx:QU;^ܜ &:QG @U;>qn98xrTo IjZh#n 2A~{e?oooWK;8(ZGfp,v ĄL:h1!W롬ͤÛ:FqQHDjg9ŦUz9?Ɩ)`Fz-#`l,:^qPxD1\Vξ]s ֫^JrWENPgcr#'#<7kTFgzСGл-9رrmysc\ k L:E*_(+"&:ey I:QO G&עa٨sUāN YV[q|-^?mC2lvrIпBBK%E!;(@ׂq,kA    ,X``p,0X` `8`A|{fܜ$N}G====UoWZY2dY=XD~κiΒP4O =!cmȟSlFE|{Ԉ(#`zvs}mN 1rǡ~v;ىtߣѳFۼ;A IDATveK嘦.扚\[zR;mU:^$:k<~;@y+@Lzou\3mί˫}`3cd҆ZB];W7-'~]?ʮ -kj<82sKI5#3tq]t`O#OH`[ZJcAX6_{Jo$ CHo@eHu jQ!L`fsm7!{m2zؕ^@) sX(ʎvua^QJ=7>~JD' MW<%m- v*4CD~?k%x1 p"lFP.l!{ED"go"0{ ŸEޒ ˤfT-Хxy3Wַ@~SS>p xJ)\ο947w+x>#rK4'*B.AۋJu"OB@iM7@~T/: _2H}Y2 e.PrRVu/hEp {`m\|wSl/ju}| 9!tLWrʹO}QL]Y)L/ c3k׋r<|W|TX&K>S.l0C10Zm6i>S@7|߿[DCL>!˳U6@MPI]}a xZ_/j=gWte^R2*j ٗQJz _ ԅMg~ok Ox, a!1*6ц#.)>쒘R[jw@sRU5FY@Ai֩薺v`; W$C^p{\-~s @^ 94"{2O[O+ՒiE*O))]yy5_-:dys@\[jx c䂤Rl?tD^y]'<hB?mi'G֜5latt\'?5|3V5B`vsįj>>X54y}—pV $_d @1k|+۽L`&L" :ua |<6C S&J%U|>HpN(@]\mѾVqVLSP!\ AEnpY;La{_zx[.G'#(*S4WzhPe2J~}uS.۹1X[Bɼ+[hW2tzԘ r̪RJ,ޮĆS9z^ovɲ$^@@Z72;kG=b}IzA@g'fD XȲ*YMh4q˻5j.iO էUHIJ5auod4fU'|+ojԼD{3߄$ $wRY=2 ';XiυS@M#`#U9)_,2ȩP$#B9Jj^ZkѢ<%`3()e2 T$[~]IȽf`rox?*xJ&sEefP.}3@:R4Sǟ:T '\2iH ϸ^AW20}9:/O_D 2jLbfP"mR:-Ҵ0W@ y*,Z`)$.׫z-x~ފTj~q3lu ,jIAF+i٘?nг1pD換fꨔ98\CO ҧXn듺6ʫ [lkBp1J:w'Bx:@4dЊ2iIsQD2I6n7=ynQӌ茈rK5 ?蠗TrY]]teDWk?,[/]˼ߑz=VocfIfr`,9͒dZO"0f7V׃qo9'7umFy9M0n F+~Y"ڳ99)~Kn==:-3+a.<̏tG ;7GۗS UGh_<䯐ӣdq^lѤ ").= h4N-PSÇpj:sDaixF5=#0jRi{^|}@'|q]ET$wgFJȇwswID{V_92}{g8|Cf4"j2- 1osk]PLx(j9 )jSYC+2 MM,r(zoA[hWcPrRxV-rt)IҼ+hs21(ݪ=D⤡}Wճv]Y'Hꆡ椮vfd~s{*S@}*4ݴ#\kg@U#qCfw#)8\CO ©<~Oq>##+&Uw<ͯnn.fxF}k Gv$5wѬ}˙0:k$50z6icc^1tK=i~^+ѥʯ#RXP 1$V[$%Oe0f_ sYDʡ_@pprGqL~9d  yku-J.bLg=JsU;_NiG%oEL_S!( @C Ԋ<D,B uzͮM b9cK[Yw1@ 2@2~Zn^aYŊ#He`O?=$Zt:.lH"mP5dfHwx%f_B5q~zPEpvh9dtڬ#Wσs`欑9X>Pl5idgPfl0~#M~Cr#i:Q#B&JbN䕝,&95D>tP0a>M ̙2j"&utJ>C*STDM׌~Ru"3:\ Ï$ʩsqD";슣E':_?#f$NďZWoݑxAE JV^O%Όl$` SOéjNMH. #(QTӗez~i!0d2VDf@FѡamO$|H$2qT1ĜxR׃zIb_V囲$k/ 1j5`o2@C9״"ݲ+Bs.T!Dp|+*愈7Y QXي:=IjA)c!kpf,v4޲[˃ǃ_DQ6+T-L| +!qZfe鿏ӮlEP^w#1PA ?5|3"U,nrqWdq1qc0EZ]t1:%w-r׼ִQul0q&Yz֟rn}Z䑯H~QV'皚'#uW K+CΑU[.CiXd( Ǫ Q:SP:rT_0K $R'F1@iF *w?ySl"GFor"/(ˁWoy+]DF+"HsԺ $ʒZ J BGZHMn'3ԲUMuAIC4rjnEWKՈ.CE_x+tf7E{+WN/9Myvi[smJ~1E #JV^(\tBr>8hEari"jܽ ,"#m dHTLGG!wBC* AYMe?N/8CQ yD1\w3ttK}?@w=8CB2 Jod.yр5O)abK`2z8e Z n8;x@7 IoZRA:А!w[3*]~_}/ viO.TcNk^HdR[6 if#x=\ *^"CPLMHވ+V`Gt1 C2(WGH.-75B0 }H*i$֚Ԍ2)RAcS: *0:gղ&yd&W.&S1^!fW5bQ]#.?ٟٙh<ɬGr^ju@ %p9*tjerSTA@7 Kkus9k/ C,Y'y3[\-o拷9J{1 `nI@WHPabż1M0ύ\N DOMA"Ă*"&4P fO[|IG9}6^o`\'?5|s<}@8B>W"q| Dj7J#>X~/ 4<7Ca:lLu0OC:^cǡޝ_շp nA5scWHE;J+V sɼYʊϝoOlF``oxZӐpwIupWe%bMleo/o6SjfHs{g|zecc[k}c \]"RO|mBUOeTơf]7BӚ!-x6onu@Ux|{mM.|%hF45ˎ/Z,{Z s2U-|{pNj#Ln{W'+9[[,Qsu!%00-Y1ګ#.gEiOr!Shyޒ@ɷjX(PѨ|F*?i8"љ}`,LE JykA-^'4e{s *=&Is˫\?2Y4Z\i:~Z"o1A q{h&oIߑHfY_p,ܒg8W0LT;`[OKY)2NS^ԋz]>bcϲY5]aӳ1wg㾳 h_`Z 'i=ƥ@!@ƽ-}?6m‹h5Lc=%}w #Q~ 0X39폷{J l҉i x#9`3l4jP3SՓ)VF=zzx+2KӳCJ{?3HtA7VvՄĜ@8A=+x8fi,{qD^zNI3 -"9>J9D^\-׿l,yr;s'ey:K|M9qmau*^U‰Te+).Ülgcu@UEpj>S9~ kFa~$;BgasXm9|>>l\ل'|HX缨|D(!w*BJ,f@iV%,e#*@9.(Ȼ~"ɣP*ڡ,v!4u;~(W֮T|`ZRP|4dS8GY@V7(1&z& IDATnB K* *s`hTGu+DA,,ģ(/#6lu.՘DC-ƫ vT]ʧVj01y֐4 t`6vMȉR1ݑETb}H/h<ŧ>[,jI8\CO ©K?|+lpZ5Ъ\:6o]?MB}Yw*6Q$sÛcLTΕ/{U۲F7ײrkm#Y Az̗H./Vcb1iմ zZ^[~XØḾeb9` l@$jq;85{57ƇnCD8ή9'y 5iԏXlq'4s/ɖ{xřzM`]*.m)e֌Qx@ anNֈ0ՅFw6} kFil&L_gBCwO ? 'k0C85EΚcIB 0"13td0uNEZ&'|A$RARA)mH@ig't/N]-M(BK5q,EQ5 9fiLWSnWB!1'VΦ Ǚzسޡ&+cvyu~YJWFTҜ%ܦH9Ws%8{il~s9}،_L׫᰹;5ܧ˙fԝ;`LQu]jN342 s/e [ 30˯=~o#l)|ų9,Yhݣg94+T.Sޭ!t@j]oÇ~oZ1)C~t&kNe|SnA{@b~Hp^T~BSβZV䡳ū6gezz-d> Dz^ŘLڃG>-ٛ%˷ջimֿ0z;_^ݮoV?%v̒2Boj1S؁ySp!nv vn;5H4\QYqMzѤWƔtcܚit?o@}Mǰ|uK/t[J.S&TVoLDu5堍J#6]/`zKΉ:趭*Jò΃,Q 5K#!]${sҋV[[mCur$R.iO 璘1%v Bf_ݿ[_'wwKh*DaPLW8 LF`T=XN'_,x2X)x" Wsa:runk GM(e&}xs?dީ(=XJ 1 QLAs|4LFϚ]_#7* c4õI%Q1iTcj˫[4({o)p^>P-SQV \E_! TfO- |PϋL%>^4LF8:S`&38k-qdCzfz,vͤZaS%H-?8}}nۈk`;n>k0`~mI]g5{ݮnW_ov;*Y{яh~|X]_l<:#qͯVW3ӕtӡygw-&!=o? Q4]On󶕤/xs{V؉IҐY_c͛Yopf־*BTWKrT8AHb\*l|{EdXLE4:st;6t(/Jcr(lK6h?˅JeYɠ#ir SNOx.RT!d;3T38 qR@SsBLs ?}Y (߫ψqCxm7 ɋ\_V?'~Lw]OL_Ll&w%|2P@rb~5'OKG6ܙL)tsj85rӪo+MsbN_^NŜp"G4i͓NWq̽aP{)2 Ӵmcz1qNtIfmz.q %RtȴLF6S%,jjlцbSLl4P:"` ORQ;*T,?v>L58L$QH?ŏKL0sz\|s ǻ~Z7F@_pV޽{w]j_OP~ϱڰlHP|.?S{v{nxv|?~;mn?lo7ofœ7v4v_oT"ղ|ؤh+QшUpsjU7f_JU1M|?}?1l~շ$<#o۝`Mm[ AE@r-=δkFeHbO+<j~IDc;寗Wsy3cr/L/fmzG槕#b6j/̤pv)@` ]9t6wԖ*H.4ZlE%cH75kǺB6"W$ sOyz&d=U eDyђh+"^Y߀ppBw U)![:_jRS]P-78%j(IO Q<Q/~p -j> "Uf觨6_wJ5zٌƇF?@ LZJyVoLmZ8~§ mWOˤ) e>;= d#1sEH| nlVFv+9Vȓb^V #r&w̟AD`Db0Ep ȅ?KB}D6( 7u'1'3O*MtĦ1YwV +ĸ`oCvMj#vVĞS:\rP<KG +AxV"FR)[rթ iNA#DwG) H]Z3${8P)Z+G ʫ_ꎨ[3$7˫bF bq^6wt `>~jnYMj0JLD:r۶%I4iۋS=ǴmWӜyQ.7~ 50i @@bWii;ZTпTjO8OT5kӌl)SG]߸Oʌ?1DUJ^ &%f^eJj6Bmg.W孡7*=b>#w\rݭ:C{8?\ '5< j?؞y$8 q(&%Fw!X}B431'/ " 0y*)g!n.gl}j/fTr_!qKߢJ YWGwgB)`2T 9a򄲀@gPǞh:^J\HF1&v{d;5 tEBl0)?GO;;E4BV_^N_lm(p`@69ME鑢늱SD-7yQӣw/1RO%ucĦ~ʥ"E(%VZD3y ^4i? ]2UkF,Y]CЩ` o ͳf4nb24%- ="}\5Ss]" ΤBЍ_-'$JWl^νPxзUc2He YG(y%n8Vnd %]u"{ /F׌h4k_Y%1*r?,J<jY!%h`||wV7p؏O^`.귕dlhU5-iO-`BcˁuӞ'}4(r'4Nh1G+sӿ,5|LNJ-@3~>Qb`~R+j2D~κiΒ4e~c}ͱ$L_/[kQ!")mcq"mu82NtJ$bFV+}G=뎒Hv:eQrPi [.cu\5~LYH{4R-Dٳf%cJFϿ; =MɿZKN fstCVH|lFGy_.o߭~]{ _RGUt*wC qٮ=\=/jLmCr%H] &i*٘f=j5xf_ػܴu|gb*_ӯe&˸ؗJYB"2P~}s}CD.]N!ڭDsL_s?nHՊ?Y#cٜ5i!J`#iҐ> tf&+96&T};f2 L>|,PrEyI")V#WKѓՅPbˀ̻~D yWvެ.q:%*mY~j/rL"uivgr ?5|s$NV߷v"5"cGzjx‡r= C4(QLr|F6e u5x9 #鍐5|4L-RFjtlD:& ]O ߱%2+V<<AĞr(#EbԄ''s%]{ ugO+YMrMgGOΚ~811§xF#:NoWT>+r/ҵ"/%UYZ#sϯ #KՒFbuՒ3-ە9$<[IC 5s^o/[B%v'`48S83Q`"5Vq Lz>5?*N-PSÇpj:GOe!) ;A_aO54MӞ_|?5lq5^^-]솰4͈Έh T˅Ti`iBDX 2*o.LZh6j,ȖOrf % /Ic쾫at@dCW%=F"F#9 j2Ŋ墘}>"=?Oך=y|YTe{=7vv9?׏9y1E#סP}j}n"(Q\:y*BH -HH&0$m*l!M_:H2j"8\gS sUiA@qBO-#Tq6}ߌҼCS9~d6 E&gVi"jN 'T2"u=lC'-!X m1 sNego8b@33lw3z#jg(MtnY+{ 1_<[VJJq(A`;,nO$QQ]I:3)8'2jBbNM ᠫ~ڌ_xLJs4$A@h%@ZG*h`yuHХJ*7"'%$MRTdh^djE ]}Q#65Bc\4)֐j: 5z~ 9z>yx{?k(@ݗVƽ%9tm.Eo{wZ~LCZ Į:Ayb33@`[QEj\^p {"`JU;C&O&N(5(n;tx˺mIRTfE38T¿G$_m9$TR+،l/l?_Sui-Epb$0" - _t3/tJl/z]}3wHoeH[(icV#gf |CR{qq1pƻ6@iRٝDp(#~-L| IDAT̰Y|iQ#4L q\٤4KZ\(%'nF8GzD4W~UنW|a3~1]\n(?˙fd{>>fsֈ%X)}—1ʁ@wGbPȽuuĹRHRjl0vҙ#ԅDQ %(tZ352^"6w*nv vn  ''L½ daW(7,C0bʇޗ(oT{2țw"5Hfi4 <)%iJ:qq g5cj@}v,RkdYro~v>Am׫[1u n?l%΢}.~랪7a{|Q9pa^_iF|}>{tva{c@^cӇ-6eW~#( ^GO/f9RճAv^مlHHDuw9A%v-;@GeK2NhUH\!8A hKlCOB@OgM4:%MytK 0xUU iQ(FTa_֘?C+Cg\7§<83)dds<' 7Ԁ !d2}1qϓN^L-}d:faU'v*Vx4&h(DxlCs96%> GWZ%|e~`L4jBSPŜ<7dLѧVi=?C.f~"ͼOUH4/7W9"W9n:zq:9ɉ̯ZF -Ӹ;A s@@$u -]OC_.ې>.\VYCR'A* ,uVVŒH4 %R\Xޣl&K}K Xz̏S=tؕ+oOd;W 40vEI8|>YßKt>xtF[<<\L??wo_}b>M^LvvO{1 ?uM?fcIMz72 /sZj+A4ռյQ=;0o($+~:1|YRy͋W}nv}DKM$Ȯ~ %KH?OA"L>A.a@1I8;rg99kRb=U6PThFb}ˮ$8][g]d+7-B\-*"oc,l01h80LdL|t\ݏ_'w˛ս\o7w|v`b,_M/&t:LNN_L` BB0#`?l'vs=y  0:&`bBnZ{]M*wPŜ09Gi EhޜhϏY\lOsQiYAJdfiUjsj_Valݘ6l[L ɋnWyrbYrzs9k+Z߭3Ѧqac:\HA)D!>7͛bsܶ͛b7]\-Mۅ?qu~3KOMek4v趛RƴH[9& ʑ@[v7&aͲc# a^e4+!My-G&$X:-l$kt*W0dJy)v<&W@R S^U1js˛OsÁI&*D8,zap}~a.)؜N( lP]]K97d7`1n̜꺆M \JXY\;+ ZJ@!+TzqJ`.P 1ɋz-/jnoW^WCxU8^qw4e3wY*8?@ LZJO7&h>&(z*sB< ]GNku&dyK̚::wyWS7@q:Jcԣ*&>zрUuY6\aaQݪyB(٘?nгD'wf=Gr ?5Hd}k6c]A$@S9>3sf)ZnN֥ujT/Ef.^wCŀٙˊ uxLWE~rrҰTݰ^zܧHGrww >w-m_Q& ǪȈB<1`C v$`ve$x@AaKS̴WTj@nm{G${ )3@l=Z ޽de&Ϫ|Ji ")|R"Dsi e|V XUuRhJQ@bVLǂTZZRv)q֕DÞÖ^27:HÞыs">\/26LHC5} ΨD1J?'Ҡ=$h4B wŃ<.% dk,E($O1qs6(n`#oaF'=TJoMD-4ߢo z$J# VZ T?b`uIm.ֳxCZa2:Kf6i .Z5zlڅ%BT R8rb $L"V/֑U-)[Y LL@fdr|02il2X"'|Aę{V D` bOLPPRGPs6+*4hF*֧#5h1b@ zZ܂/#=hR6K9 TzRtdNDI%wX3kEA⸍weEjhKz>Q3^}w6 ́`7ud`>:ͨKp׼I* n:!!<4"I~7hȐ]9q0>뀀Ic 8=яo,5_,Fc[ʁv޵'[9\I"Ǹ;E i \%K4\40bՅ)SE5@~N=viuyQ2W^ed;цTG3%$b3e˻Ǔ-oXkxQl#o AO?HC3S{(9sg 3@x 3N/(|<9P%&⏤qOIu[P{!.cEή;uvʲ ݻRޗzD t\PLmQxh Sc0T-3?CU+BE&6[* <^xv=P "=nRn'=(UpPy@ ڝyn٨gLQx4p}q @Ou9L2[Cc'}΢Gټmo~fxVS"JEp86Ud0q_+mTMWUFE uFW;R 7"s%(w*aGae$DƱC_f|57_O1&VJrH`_s}Ÿaj*#Jr6CbB#)*Y7jv3Pɞ! 2_2vFiȮ)z۟l?@4))AS7#1 I#F5꛺|YOt<=/tWvC~~i ŰyZ4椇 W\l?/}߯? qGmfڝ̝hY΀|$`CРQ`302\v (0*bbNaQ]jho`) y ~!n L>E-!/'/=%+Iw}Cy xK L@ZQf2ád|}n:f<5=$v fc5C /d~}-ꔛH59uD\O#T__]t|I˳N_x{zǓN<mDb&zՏb NϯLKZ>Fju'7ȧQdIqZ^\__ވzSL~)l?megbm Ǟ8ؔa{aG'I9n޾:J薗6OMK [[D|\a}nwzLD,f5OJU:g+T?pq`llb^AF苝W? 13XߺS3 ~1|fWKFڶ݌f trbg3-Ůj,p,zl0] OWW-G1gBvMs7f^\o~;Ue=飄褬0bـdrx(EI%^c$ro`IDscN>mC?6^֢4_DeۥrK_ ]Ů]f,*GP6@cϻY'2ԏ$-*G˯U]̚GS$X~B5nDl55fb*VR5>z ѳ9^zgLG3$zx?C5Eh~DS1rќNeTq'1_,Oިɗ7"<ϋE Й9J2 `0c}.,NEh`D.puD+^P>i2Sβ"whƸiYf5>AS6{lg*EBbDZqiRm%?'!q{?C5EhMg_րBeR?'pϷoU m~[7OXKB`OؘhK +0'|OHfg&"?aZDZ`"g+m7}76T;"@m㳍89|+5`~TLm+c ѮޢAD ew'i? F!(ChŞjue$Dx㝜]k@jXV"Nίww)Xv#* Lki^6RI[FwnN/ټmo/eT8GO dF F)a";RzPjr89hu amB* 0\I&>.atsqh!Ppf]9 ilGy s$*,lK?l#K32hV&=_>Cá?4| sX#gb)!#da_(>..^^Z<`).=`! Ќς=y283sw6u]<mAk1SΣm^}ݐs>iQ|Թ'hZN\]Rw`-!i5X\UTob1 A.lͮavK$>rLJ= |X,U>_XBO )/89\%ňO}s)X.p,"EyRUt;].T锹@Ouť]שHX-By LZMŪA-z"KAvق>}G3'+_6:5z;BfQn4l?m~W>N O~ 1-rYgaM&({{d__$ #otufA2h0ut#:.gn$r?B6^1΁NZ.K S̼Y; IQ/ \+O?m;%<"ZNaxJQ,$:imn.0_<@prAۓs%QzZUnlW 9&@+KЯ>GF,(cs9ֵfD㴡+Jęyqj_ XK}zt^;|2=k%Ȍq]`+KiOS.==u}߯>A)BTEKyrBIVGi=5QWP"OJ 5XC f&"!8|@F/dުM.'+#!zh'W`0 %WoQj(k\](xS`Ff,*?["eg\+C oDFDBۡPƊ߃ mō^daB;"r"Kc\Qͳ˖l5ZOw_&5)Cçph:Si Ȑ,svD`xd?i:/Q):*]10(m`a ^ J]7{֍[Y{J;ʑEr&pb2'Gf#>j?0~K=JlTS/-_X Ll!Ϡnk 0KU@S[Aw[7ݑ2BWgXJ#*l_tBþ"<KjR!kfLU'չ>@_!%*'2Fg:f< O9{p՚6dؘ  JfM|7"FYafNTY3oY~dgG Mh8!X d'FlΠ1J4=#\$5=%v|^  T ,V-onn-Q#Q_)|jPo j­j^Dl8)WP|!7* ~ _ FmkKwT'j߼]-j]20?lۭƎߜ=df$p8>X=V᭷bUa͟϶cHM}h WUtԞ@'"~Uf9YHru7F xǕw!7H"TE/ qBO!?R8ȹr x2:j"7p[-4RJ'RM&m@T儦XiZ5M7Vf_rs,op3WlWer4(M ޭ)@ZMFxwEX fy}5") ͉c|ӾV||$:LUFQ?W+4oZ7 {>-%ƖЈytf*{&$l:y7Yb'(*J1@Ĕk4d?(BEU$ 1{%\5-I^,ʕuQEuގ/smfݬ ;e_]b`hWQ?GFS (AP.z7pjA=ZM0X {qh#+l߻6vD5"*T}jbí{ٛC0Ԕ,u' ؒYo#ly׋[ixΙ iKc%yOcB)KfjGfrr;G}reuI0 [pJfnW-K!5"' 9a&XfR"J ⵘ2dreSeV[{cQ!%[+ B!vO{wËphs n)S?QŵS DVYШt&UPPK'+2a!gCcuN֬^{5H-þ"}7&%T~?Pz7>P_<4Ċ4gI5PTi$+6G˔ºjm uEHMɈkq^ ] xX߷|VPC0z@W!Tx@K , aסz۔? 2M~D Wٜf 9Xle+㟬ID8L?oab'0Q ¡L_8+yH(ZD(*mx^==_zC]޾z?q Ib"ߌ L >1!z{xonoowx?JB,jW=LUjXaH0Xu =6qIL.U-nU`& \l!q@,[]C2\װ(31t4lkP{$HlBtYbO+V-S\5GTZε2],ׂK&M sJJ5hM'9+CGt3-ˉ/VYδ,wx3#4_0] oT5*σK2<N(vYK"w{?~|17D*O*p<@+$ϵTڂ kuY((7o>0ݵaV4*;%(zsk {-U|3g2UQ:DˌwӦ?e`G:Nbu+Sm/t: [K7)̞͔& #BPʂI'"ʔUǁ:LkT)nǵLf4#c'M}鋃tJTU64$B1~a^e٣dD*9`ax48h]X]U.ϗ[-V gs{c8^OtMߑY)WbL_b=~/W'@B,)xFMDeTѣή)Km`pEhVj!I?֎MdWϬzu'lʕ'iŃp⎜ F$U搼 w G ڶ`Y3˪_}hL燠".ϳ]\}PvjuLkTAmh fBJzˀ$5)7y1._Pu2Q@k{o״xz=o>Nph ¡K8"&ziv3W;Zg7?q&]4 U3FtA4_4mۋX#t3° >-xRYBoitqF*vU9qX1%B5ipQk2H=W͋ cZ_sOgB|e!G-NH p^a Q)>eLʵfXM(ht3X⃵eYph ¡K??!&6ɽ0g k&quqv{f߯'|C@ cS*:4`4L>R# QJU9{Cz"9X{,lZT@꺎M4H&]U&KTxjKpy+@k1ej>N!`BP|[pmRnR}+X>K#5FF0$I|fn7{6}if{O QI12n=b =j6i-^zݖ8j_=F3fn~?n֯͜R71e'~$S84KkOHUG#KÍqbiͰOg~ݫ (ց& Vx+\zV2}(ҕt5}Hr4MquWSs3;"ǑQ # ;;tH$fLSp"jOjATEy irusxd3c)&`f|ƹB0Q;QZwۀTZE$*f\} D,'dzEixto /}BXzbCc%0W6 n`j21^Rd]nm`bXlQ ,q)`_6|;n5[sO 7,C8^FV >UG,% _^_ۮV߾;Ov0hИo o#2b?p<"G+$>#ELVpD#5~RxLwPh۔{e# 8@;R㧾{۽O֠4ͥ*d;1..>^ fCә 2Pya9HcHu:>Gi?NPՕEj&MTu:VF\j<*(lIQvn ר3Ja&#&;bye׿ ;g$"#JK̀k Xu% fXV<@**~)p5qqXw0 ӫ*#dM4,RaMTB_ *ob3İ`f5}}pt-;OZWkyTPrվMp69.ML{D.p &ew)J6ڊA [3SRYJĬlLD}[NUb;$$W-Ԫ6Y@- ʾ*4 w*>1Cu`6Qp/jvVͩpk'ShfŊdẐ=tG$L`N;FrqfZEP›1BQT{|[Q>&F!tYk p,UFV<ڡlrMm7ǵsG*e9`?'Zybgi0p*j$Q#50'ө>)?[@28)"T ͣVoPAh(vҾ%9O&`U&0 %f uC$6ǡ-O(ghDȦ)Yӗqj-h#/$/M0vʚM0pt {/Vm"1J2m܅)̰vSf4=")rO OP"DŽ銇#jU? 5|n~@l@h<id^I]tFDp BYOZTXͬ /9-Q0\L,F_w_찥٬;-ݣ:0ù#)xB0BJ rRSp__}seB2嫵ZyBncI@Ҋ,JXJG!emǦRF16bϚK!kʬF&-I9ly[©o>6-I,7< l^1Cӡ귎'82M>.'C:DԸvQE%aˡ(́v_Cj|'QGFIcLZ&S`ԊBӼ93X;k~zO!r+Ytja 8sT55Td%UGT'ؾPuY70Y'2-Wgd/K >v>Pg-)TJقz8U~U¨MYBTeD NbHI@fܴ5QNbcH^\jPZʚTJL O6:y%|ԪF#aTaA'1^TĬ}ݎ(2a@QQ"rH 'sK@N?1TslQ3^r}?uT?0JZ}*!u.R9oQᤅKUun05B*Q.6NqVEk-(=mژ)vj/a/I)g AKhۢCe5χ̗Gi43#|0~Pv&=ceqS|j&/2$x=G{f&$c _3ĕxzd\Q}K9>o z7hChb<}U97Og]l1իoO.NӋߏgo)^(V^. %UD`YjBkr2N~ę|e (ˮwQݯ$vgA |̼#E  9=AH*xwvPA5!}hZǚ"GdiR{껳aUgN7ml]@]=}D?[yBt=>RSĕBn Tgk X7*ᄇx&4DʵFl:柑)r)bT=#-9Oqٜ?w=^ӳ9O+reB+3ohj k!}Qo`Nv$- =)!@Xnw}y~^ë'חv<{'J(!Xq9):d"X/0q:ICX@ۀAV|RչGN?gTH3ya(+@[C0cjHS>Q1nߜÛK9\$)/ĵ4w\Uj#ZKLX JGuX݀ G 7 1+UuW8 _}zɈj^e=S84|T.@j*FZ^};ÖWb %z-⚆2˟KxiXDL3[.$ֿ5B-s9,XQH\f鮲M fWUەbmkDDAG ""D0b1Ϡo3=21A%/Lh֖"Ċ{ ]4ń2~puqzrvCW*THp]xz}|B]ƍ1Wz..)f+5>R%/ cbdU1BBWe"Ʀsek oit5zM>CٓT pIj0|y%f]pƮW8緭K֑(0 {u|Fnr3X,[V$dm˗Kd,_.Ud}[\UxɘoխqOrʱ1vdlE[fBf=ވ ` 2&3 yVgwSvҡzM>CәN8|c2?ᒊSJqtg?^ξ{},?U4-^6xzqQ2%LH?IYR˖}P e1*qy#aL d3;XS$#*&-,ߦo͐v-lGu27VkcӋ˳_n63zጛ˳Ӌo/׋˂ )Ӗ|A=φ(N楺n޾:J:}oͧMbS8D4o6ͧrLrE bN.yruk[D$':=>9'?\@m `Zń.NO~TB$)[ Տ0xٮ|٥6Xn;zd YF=|Sc0HyyT(TAYx)ʸ/,ˆ/y{٭f RSw0#t{{C5Eh>ǔ C afP 'g c/o R_O/dOcx("pR[A+ -jFT:ufQg} =qЕqspd#t(Ɠ|YK@TQe+L~ (_g3&lXVo/K|b:zp}qu 3 k_b08ޗɳ|(D٬wHM4=_^`>K]|FŦh8u/|DnL}/HIҝ6rv[z'1 eiN~\p͠z}ÕRIRxPj¬]|W?XJ|嫷Hp}%K6y,Ou 6 P崰2X90֓O bYqi [ӯZw6b%e۸o/ɅL,q+"̸cռ_uCQxl|OvG[Wޞ=9WsuWlﶷחO[ȨG97&O~mU!(*3J'$O Q7;pTl}t :"R{2|kF2K6RmV2D'on٬eTPfiKD4v䅌l>m`]׿ܜf{50IUݓM ' n7<S '?^zF<;"?9pu~ tUzyϏ/}C717{3dtG[tdm @v.p kYtX VX-r{wTb:ϒkOR?lԋ{K`ĺZ\$z9. K3 fZ4' èNon1RI1 o0!0ODT/۪_#&5)Cçph:?Edu/<$nNQ|/{#NrfeLm%8~\CqWa2@G]7o93Uvw,AwrL ı]ws/)0swᎄ;=ANr/-^, 6%ES닟o}|>_?߮?\_\糋KgUodn ٤0n?7x</W˗yN숺Etq yu}C|y`ve:ZE?Q@Iņ[\'b̦6Mڍmk*>B  bX\.\R"*>JNPglWe=_))K Q~`?+sٮ01)p´W'`~C"+vb¤ GRkae:hhCu r+=E5MɾW(HzF/NI½ U9 kʦJHOm[D#r # 9/r;7jyvrief#T4(9N~"= gC*=d _[;~CJ_SN<m.G,DL =ˤUAPR brq۽~h,RhR52rm0?Ht^#sR G5bxv> tD:d x`?[.3m@ K,<)<̟AoCzVZ)@.*`tG{fFv Da\rjTF+ =asys43832ds&~l*],Ӌz hɖ˕< I=i|5ntrv9|3Vr |f3qyzy#\F)a!RzqusC,vn(˻5?-Qא@KҺ*+c$^-^̌挓 vL8$,,y 45-u-ّ.V4Ov@&S]ٮl zT [0qdt/ wQeqrv5bt^G4U&ih͝mL3Jz{5N\~^e1ͭ8֣қbr2 Sj@/K X9$a"̟VI8!@p^Ru4.`JyxgC37䇋@Ed 6$l( ʱ 쑜`^VCͅ7~ni .G fB.ԋwrw Ľ 12zڪAlLC/C9b,Uo[/nhXOC _'QPBZi:WO="¨d+q>1"Y0| [ ?T(UU ꙑc#Pr& ) IDAT1$ E2 E -yp!(izLLf?0͔@I?Ff`,O.ZTA Hx͈K5(KL JDcRM"K1 »F OjrYFL±G!g%pr-KJ8j"ճ3~1@ qm5@Z.鈜jYi`w` _#&j),%*Q)?φh#iHNb!7- S71uhgPgy``G6gtފ *h$]IS%q2>LM94&Z.bDzWۖB7!LvSlnUhE:e `(jV#22+(4WOEr -'c91Ŋ3ӳsϙf x{7EZ)Ck ó<8'J e9<P)W'goo:g7ǧWZ*7w@'* ˕Srgh#km eA#+(.ETmB6NMbyHY&e@*b~jO/R^G#4h;cS "Ȅ^j-vG֬U[ٚ,%m> (U|n>o ֱ^Up{(Klv}ôI)V̊jPDTF`g6*>#UQ=B1|l@lqidVk {hTݳT1Xep*OUq"ʝ%+D-*s%1mTlΟ?ٜ?},[=_uh9 ҏIaa.S;CÅG#nw}y~~'QRRItVdbǫd3'$٢L)k J,yO@89gpcNQ|8r2K3}OAr.zH}.YiτDib}2G*'I1!NSy%в%]LќHS9'?Gkd=]]NBw4k@JyDyWi1mȶ@}:Rbf|@B쾏+ů0XSGu s QhhPpLX}߲$! (Q.œ 3 U"ϗ-M7+1_-S84G?(pl7\F'__>Ocb r (:tҋ} r;JL87CmjP3c%#YFix˴%Do$WpқQ`Z%M/fM5ُ#8P)d)G_l }h h]ϵq} fӳx$ e@'Uo)^ V w-Տ9BlW?V_.yGH9Xw_HWfeE(:;Ux=Dҿ0ׁC9EhMgo\Q[wl'tPfKj뷧O\&Ec"ȴvAc{K{#5h i150en!SUݕ,@u4'JEǥ18'Sź^x7BrrPI- eEuz-:Ѧ˿~%D8BoX9Sf"HgB;8X냍@ּ,!|1=)ns5pBX[[4Ax,/Wӆ2gLn͞G-- Ɯ_x @ϗfcqK? htqC¡圢?4| /}ʨ xHY{t$|p!oAy|RK*4.Fdo-EO1SHh5T`1غds` K3#UT_F5tN]3WE) JX"O‘uYswO /^}w6 Ad`n?2Ŝt-(qXR򠄫S>> \N"U oCf^|M9GꢚN]`4hBb1Fh1IT2©1|`L0h{SL~ыƔtha8SOt3]U{Ӝ"3Qr㇟9ǫݗw~o5-cDO&pel`V "YdS As3e {;9y$BW kr>C)6xx+do9s;gqL;2n~>GD˳bXq?3#Ȫ@fᘠN)N=e2%ADFd|#`oiĄck;#^嫷I K?`G5}~k7. iFⱀzL? oCB#W&wZ^W3lC:z/[t/t`8ׇC9EhMs/埂BH5g`zߏˈ(ҫo./?YW"Ύ2ʮo&6$s+{)6pYQ&cC^|R>fc:f0IPd?{oFμ?G`@ ~  @ ^`p @@A@A@`4Hu{oͭ#V̎FRe)YVr #Lm،_ ej>\49w4wu#=:Eq)Ix}:=7vz%郓鏳 vZoT=Rb fDy`Ghjd0-YW Q<|X ?bn!myhtuJuy8, G[T_i Ȳ*ª箷W\69e^VF=Y&ߜNrٶfp2z}\~np2:B 4蟠,5|0t;{%!+_Ң7*b)4?r`f f i 7r, ~c*?~GSkkE'x8q(B*t5H1!j0[Oo~k+$] š?4xJbrÔqFʒ#6ӳEh !>iU ir0S蒷 =ʢr}%GŐbźw#fL'upmd=5ѡ/FtD0c_p8 S6_&Ngpr?p}5_|q5jPK$DD!#:{y[PL==BJvшhB=Xa1T(b8O[j8Cqdp8 hExd`b4zQuٖQ9j+8]./g6Gb 5o僚f0^gW ˷RhD#:5pԌ^ w=]QBk#d4L`tW?]ltD;{:}62+!>T9pv+%dm>:}³Llǐ}Z!/zD~MbxdqgMI"=؅Kx8Ì-SWavL)&8.6Y?*|E٣jc_{p~+ZG ~$ժ-CfRDՃҳo6`~H S?ǯϊPM9Q#s8Eo 5C ./IvW?ͮ~__Qpϗh tվ}ݷ}yH9<lw GÓfi]dQ"p.3y6}wY4/乑t?߯bd#~Z@"ݥI^Xq0rQG#4(+XUYΈ/V:G9ق\{PPYr ?y%zGefu!"هn!yKtG<qGsa+r-tuV1v?=))`8I1X֋]Nf#u2 w/Wp!}p\GjmA5-(U>q#yvEJvS4)r}s3lxn~[N/"^̗9j,L݆v=0/%aUl~ wRX\΍Sբ7aX'KԔ+_9brl3BP72_Im4LƧ?+5Ȉ҂HA;w!$1`&ϬcKi:W˯IT} UҬ127/k%VvaJCĹiB襈:~9FB@C}wcԨ] 5Й "ݘ֥'i<buc{MMsqy n{\oojb՜ {Z% ?%LĺXDZV@Y.ZӲ. ,V[Or5r2$X s5'y<kjQ'R== KDn̷.?*RPF% v'(=tv<13opQC#5v˻M$K;}?Ghz-/!AA!2|1AՕA̺9;'tԴwW./FPb)Ҍu +=1j?Sj#fyjwuG?M*&n-Qr (27Dx9 ר&i&%vγ Gi}*Zr笕x_owoE̦bgq ",dD,%v|yzӟ)kΈr[*J Y @k1@4&'{$'SJ܇ryi0<hvz,kBO[t3H -$UP,F䳷s民9(wn73F?v6524Т09nDٵEؗug$V.DŽ;V`f M:tgju:Tc<Ee`vi%AQ# L"dIiC8B*N F>@\&0Й&;t`)eGhz-gO`2R!"> mxn IDATS72?_+rf>g3+ʏ6V&k:앱-*3 c۵-Қ!T)(Nz Wip OA=03.'vPVYl97Z`ZΞQҕqHHb%!Ӑ$tl7go w>A:9C2CD^%WԤ뱢Vr"#[;9D=bKF*(I}&uqWw)Ck[*uE?_Vxg3::U Ndz]EVWIZgnJ? * Ɇ_}f7Lkڞ |Y>K3&G-S2FVdd5%AC)C 4HVg 0[wl$f-d#dK!T[s8SLYۙxdltD |enu6M@0 a7t2dLH5f8u^w=%ttb[ϔ  GwyLޜY\zӟ ⏂Nܜ$FZQ~}ܺCMYP~Z/\tg?˺PAЕmO}6|YZ zS.{ڂ|DF/ HMSPZ@m:f+ДqD &gJqg4ncxf?rx0H|$)1^뜩pLkUJhz3᱾]Sqb@r[lK[s>:}VJc} 1#5Hg*-} =-`+H5_"1OFD+G=XOAcN&h0(c)N_pQ{?ChT~P~&d m?=yXwN gþt3bhg# fXK<͠܅'QkI m.@"G1WWZO`!%RH|%]fWDw3M)1MmXb6Ԕ^C(mnwh,uY=FbEM"w@˕y({ Rhy[fb[ZNvQi BݧoWѯveNǥ,rϝ^\2ʌN C>s3 7N;˨1~[;L_EEA}8t6g㜯 S=T 2 g|W4pƳ'V*e|sCPǾ[Y4;@>uM(oX!v jJ6'P d7PҖzd\Z"P$Mz(Wn5<7M[Y#&Xvތ#TT M YW_q@ix2% h{h!"~V9rInfVjL|ʈ{X ȍِTu\ybwrF@G?$z$FUsvT5HOr_u`)q s4)w?Ǽ#^NOm^?i 6)!cLy\Be.0We%%.~9;&2xfB-3f,CG0lǭ0z~\79jCZB%fUF(_V AVav[E:F׿)U8+pe3=p"^ gUF)QIlQ.U ~!VZc S+dP4:5㟈.ϙJ\<鐶AJI;|E 4M|VPr>Pp!4:1_۶&S=`9!tg6r!5C4:^_.a,nX2$l*zyKvI?\//W?/5<F/J6گڶ)ΗvݬٛooO(渹d8F/G]ӥ*A`4 C:j6L2`raP:zXWʯ6ep8ؗ˸Dq0f պIu@ X.RӋ:^\n>| SJ'4] JtKC ;QeK_SٰD{1o׆@i9g: eZb !`tG 4 :9\gi>f1{viW6|/ +$YCXWph&E[Mo&PG9E%\|CpK$SW|߼GSF7׋od'_mc=ҹ(˶E]xwŞt/Y xww'~_鿯DvnyciOKJFFᠡax2|ڶ;Xrt@y bdB(AwpY/!~>[lt+߯(JNFNk!m{/cZKNP ybu+I!m VOm2z7WV7& r{=bH3=FwX_~gEʭ72)gf)Agӳ++n%mrm\V1<\3x@FlzBoV/JrO0uH'``>db8 O8d8 ]Hh(z7Q#Og?KeRoKkCQRСa`4]Cb)m% Zo1N)HN\\@hHO?}3po ʛWF8Yݎ7MZX9Cg"2y1Uuh% N\J=Զ~5sHqNN^M L^jꍬyO|GXU?PjoL\w c 4ˎ/&왎G|V#Nx{Gq5_ jo/>Crr,.IhYoRY_obnT }Rkkp$f&T(ǒM`J:Xt 6넺7v[wqcP`lzKRzrr[*'kd>PWFbr~>\Kh4ݵFhؖ܊t)g8 /SZZrm}ڌƓ9z5/}_#j/Fdªܪ;T6M[P_]1Ũ=Z\S.LNe+Jcc)u d|9%[@>&ò(e(:5{ZϿk󚎇6Qxf|@_{@} ޭXUȒ=ےd&+rn̗7x7VV(k t lX8f8nX@̲`feLyb]GϢȄ43 RmjJ'hIó:x`^m#BNgxqvnI,ghFhbzXqW1|8-#[" ^zn~[N/'ꗒ?6V] tioq9R/FvwÇ  >[#`mtqĔ Fv8QvvzJJd|c "}w(04MY/d~vװM/Oy/>Cph9{wP:G!wsGWAwWIwy }W rVJK"s(1R+Q~X3 h)!QfLH IC7P>GuM?AڶmFvgع9YC1-wjNA,d8`鏳(9S*ݒ].qL%ރeD#/Yj-Et2F3O/x8tqa{uGhz-gO,7Gf<@dePY J=џP Ӌ/c 򈾛$"G2X \KVKl} z&Bz#f&^@6 #Q@8hkz'@pSDE@O\yrg5! cĮz%tۣ+XI]R2e\K<|%G8"ja@<9 `LY\4nn.r#!Z`<:-b,;aMȥ rp@: Mӏ:FᚢS&]!MW\O|m =:e y  H yAI:h0ŷf8 D Ku],Yb! AbH?]'zylzDnp1wy`p)!6OI!hVy]UO%#v͇lǯc&n=bQ SaϾ]պGwy V2RMw1R.`Cb "~JQ[ZNm#?4[~3#8ʬJ3=ⷫe!xxw_?2nһϙ 3 rv'u!l;=e )iSBJxCсh-` 2QЅY?1>'f}`8vݡ?Jp)6`;E!kװ4`EDuZ(`#*:y$_f~t ;}tske/sy,BBiO " j2Ly& cdÞEAHG+l`SdK^'4\K VY'U }o{ڽd׷/)vXT ,4C=bZd1Q>f&C8ƒ2$>!rIF}Ya4 vky{{PZEq( x"jwHQ&Mp;(;0[k>L5b]24p Z~tcBhwRS~5c|,uagH͡jO&Qi? K;r4`f| X,Q7˻Şc1=X2L&+`YDWNc\JawY~iJ*=<Z&芔|[*}A1ҮB[ &],K{g-JvoLjAUAL/['dYj۪Ϊ\Wf`6.duyiI}/tYҡ6IQ[lpjmEŒRS3IdQMw=xsv$7?\@%sLȼaxh_lg|3c?ӳ*;4@na0PfL^"p𙘨`E? I:83DUzUʱx3@i溂j~溹< 'vL 6UN3a>&M4 q/:x+ )n=ӯt#$㽑UQ/Mv{*Ny@#Qmw%sUxt*?oa@-{W ͇k]̻8E(ǁ3=B%yv>_]cre1恚N g2KS6 >Ԃ=U'i0"GoqG$n:hP^>ZN_zjT0 r*ƴ#BLgFQĀѫɛxBGMy{z뢘ims:X{v%ɞCN!()|zqIpzl}PDXT%(䠊~02 jcdK6s jń=7ܲg8ٽc{BGhP~VNJ0JL"TB$% :;\?]s<8^\\^^^.x7G $f 7U󒟲$+)&&,/:qGYnm7=f-Wv` xgxŀwj" QtUr4R[;-ݹBl2KAeRwkuill I uLZWuDvf%Z7,fO]38fD"<+jU|H/lF6=+"ՈY,*>"Am=7U3˦k% vgbPs9 KTKkxȟz]˟t5a>S:Jd̟6%ɘ7Y8x5?4Cph92BF`0>QzNNՖr]*0yb>lbv^opPfq* 1B*z!,>kU MՒMxhW˫5,ۻ55 sv@MYKS"[ȫ?S?$RPbƤ'9hn(tmdNբRV#(]`0^>Uq  w"Ͳ=}p[R_WEąd WY,AS«FaPܫ 2@ҁ D:`L22 їIOW&qDt2F3OEi8PbNXqa{uGhz-~1'DfMZ|ob#McU^肁n>]-E衵e9ΘHUs%\6ًwpUeqC֐ Тi"؃J(`M @VZ4@4BC ʀ wm>3 xqhQt ƭ]P#p yAF rOѺrfX2L98 N5^LܠO:ƮJ{CMá-Y XKrN23ŭ"s|:c/+[@s2t~xcUW,>~+`={㈔! w!g0)!S"oﭒ|rn̜~0FWWW@1.fNn$-|Ҡ@*7~D8MOm/z%*z9 .L E=`SX:;vL V]~|ʚ6+t8]fK'|dž}ld*AmZho}{C%=`2v=R1}9R:"LC ,i y{Y&R͠Nxn@/&`pCkUNm t=ksOޜY\@–z%1&S7/#e+ܳs߹j$+FE\Wh.5veM*S&pHpv; >X e'bFP|"z3USRK֤Xc!Wa* a 9qZy̴*@q5>U}'i0Pg ]P/K(]3\#b[fQE *؛u6quucc8md#bX݆Gb";|ן?4_>仧*9^{x$Ɉ {<.~pv>!? 񵁢B.ᑮg@82݋,j5fƒky]g(E bpde5x!jr9,q;Qiy(:-i-%V] 0ǀ]Ŭɣ~gAx@TF t:(4tUGS@%W:c%vI1U3&ʆљo y뽠_$ \Ŵyh"0u#G9K"5ϲH+DvA:\o~kx$CwKtއCSVJ($kG(HwjNė'*1j:]fX)/B&YZ@uXԧ(k㓕 d,[T;8?bMrN"]U n~˚9{dHU"m96,AXX;k2PhMy#l`<׭dLdc]),)]SzDKs!5/ qKIv,goY/L׃V#$~3}7e Zgcr,W^ o՚O#ͩ.T|ȪfND=X'g'̔ h\Q < nFR͑sH?2jm-^4#`8ؔd^/ 23"_)>f/`z1O?u ˟gyielƳ|.6~}]EDS~{BHcZKVMOL\* IldU{ 32!완<1dgxLO@ CCFQT` vU k5TUtDaXc|H 2@/QٗDOK  'Mj9~ IM$zr.~LKi: D姊ӥ; HRvglAUْ%ʎ,g|JqL&5;3b '2V' S3r!,vLLGSrחk1c<9'kKLCXWK CxXrHRY.]>4ƪag_tx&֪HI-{i=Gd怰Ղ U1:(:H'cRʨ@44M8Ӏ=њ(,!jY/L6yEqX]_C*\>HEv C]"lW^SjVTQ"+DU _L>Yy>JjqlY#)";uv^qM;&B9_.?bzHw.>[ Hc3X=F!BX3xi!kw+i4? [t.{dstJry:`BT Jdg  Ҏ=] V[Ҍ:kl 70}xg ?KQ5H)QG7s3#"d0}& r>҉$aQG3?&ԒS[26>SAQSĎɞE@wǬ.JeNZ6 @;F3=8WVxH#)ʇK\zW2u\R,asw=,ROnޯSr aא ݱׅ )k}]ϵprNp}^u8XB_FE~CI0}$)#3` 5wC΅Grq2<ogSINF7ׇ(J!JGq\"w.'2Ikp 8鎎(ߖB{x "JTǖb !s/PVH>0<#juOA@sDC+m>D@s.y+$#)r ϻvTphg3$WvLyӃ@RQWxCZ"Ĩ9 QZ7k)2=rvu&&õF̋"]rAw@vԌui䈂j&(̯yv5};g Fj5w'>e]8_ A=*J{<13opQC#5vqh5/}?Ghz-/]Bjw{0d f+(l,9;'tԴwW.DO>"wj J]^JoQii,\ɓ!#But Cs24zTX"tbrUctL Ƨt =/ʊr.0`+j~v>PZSfI_@_qI[.enx9V] 35 ?4i*a4~^ÈȲEgbHhB;e=WWs\>_Hh![1Oq+oGk%]5d 2¦Gx L )kxI@)BɈ=U`ğXv 1at< >⽧偃4nC'Y^?4*?(^ukݱhͦ̄*vGc|vsZZNȕD(22fآJ0N{?n6)23R߮8*xn G?ȅџb=j] ̐ݸY22rzGM!TM75jŤp88nt !h8zh>g]~_^HNRd1?VW rU|50< <8\[b Wd2@_owD!p}hkRBm+0|1 ;fL>IcƌC6?{ؐ,A'霦_iz^g-PQ̖w V%,N{E(f+)̀NƼ҈b 4Cph9{Ϟ1 f }0dl X;Po.G'dx\ [w.[jlMJf~% YWxGe!BDZy2C-P>ZN:M̻.X]" Qw5C5Z9 @|W _ #'QL /j~7_绛'ы! O(;c0 {y_ԁG`Ro~6} `H4z9"S ʸh4k9)I+NK ?DHZ[~>}YѨ\tJԬ+8"~۫OouِB qPC5E8AUf3q?T  p4Akt%w^އC_~ /='}<9~f?)wP̈{37@pdˊm_3a^5מj[#Ny^Xxa /  ,4,44,4,4,  0p0p0p0p0pааа0P0̈S=y>=UY#"2#^%CӢKKVAK"us\AfQPaKnh G۟non# miBpv SݮaS{b, \evz(o}3mvn /֡:D`TkICAd-vF:#Kө_-C̍CU'4M7^2qBrj־~_jfrJmy:C9C5z:SL[, ]Əֳ vbu.wU7n}0\yD3mfӪ2x(&(a3} 84d Ryx:>r02W pT~ -H񇟉[6lQ!д/'m۴/[R e;Lj dNǫdxucPz˶m[hp.d)&[-/.6@-N^L^uVU4}F9 HHiT`M&IԱ 1y\"glp8Xƈ9@[&m)lEGH>'SYa<R쭚^0 2XG%.8B'N[{HgNT9 AkǤo9EO2}-DB0p `v7zbu_ww7׷Q>㯍!Vi!4{Ncd7V؟!`Dh rvELG*WqIEnGEE꩗$׷ģB91;"+WUHȑ5jX?),Ov]u]EK9*hienֺYKqwwا#祼[- ؽ?}旍` ev,l2/yWEj Y?4jyN8/4I_8F `5}ծR6$#GPĜÎv / ?5|OMg$}?Qp1Z?,Bύ*nn>Ń%,~E[#|L. WD^4jE<r2V3G4s>Lk,%]fн.v BH|~Ju5/Dsqˍ@3K Z@b%'k߀zNJ ּoi^R]DjOēJzu,<#91a1Τ/Fm5x1j]DISRr| H1/2ؘ]ȘܵlB6%|3\ ,l)+)t#2217D<(Fu C%oz9–{RDLR .Ԗ/..C$!/a(hXlD4EZb0q~!{@W^]ׅV"FSojtʏ>x9X`uzcjdF[3[ۤv^|ż$>2 &oS?x  J_jl>oPQfTHQL7"/IHqT!`p9Fv<$b3|RLsf!pFZ(;n|f슃Ф|n (- QkUqԠDmDԀVXWKd]KӤpdE# tmNYXcllԑzRe߇@J;.o|kF\_ر}!<ކuz.Qjc75<|Ȅ[ \}ZI>'4Y1>.OTW;.[NL p duK"2w&uy> 8X`H(-%Bm/DXI3^H8)c!Pcf]bm_h]ŮjbLEuwlXRZ 8u%W+9?Bꖟu nx * @ϽkR"4/m_ON{a̒[ zz&G`jL_Oe_>xi)ߍ][n7& 6?ĕGNǰ3H޺![RHAԧ&'vө7|`/\҅EՖ~X#ڐj[mϲ֞g%a+R*}*axeo.AX(<@KXWoQ|43kLd|+X < իvر=pǰ5.Q:]e51k\4d?iz'1ҫ Vws򭛆?iKi5ѥ=>7B7Kз`Tbc/ Ny 7}='w*}ם`:LT ']ޞVSoԀnf^!|:],/WjR2Q]`C;#[FzuvAkvT4XdjȒ(^,L2Wl'EUo%'!i[3qL~|V~Wf$EKnz~a_+,Hv:әaYfZہK[)OP Ѥ.'DX! 2p!#fjݫSPlO is E7Zq@sn+ͮ/&DD[k kI]"cpO EK0az#Sѷimї{':%GEG[|mg'?EQYCO O4]Ub^ P],no>.}=;Wڗ77WO˾߷/oXyO s47J8<kB :xz?!顆 ١f)u#Ip> e!Os6@tJO*DoQ\W&Yˍ$DКpփ+; "У<|H$֔eˮ)d/ J8g9Xn#O% ꯝtGqq2xRgj|,0/o- 2̴7v]$N`K6C: m}1]O_7ńM3=G^Sq4ErƍC#OѮX~,j۳w.pAٌW$j ,ϐ,nT1ƾYU% ky2o+37yefbT #].$#PehD}cH1uo{J,X݁?mZ}AJ .[+?_*[F̙ҷ{ᯭ}>Vp+wؗ\Bf.6M彵 >iD fсtJB^wp*}8t;,Kg-RUyp3,.LM2/2,VI==|"濅c1D!<5Q#*6R4l F/|>7 6 uJ ||k'n>-gE;ɚhhCldq*7 :ƈy])Tw̷"M]U㇃ Ħpp˨.bU:, BFSʹV#=]F`a/at$|o]֖_䈛 s,bxo[MoljsZ>kl) Qb\} mm< .F`^t]6ت Niᇙ!DQ&e `Ñ_'L\"{8"LK[Vur/wu}˽`yOxJ ?5|OMClބ`x1ʲx[q7oWf=o!f7x6>=}\^OG.a5n]O%>=qQ˓ XnJ9`0Xl6[ A`#I#=!;OPJ!<5N v5¤%8Qjy9Y|?~KzeS?od' m݈ -%cȞ֕I=/j_tsM= KrJ(WW4AlAp횓F}ӄ?= -~ڞsӽ<@:==`m^4oN?pv))} %%'&w2EH|F#,;p3 MBIѝ^ KcCZ#W690hѓ%v}4ޣ'-T=}uhEtlAζSJNJZ_C?$}J'"fVHgcjiH IDATf чLnNy9{EFZE^bX*EpIbSa=4o}{q_\_]Xx]̰]xM&`h bSzA,>a@/uOmh'J_w/Wr}aqw.t]7_wwwn7!j~U9 !Qm]ST@5hlv]ɪ/Ỳ~^iZ|s97ՎmqSnє d'gZgzL( `.`y'woMfZNPI‘Pjv拯' \JfWfYy U`ҺJCʧDł0d}#HiXHVV9"V^-Kwʣ >/TjڪQf>[TLES3+lB1B 1-ob `] #ԪfSB]OӼǾ9idx=i'#?w9e40@6/BaHZ% JpZ/m4 9Z>][`nFڶmחOWpfT$/X.εU$"ڍyܭ~bx/6WUnU{6MQN0&E(}Rj/wcEX9ruB|,* v, i $_ȆFUbɄ'ܪ$%Uu*٧=*4w{FQˇ(3*u%C]L|h;|šw*WfI%VGLtgaO8jv64˱ '{bXbj2ѓ:`)=g>2vW//[MG=p bpx;=߭.?,}lxWCןVPcpwwu]V{ŧi'gϛ<nUh|eKJ_63Bٞؾan?q_94ex[BջVb'-;]ln´Vh|{i9XUѽp&`D [&N}\z7 -wtN 2⾕r-XZV w3DsD&lMD z_Tqa!bV%3%x+|e7A3(}“j*!T\bܞū=1lf0,=)!=ixh0`E,ptEBjQ[ج73/(QdpM$R|-`I2I8kduje'|?! 5Xۭv]w+q z| -G6y}9*%X4e23g% a7[ERf*,t ?L! l>o]G7+G ?x /vz olnqlݍRPĩ()u^);Y &般e2@p`g`t.9Q~^ex̬WIwCSJRFX!*0<3}l=i2SH%$۶#&x *g,qzԍԻ5 w? ˦{iR@-1]I4kҡϺE:6vpdmJcoyLc)]kLha/IC@v,T8?-eB3z jӳDu%)BD?&si2ֽ9@'o|[E5HFJ uE%_8RC+m:¤DZ3¨)rp 53g1?j*d^ 퐹0l)XcnoTD4M^*r%HHa`YIyPl<*Z(X=!'>>Zvn—PGS?x][NJoR*?(o trS4:2kXG%m6u'X0}ݥQl/"ml`+D g&gy2)de<ꊴICR2E8d {͓DI97N:ض3'} ӷ.9qXIk%BY_G!<5sj-uv7$duxѢ2 5aYb%6tR( Un_p0v+U߮#ZJ2cY2\~FR$"O+ =hNȭNvu5DjZŵB5, 8"4/m+S,/5O6B5K!Y;z6_mHwK)Ckt3#wta4>+C}weGK[_%T蚤 +-|s+&0(|1 a঵mG_ɟ=l'G0ֶouK`m;XzCO wQh$_`#2';óf·!M/כ[6$;Bz-*76C]lѧxizkD㎄U}ʬ$ ?їNg ړ4l*vU KE.q,#+X `-[7I&鑔Ѡ< G@<\!]-|n>^{wy +ml~>DFQd[20KC(>uO˰XCC%Tc(>V"€gtV(ev/&u߿ƾЗH刾nYeG{5Bjsz !1鋞 NJVG&D oU. ,w1_^_..ϛ4c볋Efu-?/OߜL[-aρ\X>kkcglT[j +ǎש$xw X7R)`rLRM2r MMvom7k8^CO SM_6arHd!G&_cdߐ돋2]#(j D9@汎?m+ȹ>[P"'rSlvJ6ev ϺCZ #fe 7F, 5$?#<#?nz7E~#LUBk/_HJ,$YeZ$92B], *X3R=5{f[,0/)|D dpW;A_FَK]1!,VXO擃uD ȢywX KH@ڗS;k, }'zrj'?$zG!<5uZd dߐx;3K~D@.Ơ|>/X}\HlN" XiJ0[WH'vG:T֐,DIT(5LCC;A^&{4⨌$.Sē*鑖s+|LNFF,XX{bb3V0VG&m1ぷ:u4͠mcu:9L'B2I}t]p'NSnUY%%![}z1:~x^}h0Q'`Qz¾i]=WhwLct4O>5IW!<5ƿ"fV !67$ؓ|^Z]{ ㄜIL4Q%j`Ai;/@6dnHTN8%،K%T\s`DY`UUUqe U~?Aj=c G踊Im6[R+Km҄4S 9yN./wYI)-ؼ`&_zG (#. `C،̎_R>MŶ 'L.lC2)-T(<}Ɍ%,kI]-ɺIc{^%I}lc}u "| {OcNzj0T?5|OMPj~@\+P (ࣳ2^֬WF!`v/ JFUfb.kxgNI՚qLZ~T(;JheҤ ʷ8" C qhK#]%ZPeRErzy1_^DdИ`95=i&=$g=%6!{aKK|Y*Һ)C'ak%!yTI9ǂ)tS\)Rp!kjB$D=BUJXN W߫}9=n5 մtn^^NχzN?"B%"Mh-`ШMI٘!ǿ!N&z;|:R rNd`d>(|¥)cjX*B`$EJ5HiGVA3k?ceb}Iyb|Kñ07, @2#R0ǂvsG-BwVRs]~ F\>w,%{|'3"' |m`@O%iƣu;7Yn4s*"[,7L3N#fKCI@#((E ilk>#/~Ofk_/ٶm'abߓk-?{^LE,D5& : d+ C|~MW;@}#'SYaHGGn0 2mnewi͐z#NN5n߉LƔ_V˨_FbQ싶B ؂@ D'lM*p"+)uK=0MZ Ȫ~d3U#8Rz\+% @ha [<4;d:-T]2p\CHxN*Fi(kv!pAJ9ݪ o3Ldqv=HWIr?5|OMg(܅ͼpΉ#_4 8}Cb5<}_f1˕pK@Xv||.{6F- ^!U(uKM+}-K=hNl߇ GjNlOp#W=7 E) ,_͗rJj5 /ïnqմ"K'Bsv~v~Ѽw7rtrz\Tq^SaVz8 eDž6*!Z*,piO QNˬ'sO(H1R(k֕.՞|_l*^f!T8qyg+' I,y8 ĖvOaDDbR#Q`6?!?L~#jaa[>A>& ۜX3 QU?՝p8/L:x&&ILzagY8`݅\>}b}L^M竫7wC`Bj35M+g>υ%B^M#ӯ Y’x)Hʤ:65^2uy':6>Zxݔyj{_dMdk7=krٴz k /YSG YB%HYH9^ ¾ pCTYɫP,5{MIS()iO'&O.[{42{?zop\촱ehM?6(q:S-`s8FH)C a-fs\fȪx%7z<9ׄXlWU賾6-p.L64J&$yO CÐ]I:Cl Nj_"~uXzZ%g<뫐#=ɫbuzqwNdXW{EٷsW껋I_H:CoU~*L궿##TӴt1{sݥDty/rJX eca{;i{豇.ًFQo?NaAZ/L$: >DJKb-RH,? ›^Kh)Vd tLi6lYZ"u0y/5V-lMs^o#Me\Tdˆ] u&"B}%#MP7i_0>^ #n~E3LB2ڶiоhۦ!G߯t T/oigmN^E8ej;^пXbuZcF4>=/zOI/X۽Szdd{X ^72u83C`t|^waf1/µesMoҤPfj5&szA[P yw7rk G]I eR%¿&Qmr_L +lgdV )ŗ>MfӴ/i\}ZF+G틶y n[ŷg߇2 7߯__ȡi6,eU*ZXAil[=C#hOlc0yYVni+n"QRd2/[Ad R8pϗrnq^^\g7/yaz:{Z/738~ҫfןϛ#ujlӮ_ Ւ2CERj(Υ# +X"NSMc7 鈉GĸqS Gdt.LV =ġCL<65AhVf#ej?/IQY%w?==xK:~覬ɡ|0mew]cdK Ny$٪(z%Ԕ$@ʡZre*#Ztۍ~-,KbGE=QS?ޔF(|RJJC'Ł݄LMs k9u|HwaygZ~ZjAD\̧>/XkՂݶ /_vdݹߞ~].w_W;R|+2wWaw $|GKy[?S{?%%/Ɛ}0hvuO栾~"=1R(UO7i˦o :_iXf6|}3sZUJp4@7 O !QE:oElZ/Mz"xXkjBrx"GxyR:1Pt&Le O\CX^IdB85kLYCxqCh^b05|s9r@ `MySy/jxx Cm2dW29,>Vo!Sm5PCh@]Z VVs[O]U/!LWy "6^z d!x6\7CzAÌ<e_Ve|8-)jl"ql.V<&pQa]p|#CDǾaF9,H߉YB{J0y5R&ӍAj.pu#*jzŇLHʠӁ^P-Sm[K\}\$DKR'8OW~ 8^kLsuy%ǣcHGuIK-"fZk`6#HU0w$RrLVJVM2*BeKQ Jɕ&ClQeĪ29 XaY2d9e(@Ԣ)7, հZ +PiFރW!gZGEҔNbD+,S)I5Wt5=K3q"DS4t[A6>4!_SV ZUǿ3pP,ӎRQYWJƧw&AU*-ѱi ^v櫫‡[|n2r6:4gW6gNdj$8#4B2JbwdppRAz&`7`PMT %)8[I@dYWJ2"}{EGjl4묜-z+UkQ̨Tf*뮬 L~ZҏT sNC2̉*:ؒO\Tէ+!O/-)bg6mt(2H~Q BiV5Õ(䕝t!ϗaSDWۭxZGJvQM߯ՀCy4=y6̐W+c,.0^HI1dgC9cɟRcM.}t(ؑas^FX0UR6jèDp6#t<+y4B<٤6ʅQPlnՃd(U JrӢV3띲43hnQ%Y%Yx\朆deP $@iBpY5)xdwc9Çy;04V]PA4l J4(r:} t``*5Rq$xEmI`\Iz"Y @X2d:X㵆1& Ux2iCTM+Rsر`ȅKpfa-[7IO$ӫMo:|,'ݔb-_^wV(0ar@AϛsO#RN6TBRN8c<[ɷh,9kk%ue~ktAl:+5ʼnMidP )>J9cPx4ߪ٤G[Q^4CKDV4,9xD{'Yy"ⱈ*Ձ UBU#j^X`iRV6QATPj_B+ c 'o@j^J/+ʤBpB+UQe(y` +E5_CYu4: MŐcC*H䴍U~`cYfMSc&w!&wu,ɧa{~di _;R(8cv$A澐-4V(.pNk@-auQ.de=<,7VlyTl<_Q2v)Ɓ(.AI6(UHX (_TF*0*6kUaT}F0<u&%h} iry$4)BJ,$zcOwhƥSO! H,0ivҕH6ZITtYvNIɓtZ#eA+ik V4]@ vDsn(5>D>I$z߳c;k?Rs$zꛆI,)Ýo9mxɑ}[:( D nWς@1hþV7?I ZaW~DUCw ﺮBSi 25 3xJIyTӧZj nX,,\K^^,5 9`7LUQUd,P伫T5R$QZH5sW!i]gǠJl%O^4̺Y@~ <&b+qҨvi.&\S!@-J沼C}7+jQvr TO9{*:JJO1YW5jcEcY2zYa}P4 wxP<_QQ+qiXҦ"H* "j"d WTVu}3-r|Y<Ϸ@xG2ԺG;h9)?VdWRz`">T&ʥ"( f!0Ϟ&Z Py`'N7to1GsxR P=F<=HFShEN&2\*2Sj'Rup!̜*\~2O@veo x)kBZr̵:MQU4RU\AQFD zXT^+o YL~ܐDdm5F7"@Q z g,`DkydLHч{:0?"H`u| )qRW\hMb+ OV6fȔumcDJKgb 6n`rFs >)_cKQ:iNŸ:ƎSKW \KH/=ReU7'Mu&XTGHԑ MWUv&mId %-Ҕ < #R!@ހh, HCƞõЍ -B6q3[H60戶4{P&Gi*[*IogJޔ|w)"W tctP*D~aQD.psi1mFUkD|Ӗ uҚC*k8^>)4ŃI RУ,$E+ON {dR/B|J!X+kj["k/}ʓ5hZ$!"Yފ6>`_uk6l@touP8$jz5y)VnĠ KCsc,qQ٬Ԝ ">~F-Oc 3ZXY˧9 a8B%BEaq8˙LAMo3'et3(5t~L$8ԋ1;N,IXV@̨_o0#jwy{ڄ+F޳ P&EWqTVYX{xo%IϊUpd]2 9)+J~)ן2o*.qR18B^ԨSz?oN>^/|vS?KRLD]b$qV;8JDh@TAƑgdGA|*PEC>hTG=i`tB9z*U@責Zc=su*R^KV=769Zo;}.}䱬.E50lvD4n*R ~=yl*Ȥ(YJTh@YbiG6޳cFwnU?f-V/ywHa9DnV" ?E)\$ G (1>N UW#%RJ,vJFF_}6>\w%w$^>b'q֛TB-1a>jV\XKfS %g1xL:}󴊺ऑ"G\VY?D&!np13qf, xL9ņȳ?ZQYxŔhn -YH צ7O)CgV}H#Rp0J\Ɖd2PHՖĩ\*R-Ð(r ]3󩶙DWGz<qHGiMd*z{ jW2RB0aO\ ]8X8DDVzGѭO8<]E=(Ur:$c2E=\qT)~`H88ǮZE0A-0FyRHnJ[.VEwcnD\YPIENDB`hivelytracker-0+git20180223/Docs/images/insed.png0000775001024000102400000036207013042730153016354 0ustar PNG  IHDR,brbKGD pHYsxtIMEB  tEXtSaverArtEffect PNG 4.0}u IDATx}p\u'睾xxlZP jAC ! 4ahdV^GrR)O&5Mf3)W*J\׻d)q^WUYVdEfe(aiVp&a٩ښ)zs~߻ /7V%80\+!~Ǯmz~QCv1zE  <0{wb00& vI.@A ܎ @#U0։Yi a۴mCJggXT+^= `Fc_~x{ aqzff9 ެJX}BCU3&U#O0:0,Z H(( ؚcC5#Uq@3TQQ.7hZPuj݃^j({vhԳ^aT0<ʼ2| JDe,Lx{-@or]5_ !*tPq Ά(MEˁ2C^P\KR Jg IX[1 , @Bzfz;3ϭ$a`숤Y5‰* %XLwX{'/{ܲ'Av*YD0 *q`D.3!"%T&nVr>rw<|.,y yD$;.\<´:R8&y) e/HGN|ޟ;x=,,,[A$YQ@" }B0Q0MDq8X8fL2w{PC\aA P`P.s@N`A8 ĝL!cY0iDÉQ:U$d9'(Yi ` 1k#@ ;2F``` +׍^@a PF1NVunDB\<2"KΓFu0`D p(QAEH$I(Vk  ɷ5W@" KgLE" z&Sm[^@Ο{i4n_sfƞ8zrjj2$-4RlWz羻vnґFmsw = H 2$UnLO/}&rv~LŇxiBpѼU^A^y@ġjXY7"sPU?_xJ1-}@U +U%4C~ cY z}5A2Bb?j0h]=//D(OB Nl;ghP/P/BGBȀJƒʀ@ '^> =^RV"a9cfqWHI"R 4s(PZa  hD(D0dayw8i];<z\Xn 5?@+3HO xa E* puj~?k5Mwާ@41~E~U~e~Eh4lwezyE+k\e,KӁTU44M3F9l6zei=~WI+٠>>M`}H"HX盛i ,O޼Y7뻲ift K,4^jjͫOϚz=^5QAj6zѨSLWdz] Q*ji}-@TWnYWD'Ɔ#cd[[ϯutmmgY}uɷzrݸ9|uG͌:@ei}WZL5!"NDd>M3DfFN_zc?+YǾqvqyѬ0]K=F IdfrydskkO透X & ~C~OM;6nOGGک3ϭkj gV:5=%L ?!vnƂuo?f#{'OͯN021913Y;S_:2{{i-=5 &ol/{f≿<=2Q5pd0T4P2\oBJ-Q p|qs}csk}DekGO/'5/ $+x9P ^ PuN@@"7QD!N`9 :% N}]eA DAN|MDH&Nh%$}vX:aCu$Pu*{8*aּdx A?CX]wO"YS>sfyM_D6T-S._{vglqiidx,Lz^n0)65c/>ps]u;fcm}s {BК.`w?y;sKԳ{6R)͌F T՚_?n祭{:'>xy?yf~Ǐy.,{nٛ667FV̹ߜ?yf6R$"R 򉦃i3$rԹm]m݅vVP#|;te}geF:ߜ?r,sXYi?OJ"K5!,Uj2uo!u%NE:' @")i"HOR nXagjd*? $hYq9g0852x4(B$<d @@@"wcN#0F+Gc0U-j04/$$J2*Y)E8F QDqB FrrZx,XJcn϶:/mvzOWS[[Kkwh[gg&>?ef;kGmm9N-u"f{K=+L|gB(Px{d4L93 zmi0;=qǾ1e yqlܾ߫hM?Г߹;[WO?S+3 ˓#>St{uٮ4Gzbʴw_<{F "guvVcϯ𵭅%z~t}҉GײO4.98r]yEhƹN>rni 酵ﮦ^'nm^٨g XAiߜ_R?66'p!Kk;zrꆱyzumY[pxc#iMWϟ_WC"ı3g:21>h"] $ JPQz[f6|gV?R7HG|3rMߜ[u7lډo7lBl]]/>ȓ'w>n,âhMx5Do*P@fQWFshhHDzMDDI(4 D] h8A'F6Ci(h & jj(E^jv'q@uZ.Kt2WZ0ʉ10y#3mbčUvhYNF{8۠h*@Ba QE HR\B 01tQ=?3+欞EdfrRBm74!w4qZ(;uШtnټ$mhhh5$>QiGǑ٩)AiIk5-D!`blT\)[ā4M3J"p^E^}u-q^V{$O0th!־ڋ@H:+XPE^Ekb⚦ދ@Ik:~ݰ$D5HéCq; $n6hNk>QMJ-PT.NxvSHNաI3")0Ue@eeܻy!@^:.Q*U!^l(ŽH~  IbT*dtCQ@"/pm=I ,U[ef >á#J2`$ {*ICP+ iy^BCQA%۶HnbR h?\đmL!8(7n#'/ ᫆} H#AXs@FsDќBt1ALdŸݻQz)x^A+E!褴&H5">;㌁r.FuUsv<%xl;i4#1*ԥt DD`ty,/ -*=4o« EUzj6Hi,@,PތKm&gPh}M>MmX(\d˥B#F'$j(6i4RS"fQ=m(jO$乑EItPjف r c0â8th,BHJ"GA8)=ȬEWl]YTjri,gy-ޥǟ8ҿ0FЃ℡HCLy/8::0BGqx^8m^8'N:U*ټӡ@vʥW+gUH&壽 N5%[/mQ o@yj&O*$뿙DaO~O^~ {+kGז|~38X!{Cw*c|wϧ>Oo<|χn_>O?oG߼'{}$&)BF޵qMbȂ"ݩp<XxI w{d.#*O.~v<>/>sc͋6??v5{F?cc'Y(5J{ @]'f%媌X֫lry~?8ӚøxZAj&tr݇o?ptWYQQ^4 OU@Y\2-tF)׎k'uz]}UDR'gKR>O#RmJR%PMܳX,XRWUrnA 2;UYjv^4F RՇxJW4K ^[]]Zxn5J|{O>p/⪉[_?c#?|s[W̘zP/vv}/Yw_~ėx;Kv0ZmpսcaG>Zo[ֶY.QpH|tWݫiad5!%5;5ؑϱL IDAT3S qday {TA\ZqBeBPlkb٥n֧\ Dh)+ `s]_ $0`k>RqR2N^jI,:"=p?*G073L7sKpQmʒޥNYQQE}}9A(B@㛛daDLPC*s`q"Њ(oeܛ 1b)zAy(L)GӟݟO}KktO>ՕeY_]>O~ޚ}mNhٍ7ZU^@guck{q/\ Fcr7xOTkJl;?"Fic0 }3Ǿ>p~z::۹ھ:=|5CC͡&@fj=u?ڍE[)PQg8dll0KRwj],lne{| z6 j= KDҩ4;[BMUduOR$%}oI|cПyMdfbx/˫OZ0tec#íFo{܉S s[}};ەiMH{$K<w?w9,>(^LZ6oЙNP)4H0j' .7uX'I($ JdcJ HpHuꯞ(|M_䅍^l絷V\<߸ks)Ї7C^;Hݽ(Wu47Lњ{Е,ͬ$P|*2q}?o~Χ(H:vg:4?EDҚ¡w oigl_?6~]svr쁯<Z3>6"1"P'̂hBֽ?2~P}҅{&y>ȇ?xuvxXofjS_x}M{rlhjbvd9vakaMրI1K%M3qhqvlOGZWO8<9>'?j_;13=5.2{y󫪾֧>U8 -*̘oM3D{yLU)C9EpYms>u~WngrC_9Jc'?>P3AM }ɚdoC"Qya(/T0vF C~_[o#:~ׇ^5^{NP߸@ ?t;o}_~7'zW~yqOJo6ƣ#ĩQ)" (AyV/lz=`(g;l,Jo(uSo"FC\O^]?źsS%ޚW߿['JYrx'>vWTi]W" {fߨm{z <W c߳_Z-R`gk?\|*i*NwWGFvk疖 #o5:ڮ覆Yb::йϯuGG^qɹݳHcbwkqb{ *" w8?mya/o>u-Է'}ӓ~Dkhbt=nvz]K>^OTd5z:;[[/u7{4QUU<=Rȟijt晅剱aC@ۛiTkz^ b*g`$jB&J;2*HUX>hh߹z*Y{ "*КuáWkvӟ._~\^q^Ub}EE~k'㱼3 .~S]1v~@Ǯ_.zǮ5w%cmln}*N6!`I^ 5/S8bu嵕յ`:11'7:fw.n7QM _Ώ/wpę7oD{M} vi&pּ$"ѕ,]=BjgW[CG~M9?q:o[ZY5߾uj~eYZ=_WkaMn7!.<-=EGn ӣC>}7HĩVП]9^ߕ _UyM{+C} $M3v9/{W(PeQ oO?I 5k*XO?NUҖ ^JӚ2' "zk<1J +޷:{M྽s TU`!6h"Xcr!$@0MTETŜF*i(<.NxhqtCLzĄp@o'͛ l o5{h/*$RZAud0bWm0z}[_?€1QƳ "[gX N@jkͫS_\$k 1V_Ef(thHU$" dHՇM#hNCD&Ee2V z1yo  4$Иu0-iJ2N+.Nk>4^CVo?}gopw}C!(`*`?M]O<~LSO y8Gb-FY9D%P.FG)2]5}3C΂2?h좠zFKU d|L3,Pd$`!A\4cMn)L^_9g zb++2fŠb2":\ΰy%$(`@1 "1RH*f…gH1XaCڎ4 -ʀxSR-7Ѵ&Ȃ2 @LB)l:ܾD@e>%Ǎ/\?lyUG80+14_xr@^lp(7J`U#2 t=}=>i*0<ךW+Pzp3Tx'2 8c "0zq `i/,H@5 $:R(?1D$u@2-/blH hGrDb3DU$-D%h%2V W8ԳW0y^?zbC$. ChUJժFb2X̊Y)W8F6GO[hj8J y|kh(D00# J_f'U,pREb'+*nn~ng%h1B5qaU ht*nnG@qNiqV )he>T-X/jk5c3D%S[-"\Np|I< 轏ˣNА zt.$N iU`CGK`@$"6 QHEA|_n<0+K{9w;Ur>ȋUDZ/r.CKU /K5"E <#كfyn,qWTDCE  @ V r`K9pr[#-5ؖJ,Nt?q +a܁_[.덡0(Wi(T֫Q?055c⛨b\y㰭 J"ZUxUQH(1MH)ȊBE$ଌ!>s0 ,G܌"W>^%('D^Q")IyDLͷ >} *dz(Xa"IU<Qx({C`[2WGFQ T *o҄$īPR8iE.-mC/Z<җ*y@uj#9)' d} IBr9 v}O>|~ Mƃ?`)%Y Eg=>}g%)_V2ab 4I2H M/0ƐCHhv D'ujFL+U3"hy$;D)` ERmP@DRKTJf8Z `s@ "@g]yOkӁW A"dTbH4ZPSjʂ'.=#`KqNID2DX.7jivo=c`kb90Q)b+I?QH!ƗŭuZg4XiP$\ 3J'b`oE(S0z/a(\R'$e¢7a'H|]Raeu914:xF]Ye"$,MVV$ #@ O?}lnz:ss$Nة-7NwAL4N/`sFe֚{3s>~dWn૟-ggޱ7Oeyz{n?[U=π;,_er~fs@VO/ﻥ}zay-ٴR)7T@In ^5b;4iyz5Ѭ7I%&7W7h[@ >!C?(_lWּzUoVoĬ{ԓZzT9Bp( 2&\fu""5 QZ>MSt0-qX ]/=<)0vTR&uiwRFVXmC՗BHS/Ƞ=[':TJPaSg]™'>r8m#w> ksGmlN}Ɵ`N_mP#|ع~5AՁf{Y |˛KP }H@4+@HZhi[Zb{6_S3O.K[Og{ns0vM[ L{^&6GQռpD []9jM k m ̓E*D8b Ȁh}G& s^q_tALEJ灣mcwyؚ#~bZ/U$=A }ɲ1A8M#`h`5 Dr@~{/J)ԋ*3C)} jc%31c@]TX}z][ "{&2$R=1*&yGj;&vrveeqieskDPry\. "z xbY,k/eyẼSJTRsBA88R*"ң"WseUxL{‘1/jʤ JDF>N G+/6;&ȥ(d4 ot[>~WV'&'W&kH5SZ6D ($eQ:$-{I[N\H}-=_޽|ʺIi[,7'u?y\)}yCb*,knBjssڹku;Zr IDAT ^z,^{'0wpA=c"hm1RU$0!ky}-[{|ٹ؛s:!|MMp!1!&CWOet^k/q3#/%]S(pˏ]}w<6\=~џ/xm~Ņ[Y`b\^-I@٥Q{7( "דޯ|z . 2E PL:93Md=h gn,*`jT6XƎʊ 4^޾췦5F?DaZmmm 7S05917U.(zV}5j+1jMrPȭ_jյ׎ 9uj:TX/qJg>S7O]`\+4E KKW>qT,啙'g+g:RV﮽Lʵg֚ҶKFGphmS?zzbbb>DrmNRA LNX2% ""G{MOLb|ӟou݇ϝc[BjRϞ@Ž2H5(P"To>ݷ9w/0S!ZU /5kW5 [ޥ^BpT*;kzT^\{tӡt^^zYZ)i_Ozmyr?΋آvA!$U'΍u$-^Z!b9)^Z "'o8 Df P6w3 28h];Bd~t0\^66`x1jրFaцj*W8\hO0AеZMPc"RU[~NHT4h&Q!bqҸ1DE5h_qyNTe@EVZV`kl]9t e]8!"ڕU.N]?gHpQQW5ԝY:pc n؉ ɉ2sB LRXb*0%'vҠ" .#hpq{Yd@)2pB?{{$Ծ-B $GKYk 儘 )3!/P~8̣5ib7 Jr2h|{ yE6d5g"N8- DD1wK(&7<& vl‘ K{@P]PSB0Lc&U5a,AN`++ch F!: (Y"†x"ً%4i\ZP焵Og}uEب8{**TIт1K=<;"rOI?)Xh%e=Wk߳GbyZE.V&# x"` ][['ÙMbgmSFq jlDNXQ_VړS- |g Li9[rrӮ bWE YN整 $P$g6AU4 r[+bilTC4 ٺ}Rv^`i * @1'ٲ8]`DDg=鑣?ttz6d9`#~= Ań`SJ<@ib6 Ь~PBA34, ;WD1!$&&Pi>c&Hߒi~(L x(Eaw-)TS7+TA| ;T(E[ E|䫏?3# YPWڭgC$[t@g$/P \c6-h}~ʁu UYĽb/]̬񠰙$ D$ wx~Uh*FAHadDr؆Ipm06 !W%b_e=SYq2&FOQlP}#H-Ď5[`%DL-"K#@Dz"ńR.!ώ,.JEΪd1B+Koc9cUJ!(cE.dEkR N`3Zyfbz " "/L$D1VU"NjH&2E'Y%%u]"BLA~,6ID@ Z#hSVf Ο9X,m27" aM4.q6n|4cS:R;)c( 2/p\P 8zP5X4jc_?С[>pJ(*X YpWk A TB Bb 3N AUډO}B`''XrC _Ȝ[tA-" B6.h&ٰ@[bjNuˈq䢑}u-i b8sX2]Lʮ ;0   "ȡT*A>٧i`(kr"9/ `PU#l/c&b L 7Ĭ}9r,S[QF0;ƧTbb;D5ND!]IO?c',a$6v`3JD1c QMj@g G }'Fl1בpP>` )f/+|`=G1aB4Ҵ &d! r+ Sh$1 3k&Kk_EYrbFeQG*bרhY3&ArD۰u- ""R((& rT:e D@^"PZ^^..LIK  D"WvOu{zʦ\\,ݩd"}JO}nO15hwzǧgy3cI` #Hމ33ju@D$ %w sEmEfڐ m}T~U]ߜ{K}v~)7_d Jf>ebJ̶UQQy&"39b-Z'Jp` F[LDf@ʵݎ,7~e"XOX@$ W).e.^h./=|sg&f{f>R`s˲Yk5Wk{w֮*Zbf\tqɸPmE{ޱV)6LuG}bMǮiVwM֏}|_[kص{FNL]^+o&~ߚ+tcߘ^|aR`P/D2;uZuxlu޻NTWVWrr]'U᫫Vgo{Y{ģ_^|auLR_6W)s0Y) IBB}j&W߹V.iuzwM~cs;'؞]c^/M}Z,bi{Z*O9eNPj &;x3yxOi٧*O쩴X,KbTJ"y#1@ \`N9'@6QVnq7"L鋱 3[ DL8*JGSj֮Hd[s rz EgW\̆5 nZZ]y*˝9+zz t#`ŚjȾەօ^q~v|Yy-gi ^3:\zֹlRYY^i=3n;օeB7W^|Ku˧:n-]1 T7=ΝO|K&==};ڿ{w[,W{NsrR,Kkc׎_]&P6FۭK&;tϲL.Ii1/wN,=r[=P)3wO~EĜKKgZ>K/6;FRgɥ}o? f߶ԧ=1C)y8 D3KȎ54js,td>ϯT*P5EN1 "JQ/DL =$Rg?0 NR\sUB GtcGJ}*ыEȁ 3A |{FPV((X2uT (֮J׷uGƛvW[u@\v>AiU/&u j&abуD BkcTe"uCO1~$9uttT |S5dY DP0rh5ׄryYpWus=y(l+V KݱkFDѺQúYt- @˗%ګUUf]NS 57b\"8kiv{Ǘ49SBe=y/ɏ=߽ommU9j^8\?:vӞ]KZpmœ۝vq(mvt:={OL u.wt V=ZUɳs+|R*GO۳kjll/[g7ᙦ}n&8a"*n+/ۯu˭fۜxa`:رzpEt[oOO];<ڵRkqe:519աb^vݵ:Vs [%DffA@@`E'# D#@bɡK&u^H[^7P(X%3j@ijH"}/ MB/덎t{ȉٹѣG}3,5UP 2sz(y>?z g#>XԸgjσ;''(!=>h4\=7Ӌϭ->LT`rě\fm-&( 6>@d V%(^)KۊDl6xcp|}t @e&1$@[oNqU:4e R|+-j\]C0 3)R@ cbE޽k'r`pc- Q(]7G;T:{YۆED 8zґ rB46:BAvT{hZꋙ]$*uJ8M@tae P0{b⦆ 8'L c"cOTMhթQ81Sp2N=T(@}_0Tx @ # 'Fd %DQzJ% ^ fz~#K~h! EdUJ "̐EE pG % )8җ;+'Pm>\xtlz(aIA ִufWn}N@ u{rb; A}Kڻ`,|G` Ӿj?< !tc&!epܹ կ2Z`<' Z)@LL1< ¢NWvݰ@ Fr&P%%=TpyWc ΧZ4\LAm9"Ӈu Wc>V LߞR^ڷg7tل;UXh~A% ?%jFAab.!i'g. ,&jI' IDATA0a`h| 6* {]*I/-} g@w8iy7y {C@X[m GP:|u&UjG^if)*TY(_12cE65vo uB>wz y-gxSqc*IjV6J03!S(6DGvd4._*//RuX.U09uSC,S;u.!9cРLl#@{u~UX>\u@_u&j)xʐ]K ;8"cͭKj5. UL ̳GNVqj~n2KLbjZƹ`#&-J1=9?' 輲Z/ 1 dk1qG6@d'#{v,yY|Ȟ(&*(>   97ȶ;t~#i7HziP9& @>8y t@@}Xbj,Vۛ#yskآ-!^}ZމbnP1r *D29|tӇ|>-ڢ&m0֫ƕՏԁڷܥ9,<6V fSo9SR36}Y\kg^ogEkשko}7[Ed҂=zfnR.Y%+*>?>v?ؾΡ1[Nr-㻵g@D7j?>[._~]Q{Xxٻԫ]Y^9`{.jPvL =5o^|~Wm6k2(>-eW''J%]}h+GOxb&gfǮip.ٞZ_t^8VeYONGWڝ'\^}ztG~vV]+Cv}{&:{nW7v}'/,5ߙu]cvaлjP( Gjn<  {o/~92Q1-ڢQ@GXк r^C"mԾ#~屽&[>53W*K=srls KߘٷwZklQdY`gV[}'K8\,21Μ;gj{Z|UjW황}sO/ZzaX䅥e.ʩvs˔ KisPoiJx ;m 1D`&fpsn(2,w EGOWZ-U)vi[Z.#ˊ ݛvޗ}f/'Ƈl:?v7<|Pjq(.e }{&v.t/e|kGfo=un.eoʊ6ru%=Rڹm+@WW3%<bbg'ļo:1;愙o(z7P/^&&5Q;񍳭$;F2ѕW:>oڹyl-ϯt>^{+@>V_t͵ddG-캁1rb63ɳO-wki ;s~tGgĥS㧾}޳kfVK)UfϾc݋Zȭ- !ѹbmګ:$t9-ڢ9S'7_i=]^ _3R3gPŻC$#br/>Ʃmwy;bL>l蟄6>FQ@$N'by<.~'KK8ĕrqE[àȰN~gy< @s[T)(AxSOę}^T,Ve&N|lhh#_;r}LHEccB.P;iiG)DBc֓!Y+/v~cĠMUG@52b+fyP 'D5>2r a "G|UTUʯ/.=q=CGv]ۺ! 6N3j&+ٺHUX[(bX6".T2 8g?gvFlrc4_`zREYv Ͼ=TEH_U`Y_TTש 2^Al [{WDbݪ}sܝjȲLd2 @Pg P}f/^2Rx?i\;d,q<@dVuKYTpH_,:#fLh*W.x$$J(_Npy 2Umn`udIpdoyư Y?0|/d p4- {֠D趛CuuGj#wϨvA>⦃PC /2Q qUV @+q(vD APO ,^ thh gHdCG ^?Ҽq_/L̡pyH>#TUɶJOr>$*ƝcfV@_w?* . (ٮg4!pTĦ}pAC+%q@("Rsr}r% Q!@Mæ}e 8|3});jLr.>c%JB ;'}RIݫ. uP=n*]I9U۵Gvc쓿'PDi(M5 VW{"1 tpBfeGtN1YA]*"eؐ^rqն t{ %Эe&2̩A,^ƭ~8r2#dE#&geJH51cqx8EP"ȪbE fs9L<6Q9/ l=َ(KT&(2h tO*cyc%zf(pO ` 'l[^gf#Q둯Rzo? P#L/@ b Amy\m D)p.T J s| 5b /^1\l#Sd NITcpQۯ&!H_bq{(2 l!\Hx+ pN"[F!/kșL@P #?m]͆x>A9F,Sݎ QbeS6m?T}S)⡨]Q@6_mV/BWVxN^i.* ȏ^nI@YPξYatRfPU)a; @JC;󟸻ZOoG'>O?1ߋ+r}ֶ/kDHUȰMqp_[Cf??_y}07BF.TXXuA`reMfǔUʠ(2!!cAUUEHА f0*H 3G^LTdx|ӨC;t-($GV%b#YxĬG"%?XL<%(B@N#vz3 {3-8bg5hS~j ip^U$(`xvS*(A:T߳X$E$rDfY-=5;2Q*9 ʛoˠH$CM!//hEJ5_>"SrԾJ eƠ}!S2xQ8\Y;7|灝??iC?i_i~x"^O=v|㟿ӿ'OCzU3Nؿb:G/[GO7ՀY_Lj@LT ('6BC_=yǿ83(?::E9 `@i (f\=EQ^OOirw7qZ224 CQ4 0AAphTz$d:?OrC7jjjn߽S)vK%fZľX}ߑC8 M)` *Z@o'āpd,s+;+Yml Ll!1Yida6sTkbGމ#\TRFP%N@Gފ7}Z@K@es2l 8ժ}tk_5wXWKRwW6ttvuvP3/,u[ZwnfI릦[mxޞ[}sKw{z疖;Y/@uͧ6߶m}`ۭ~۶m]~{zDP>ܷy;oAfMԝ%}m {v:{k*?'v |'zK}l`y5d/԰ V pȋDz\E:z3/TD)dgu;u`SmE/AGƧ*wlg6 "j `\Ǥ((41sQYDt;.e:c/MF:'>q;wPR!qcPgybC!_4D<}>Њ> tuǶ;699^ ;0ZkzZv4$p@oh0`&rA0L0!dp afnDŽ VDT2lpdBc8sgGL)$ نZLʞ5r5(2)|bcZwpp{c^XV/1CpzdSJC샄D CbX5MJj($D f&/&b1^mjk Bm!/&B1^LS+&I .-FC@ &TEVllml$cR@2k&X Q 0#DnD'urg&K׷TdʓK-DK<[Nlmnl%TK=N@EGe&&(P3۴m֭~W& 4;qdT ?X ڷgpM=?l$~sR,=R~}P9@Lڅvyg͚ޏ~@R qB+v!zqDBXm!ҝ, FO~#$@]Z`Gn_Rğug7= `)ێ*"oeNe3@ '}h2{@m,0~ڣ{k{t8|La]zZFU t#V@$ZdI mޮr/l%[;H;ewCt NL^}? u:>wԯN0*̟儈IbdiZ뉼 Y(8 A@ Z PSY' ~#HVtp :e:c#p g̈́۰/wu.WmJݲAn&80IEu@=5O|F-5{zd)_X>ͳݛoOT߬r([R5jDZydB2%~-ܹWa~ܩԀwo%њV$Wmڡ/\Qxm)?o;dG8N~k~wO$%d!>wh{yQJ77䞇>%NN̤S !==1; 7DӇBoA:~C:|˳9t ʡo3DѦ,=3:1-3L|虱i DWu8p ӀN#vJ}jpvxlvvZ𵯟ɥ;J"@He#TW,Z>/L!gN_w hx| `yazn zG- IDATJMQ&21N(`VěFg1;B .?IuA G ,7&U#WDI1xāz^DllӂZ@8$n`n0L2YxА# DDfR$ W HR[.z ;!" 5VjGZkSz kXL5^v*H"VMT$=L@ɢj0C! q$<"^ :d{$=8(e*qAdB#.Ȋ2ZBDxE.ʳnrCR[P[/tE 5sHהDP8R!b>^?f'oxƱa׋E.I p1"v{f#]65" EBBpBIv`P@,[hmP,c~?~ڂ 9KU5 !7OϾ6=r62wgWEY@(k-4d`qD$>\腑6j 4Spϲ\ݺ <:S2F'n,ZK ,??j%fѿ "1EJ*G{S篊iMվcտW.`P؅$T3@"@TX;_f<+Ӏ?ɜ&1؊,Zǎl[#R/sH \B[CGWMZon*j] XR?Ey[b鲶 _x 1AdځUMbȔ1&Z$Ne-{:z l Dʹ}3خ$) PXIDU ,!)QURU~XEȒDAY+L+E3RhDs:nf?FI*aa%_fi@LPHZPys^z_&MT*1qA'r  p]5娎?0$)@%,-4("I!{[4ճ+& nR""cLd Bc jﳢ~d%[f&GG?2?P:% {1qđ2,r!EBxS&AoUɅ"L"&4Llm9$& "qPHjgTx*А@ ARa8!0EQ$#&"O$"NHQXߴLDDDqC` @H9PH6ZRH$AsF*!sL C61a" s{p`lhhlhG]P,_+F?V ?;DWdp3'b$D |o]r^N涷#e~vM--:Tf!{ٌ9IM$9d!)tJl\jJ$bcmX*q%@@<;=5DI,;Dpi"c8@]i)1gs_{~qV`H q"ZDLt0*M@ΒUD`3@Qd,) HrPC~~޴!%Bȁ$Q"h̽7MRv$@LܒE(膉ߩB^=E N B*r~h Ց$qEauTڨJ2753꨻?2?3]-/8|]q[RH O߫jltˉ*r3 Ih?AMK{{X~i<w^>ry27_PExckB63M--?+q%nnC8.V''TT$GN`= "VĐ@H$UVZ5wy;d.'"bkա)8YaXDBA t , e)r5yHLHĊznˢa Dԭ|Z @_*)gnA+^E"7 2EL=KʶcVM" ,a]PC|1dH5dz`ҧ7\A;իRwuST̂% q9GD@:{{Ǿ7[ڮ<ڲ156M2Km;[5ⵥ0w]}j(>̿WW_j}&6F/`L&&Cn!MvdsƆgw햎f8Tfoljnm)6|S{PT:;MT(xMqr|Lm}}N]/Otԓ3,&cD `lxx]cǭ=cm]ǚn㘯϶$YQ&2lSցt[gC"'@w%+x͖6off  -̌cS/;@RԔ q*XLz!E(_Yޚ|O lmj9C Q>Xʟx_#[P>c||*F©z|e D6[Vt 3o50bN좍&zV{^ c]= KDF˩lu+W{Ք I:_ I%^ I$VİqjZG xJL4>t|Z_Vߜoz~ܷO֕Ugת;wO_u N{2;3 H[GKK7tF/uvUߜ3hmZ)ٖ3;1Xt#0"6Dh "*HjcDJZ\[ @REB匳ϝ;9\F͍熣u$=;•DSP u[ @8^[2xI8){.p$LoKPHH3eS 9{@{GEQ3B\)&@vouUn~~JYub@iסTrq#*qrG$ND C{""n`$铓8f4%"vb֚d6W|ed(IzM;VS(DQ4r@ Dk jy۲Hɉ@;?4>`@rZX)\ EuskRwDj]jsib՟*oVޘp)P2\9CSSs:@0,;kBɶ$ >V 1Gqp+T$C5f1^6p4Q0 @2! L%0gͦx3FR@j;v<ƧM< ) `e^`UU|Bu AD/zkh$]JOePGDk@-Zڄ-[&||)ް;:jjiG@ksSBccha9#.װ쬻lh|N6ugyXDu<35տA$X9-͕/5]6;5\Gw w9U,kpYg{7m}yr~vvR$wnDsvQ275ӻq=`(*nb3LvsMjĬAnׅ-YI6y N+ B$8/KW--a )*][b6Uk@W( i35g"sIYH>#Η7_ ʒ) 4!32FJ( CCfߘkPHc!JslMݱ-@n /YI16>i&2IG<_xetM#1Y@Q9XlɊ4I |y4IKDdSvlbc7v# غC^ 2)P#1!(\Obrt]+VZkK3tYRTCj SǣgH誛zq@uΛ_u+s,ξ<5X~Z_޺djm-x[~zMt9#tIJΌZ'5O81 ~թ` Ȫ>3@Lh9 94L`l\6d=[Q(6/Ts)"xmSѧ)o? / ?c'l5# bϒ鍝C/Uy LN4 jhODꡗjDGғk:Sfwn{ekͭvgw<'^+66j"IS3pHސ9REtND۩MHTo)or,gS{`g捉j1d'j ^43T0D DN%@/ĥeZ?<\󸚗*)ZW4Q45H "fFE5'd2A1!$E&^j( 5+QՒ,(ىktfq2;;ɉle~Hۺz5ԋMuIj#(tI,6TIE,l q(9x|Mh掃:t] B!U+P ʄL}E*յ L A}'×6tmQlF+6ՈLG<{S|LR! oJnejX,P@IG발@恻ZۻNyՏtmSĆ{J'9;5>?O})w+F?V/jjʳ*JT Ct=c}[K@V2LԧOm$o%'14S)!ǦM7n( ؾcS_ͬ .׭G3l4:Yf-&]mv Oo==Z]'O5-h,(\ kMbm S#[O|nȯJz7n9_h@Ma];Kbg[Λ['gB^B[k3+l$IB Ŏ8qC›SZh}MԥJs# Glyb 6fOtC#SS+^Xa&/$D3"[3>({ M'?]?IPչ޵5oW~ׯW}?UHD(0RdBc )1H~Zwk>un e_h]kguO'?f5%X#@"̡8gĊ]PL]s{ƾt;X5'%ߙbcBaj![QfUR @!ad Z4s6>7<2Q`?,&vQk}}O~E3ܙN55649_.}8XH8J k 4zcso׏4rඞMŞ[ڌ!z{B ! P(Dk B5՝vN 衏>r;~` :Nv>x[麎Ӈnj| _;|zRYRX8I8YHlbeQ to/W ǒ$I*B˯4o6]u/Wv[nx(5Bx+?9>VNrUQX[8j.: PH!K`񂵩Ej~,*?/p}MWT-w;MvN};zߵx!ZLkv3͚́晀+B!2&IӴTlk bc& NJTŨXJXbP,rq-".F\XÑ⚨6*5@p D7Q.9dN6`pW9`ЋIMo-檳uV=}\DDM-V[($6ύM6KP ȰK}_qGwy *rY93:͝7|#jU r.TjBF-:/ŵZk 6-ƅ^>-B35Wy?qCLΔcwoh(*k)69d>kJڦq0ޜϟW LP ) D-럇8H̿6 `L޻ƿ @ dtMF{(o ƅ{6FbS/OT.Vm4k"6lgfaN}' /L枖s9 3ו7gk1:eגkؑ(A^ yr~"^mR{^m2{M:߾qBm~~N klX g:[ Ͷr 0F?7c'GON~h߶Ə1cbgǏxkHb]=5x_z~u>y~FG|*| TSDrB 9J2AA @d Pc&TD4Z'>Au@p6q -[l uJɢbaMh0ˤ}d(d==ĵXPh>AK' bHF4B=U) [5]X;u୒B$ ;E&'6GyϞuзW5 u2O~j^9Ĥ|J$H`SDIV?RuqS8]S|6>q@^X_ge;- ߁gDDs@ƩJ4ⅤX( q&jP]V/բBH$&q?b:'?կz[i hХ["9 ֊56B 13B-VEK gIk #0ℜ%RKj {`6 xl.D]BG;so'簦X-~D%AHT@1=8d+(DkAXh;{b!  -K~@ VѯY@5Pd"pb.!4Omf2DZ0"60;+/?w`Ex]b58X,ZbDGMz7:W^HHt-OKB@' }_O_ ?فW_njjRd_?A72\ʟϞ݅uG;2:uTW{+)۶;v?{l}}IaNXsy1 +I*ΦF?]96*-;wi<}y23ʫ]e-ォmq>zȦ6wrQ`4Ak, n,Bb(Z +/VlHCVh4 VEr ̩p+Ic2;+Y"_GҢ"aJ (3J 0훺*Z5fk|B&2  m-Dl8NjjqL<$8ˀ^@!X'g x{wݽ=uęN0WFL`&]Jl aA)Dm:#69eD.ץZ/:H}B4{|k,gv$qz,B$6N8=<=xT]WniX7 l I]8Q>>q@@&FqG.9ZuwhT(Fw叔km{P=P {M7~瞝CDnޭy;]?PjD@JھY~m:`G?~_ĊpbSZI88 /i1Hr"&s$I܅Jο䯛GEX +VRIDsLR"֊6IE@Ll^@`L>\2u֋Z Һ+o%Tm)2qT, l蘛eS:M5F_g SҺbG{mwvyj&<r nϾ<~#q֖R LCDtluRۊTז4*OΔ;[;& ;&g0Q-`&/5d1Swt?yxocG`^2&`VaͿ:Ujl]Xנp^'~0 [O8έ+ڱƚđ9umV3 bQ?m{s"^N}m;G&_ؽ&'F&0Tjjmٓpxf_]7?-N.VuB@Wb 8lNn㣴%"k>( {bf2=ei5Wł(LziB+4o#5f\`EƟjTj7yg6Tb"!t48Q)W&cS]{Sn' ڕ\ % 0??ı\\2ޣgv4 i诎u߃\pf3fU 4 *4Jj'i8Jw5;xh`ީ_޵Ӈv}}wύo:>V1=_Ƕ}ʞ4;d9ê[왫Kam Л ~1Walܲyǝ<}?F]ZVÅmӎ;ٰ%ZWo'*Dx NռFzBr:W~w4[+5wp_,# l "ݧ;$tڟ$N}䔗8`}$gfeOJlcuB{o=$jը]c[߷뾏~oxooOQή_o&q^v%#x%&%$بMn*W$[ݷn`K5|x'Wn$8yi=R,IcCg{޹r L>?>G ۯv_D:_~m5i`&VxeMfGcqT$$I fV/UQ'1bR*8K ̙uVPU2 0k1!Y+ O2 \_tev))+RM'iyc44vT8ݩWg&ڎ}lOw39sSSq|{-m3o̎޻453_&5%ᗒ鹹5]vӳӸ&~04j5YW>95\~uF@D+8䎷O>G>01z㓯0LYΎu8y/5yyse-dҡ}ŞEKzbH ĭ_6շZj9`^Xv>M>D׮?9d6LlJXY9{.\w{ ?{߹y?ϳ/L=!N|>hK˫<ϩ2?߻^_H (@7F DDL$qgrRՈd1X\nmn*eNCƦ5^$"!IIj5\/s% z ~R($b d*6QBSD$sXPDޞ7*==*NΉW;:;^-#` @땾覀 &\Ac=33kj.NB/y#2m).KM۶mkw|dž<{nFltl`rs1O 0034B3 M+Zʬ*ZfusR7qmv}Zlxs96QTuGUYfTQe`)a;q3x 7ry373?>sPVE/C^|y2/BcMl7$- P! y&M#>02rF"˳4lCb>MR޴KtqaOܶ0{fiRg6J΢0ɔDUH*bJ!xm!"5IrH EIZ:Q'+,%|F;Ӻo'K }$4yS7FdbUmv wOdʑ$_"xh:yh/H P'Gt32@Dg`@ϕA`$St#w6~+%JlI_QD(Q{'#{ow1Ag =*$+h"T kwWgƝrèܸZm|tZk+C/At [$,"Lx ML.Ao -b@}pG~e{)v >o?w#_޷w v:U{~ܯ wOǯ8DD4cD4ѼMHrJ+դ2ffĺyf/H(ysbc UR;hbt׻8-% 3Tf5$3bюYNY٦jMZ-攝\F H9ЪDAV4FsŎd`q#t5nzLԃE-zԫ!D^Ӟ?<͈oywO=O? u=#?w~@xm|}S{?| ҿx@{v** t(BU&ATUTDtr|Mo=0,̜n2}[L_xhP r%+JbW>4"AZ +ij֊F^Bsbc(H1ѪΨvF&xkm 皎g$PS+R܈QE@^lb0ZKbFF\ /?եoٷ}ocSxwUgtǰ0s` iT*RC0X͢(<afW |`PYPz@A6 R;* :ը ^U%#(w5P4ߦ:(a =F*>~oFJ?|"jO}ѻo۷{hj#0|Cb"0'݇\~{yw߶=ſG}'Ɗ"1Dx0oOPjˋ=23jafϸ_N|?~?!7>s| z [=zt-~]J?-nZ(<\==@w+-Dė J+Rs;wZno{4EXwź+/_kc߾hLQ+rlD2[I-jD2Ǘ25.-'kK6[ax}o[KkO~ɷrdR^(SCF6=no5-a bx z`UEhv33PYU9T@<;$ +bDJPSg] t94 Z S~"ǙDJ쓫0WсBC Z]II=t=oRXo4dPٕgW4R.~ڴW I|Wvqԯ8{vyB*"¢!j-W( Y `;~(9G]9u{>w)"&Ɔ??} 3'pCi7BWI[%uky٨b@+#%E}Ҥ{-߶4&jV|CI!S8}5&JjTE]rkkqON_z g+3^76AO?+aKokzKUPDmI 됝iRWR@G-nA*zE9hCQ*&N_/pzo2zu"\5O_lD 6A y%Ԫo p+%5Ʃ_:*낥zE D['ef*H! A4H@n$7O(iho+ ]]^+!ekF^%!Kj &HzA| i#FDUi+OgdbjH0\m[=Ӧ `A T~3B9`DHƊ Kfi'iO3HA@&_b9 ̈́u??SN #کHn%  +S$  TE*Wdt| K,e݇KE&44!w[DE^0PYne {A_{U&@m"'\Ƚ w?*՜T'RL@*"i : 3ZUȺ:c+LInV0'?X}*lӱQ \ss_g#P9J'o=W=@?tZ5 ~MZk^;֏zI,?.A5(`;f4Э<HuZT5;;:=^-],ͬ,Nީ֛2بdjarrAjn'@K4JR1$gWS"5AcDdFA%Hz_K`KFtˮ7NͿ[e扙o/+*( R U#Bh Q&J_I9AATDytX"@2q F]'&Mը^i93;PSm y^sliܹǾ~«冕FLDE@3\,Y,K\rl;0sr9yf^_g 7_yvZ5  `4 `[>G_;6.WypMNOߜvvwϿеu3C^Ui5qGm얉[ܓ?{A b?uft~*ޢZ5'G;ۋ81V_Ϥ'yrbbOLNMqhsdS7 X ֩ÚՌW._""yʠ)Y$gUhaz7LLru XѱA {:nE@[w^+%fïqqptkgPkgG1 w:CvSl/:۴ȥLa1) 4v $-E @ręgVӣ^i,=c_|!xÈܲowơ|pp=k vfUD%Aŋ Ճ-"/r5hw";~l9g̩#esF D[,<>*ŝ{(+C *&&J بw"ԒCUpNA$<UIjڌDU֑x"ʗxo1\Xõs^5K!9:@J((Tbg5UEZMFT~āp?x_`9͟}rHTp|lez8 bu_KuEꜚmS3+Wn?~sg{7Lrnv2t 4dp^=}?=^oӗWˮIG:erj՜OLP*4^VRX5;%4^dPhŀ|@N-W&g;91QQ +_5O9t7K fH1f ͝]ֽ"έiEv3k9wF'֐w ªߡ`ѢLX"Ճ-ENGgZ ŀbn~e&{_>pӻ޼A?޷֏j[/=;{CMZxŎOU{ [F?΃w5Ǿ|9%'+F (i)VL`dSY`mEU[ETҌ VO<'?yvyDCm xfؽP) 'ᠯoh,e!Phn+t@UHd@Ug^gu[^{CbVJnU2x H33`1UVRxieFi Fn8x*fIQ@cX5B †kW >:5paD+G]h0|L]_)1SNil!w?> ֔ U}甆V.sFMYV\(s|&7 šFZAóhX"4QTs[`h\ S kX$bPO& @gt>8X 2#ZUCU&NfMa"tȫ%䠛 IrkKem?8LF~.1/g$޾>zK}/|/oR=+2^\<Ҩ'Kc-E@+mvAoʘf1BzMRm)i[~[ɰdy@$NaզuMIMc5nFN=]P 䒁uFg }{?K9O}t3r?w ^5>"v#4\ܓe*LVd kz '76im]"tn"( p[#`Lϱ 7BDt+;wipN1HvsPTK-qXv̵~W!WuǓ۰AD4ׁBsU)2ESe"$VBx`ǰp].>18c"b=܃t4#%Z.JJr0]mUYx#1S7O..́ goU=@ [U4WUZ;AF U>X#xԑG"_ "־ `c$WaM]ZzOD6Y6%Jf'W|0dN:fؖ M|֔L%P2b$ F MJbDu q6I2̊MׇsAJ6˔iŶ?ְLDUkkR?"A4WԾX*"Q,i0oPXfŀX}m{N*J)bHZMW4 ~ьѬVGof.ˏ?v3({婯?1N8~O㓣Cϝѱ\Z<۞@2hҳ,eJl^qhdhh~tw qy]OkqzbfɝÈXr3=Ο+u[@xDplJljvrH^3 Z3{uc·5.IgT"ւ (*61/fDDL5"BRp@zH^* 5 Uǂb&BDT2! ?j zhJL4 O#2C 6$/b )N`"8OšCG$ZW-BD 'x2]; G~ ?~ &<1ӻ! gׯ¦!(ZmC;:ߝԓ'gUc;矝?{kϭ  /#ݥs均ڞB 7O'd\ R$ėtkSuecME?Hִu#:jE#2e+wNG 6^KhRNV-ySLe♔ P`kLS%Y^.M y%k=>bɌ`շSAfrU\6:Vӵ_EN7LT.2sڭzF+ Ee:kac؆ΎN1PTdZ^<4SF^~}H'銈ɶNFCpO R@ę[<Ȩd{bgʑuC9FݘWekGLvi&*"O|3KÝb}6Dɦf'W42cm/`)ڝlt Rk i@UU @u;* kΥv+C6}Yэgb#gf>DC|湺$K(Q| qX) qDB3Xhqb3*Q*@g DVмz4H@89&Z 庪͵PRkKx'9'/?ϟ)Q_XYJVHuen嚓]ZZ)r, #YVbú @#F^?gu3< =c{z Ab/QRJ!^=őG*YPC/SB. * zHAHq{2B2/S"=`3hV$\ҝD}iuOT3-T{nԤf+{Lۺc#.5#Lyfif~ ➛|'>f9zc}ס6?Kyfi% YX.?3oϮ$SMNpLgOO^ Z7Ҭ4% VL4BDTDl8$ڗ*Pu%T$ q 䩫fiS]6 çgCrxG)a> c?ϛs̫ҘR˙]Jvвh"f:/*pO&F:)Jf@tD*o7ugIXd$k~Aٽ`gβ&h* '5Q3@&# {n=@5~NTdvayf~TJ hӋs$bSn{buurfWB[]zQ @TCZ T*SA@M V@[,Kş?Q\բJ½'S\7Iy th54dt9"f`N AL&Ui  Қ M{$FPĕ2 4E!I[јZ`CKmv|W-!HǏ-nhJ=!nN=Z*ӹWiWqp? |y O/[!DPCFԑ'=|R<|(s$zI#lZ ˸~$^3/1Q(BW_06@SB Z"(AyQծˬީH+Vdh :;h`9cW՜6L'\UoFoiw0ZZ,5(oT mgp *=%tFㆶΜ@䦈[jđGS#yfG9J鑯w G=1hG =0g:pEO"g([yzS3 FG;< |I)<3HH:53)MTb /ŇU=LpJpUxƋexbAo VWfdfV(kCK?ᑱ顅n[7eܛ&2'Pjf"Tʪr/b_FpQ8~x 6:q5Q=,E|4 @I9[i6DS'*Q#,;;fym?6ٻ {op[<#cs+ Kvpzlqy( ,W+б3#7h4gjP\wNNgddXc % W=V)pz< &A&o=Uv݇ ZJ_<)3aX܊#*q[+vM[y'Tv8HV5\0#T=We{SĀGA_[=9>995 ;?_̑Fwc#[a=#7KLI[ٶ\iyzw8U ߏâ6DdƆA@H 4Dg5yH@Hs#u@1IZWpz3! JѦx0DјR[6X @Vm+LS >~m9,MH Ie !*I5TnH{J3/H %y ܷ\r?"tea%uP$qiH m i6vNDvS߸j5ZluلګDwD8VjVΙ&#>zjF`MCpy1 'έ18Ze/oMםӽZ2l:oNKRYW ށbIBq]*lT>#= ӯ"A10Vhj F9ԼEᦫnijiʫ"@iE=$"Q4Ғ  YK5C_3ߞ{??D +W$ +U@Y\VyTBsSZ+ipfB"Ei !Ғӥ5~ݷ0I6l@~ޅ^1vrFwuX-ο?&+/Tמ[{='GNmF/鎺ͬBtn/D:"0Y5 /yDB3(P9JVҵBAL 9jEѬ! %ST#EJ2F5KV5 Amp{5$@<\)`kU'Wu'{w0[-,@ۮI} g$|&Rm n @YΦfAUD3QqJiAXUy'&~4V@&n"Xw#CED d@%kZ,z%ǎ=ΡHR%bŅw_PpF{ 81$&;+A VZX&fKl *$;w,Q"DM!0{$y J/2G׀t)|[ɷL\!!0 1?R mHold w} @ C!ZThM0H$4g44+arT(kS+%/k?ԓS 0zDOnd-u\יwO\ C 4#34E+Z á5^G8^W6rf[lʻ?vRnM*ʺRIss~覸h4I9MCA hq@a 0]UED 1JC |J pA>Br ُ[nC03̝&:q ЄU$WDሩ U ,VUfF 73ԵrDH2cCp p eN&D'}T̎=H6®i'iGoߛyGsRu"s©C^4 1}⓿k cKR:zQU-6 h!:RBR-n1U(RڼNޥi@VvIDIUANTĽcx PS7NW^]X*ra]` 6N%TK^>{z', 3){f{x!#:杢 QcSg,Sʼn>ƞ:# He{q'&rĉ);og\% լU9GlQ]/i(ؿS $h:0Iэ nY^C;۹$*f` N6F5ΥHAy.ƯHdFFuFwap6r>T)Fz%˹ NT$TX(`BMO0FQYC?heć^4?S'R|>;ը7FngurXZnzkg_^?qdx; #O=; zؾ#z^54҄}O{"N? LS:xӡ+'PByFO.wc~3;t jld*JZ bSU퀠 ; ;/HH$7OB攙{fs4Z8E;zWJI@pP XiF 6pߩǿ|߇>WbUAdiD@U7-w"bb"ټ-5#(P2xmC(; !k^z@f)Jh(@df)-BeyA(!uؒNDlHߓ'z{kg151YVt B3.5KsN'Z `fvVD Z(TJr?|?+O߳?uA[w \tq'[BD PDE͵c~'B QUvVo8~`P/W\-$_jW5`x#c<`v'#?z:"{:k  7OV$U 6@UR1xIP) RTѭ0Lwtm˷ J۾l[=QGN}@p Q"EUΝ|O__Y=p9Ъjgƻ@(a"؂) b458$ Zjf%f37#&Sc}?l6f@DuN&0Q x c@UU@r@۵]Oy@#M{0]x~JcwFF/U^Q4Zphڄomt337R}wA$5pP<_Zq5_[}HwgVG̀*<׀/#U@ kN۲ZQgDþ#! q4F 8:0^ zg:4GPkdfiuxZԋs=r }`󳃷όs:U"jF9 'ȱ:`BċذÏYTa= U*(TE5T+0mήw@ NNQBKD; Xͬ*8iVAR$Z@A h-b@X@^zaDgW?%4E7`&@UjYBߛԧZ-A̽{$2Rdf~54Ĥtѽ#‘l*!;.FZZBE$`E`ŅCw:CwD1bqPW\P_XNSc{?v__idi^gƅG̷ 8鎛Xh) 8q`ٜSBdvݼS {F NLdvŻW(2XB'rW uA L2 YDT9,ED#o->@4p*̥fo*/u38aPy3^d|~ 3.) S"/ &Q7g7i._h@6A؀v4:cQSpAb(TDNMNr Xoey*0r[s2j0u@!WV@:+c>|-_E\-f O_}yP8:qt ]_h轍zA'D -ާWs R)4aZgG* @C$:$_)ILZaFD ԕ5L:TbX]T!eMYÀkx7i ISv"cL!cF7ψi 8x=dARΎ"4(:McRK5EKmZEdPXȋk:r{]g@̆u1N(lw(U w0wӽoO6{]\IG<AE>C%t?;}?T}Ѐv`B{v=<nͼԞ]h 49yvD{gc&01HTʠ[%Ͱ)K}sBAe_[COe;ߏ )h:yfr - Q2kz>*f"ME; W$R8)U+QϤiQj E͆"#P )f ;phm]gNA F Fv}O?;Q}j>=xW=w~9p؅ط<~F^Y΅uwK#tkԳf"ZS?z޾31&S@YVϲ[_'<{ai}?|3_zhk߱{?g[#OK@#>zǔR)$"4H Q+R%ffG$֦w cE)+S!Xl}^5ZXK!@A *(~3{o>dj[1Y7" j)NDYl_R`.pRZp!TT5@P5}*QbB<^h'a,hp8?LOjW; B@{핕4ŕS禛Yl;:?U8#O-k[ !""AgmlG(j[Ͼ; !Pe*@ə?=3)ȱ:PhtVW'&/{lrۨ)`pO>sNTKYLR.ԢEDP=ZV)+Бe&THPU""PHe(D!3v ԠR7̩h)!RFQm%֯"/T Y(D\ Au/UU2>o.pR1ɨ*%DO@ֵ- Qz`'|HYZ7g}N|9a0iNVIPr2rkwo a8Ndav~ p1 /ܠt"lWm*"}Gi]n3KǏ_lVTr b##Vĕ=i^9.Ӟ4eOD 78 pܰ4H\geju}~Z^>8@-j*V+2ȩ03"  B, \Ru?wO%^bŅ̥i ^_, @*ҍau f>A0v{ȁ+O3kHy-68S_N;z؝͉oni:?tn`4[^!^#E+y9ݗ4Poޙj)*J :{=0@y@3|^1-A k!kڌ "hM0s<d;ci]f"ت"QAoUC FNbBL.E EyGPy*Q.;jR"R\-(JZcD$}F[,5T'a dYOSU|m:! JQXf_} [aG欈?-Rgq518(c.IZTH#_z\8@ 4I%Go &a4M{! fB Ɋ @B P@ jA-u r(euvcvwgf A`U'\wp EYc#}T!g{Av=7"?.8,o0P"x[<{{8iq-yJ7P g>gÜ0;*xfpĆRS"Ԟ92h\?ش;[lzgՕoOkY5 ǮQ ׍ŝNtGJ̩gNH`'*[-j6©)M\ũir%*(wS (Ss8gGj%fny Wsrg{G__ P`f;W:곗ۀwx515Sە8g`DI%w;~Ǟ|ި-PZ2X<g !Jaz^4Bl *4yE zhdJ'͗aqؒk@*"*(Dȑrg bЭ۸مo .ȪJԩ**#EDDKRԑ <)"Zډ[-G!4rF,V]}c0v s+˫+@pAT)6uASڍauE6cP) _D% y((C{]$4R Q#@E窢W~ﳸao^toʱEnb#v ?ΞN9yneajeh .e~/w׳?y>t<6w?⡓Ϝ?vߡӿ?}}G9y`ӪV.'GP[DQƅqNGPP)cY6DXq#~[_BY[^9:#o WRFXFDSe"*BꫀЭ7F!W|Y)/7BJ7u`ʢ 6FPQui6RjgNpG4`'@jruΊQS9hx//=ӗb܋u4p@ d! tj\"!ҽC>RU?Gg2Q- L6DBK?/>cG_ۯgٱ#~ N~m{Wt8z|jbr}?}ںouӹNN={a|zaΡg6~O=s\<>ȓk{w;,*nFM T#VBdbJbp$P6 یL(A% + OgCnwpgo AA !NzcBpD p*[  RiF*lgPߝI @jrpT6 (۫xndSmUH}#V+ 迵Y]gYދ3Cw {FO/z{}ѣSm=qz݃K\ݭv;/ Cw 8zDR\~G.~'>0}O6݊>}v^<~Zge5۝p_'T[/-Ϳ8{C=7:셃w0y#B]#+CM87<1Dl"") \KU"뵔υ׀nGU_05&j+-chvUPT1! FO>͹3'XSEqg/^MӽٹNSӳ#WV=$(H»}ʯ(,B{11S_j6zvP"}s/>ph5 @GZZg'{+;I hϾ^~[X_l~azaGVw~,gj,k^\)"΢nShEY"4D5є%}c#X(Yp^NသԿg/`,;z;&[u~NjJ RQ O,R'+.s[{G:}JU4TڼsRB0Q -u=` c*o5[nvzjV6jicg-[  eN=1DJٶ8ή8rbC Aɳ3 aee}ŋ(RhZ(Ƌ{f'7tB532m k#0( O k_4%9Fb4fo_a_-E7R92SڀX#]LP:Rz^x$TDhIa'?.8,8 arDKBLI5h*9TQ\C3S8?7}!pJ@N6]NRHYj~,I4ATI %2JN^ ݋߼x?Q[ D[ÔLhmj+BvCN 5b50'QD xlY@$iB7tw0џXXZohN~mR8< ੯NHĽcP.g/9h $ QRңDbwxRts't}?>(TCe ۑ sס *Z28"?.8,cJ(*CTU4J)7Ap$.]/9џA&ϟM/* itL)UUs1E')1*MkXW4xN/jHLUjzWZwwV"(UJTQ1=)R(+T+["2a"C<8BooC|]E"5 Xp3oNV^l@nk>7[` ~N- đǟxZq+.{H;f^n7yiKK'xĉ#W'pdk$qcDDTP ҭ$>{&J@ 13WODⰶnSvpUox"0 UeU [ Eӧ>ܜU˅xf8BeScRXl6%4('QvyEJG!AI=l(aU%\]9^H@$Atr k(T=YUjۥ1zYjš_@@,+)@v! IDATk3Ӵ"*Ql`i+&0HEѣP+Э 0IDJ\p`֜ZDDo׮EN寯4t"-繧eaᕕs_|>?5ۿ?\Y_snz3mJ 1fVW'/5Zjt|,q*IС@a98'AUA $GL@dTy78b Z)WUJP(oxw)"BeaQ<ϣ(!N(zuG ]e@ͽpqSC+ ozAo awWSts5\:#>.B!< .ƞ>Ol tke% 7)(@JP@[@g5uP$ ށˋo2lS_/OA1pώ"`kk{KK}%j'>88|{^ Z9_zf+kN+@ƇP U "(X&yNR"EA Fސ+aR⥳Ŷ&oKb Th CD'rDATIͶ Zd7P4cb'fW{OBXDBIlpC gN}}3/XOvnj [iɋSSCU+d[wM-.6EUSNߡfCY "ҿSY72Z>F:3QUnw5roƎ<~q#`l򅹱 =~j26ݳq(7l ;nhdhg{y9 .u:V;]Yѥsjuw։gvֲZv j,egmi^n?ze5gٮG؄wPQa:!%boOj! P80gffRqXk)ҁaQUh# u$a= w[뚒\, cES%dI <8`z,h0QHtf >3FN*d|j A@)崇4Bb? 6`f1\vD:k 's58p~bzzn9k :ڮzՕlg4֧f|KW sswN7챾5亠FO^e ݙ}ʚL}J q'WW,FHHU2/A@3js64Jp@ҫ UR 4tPՑvgl~ nRNQyB€E aXUA {sD*/iUyClư7(?JL%"pa͍ST!x6o{ar?rcrV!ɣL5K UB AX\X戁zپ<;3=542 >)QYˀPaqlFkrkuUս / .rG̡WVW  wk*EZ* "D;&=uXKٮL"ްNMP7t |K͢`^>){@=P%G-, jtIBTy|OAlrUo| ZksG_E;{Xچ'~lpXZlVr誠 n%7HR&|5\Y"UVpQ Ѯ++K/H+Ѭ#(G]}^196}/@䈋v{WZޝϞ??-4zGzf.عK֮ҢAv6_\ϾZiڸ*WV:y~tr}vfxbC< 0$U*^EL0u QG}n;./a4e+'pl"` .,u̞ p Db#Q@TM׋ 9$_+VӾ?8,JzX- UQF_P7X[K&nzᄺJBT+5+tuWM)ZFJcR٪kywyV\űiԬ#" la-QxA@T074tG2@0 lqajI/0@`OمKP(|\'1jȱU~Hj^"h)""|BD2 ҽKKPĄmǏ?=sԹ,k{~=yf5kP?wc}Oɲ(Vk_sYO< /^_ q+D  *yGm9MS℉㮝V;\J-Rap&RH/kk/ LN([X+JpFWpGAI$0tR: ^ۿo=^^\Z&fg.|BV9,#Aj9{`>1yO=r 1CIY7kT( JP(*l5,otFS'OA3T[lIoo>s~Օ,=nhՎk-8{q#w{7f~3KDA6qU=(rk AJEfUTg9]jFnkߜjyQ\N&N (T`8ĭRnY]LxrТ("DUq0HA{{&PPm _gWO^O? w4gOjV6 KKK7ґ(zk54Ҿzesӛe^^^W AT=i%bߓ313He߄VY]ȦQU"Gzw+)rs u]˾Mp>’0b(Ua{ʼnHX{:LͧG!z VQY*FQ3Jy\Vj˕JeRM6dպqaiF(Ls)a F i,&r-_~Z- @DM{*:EϼD[0wUhP]>\,5F"%h.hm/,7K@͎Y~zl] mqn*iέPw v'|l =24c'!"/rGw#Ã*/l b7~Sk۬X $%KK3ӭf=bf~ 2pӍcϮmY ϟA"cO;?ݒ,RXOH{ T_BW|y.,r׉z- dK@ \'{z<7;ʴ:ZEɨ[+.#|oeYҺӱ,΋kH#h ջ=|/E0eT$(*ztmC ɮ6cYv чn zAWeH_>Q ARʏJ`eĬ@YT .hx٠3\u+cxe{8nH(7 p˻0z&򅍵N]>ݭNifա<844$ͯ,Ϟnfӽ/1xz;o?\PS,X\*N 6g;}L繞|z;8@Z"NMr(x3KaAryFv4={˕Y\Ca c>߀3+7S"=^ ;FXnZr8JCV&"4%m:2h_Ȗ4޾=o٣Y2 zau/ V 5EtEg# T}D0H.LR ;[(@*j׎?؉s_;z4~cEЏ=q)w6v7w)qKwˇ~׏?uyIݷݴ+hO$y1f @M@DJTVfw*qsIdum42(X4 R\"f]S Z#EVoxzznݞ[ yI x4ј~f#fH<{'ьU<<Z;A๑Kar10YL2&|),@E; vUB9Q|hO~~`̢|g.JP>* P@L@%J^!#ox\ss^Xdy[ʷYK UHD$>\_ 0chʊ @z 2-bjv(N DF<75wgsA :ͅO2vT_rrH/yEr,vK;e I;HFK(Pҧ*AJ#5F "=Z.0h kS yO=E=̤J$6F,MIrcGf#`7^5P 8+EagL|/1IJcfU"Hawq 7"\xeU&G@=ߜ06XYsW/`BIJm3O$ "B}&[[$@t_AS3U@A&.u~v}w|?K6e ohwG*䌾hjl.ah4w6-Z !0DĢ0߬A4\<{{#HĎZ@Y]IGmۄf%"$dž]d4= ^:aUW!՚I0Xhg/"= .4]ću}g~Ufj uW? HNm8c~#x R7aBdU!A!ةA}VH),`q`=W=@MOljj)"ԯ P܀]+T^a^ωK5E,sUEW{ {}$}0zj$͙eznp{#ӾS,cm?u&u? L hfg UA.nuÃ|sw;i=tGmhmmImi$"@PAh`p@2y%\(;=D%:#z~"v9UAfL!dg !A@{{Az,y#%N)I7!"]DE\GwfSR!Y|G";$r%~zV e[1UveX 3 vs A5MbHZo h, =sDGK03FZ㧧Yd%Sߘ;-Iae㧺6̢/J[T|:yTahvZ JS, bdhO5[ޑ^/!btxC.<4r]{亁kLD !.+1KGİDt@gbʎ B }WrC,&ׄ"I ]\yXwԠ&*}#ڹq`S~d=ӹz9F͝ @ZF==w_DTwD׸ڊ1}̩xAqF4XУqˌ(k~mOvȹk}}l깯/I a%RWZ^}@ 骺?) Н@ru `thh; [wZG?wT:24027- s3cHS;u4e^[kvmZZ3,Z- Y7VkA5ẃ?ujv6z{M]?s? Z m d(H֓}X,U+^{%qX@?*?LXwBHXb&u֐~ $_5AW>D <5> X@ `$KRA%0O tኹ^>K;xDd 7P #׏K]Ӈ'i-TLVIhE4v )$$CphlvqoK^ y3=N|O~XDs}n_q_ݍ}}}kޓK$HEvG}3Kk'mj%@c;j<}쩧״gf]Z"ᑅٱ>X,E!E_(I=D1wUFֺ T I7Ӵ [xIlA{i5}|㇉Jk)@"4820`K'Ce텕'A\cXq6"-MHe.92IG߹ߓ#8h.\[~kHKd%nWP$ !=l4y kGřI%w=71YXd2cMo"~wg[E(m ¤mRthjvQj1R"&z57 =ov螝}Ϗ$Q$iD e( @"&&X=4c.j wI܎WQadT!BE u 5p+F<+?gsN&*M9DHK/LU2zj(zj?,}H"صbggͬ-}DRnР]%#QH"#n_ IDATo ƎuUѿ9?8ԸOV5p::tp#OJ!*ږP⒴?0Erhy__1=?#^, A$5B1@"o%I*7ݭc4%0g=K B7;,zѽK)"5aI+U?28,@n\f MTE{r/@v,ȳoD$ɽx[TQUD{@ko-Tk# T,mWR]D!LZ7":Z\p"Pn D(+e6sQ({7GNh6 9{~fl=:;z7ۦ:7ջadȠnS E^a]HnE53cn$^MHE'AgšE=_TV{Nj\Z]G$(g4"VQvU]8ky@+LWea-:^!"{% H&ͲSv/ǭΟ\%K  yFYf1kd%BN !@cmhTk өox_N$B0Ln&TSlҪJw "D ;0$WQf=9=?48sk{}Fމ9ok]'?R/ES$]_?[!˓ t%nX ߽ppn>Kp˳q=4t&gWē720ea3Kg7@u&_ @I-)̭_K1T Ќc^C@#긔H'ϳ%P{,B5 Isy&nR,j)?z)`x&jbbbem1#8W_fg&3ƕ k@+P;](C@s1Ykn)z$:R\ZY~|w x'71Y{Ĺ޾VW?1 +.I.}{yKX (Lѥ3|7E)Wv7W. ./ 4{PV3GPp3Dtoos_p?~'>}?+KK˪As}{?rvG}p|||yz_}?{Mc!giaa.[|/3OO16O}c#}{ %%$fnR`IFm'DX*"©"Z: )%xxw_F2.IT| 9CGF@wHOӞݍFS2ggvk3:/w[[@5Q҅D")$$52$H~U__SDddܷV>}`u4ZxXۃ&I?̐gyQ΄Y=<r*s]MLNfV^_$٩85;fzϟ2Y2ٙ?1vams*8g&ZZ@^4\+2zper5XcQԧJr&bǁ[n_l9\9N3"7o%^Gn,r!a7nPcgcbuY[Šomk0+m ^"E䔛ԀZI4dgSXC7MU)Y @B琪j:n%NF-@r ũixXV#ᆬCYafnndN8;ZeNǬ V:PwQ-Hyvi߾i $b#u ][" Vʅ8imYl0Qɳ%0VK1<E>yj55L*bI@H[$%qX10m?*8!D0LsI*UVZ@D؆:y}:L6?adA JV(+ QAAz1VUŵ5CdpB7LGwn$ۭww/ͳƸuRՖ Y-@D`RMR}DKDo^z~ Kb{ZugSݭu?ʖqTsyU)QJ?;_1^?],;/wX5l EܔD BsYWK95DAs)GY\2S|Ã_}H.(+(y!@ Tsu²|X7m^pUqgR3a_ڃ<-[oȑpݰ @aa)wGĵYZ"s KaqI8al5GˈUyaUŰ$(_sd$ALE?*@$@[;tOo_MbGq+k?oՍ[op3#C{N==/m<;{Z k2KaѾdy:{U쵏׃zu1}b)4ʬ2 @,MW2\ɕ`$ VFXEL2eI<).jiܽpW3Z@t"=9 )bE xB8_ ?/_,{c@:}`l@GP^X;tHcO]]j{s-7=ynbcpvrzieIwx'l趏e|ϸ ۻza}uuuhF335`HSڅ =v{qs>w޾%]oukDfy;M3/_mTde_ 5 h_F !FqD BYVn!"1YҁKFyXG/as5nHFW2D;RG`:{xq+-^4G6)雁f]"6K3%&@wUBʦY,zr$O#I>ufn{LO.H@فb~qumTHXx3,>t7k4 ދTUܒVC"G LGV/lySKL/}wmpM4ܙ}#O>|huP["ȁ=fu#ϧmk4[hͰm{g[3L5@ 87%QZ L㇉b$d "A4SURbG{0+ëE|߯>W0vh^ @{4\EB"Ct:gqiE0@"GRUpm ):Ο E ?ֱjY$+jż%l;HCZIarDTx}}XiQc\`I6BF 2F3 o( UY_3 ;yv՘_XVѼɊO~qww @Vo䗧o5@"s V7Ξ[_q:֛N|y}G־֮hOD&@T)TRD$SndYi9%Q33*jĪ.*de@Q +Kkozd#W0i3*3JQaPXDqnI߿wYr:;.9/ؙ;oqa}cr`]OP7=<ו:.fj$B_AvghZ4cu4&Ys" g#E?L`"C0^] iDUk$ŋ %"0zPj"44 I7 лH P4[G[2F0f:<^z%)+H@ Atanz[<+:Uճc\ K* ҝ23;7_%%bjfn}maFdSv jp T`)}@+Kwo H(4vXK" A|QH;sxH>AB ]4Pn"BQzBR8,.\նzpU? @$i(4 xÌ$Ok-H@ABzD3MENc=ED2|-a#54ɵQ_BDQ VnlFh&(FL,B,Lk-ca9^EHH)DTFw/HT& [5~n{%}kyʪfW;bgQⲦs1~$Yuj.AV.pQd bM1:"0s|<VJA+ 'F!j(J~?$$/X X ML Ye]m("Zw!r@ ᜶ xxjGMC%9"eU6+u7HVH.#uko y*m,+Ϋ8 ޿1^D4Њ"-Rt&/hA`\ EA *utqWe5e D9x@ dfFoM:PDw2/L' ۼ?ƇU̿_eWggt)RJu> (`!٭]L~Ho_/<>hWu5֠@Z#B ,ʞ% V^$ #حU-(;/9}h,_ g@嵕}m8]` BZ>aխp 80䁵{˧>>~ }{_ZqFdK3_<Յ>d!xnu?QZd`AӝWVnyM v4|V#Fh;&lF,r-RɅuDD!n]Y$;g@^ QT}fJ (0|X)?re* A\ H*{Jd:)."&! pXw׿2qf? $dEx+SZ"def/- IDAT8B $Z u`/I+@5&'A5~IFX aE%3K (aI>U~'vRTz{[SϬ^kGL7X9{k}]ЃLQ W4 pxsک39~R  [teշg cXJ"b1_H=jW&B@sP`ߏ3)G  ^/!*?a@v6SrПU%"A$K +߽L6?A'P.е&p2 *Yԭ[^i1d\D3/ ̈́'wj +:fJbr.V[&P>VK6Uy+++K͝͵nݷ~쯟*i0Ɵ_!kt٥Ωgn~K/?5W@J3^Ϋ̞˂t_YWke+fRO\Frdj!EE`UrpMT7K( DɠDD*Lu@Iy {Y4!=(riTpXR;46rb!->f뿃7 t(IxF"Z GJkJKy!NaHJ}omiOJ2_ 3-,ٳaVnm]m$$+$0B}@~i.n<3=}}}g:si6g%@YL,8G՚M`{P65N#%"s^1qYnLmqPE5G666ٛԈ<7 pG:r['`rzNᮒq_=u=jX&g*]^-h}񑡇y?;\V^XWVzt4-zTz4U:?rvI\k/ՙB@K")}HG!A%|BvH5ze33I۞E4Q!7?ejэQ iqMyZ˷}ȞtN0auȨjo-0rhd,ϕw.,-.QGW@ IA툍Hubі'vj22E%GlmWQG\x^׳ E@"D{̈H^/70tCS{6c'}hrfÏx]HrC?z݇O7:_=(4z}ؓgqv,B;8vz#9j%rSj5;>|ZwH rz I0b4ξa_Ё!H҅3ͪ#w׾!R':=9 = @#9 cD# *"mNY*MKګWHckB˿NN],OًW`- lA}W$"`VU7OmT4.mjar#5RHrQk{=F^gh2"*6/|GO>)XB,D2+߬L0lvI):X7ZfH`.$H^B=+#XԽ0?yII^h&^-AH1ȱnPnǑz~ I6è^ U5,& &#U{PWSJ?l$"j^ rɾZV7ׅz* -tJx9?G8,9tZosW+~E-ui*#@2Нt m*@o)&ٖhD+Hl{ N=4mMӴ!XC a Ɣ(uĕJ2.x73d=㡼^xXFòX&`!˲Ȳn7"wBvy4~O2@`B4>03 a`SKPj1@4WKCJ ś Ckwxo߼ǟ_~ GGwW>)U06Xh rQD "U(eJl$ CYi3$z z,"V c.sM8:.>8ѯik}?gNwѡ 'CGЀVW?qs j: L UE qAV%|æsx%Ua @tc3QD5 n;Eu?䱅?}~ЫAZQ$&I5'9j`4<5s?O~O FyXP$L;h f -En]^q\HzJmY.Ebq`y\Ds!+DgW:!њ26~2(N_$$2={l29EW3 1hm;<4\hv Xp|vܼylPmi:VD36mtuɍ*IXTy _WI\~DUUd(l+EUl`ͳV9 K$vҼ,pI *TA/,/OM%-mTZkȄt Pj.Y.YEZ ֠PB}n~-ߴqqH O§$t; i} 9|e. <@ $c HOV7Q#H`ZiY@*R5U*Bڛ(!0X2 TS@ŽNY' ,-l2o۱@novLwW*;?"%\%XATœ D23Z|d<,J )8 5n|^Vj?s oǁ!4 D Vp,">IҙD4 K~0l<^CAD{]g!&B8WUc9H@t"kTf5s^?"<! 7( #aͲw$-W?: `֭=nkt0\<&?nfTJ;4F&]JTFr0,Q|-ZEToΠ6btߡYǏ`財>;>(}r:)NQ#ZDFt ΛMҢ86,W-.m/-Dj@i)TwݨX8a`z4tK~an=@s[3 -oQdsCEUE76jZh%"#I, sW5)1+Ds2<%%*D<*އ WoV92@4az[nYXÞaY5(׉TL\["H!(^d#Ai%2$k&:' -Aɶ桲kw"CU=DXVo`"jx,x <T-HY\4EC)j DH!?u Q ^az5WϾ7?A< f"ϤjJʲ d:]B QEɧTDfj*b^b@ !9JG͕ijXm2hx^IJVkX1AJydTiI*m-xqC'5@D" @0kzh,"4hj zu1ҾUsUpဤDy2wĎmZFk=ura~pGY2fZ\R@<dnc h#gzAN@4 jh֠`OU~ֺEH JXr%G05u@u(**)%/ȇ%uh0P/G^%O".9 {ATT 9b 7]8y`!D3A⢗H V @\@JB)I8P@PԊ WT/uh''=s.t#hO띞Z뗲{p&RlUVS\%yk@9E"g"a:tVr=,-? 3GW{aW)DHDN41<8ȃK7M?84A|( ͹_ bxA]!ymd4NK@ a0!9|S hIgHDn̝.{֠3^}5qX5֝3} 4R$Vɝw>q-ynzm h2^ hʄ^X>VMhEyRqui re;N% / 窳?[HV$A>9- @m_ZE+_*`~7HO42w8}y{1G3[@_Mjժ+JYVVA,WGT"*`Bܹڊ|S (phɫ&Dȱv{D pHd`ҋAIHI4| *9T{4-[Y-/E zUqX澅t,b33ߝAtje@ Mj#CU*ɴ\"s1}Vt.DvnEn_ZbL~uzĖk7-:ރniw޶б;y#ڱoܰ,Hn 3EZ>z|Hk'nsHeB 'Ju)ػ߱ԩ}ߜWa4bɅ]7!on@@LF/)GC'gm~~&ȋ^R~'62ubZ䒉@_KgȢ(K  i,輹s;glpUuvO } P Q1,BQ08RUũ\e@YƠL8<^?蝷 cݕ孓::azE'5UUhJ'&k+:_?q}^YËH.UVVV!JY٪ML<&,dq׏[~|~Ѐ9}fY\=~۳[86=}HGPJ&]s̎swнi,T<9=yvDgAx (P,iUD:ǴD"A3hʂ5%@@CCC"9reeGgH鸶ZY0 Ϯ`q蹁rhf5$  !x*Y|ުR*d[x{6oD zߝi3NO z{x54`b|Ί#,OsV_𖟻 /"v@D2r?@TJwepxgW5 +͛'y#yl VBFwo_ZZnZǾwrӑ3ϔ7M-MNN(6 B!B0 #i9Eg^ZYU2Z{C ]e?5e^>7%h9~O[\d'"?['L$KLTNq\:iBF@BRh`}Ml!`YuKgm(gN6k./^Gn x:ffjM(Mj/-d,ߞm3sSLiSw9;cŁ#N>{۶M?[:!Di/7OP$KKnCU*!axh۶Ze-HD{]["Π&*G׷! i-(87^tJcLy/Z$R%LN+ IeH@r||F [DR\" PFci/x!TU %jjjUH |OL$)A@, HhZl )J}ؙ! @2 "y*U2 y# 5/2pwYY^fRY1VSE UC RJ5 q$~8"] *V@F a@"c>*+3"rs54[7$;<>]oV-)5N8X1wb~i,kk(k[IM,jެUٓ p0gO.0@"ag!`P>Ud>pc $uhB5)NKVnxzW aX-U/0*. *(ژHQU@!`?맾E.UTHX\Ӄ)5LCnK!""C9ӿťڇסS333y6 hhM׃$@3pxJF( H+\ H!M\"s~ Jx"Y _~fy/Z|<8HT~fwfđo dL$d/f-Wo4Уݳ5M;l||GKe #2gO,erld* I݅MӞw!f$ٗ#TV+D6[5ZզM|ߏiuzXuzoke4oP56 b`a'n M k.໐Y*4Uzg `\3Ӊ gfg,'ߝq9':R]] 6ovLD>wofcW=~`K-trW@_TSҏtv;d wx`+WUPIQUMt`0iZN9[qu@$8gV*lXZvS~ܡc|S?\@{b[$x=rtG,"VI(̧~4PqJ@0(M*@2@! m%zaF3""! ȄֶMW`W9@r6<16NGP֐Lӓ TL̠s%@5SB^Ktvie#dlio63?8ёֺ |4>2!}w\G,M_8^ }la [*5XbD 4;q@;no[v?}Ξ<o90SgeN{ ɇzه~{ yϩaʹIQU4ʲ摌=+amNǞ.^0*iJsi&͚ r%؜+lT)8i_^򋩬Ԕ9"0IH.eBDXMLyå-+eQ,=&:WL?yro|`P/=6\ކ5fZ ^l#SO>+`{p?9ؾlhw] _]^jۗ 6z^9 m[D[St5[H"A$HsPGǰ Pܖ|_98P8DrpU/xҘӸJ P뀞:tFԁѱNg+G7_Q}8+F!a}/9-Ϥ-)SB<}/s;͑j)R`ߩY^1)++G84v^BDUK6XPdb8BgNn_6<~hQ'H&" FAKIK)I8 r!u׸^)yUqXεvbF0733r!wϫu~LO}MDT= x&șCt`եlz} (qbM0F}nj*A@<+WWղL"$'_|: IXm"^ȋF[* W !WUds"Ӑ4%=W/XlJfs'N_9b 0+[ V pV@H?e"j"&" Icy5qXʪH7G;4Ӡ@њV?ˑ LVeUiM2)c0Iw( nJ=*)O2===}Ne:5=^תV3ӧ⧏Apa/IPk8wԡ L(sdn CCk7%4:8K~s)06iLo^hE$̹xHw4df$i5sM[&,:@wM_On晧i"*7oL<& ߖ$-h'5%@m'{Ꞁ@T ޒ~̀$Rc!\u!y$(v M"4 T3phUz. gZ }C4E3x Q2P&V܀Kz*o7J^Jr`ywiHwbo4{EԖfJ-Q0b@')ɼ{dfөƘNd0'iQ=`f)6z@`_:*3'D:O&?8d⑚G|~[arI:9>_W-ry;qr"jz^j%IЁuʕƮw|Ӄ׏Ѐ{=~s#KKN׷>MV'""vsOe > a7~mT{AW=`f>ͬRD :?lhNt6 '5x´feVVyUI~#׏.VZQG_FjS%)$"Xs.kh0 ڰ>9ΚHF>9]ZѡvY@9?!",ڒSEXL "%`~XegXV%zNAlTjhHP+ U]2B%馞wbC޹?nt`H ʳ}yhy}>q[o:ɟ8 2qbI͓ f R}v;N-,1HwyyߡvLwFu™ŝo;Wz}OL8v;w:tˍ[L8:~;߾mjѝ;]mˑ?J'*ճZ*Bw"<&聶("frAV?7+%/WsWDU"T_V++ݟ,fH`F{6:Jl*,,̮ۼ;o2N>3:: @V'kfN+]Tyaf""LS)*{4m@ ߆z](`"UH٧Bܵf ݷDdžۂ,b'K_h؄ 6jŴ"ųg>QEO[L eYaAU48ġB+|huKu| >;>t>=Ҟ]Ǧɻulzw|߃m$m-W#~G{;/^+FxJ@e pUKK3'۞Fo<~ƭW-"[~ơAfjaWw&' r7m;ٹjߺ*Ҋ"'1~!# Bidb5WQ<=w3.R1Ze|vr ׹²UBd i6= `k/k:N?jnv I%{,DrȍP"We-Vڞ~{}☗=_=pǭ7u^]_?xtgάtj8ŕ3 gt/*s{D$䤩6pyi\y,Fi_\im|G[Zn|$=_ˎPsf#;wlvL[6m:W?WXU+@.TRb nWJ^/5wj0)'*SY "~Vk]w>^D *̏#/k8e&Zd9Bd#p_Җf o91?:>\`Q@{EŞ;ΘOeIV g* Xz`fn~tl4#i(|zZaŃ"Q ɵ{0qX-a ɫbYduJG+ۓmd?kc_ly{?{]EeAZ BA &cr^ćD :h\Jizk*rDH"skiUGҏ{i{yee?WՆ>u=' /teǶ'O../}Pgr']ZLd=5~c]A5ܿx{--xi5DkXMVCRi0=8٣dml8.DSdihR 9@IН^qES7#ݝG)j:})+od`=+ .ꔣEJxI)H&u 1tiXTE4VDqhƾh>+1D|\e_'J2"T8:Vjy2N2Z*"H1ʜ9E#E[,m g9j*\3޹z|܂ߠ3O#Œuc۷mOW2Y櫓/-A48ч-5DQ"/òr5BfpXخhfU&FD#H.P@s3nM ) 03‹1Edr0Қ!7=>\M.pO?9oߨ׬tٺyCT]VOH'y(YDibR֥ƮE[|J"M`\x!C%pӥ'7'@٥BA$ um\?D`|rb`grG-[v޴ vs_yltWۯҙgLLϞ2ifk6v&'\f~]#j!=H+i)&VCI#kVWD*qxA+7{zǛDD3g$୦fjPyث#?oߚPԏo!IѳHbEB5T5 IDATXOq0@T4(sYYt:8(:&Iul޼y]ܜ~֐4j&fU5$aLc(=obB e|b\Cf5&!+F\mH{@dS~EAy[:n Hg@+7lp6~1 ͤ=nov\yvnKDnf'ᆳ{84:6j룿'5/mg>qoNI 嚉ǟ841<ݷo|`?y|~ak%:UqAI^.p1L{┠|563͈ o`2ը*\I(}qf2WD#D|G6@@Slxt6#=Q[rgA\9775lWrzRrD暯E U0Ko[Ƞtd13pz @PUi h30%,i|ͭut("r|ؕt;#I/,?cǏ{l0-Z~ӧ/Z?؁cv{n] gv7/3FHP =70-W[;N5B̜\7lΞ+!*KR7FBhϛK8ƍ[繾.hC MNM_CA$fe_}M|)WB @nV{' H'w;׬by1" ME܌6\rTJ$=ݙ\V׾d'˷>c BV[(#$y'v6 [Ϝމ<+Hpw@VUUUY4 p%A -z8 A.BDqy| gdk} @vݺs6l422{E܊Kڅ}":,io{ pUBH%K9,9Svxa1br! ӬVU"st }O_ |OꄄAv՝;si?Bnvj]1QP (˦(]oʲ,Cd&X0r*Z! Y^[(J[M||Y9ʖ޻~;99%mXFT͘ם,_l8ʹsKƂTfYS0ө01>78#za婩qP[Zn)@ ("ʵn #,]`a|c esɥe%f@fXӪfy}$5<}ڵlS<h>\$gD/[0ٳ8$l#kP 9 (B)jf8;sIiZūsN' )p'G(FϏ ^VJv9.]LФ"WJd M(W>Fؐ3D5}͍en b`f.gDUL %s}iY*᜻bC!UPv>_ݩ,B#jYF;BPXx n*k3钨n&SF̤.MjD&&aZ! R5y\!ZR`sn ׏v>53@G7mo`޶,,q}T{:Lm;oqD~vD?9͠-\nD1B PըTV CU(cJ ڈVR *A% V˴`loyES f9%&j!= ^1444ĜkyVQ4Tj'zs+ BcƉ;Q`as" x&QӃ,9/>G3(h98PO{@ k7^ИOF ZvJ{w"$rծjٹ7mЍ(EW_~8wގbZaMyK%bQx[;p PhP2@{Ž~d{}'x;&'Ƿ?[`SKϏ\yǭ{޳k=ñoݾmddO;~wLvEA @8g9R "tO=-8bNCesg&7zn6疖l7ҳ."z.t @PBSy ϋ9"AfRؓ2!" >rjcvGxt!+i˵~rO8i(FH%T&(/߼Ngϯz/l{X9H>S ˻-fvlڛ}׎fIfҨ|'̳{d5CCDDʻ_l6Ao `ބ<@nJ)*xX((}ݼ?굞IE#LUV ՐRDDҜ ~ֶe駎\8ߨbDEӃ+C2*)i,)so#G g@7MoXz:<~}G-@ˑPlhzN@: K"ϙi{o{},N,3pTA}0XEX=X=/=E2PD1q>3sq&}*]hnl|陹Ry`lycUUy/u{3}v>3_ǩ9Y+9/Nݷnt:V^)}Z*+ʨe+*e)e% j(=Ϋ|d_YTAQ)UntYb+zhU]+yXCW.U_#ASha':kЈRU".h\_7Y"U fUNDq!8s+cڕxnYgn@7&E`Y4b /bpS>a݇>={㶭 =_=85s{w#>>͏s.}Ȣn]RPlxeo#_ɩKNЕE X5B]Ğ1hqeh7@@oK_Cfy;nKwxxu9h4v^8ۅ{81Qyqdh;!R7"xOQ>`{0j^EWn/a_YZ%47Zc.UCGxXM}`Zҁ/7Z33& ,Lf,@ʪ\g!_,Ji6 ,՛B5bCaczj{tۮ?~3߲eLUAe @4z cv)rMCpYPWԘ\v"VJؼbp|?ʿ}l4 DH =JHǴ>h_0bTd!Q*˵.\tjDHWa5ړEcW~^xX <U(PM)Ն\DŎot:<{|cm|SG/=E洲-a%!:.v"`zvެQc3@1 &508c~;!,= `m׏MTz 1[̈a5VdȓJ,xXӈ鵶WZW(@̜LyyyX)ͿL,ϙ9 A`fdz` JE{ff)UflO^ "]C 8N^ʤ V1(tgT8+[̷B}:ĭϜk\=x 0sUcBDJ"M}n]뷏D"RكHWAW*(Ο_|Z#-кcg)bc6[{t;9=3?=;玝fff5b[#bzf#yO<oWC1=;w{nC#upǷ]?'"6i`6,(NI0%רRƶ( t*ige 4dnU" $T]16B8Dnp`U LLNLLN=ԓh7'fhON)z&앛dPU+glZI.=H wŃ4Sp]rdl,8cCB(oVx|;vxgԶ{wOO?S}w?'i޹h 俜@6"ny <|Ů;_ F7 ]MDV]MD):*ڞ ^k<F׏-A~ڹ0;}Fo'ރ6_[x{Z>9ȫlYp eO$lŖ]DKv,e .J J8) c 0&VE-PVDP#痖~J@"&ϵ@ L$vmF]gڍnvRKJ%Pը|Q$2=waV,F{ϞE{~{kZy"ny O>~(CeǷ^?;/nmK˫ӳwg3##K /oCFIRPpVF@Sݩjccƭ?h6M"Z`<dP깁<,_=UgҨfoUUA DTKBn(Dú]h ?8'z5ƀطJRPh?Y-Ir%![0!(LIRPY^9RD*)2˪J5WC IDAT\/Z|ǯxrߡoy@/=}-${L~}!a.*b֧_hUŇGtgD*PqAP)b(V4DֺȰobu"GU67`,k_= 0++м"kyP!ۈL"jh6pzmKTf,$ fDT#r5gqya%:̵J{2kXٴˡQ3s!\o}ë 2>ٙW_7h[7-/_n4HkUu*/vE ΋bK/uŦ`7 ͼvwۭiQS׍nfcSP)ONnyK뱽/,2P rM{-uUrKfw~`A] gLuR\IQa&1W+[oWEfFѰj7gYMbc!krB=ȡ+` Qf(0o$y /d (DMŽ-[naF޸mtxxS'G53赣 `gD)Π־u*ޏt\0!P>sDA/p8H@ $@D7ˀ5{wxGm3^mq\4J?= H<UJW_j5PK0 +O~^?|L"8E{Vڍ]eyq]‡,.HL9QVj$I kl$ P9=ur611qvaWG~<]_3M/b%&kk|Hی WFrnQN'p0PuLjgZD??AUUmP`y'9U @֛4ZʚXܘsk+/QyX X]E#@A uOd#wWNlUUwpߞ=t~=5УOut/4zm{fNP(9& `wN+ϯ', |ﮫS_:}jwCoj0sVŇxɳF~M MGw=t!PZVul[>ܽk{ɻ-H{{G[>_ >fȖ0dZ؇a)(REcn? oQ2O6ڐYjC>1< ɇVO~!K.tn|bcAp R hT-r5%km,;w֩躬 ZUYDXyՖ/DUYTeЂK"Z-[dž[[ѷjN ipwN nj^xX-NыkO?v t/U˗JU*b ajsS榆 \sScS~n:poi_ZpQVRl4Nmlͺ֯/_t:x~=՘:v%%eLuCHQ@dVl8O%*h|U5~ ^k=֋Jl8S2;QfD䱯}oFyM(T3`Xt1*\%D$/_bQfYهٯTV/,=Q[l`F^DJ@50=Ehq?tа4ߐkZe[TBT)Hd+(@_蒙38| ,gbdDHP9"@mrbzf#8x{wv_mjPԶQڪ*F RsS=vQ}ji-)v9.gsg("6Cu;CNzD鮘|kBvU#fϞ [ &ϛ* '"0@׏VKJT Jo=uya/8dx=hnXQ HM&l^>fc2TK !^e(i UM1i s73R#/PeW3sRڱ[W^Fe@M:F~MmЈׇϝ~㶕pzv~zv~։SOp~bblǶMmxf B"j xNN&w<RaBFWpQ-N#|Cn_~-FPɳ21^yMVO}&Qz+}ɽ]}k +wVkw(Ɔb山wzvqٿ=ؐf<2CRʬ[챨 P]lҼrxqa̫?Pc}t{F n# P 9e@>c{SgNpaf/|>}mtG/Gv7_>{{-O#Q->A1išetP%"EG=7@ ZfZVJArp{4t@}'w?_nɮo:cþ %su=צPtEz ?cQJ FcvnʞWEU#H%D4bhpwOT>cvrl\ꑩPI{!"OQrS "aLgngUq ,)NF@ҋ /^i9wP/M`v(v`>UjmCovեi4R '`HDRf&"PpHTzͯUOW_݂ws[Yv}T [eY7n?wnysSi;Gk=>y~%6`U< '>t/tJ(br_#B"$Tn4fkxM..,~vg*Tp@!>pÁFHN9@7g=AlxFFZC/!jß7 ДƠ",(}T7:(L;9[|0bJ1n_~\l Q À97/S-Q lL徨|KDF &*Ǵ4lgEw=jDi`&=UDPuK. @ʐ=u}96Q* %j]?5F.zsZdȍ1_⸡  @U< jD fRD+֠3(yM "RzK& 5$L c Dm1PKg#yS R0jl-*n3 # eم5Ԅ,C  Gڷ`#g9@`1l(DLe{14P<-NU#DVQ \!H۶],$>t0ݲ 1]/VyFP ":rSUlb_e h{8(d/P:/D=IXϳAWx#Qȕ0>(rE  &Ixl:3y$(4;PWD?)Ϻw3(/ضDRpÓ^ܒ#`@TҕxyFldBAfѵd` ןz$+H&"BHX7B (w)5B ut0(9l̙rklrd[/W|CJzqu#Rr.zj#D3*44=#I 5(: :IW l_ z 5e)>z9IpuRA@وqyo?IO{4*UUɁlGtКD'֚e7}-"lDXO.aL24QQPsw/4x5{ p`i:4g=欘qyq zUCj:ވ&EMkK` V`G0L Ԡ<BjaTEԹkå`0z;q\+RY}eP`Q@/+"6 >L1%=H@`v!{ ]l}^<1=h,A0qrP P+? pE`#W?RшGgJed` ]|G=Ac&AnX ā)&W-TpLT!X)ZjL[}cnz>)"1XQ V[r M([6hf0 52.}t͍UU2y`? SJblɴ x1WH<0h[e 9tC$zE"2tq ~= Dq=5 Wç}r1_ӄ HNUXÉ0b;p}uz3QIon֫ul FB>4FLM%G!ŌΞî\Ljvt""$y5 X@uL!"h՟-r:iY| g~!%N ~^]ӱı^Xs1wė67}m5у;cF?2k_6/J{8a?D3Fl 1?U '~F6ԁq( Q,$q\VU_{sIv)ao`aygf)D؎4v%ׁJ+9x\I׌j01;@mz#3Iv9dh s-lڸ= dFan,ld@in{ 2Iov,}!sR^C|s"݉p5k4I[L^9[/%iU"P է$QD+!h}!^&k )0Xƀ hHie.ֱL10+#[DBS`5zx%z5ގfnD৾ V *d@Lj6u$TV?VF] ή[CL98A$,ZIPݰB׋i$I䄼S-1n#DRv8fj[a'&Kg}B34 "{cA3FzP9dSLuFɢ&I8 2Q*1&lg$TJ $&g; t]qBΈLaECХ&Ni m5o0oAS(Ynv.DOzШ;sIX?vL;<(zl}) mH纒nZ^oc @mu-BT'yTģp Hfs 0z"F>'e󴧡*Òp;&l%6NzkɱuOq 큠 n{OdWn:!lv״X܀YgnFĴ0`m0ڂz+N3{X~cS" >1nȵ16y=[n0՚əUBN2nz*XY+]Ih\sٶ iaդ9]An0J8HƚK4;iT9i<&l;PC5`bQ!4_u]T:$|-,^mXݼy`+w?H}IݿSG{YlAX 4X[dUYVS}.ĬU ψ bfkp F,XYPK at=D˶c!§nij V̝;q ҳT/@!jE쒕ae{{<]5ҷ^b(0&Ȗmc,>;ccG 刺wУe3 !(u (47hgb]Xg`m,[k*x~YEY&-ۺҭz1oHm.Ocݡm+n3 dfN&pySNT9&!4֬. R Pi 94q$'*gR0t= zG*E̪] M6N;;lar pcJkhٴMOǛ_i6- L(w~vi Ltujnޭ>,PlM伅`rZd@n` M$pYDZWp;4)g0k\[Epբ-p>QE'^^`S,Zs9\рTƽynAx4=A.fl# k%vgu}fE#j,y8\2xJ+DowS9<],h0Jʺ'0&Դ  ]ZgPn6 y;E'^׫ 4\޵ zB -DRv};R ȲL$9W6DqFc4a0"2fQ?U\LҎ`Y%Fd>Dy/ ݔۈQ;WgR VjbM&Ĉ @Q59+ a*gdJ]%($10_|L$oSU]W)Md~<]y2+fiyZu̐`90DZ~?>><=OU-'@Vz[s[5mϣ :NiZ}b" u*ou'93V7CwNJіw ?vOϧcީ$|3lyӴ HwLbߖMteiH6| 6ʪ&Anܴmw{]wO yuWmcm$lWu=6Aȳo~]~: mZ"M~Vlo7+O0P7 P?Bg3"}:?y<iBqdUU)iNJYUDYOUyuWmgE͓]ۭk*n|m 92/.×̊`zUhl폋_Y2czl?WD?,'̧b6v!!ʲ\dBoB!2;)z'foi>I„im>L3/{3ȇM^_lZ~]ye 桛O㩚M"ϮAa 䌲,J}Ksɷx]I"G6iW[9{/bmvY" Mr Z,3.06tǡ%L^j"!&$$Sn:cy,?<|Gũ`N@pX),^GG]./ҏk;\EiQVŦJ %n.k-k_94P>eA HB[@/a]Pt*H[n̆73m$I690wCVwZ+jwovŻvY<+q4!JTr&ߤg%wuuW̚v>uc6+i=9絓_OޔD4AY:eHDeʺ"X,;|9NxUi лo!ʏ_NmM'CЩӷjQY nnwy CCOt>v#(?|9ME)f#@Uߪ~y`)Q>uUwcv"}:\NLnL ̪V"+PM B߸4?}䄀ixNMZ-f/'墼]9HPע[鯶h"Z"}D*$(=O7ɰ-E%gTӫ*3:U&Iiڦ(2"4meiuϧ"il]j +%tcqaQ@ r v"+^.{aX}q;10)%Ff14[*q`+ǰ[3ʄz gs0CؽgS%PBbd1Xy?<З $b]d ݅^mX$4!+]+t̙p+[u[v;^JQ˜myCL!p8DJfj;eHԹ'7k{4 y2-4[{BI<m H s襯3XX!9E l6;~C4 An\dgi8ˮp库<ˈHNHŝEb)lڶ I]U@kiof|9/_m:Ҳyb~?ͦy3eնH"ϖ׷/'}$q{8US ٝ":2^eu&g\O4y"V2D.R@kz4`wooltҧ雯;0gi}߮Ϸ|V~Le3[]#K3E>ZnwG]=F] Gfp Nؠ &8p#QW_F[ۋ?zց8ɀ5L(C>uGQ';cx>-j97f%=8oiS(`%?n7{gGLo/6AN\;7>@l9T~+YB^D @uWNs8}+]EiT] ^/'}wav,+PS}ofEzrLd꾞M7-U-%-EyWk BUUn߶$T׵<$5r} DP<|)TDT5t\=P(Tu$MdkurfC;ʶ}zUͬ>$X 8_i"'>U 8Ur.ʺYon, _[(w ~e_jzn~CPU7fiJ/GE82cYgiTey=;|yY/ k|-,\N_ $RBXVY^/gəlñ,zz1ӫ1RPKW[$xg-Mx*ENDnx0[o|@Sq-SMrU?r"}:]-_Nez;~n~]7;&TyvZ$gmcuW&-[A6-on8b+0` Y{WC5[ ek:s@A+x",䙔id̊/t=9LCn7$Mz/w2W(21qI+](6-| _oć!x5簓>1_A[#z$GU6bC pKaXDBBJ iޗplv'z- CyV쪻:)eq:ܷm7{1cWw\ 7G~heuZSGy8HݡbFvWrk8$'6~n6k\Kϓ2cF}Oefp0e}׾iZu@$@La$ۃp2ӓ.|Ϡ͔}XJSK.bfd_s}ߔߪ-J_\5㩪V v:U}X U]?Df.PuO] ` ݧ[$RXV]M77_,؜[lڮcMuSmNCˊD9[]o.@BlUu}dm 1) '1{1]we+ +1^$X؈EO!^v=iۤB"fDM9cjno`ɍQ?Pp~,@t6 W&BO?Æɺ>Bn CXnUQVu!;=Xx. d5ΜBl}l G"#t|C55@y 汣 Ip4߿I`c@s$%l/F46OCn:g Y:_޴m}Na}}Iu}c=ކWB]&,3cU(AD4/cW8Ut}RգK|F: z5WaslUYS%NrBta=vMypRɳf1 cp8'o B>YؤJ8PzjuLDwo]5ߎvNI _O2+Lg3BrTu @^Lƨچ,1k2m["ٴp, !kѷ8I%3(ޏ=`y7OUԝ1YDp`2Y[oLeGk-䤢#&}tm}l [/u}рwƲ)~lsuZ!_j-7|P 0? x#DC(Ak/\/Jy%+@o9 A7`~#lSv2?¥b/sAW#ۏH"0b5s WM0&csRBX+ж"'5c,@00_N=1BCdc!->L$Nde)f">\x<11Wq !dPG'/j13fAHg 3Anӝ5[*:!@X }aꮴn3O9̢ls;BfP 2 =   inzZN7p`X눻B~>?X1Ӿrwf:2K^3E %ڨa{Q5Dfi+KCL}n%Q"S4ʠ v<4K3zĨmP@S=h/ql܌ u ^d Fz'J[ wH֖V$;DWYt\xVDخB`~&BDt7" X& f0CbSr -z {BtSH)`ͫHVsWxn& U *CW04t AAz]kfo]JWSq&,fZ6N3"Uul+-YD"h|okj2Ʊ忭)]Kdd]%hr=nl+{&)Xy=Ϲׄ{h;j %c< "*6gA| hJ) 0 G{%,qCSg¶A'åH@^`K _eB4>7O@^1aSTA,p8[u[}d0`~F!$eAE=Q]RzIYXejO/g]5NZ`?cOD1k)y(Xо3&WxAS@?cBjilHOw}kYGkw" 3k bIY:)X.9yMިWS0\&SmFDs: ); 82=Jn)V+# c'}{H+47߃}j% aoUeOԬȽW;) x׬Q[[5[?0U0݊,8 8;:#nO1yG7xq{~Zn!ʲkfPL,?5DyXh\Ҏ CUkEoF8 pZMmjkX"vy~yPWBn#g-x!irP_\ TeU7up9 cxaeV#E 4Rb)lbҖx.~WA񈛳O {IȚ#W#D(X]nqt!`l &Wr(L :E|9 {?M+ӏ.{ܩ^o;i`7>]OCgwAc7'ջQR[~C@~ĢslKy!O/KH[YxD`D鶳5al3Mu]p*9*1~kԱk(n&`  o* pR5?9ٚxE6HP}6: L+^ư;uzj]Ynm u:kEa[501O ֱi×S Բd;A$ GwF4Cy[qm@ π0f|]1w:<d:fvIJ6vz v?\  `!E4$+u6:Gߛίx{ `<@vgŜMSx1}O |2 L3*3kxbƚ|mӰg.20Yћ<|ȋ7= 1u' ز)PYGhtr71"3zW0¦s{bѺ,Ӯw~7"NS0{m>X`QǪSw>fwb/җov'dn_a=؍G(ٜ>7o~(@5x'eQwO]_ۛD~P_l{Wo&DwtF*e>9|I3s M^yJLHf2`_5{&F)@4ϓ~3>~A^f`Vw0]aѻ~ uUx@Avh/Ci xQbp𫇰y/F}>:{dl"(eknٯ+9 t߻0MѲWx\." wG:f ؄A(j|8U\$}Iy"S!$py?0LanBK,K#)`7%%{w_s+c 0CӄPBvՔ=0r`lS_ױzE[ y"ܹmy(p3`^3hb Jv,7ˎ3'ߑ8A%o4ߪ;6![Ng+SP(R-/C0l T [[9p`ҒO! VWR;BQ'VY뛹g(Y\H&[ CY $>):;qH٘g@P`댒2a$"&y3<4߱#4}sH@a+f]Hr7Μ8?ޤ?D qQzA P&DdG :!`ph[2^hެWM(V4!(:֦3j~1 B^P}-T A 7U6fJǥQ {w,5>NS/`0!BpWXm`T̝/K""a>/A9 жFV܇"= +5܋J < g HHq+xA4ʮ&b3-H]sOGElneb E[1XG+="Z -[}~kaX߇XhzyI]sBPGh{d9`JYLeOMv_:s5%|gR;` 0* ~hcC;qd?~`w̲NL7>M:n d}R1?} X㗋ɭtsDcB6ì@`.&"Xj)52,15]+_ "=H$5e:/}̪w]t~9TWxqT[`vE֞e~U=qzF $4 =݆ؠQ(&"]Щ r]l($F*x¼_9iz ^scwEcDX?+䚏23D3DRN/'{8lՂ78`BSfs9[ߙRСF=J/+VLDl|JP47(A5Jܑ=Ži! ;uIw# z`~Y ^+@_B,($R r^7fXX}iBK'x.h @9HB#SܡnYfuloc ,K?,!ia ő#opTlj@+Z"zB`Lkbg0ziw{nXa|[A{Y#Xw[OLJi+?A5a*n34gG839nWz\g[k(VyW@gt D%)Ufߗ` C·M$==y8(a0\1 l:..ve]a  @E:0ꘃ$0'0uk )݈zlZ\/FXfw_?)3K3w[XGf7~^l/7bh@0/߇2uu@!^M˺c\ aVpVЏI`,ψ\:D t~/@׶aѥIőn1:O`2ZȀI *@7f3}?3ķQnV̏us~rJz&O MZ!u.G&U /9 ʌLh7Mbut e꛿Y(@)GzϑȻ:`f_R39u8k, x۰)xG[SgBiFƮdX?'LFvM"zk iG1 vX7bzsZMm 䨳 ЫY7:fЄt&uE6"#4{noL]s?Bc dkp8j=fiY.3őQ酶/$ [t$#hnrpUsS.ZJfor}8>S5@87dE$wP:~6%{|qG;MfX2_u=x2%E^pJXd ?r |W05 @.E4x3}Rl uL`4k( æz*4$>a$$ȬD<{Q|n`Ѓo5_"ŝi`5~1ly.'"9`܉"~kVɔg2M@ruj"B~!3v\*iB>b6-B{BY5IE"'`Ft̓ǁq3/F# qL^ldi%+9!f.ۇn1a IDATL i\V +,f/qo D4$KhS@+ӼXUg)Oj}:?Id'rB̨:^yhUHRʲ̝כYIN2 qTEqcYw 4Enr{e 5fvNW+DͦY&Pպ4fۿ3= ^"m6 .2""y)@ߨӝ[^. P7viv*&`ZdeFuPu-nܧi2LvO|*jF`cK(Y]dl/0hKxWG`4|ģ!nYne t7P'}҅326|:Zn/aKX>_a M=\+S# r.85#Bz3Fu]ӄHEq|:ppa2{3wMެ:nV\-ϧv|a'Dzɘ1p<7+MWx۴kվ,_ixJYͷn$s^p$kIf&Ǧm˻z9}>>i/UrjY1"+75 ,֛nnK8Uu9{l7Lo͗rnoGi=ij& ފ6jO#W8=4\9(k﫻&g9_lwOG9×S&qS =-Xty>v3-n{iNn;4N4K;= !oWx*_]d{`H80ɝp5FT4Mק*M "u~<>2A 3XS9}[k/phaWAD=rs_;zۦR6MC| +gRp+u&gԴ-/Ӯk;및iӤ ~ZyAaw8v]hJ S!ڇi yW(؄G@ŭI9*/hk9 Vz3D[-ߤXfE'BV! 7tx V!l&6+2nlh}VHdqU "q~MT֛}cEBrFeQ\A>fk4@U774Um]I"*$|5)Mߦr6;neiڴZvrV䷟NYL/ 6t&4x:̉4 Y8}ko:r{;&Ds89:O2#"D Oaf6R\~T!HT6}&UU7Y(A 75nf$̳,*ei"MR#l,td 9IK$Be.we2>vv\v>8bD"Jd>Z(:/:Bcy.H/WwMvHN؏T 'AP,$I"LJif+0S-WU7Fv{d5 AiXWxZw=˓+8uQ)e$"=`$ eKPخU~Q97 |\ MɄd9:2 WƳb]ZH 9婪"u=Jc$ia^S(JlSI-HTMm.k=%MfӼyĴ(4!ai 3ڿvUxeJ?`/Ӳn#i5]4uSk)j:/x7{Ӡ 00X`` ,0 `@@@ATUwNλwI䙞[giﻵ_*lUC6ۦHճjjrW|i+ك]aVḟxo[:Q5E#s$hs[U} ({bFz9?|;̦ɋnwJМ^j.Nf9G;=[͗y>|A I~Nn&8xܶMub_ZU示r1KoнY_v;hT-̯_6盾Snh6M2ޠڱ]Zwvb]OP9p;Y@)w*JyLyK }7,߶5r;$=_Bv|t.f:sYkifNt v" v@۵=Xsq5t\Pٟz%]sz6:Dfu U"f2[Uoۿ|6C}98R߯Ѩ:|;^^oC$'~ rGV[.Bs ِ D4~\\ cM>lL&c(жnv\uOA%~_yJS5>x@0KKhK @]=N~^Umg/'K۶r4ՍVUW-F$ iyh'x̒gJ, #;I ex+fÿQ`z:/;wNDlQGO5HrֆBaQ9u&Bh樉񍸸]v6VL Gnm>|w/59\vDSTZ^O5=PnM4ǻcUU73M6i4|pHgV_D4fghDjDys?53"Woڵ_\TɘQ F=U/vMcuRfSyeϨX\d(-ajb2N L z^\KzyMN-JX-](6嗋mzP^ŚCig!m2h(l.1Z/՟#M{ݵ3ŸCq:py$gp˹h]w `c 2YIGJ uy}d7'Y9/6[s{O،v_ו8&EF76m6k8 gs!`<cSZ:BޣYɀS{L/NO74Vk(Q )@w95 wwVs+9Cb$uqLc3&xAtqz'. T!tLE'T&c~έ > Px͠X3yV" VRg8J$4'5[2I-]ulb:'7vHCf:;`x) XX2(}81w^}#S;Pc$S+~;o"BⱘyEN B@pE6RhC)n ɵ"z@Ltz2qᴞ,r'EY/L枔3'xA@S ir}7^4=69~̅ӭ _##M‡gocM+1r:DgFH2N/V!d'l I]lq9E̸RDUzO vp$8'PaP2$ |>GΘ D}3!;eΧ` eCGhtg˵B#s8ƛd|(Pt8ߨH>[EUvS$["93bj,1&TN3߮P?a P_q*vCft'ՇlSccj(26an[oZU: @& Ѽ!eu}hǻb>Ŭ%oCJk&GoFV/j+$6\'\mdJ罏uo{_ (-0 :ltuk`N2=d ;79ŷ\|j?^i8Y{I%ڌТN3=x̄4$Hq#ʛeOMH?nR޼O,Xة;*,™dx'?6tp" CpfyDݔnׁ2sɲ!p~DƢk' A0~7&]bˁlݕ{uTrU *r3^yY1Eޞȑt$).-EX_I)3 fX@ig-!,[}K,{=ZѝL{i6e'/Z;Q!f5u tBgWۡDG ԆqvMc$nh:N B]mz8z`=2H2a^BbH$G#2)WuǶmi \*5AAJgnl#'.b.Y `lwu穑<7:+) NXZnOՅHWy%9߱dA$1.iQ&_rN*j&5U/Apl N |@,f~HcG :p_%eDt'ϫDzBNwW~a3 G,0pd~rn`yL59%*w tڙ}BcBWh~4V8nrV   R,sR[JxCdDp R+Ù@(T@4RH@J?НJ@ly-*Hs&X1OYL=* mEh.:x.HCG ZL?쩎zH=aserrQ:L}TTY%&os buYBEy-S#9#uV(X<0=z Vo>9({_''A?Xs:l}C=ǚ7Rn'e;׊ISkb.:PI1+ܣjnCBxh͢rw}JJ˂H&c$ԯNA($jC"G A-(\.N?&T]% &ɲdwͤwEKԂXMInI2E\Ef}mzf)Ay=1'rB2(J!M/`jRz%ÂJ}gtY^*)(4ܓQ|%r%>(\@'!?bw}Š~&D@M<]JX43UY`]]R]\p*se àHyD  ~\3GYM*ڊE- 7ߵ̓~6gpɳT$2xW7)Ox;1.wD E&N2$:GD;\9yHZ[%daO HNx(޿HbpV:{Ź,%E@S_W~AaK42b*FqLNB8R{UJMA-sSe @Z05\% “Cl@7C,[ɾ~ XekJ“ i$+Y D':MϜ;Tubo6J+CSt⑤+bؗ;b{pP:e`WE:!OS<>GPDA3 @ Lc2qV; P (I'{dhSIzH̀;΍K#gt[ ZXVh',4R{:.gcL$gӶyðHW)MuO}(^$IjXބg56´M3ۦ`ͦ;[3W'ۖk[cM IDAT +זvznk*ݐ 淸KG/}99pG)(›kpm6B;\hӼl/.k j,WLuQ)t^P} ׃=gW8%X%c'UPA;)މtyZ30fى NF8q2Lzԡrfr8y}<$ 4!<={ `~u;VX6vmu.cexv:y;UNa`HS'=XV̙7_H OtRV:V̻"#Z!;I-]H WQO7,CdHD& p~}H[W%΢;bʺ |gB2ND7(]"wl>2@R8[WV}JL^%%;jL@P#47IgMD.Apm-wS"IKǀ|tg)rY[GE]]b)K;rHba)ywg(@?N=cGwә֐{Gu/$nD]΋/]) f;&$6*[9rEU1#8(](v(ޓdvBР@gNV ݃@5 xytCU";cdG`pV=<ʗ[IG)+zzI :.J)-soOG'x (3P2*ONON@ɌM@qZ)jsʩNhe=ˉo״i-r(m]qZnnn|j ui}uF?qXfVW(Gn Y["2%W-v{O>\-7Eb<W876c@ B®jTsfT_x-{jqGNL! EՇ'6: H #ߪ \[!M' pNtt.$/"4C?BD+@bV+m+ROOsQ%?ܓmG)[JL)6BJg{i3 V/,Pl2d vkYEh"1KcH:`D?2r>5j"^L>O/n`0vm؇OQBQ<.ޓЕC=$w? zP0CN 8}79@"V^{D|ʖyecY[!Wb>c;}9ђ2Ps;ġNʾG302Rd {Obn)tc]1/! GǓLwUi~CC(.R 蚑jS BHat8z#hxFo (Аo;f0<Gu+[mfnTsH"#fȮL.+#<.#zzh1"·^#KM{/xU 'P3+ꇦ)=DGߗ;>( R V'&$ ,R=#׷lgGz?90Ao~l$[Bv 3ȣ][ܿgZ!!V6@Ņq wT<*,0Rx"%9FTMSw%fݓ+뷳@ݴf7~>Bxsv^_rj%M߯OW||>P)b}?7|^_|r|t[ 09?n]/_.؍)[!XMGuSjwLDm۪uRW]sL{Ud*;"ZmA=~6m{ÁB۶d5y5qIA=ZQpϚ9|UG:o壶A/\Mb1(`[7ⷛ3 >Φg_}X&~9ZUnn W7׳$/h˧_=Q:T<T<abԤ NKyq%O>:Et`%!AU@{=+a?GV srlMTIʜ-EJ`|h6"Pufw| Yd6yњk 3~ZXmw׫t|vq g/usٮ1zyJ^\ONa27g5z0~FP fv`6A:#Ga5Nw%LKiܕJ;ZmtkFh2WD܎t:bu!t]+\ZeJÞl+ǺVDxr6xrB'4y1g^Xgu߼U^M|3vn~篴H*c=}iHo3h<0٘?Ӟ`OCݣ2^[.O:$orw_$ۘO E@ڪTsr?J3 HQ(ĀBtQt]񷝍۽Pk;=_\O"rxWiHK8-7,#nkA_R¥dՒ<+l |a^ OBwRWmjziH c{h{Oa|v\դL_N߻p@f%#O÷d8VUс 7NPjlL|A]s/Yh sAGAh !X:|9i=":|i/y)BSPLOU{jlV*lSbLxG÷Trz鼴T!Lui_lW7f ճ*"}lfp@UUmG@f^Y=Y.mߔ.Yy'L#وHû M՛nbo`T^d5vFNPq޲zFZ +GFbŰzkwQ_u?I^ӣ;V^Exioۿ! zN zX.1 ,hnv6)8[lӣlffRصf#"ٌV' "m~Nk ba*rfigt:@D3pĨf'nח/{9U.ff3ӉFީhbRXD"&/'HWM)u<\}&ҫ7o-H!%_sM^No@)4|zV3R~yZ#ZJM5jT0鄚ը"$c,)O%}{gjv'̢~%h:~>N#Oy4&"K {S$_ rh4~>>бMyt {OJ.€A(d͎bc ́(2dTPA%{0'M2)+8H$v!1"x'c܃0 `'awQ@i"ɪ~?_U[=thAD%P%axZ\J! .PMϣ4:d5@(Vz{Җi՟_i.L'?wۿ@T]9@py̛͚@u{:o{x:"@fvy&/Fz~wuwG:nT{~~Kʍ:I3'b3wGH <,ƘKNJNHpЉ#򩶎0rѯBh5FU'wLeջ\]nwE QCc)qq'7D?mNt@*?K)'l=fW78u9|=cz@ѳj=`*fnӋ˞)¢{wy+_Ntg#yh'<R<|$)=-J9%@>*l"d6N\E~9 ddU-bSo>xZU<߯Tfleջu}w#uA w @#)`p{D5` x\F X.fWN7bΏr1O]͉)Ts|GКS-CN( qP"lԷ!GqEF備jnd#O_N -EWhj<*XΦXTId2LR^"ګv{N7ZI QwH&fd!}@ž<꼕YF_q9v&Ez`#=d"3 \42ZpWEͰވy3{xI[j(ꈧpֺ$|W 6mIplG `ej| `9GT'=ٺϦ&mDFt2J P`xw#"" 4:=>@̌Sg9Pɀ(8BF_1U/ Xf [gg_]`uF}P9u`,w^Tf߹"+H_a\3S)@#,Om)MDRD7 _]󮔬STB,J= V= i\PDR' 9] z},6Ṋd<0@J FSd#p<*f8ǃ UNKߔ(S ēt3~ѳJ!jkEd$T.g sD>,W+y &Jlv_J`4TKqsLGɐ~;/T*~P2`^][ϼ.=|RDd] ߚ"6TGdd W'U?3 yT(KDNHo=3e^ p" Ix 8KDO:M3ϒh:TnL^ ="PY\SY"OvB#^8酲Ugp o}*%̭i))tqM N";^ :)6Ua>"Я_;+dlM3ڃU-ZYtO9+hgo !YB4A":~ 9p NZۢ٩J"NDrs[j8b|,oj Ipm%];4F.ڡV@R RLRF!dz? O١ 攓5R&(}T< IʨyOFE;;}Hϑ3$EIDIV RGRD:dCs4oFp?G? 1"8q#<[/\^ ZE^RR) ي߼ʄ t PGiN[EdD@(~)B0LUg 'rtEX2 "`&(*b<\v %_; IDATO({j ,fٵ"iQ`XsVR{YL*wߢCyt~U0} DVCVQ%I6`[Q U\ЧXΜlLBhY㗙B2Oeٖpn"_;(cP1gsƣ꜂+ ,1Sl3zF|: ~<+B`!}{>_:T|Nj[emC8^)rWA:7RQfN^83 LEzhPKU6@ˤJrzHk |NnB^P. s|b7i*LtYW }ۿ͟"(d,46 `3RI3u5tq;`5؎AO wKsf2]0 >TX84  'f9x(jru2ނLX͙X$eup9jAJZ+u'x!H J^Y ;60n5n._;j֗-}™]ؓ *Vw$e=3l{Ok`XHHW\f)˿WÎQ]HNa=(*8E*&tN["@3WnʭQJ4ʊ? B`՚,B!}4 GP(fQ0C|p(yҧ՗Gιג!Q<)՜(|,glKAPn Je2:Q xTYOgdHxZ~Ĝ@1cNnQQX%Ay\ɍqvՠaAjelփ5Sp wh:៪!Tth S 1fˀPX Mԋ٩o;O\?:>wٰ%wrH "|&¥ܺ7PԜݑ9.^6`q RPQmRי;!۴ӭOwDOoP\%P\YhĚ ^0ܷ~'jKLg靤Y6m]^h4 }T7ߐ:@=\`mU9kBP;9ㇽ__:Pb *P [jff%=eA†@)~&®#8|^+Tt#P6PÕ BP%M)YZ-UNl?Փ#O3ysvTXV;n, f׃tzQHxȟ``WV՞<\HjD!%q\,̝*(iAm6áF`U2cr]wǕ;b0e:yOOΐf^ D,kRM1c+.g.Fͤ#/z<U^8O :RRPr=p:Te32)"2ulk^Kci6CoE*QDg'Rbn-)y` JT'I(U(r#l:?$C1}9?m!%~A T45`qȕqt-!+PV41YopIj1IsJr֮ýx>ݦ~m7}.E6Baȕ9֜Gxw+|MJ'zƄL;5v7fFRL[#ø( YrR$Qݱ(rz${B@`y<@:5 KΡKa_:LM!ItG&eG2]VS^xcHlOo5rK~;m n3(|n7%E;IЌc*!]Js!]dX%RDdJ:-&݈Frr[YnZ?)2:!瑹]. UQme.g@UY";`COw#x T#@Dū_N{luz! &ZDGG)T{93Ms r',d-FGzS&`w|腚 E*\+V.(R3;J-}l#[w EWb6fAx#Υ{L1VG_ qle$Y:=@.ck**ʵV4$-7'xgMLYbea_& ,>]Q8KS NݨmIPYB r 7k[k(8 (n-o[ w<&3iu5wDɹ(X^㈌hOB!恐}$N'ZXފ6QgN|#\&"CDSb`fZrqDeW]pxH{=S{/"^ϦqU3~WC!s\D PED}5H,gfk?ֆoRB\_J*3/vQW]s"m`洊xԣ"BZ\:b>{9*b~i\u٫AE{H KD_ d2[|/sKeKTʢtls 2C̾ty `L΍Bcnz ?,*nkav(ߌŸߌoXoXoތ7o6 ,"&pnzJeflNxǛT:}7tfDtfg V3S}7nbYN9f>]NwYNx<ONNW33ȧ3k+jh# oDHL8z.l-ul=hF9,dERUtWѫti̕  ͻ.tc;59uxtK>2MtaEhyԻ:Qb b콁"DUbD淛O9^6-3Gc,A8#=5z `}v1~.778\,2R$2J3DrgM~Iȑpg %"@f@nj[@j%~}40Qbm |]V0Px @~;" N#'xa3UsR)'Փ(}]|f|m7նpWy;{`ZgVg7cYLvzɣݮ,&hZtf^]\ljON歶p}j&ɯ?YLY_^>6Zp2=OzVV*}WjjX[jmYk+!x7 + IHήKk,"Y@߫_76 M `yzI\<տ,4^NւտQ՟ T Vu2KٺuS={59ҥl=>{?[|٫p_yYqlog; o;ޝ@4T^^\RO5tkB,A *t:?e^]\0|_nޯ%}rF8|v+5DXh2rUQu:7f|xr< VI0}1"Ol:?~9L_o`ގdB;tZ EIIŇ3DowKIN&UUͧѳ >X' hlkŇ3;y0{5<^rGո:!fM~1?/QĐ`Nj_:DWx9-<CL^7ghnI'Čq9ço `z:4_TxӼS@?lwMn+Λ \dh<W6Mc2 IarҪ.8eqO:蠖#:5@XR6z1ƇpLݷdٞ c>'"Xc 8J'-BhPnf"W;Imެo~YڶRb}݋ve-!j\M}ם!"}MAI[ bqRM3K{n.63.ژvSsMƻcWק)wSfU^^uc6RnSt67poD7ot5k>Ր3^(t5Bw.5l._\ZܦF')z3Ay\AF5@6mo7c͗p䳟磺nWo1bvίofjZ8b>kb6x\|~=\ofhԟr>b>=qW`t PrzܴÑ鄚!" Kh1\/ /s@=,nۿÝf[azzy?X_oCGS͗6 \/"5-՟~Zvغ+(Kxv6ms8xL'hVCDsi& cWstϘ ve9]ط| IDAT_3ԏpȬ#JD̮Lz, A"mߞ..ꦫϚ#EMzaאB[^˫qtAQvfm:ؼP?eYxRcG cuuSWʲkfk hG.A@}}(EYe^L'fYO-59@tMf:EN=N9Kwøuq٤,M f.B 4`QWQAӀBvA8gtFBq( rjr<|kږǣ1kѤ,դ`;Gy P\ۋ,,-uCDS}@՘#;ln~OMe7T{SsqMd/2잵yw>cXA 9ݾF(7?X5mcT}1mr\7500{n|I/#<".j"jlZ>"HaLLd8i&meV? f(iv~T7큟ye2o6[fǣskzlUF|2jٖO_OuCʖ;'^^N3CM}0z9j{KJ䧖Mž1uHGGs &-=5ákZ4Vv#g8 q+ C nq.>ڗ8rwD>٫89+ P n nƔ ˠ;3QG*³EGld<tr0^Cw,(%(6Ornp}6Yp<à^v=NaL^(>ui'ZsSv qdX0"us`" \*:gfw4HAxuϼj֘_fR\wev3+{u=z1bꦽ}coL `mUOO3hj#SNd` 3wnBr`rN[ Mz^v{;펶K( ^r~E~AQܾی_c×~ 5ŴjS̑N[HqGc]\'Nj׵ǮhC5)rEu,߭ͱ-_̳ zrdѻ2wC-9\#~f}ɹV |zS  E _yWf [GϑJLb΀`R"E_p!0 xEPf"%TLfG(x>bg ޖFs0uWKc͡#`-y6 X\ RaBJ[Ag6LF8&{d֝2Js{Jo#GKlL0{7jG O<R 2?ñ#7 eP[2#2FE_EW;Gw"c]dV=|l|9㪮o=LDɤ4_oM&,ioBCEfUэ׋2c ]m.wy:ba,Y7$HzWw뇶=14ve, f@lvSKCP8ݷbϮlP}×x(+ h?֫ͶiZh0[mLp2_LכGQ3||HH| |̚]ma0Dd(!y.Y#;@CS;#,9{.y)dfP=wpnOG8tkLxO<5AYsؖ+JI%:B=Q8\73Zd?aFOW*aU7QXm.D<,/^$7[pRe,p[t'_/*_\N܇%ϩ~O$XТIEƤ`7:!Y3zc6K:#CR.)vڜS|x1exPt 0#p ldX vb5DI 4q{J ka{]ds`jf)*@,{c_WGS5/C@d25IiIѴQQ9L!)k0;I)[8& 0{.3@3xlFay,ƣ,{c_^I^P elЁ@W%^!EVrK)E9nf;6k!W+.h4FБje>.ܖ1(3L[db_hӓbQ B[/x%yR 6(T]i A %  k29D뻽;F.+HF|k3.L&ƙ6Xrfo!*8nw= B9RG %moݵX/ ڀRC\xjM HGvŰ1t5?G"9I]%Io.bn`x5IսdüpZ9 NͲSqffZPpMwyyq?WŸY1+JLW60k {R⁃tj@1+gE6s# 8 G F M\&QȏL[-"'Ɓtړ:UHkE"W~PނU1ULu,k! 0sj" b;.7&߁A֎pCWшL0ZM0ݿ߯Vkʲ&!\3f4{y%&[{$gb<6O5kdbƞ2XI#)2}lM0 HJ1qvy#6ζbpAm *0+DG${(DL Τ w=;Ԇ!GjM bvoq ɖLZZǽ-8qDŠLanaZ$wHCAkZ1~%(!YW11:硱fKwa@ C]VHy;X5=԰oB*0Eh/)!!2_ͷ?5IB"c׸9S~ T#_]l6#\fa注npO"^| J?O#b>׽L1*.`HF 0=J'ZBrz s1ᯂ![LF$m_IEr%at:*!2zٿ|TE~Xjxj\'Q2oOO1<5g ߠ;3P=, `{ł3-7@ mL|l_xN@{M ꞏ/eV(atӡ{fgހoB9b~~ML \O{a{yQf\ݫ\z7f# m:"3G*Ht]*ZV 5zD]@.aiJoU+"6 b)!_1!RÉuϵaB̎y@+;QC4̝ BLv@YL ""~(m&7 D@t&尻~j+ȴ{UQ*9Obe=`ɚnH) WyoVW"SE ,yH %{:F'1J@Zn֭*z4;4K\x*BKlEe,'dhCM,qWL,,t\ n${1QlJX pl.';I&f?1(?[hfoP̉r8gѤ1H[ծ:ԏMܙsbD$o8%YFiV\b7/Hl% 2)M `HdaFTUXh`NDK>7Jc,IY{;_pV!\m qcUi۰6}!](z0; iԾSV?I} qa& | Rd ؖ#ةǴ/qcݾixD= ⓝ$g!-ȨO}# YWa!Pa%108U7AE֊8 acNǺU~n I! S<|ɼ.U]Gd ճ_JۊL#v1ڎB_L ~Cai)pE5]R I@&>ʽ A1xK@|ϰd?}8`@nz GDY*$GtڧmͽalB+S EfgA(9п-Aoiޙ z?8MϨ~tׇoM >DV:'a`4[x)aprL4韆/` g8s; t"J_.LL4韆0?Z[ =?p nm+]8]It.S=OOLL>@6zM_M]# g8 ~5/#8}ع[{4^59( W+9lz(e>໠?t29tݡ=Xg8{L\uﻻ!goi@^)( ,c=hk;.ngofqoc/o܈L  g8 X-܇~Vɛ eׅ߮c켪}A"#E__֮Vx_gJH8Nn9M'ەBDe3}[>kw&b0>V g8 .P| >Ӏ[.ޔm6/mx8dbb#6KHNdczj> =oevWJTrjxB|f:Iɼ=1pX|z_EWEfк`GR _`["e5¬zӉ)7VDvWZd7ā%:};ggG?>)# c!-`r~3 g` *|RwuWыr[aVuq]_͏nqܪhE[^7n|LD]ǻ:^&XoxY|L îj2'x! 2DJG6h1. EW(M /ݾj~.}G<{"Zo$-}J|֓94>6&~g&]#)v[-gg> o0׃ g8w>gXJuLL\s~شm=wl<9'f4-4ܮ,)[hnlm2svfPc[=~tx뿿'(L|LLL8B`3XFpK?u3 D?*R[3'G4m~^ >͸hq{O׷cq__z6{33 XI~˪g8p2Q+ta!vj(ˈ.2s `WwZ8qNܰvi@ n!{#[~ۮ79?BC™>~<€:p8H.2\,FJEF 4ظ_ Mbˁy, Eubc@/u_ߧ7 P$Ch=2~f`sg8@)u%lTC1' O933iJ&ȿP g8GHWv,@5ubS%MF`&J 88gwG_QL0p3(:4m=sxGí2 󏶶&`>7oPzM33}~XDp򫢭vp3 g8p3 g8p3 g8߅;X{T XGVM 7[v<{WbG_n69 7YڑUѵ{N&2:rx(~s?.f_UN?wM9(Hu_8{AiFYg2o5m)"  #> =FI!- Gpuke |^Ñ L#q55gHESu\GlZjO4R&BTE^*T=~ X" 1LS7f!l G4ħpѮk+zcGEy :~vl{ػ!S_ bu H^z# | O0LUøq$˿~;c}B yEͩ𷯖_QNRX!u!$(F&I胛f%!<7>2SrCCp(6v1:fAMҢ1N]r2ev3 ABP$ClzkN?GӐĦRW5J6/(t0͚ӁNfazA@0䆔~v~<gk?\g?ڤ[uiڢȽɑ0@ܾҐ΅]D.`{rϾ `^I@`$ ; }=յ?ҀF?WCq7 ,g b%ĊWuX'+GR;ՑŃ1Íb(p$|#蟲 :xIZ\2= B+lJ]5£XC{lPVȱ ;@xXь= l5xKd,ι_?NXdV{MLSТ m3(zDPZ{ej"yZEu/l 9Mh/,iqP# v)"!YXݏ?H:C_7S[E{ô:n ܋p ^s@j 8bW Da\:>Acbt4.!Zڧ&ʉ ]Tv`'eEC4.ȸNٹ8gzY!\8 eWtՋ5*?bJp=N-HC#)~FAucRD" Z/@n"kլՄc4"CjʐU37|%+glDt}R̠%hq?HHj -:L g)'@K'6ջ 8!q墫Jб׎mčp]פ;v4lHHU?7Fte>Ue@X -<6wc `t&J&;F 0ׅA ,n,fUf)0!ٹWoCKdV& J0l.CA }jR%&9p=ÛbpĖkJ4I6|FV)62ad*}xaÉ3Q/N*H!ӶbHo"&-c!a\;m}ֈ#uUjUeI:5ȏT6B@7k*<@+>)fTК;en,*<5 GI!#A6\ .b@U^dރIY\ [RN_0[a`JʕǺ*+C**(/\̙nzbc*O&3~ $5Gl&r(Yʅ 0>R-R8ʬaz1 4Bt麗GA  Fi*RӮiջqtn(٫8j"[]ĺj:IȈ:eo}X<[m[9Q`~h=?$AJ#Y0`A9z`\w_Fjl#qĐD)(}ֱc.vmy[ *.p l(HĔgNBYځ#҉SJznI.T@2cJp*hLsjkбS,5AH\D1y3l鐃"E zXU6BE?u@ ʵZ~0OU/1C5+IMبC"[Y|ojH!1Ke+ أ~^EQ"=Ps,OW4!7Hi΋."ʄ~[^ tCޟ%rf>~?(DdG"/^ǏZL|K3s'E$#<6]m[^م6Q Vc 77ŚW H%OZ>w#3BPKGtK<1 ]$?UN_Y>FWfiNZ];0R;P ਒AlNEUZmǙ=KS5u,v mhSux'"b 5 ghD0_@̖X3 *z;X C^Qpve`̟BՍڛQ"Uc8dN-*\WZ]:~FBqgR: rވTQʊU rX+4;rQfR2, 5\$0D6Eum `^Z'[:<.g繹LDDd>1:Des!"0 zdˆr6tTL%Xn(8:Gb?<*7[ F 0wXJvФCLai9 C^Guwr=c! r&`D XLv."E:ge):6x -r8-kl>=wG/_ a" 8PfG NQ *ͺS38 Wc  y;I0VHLݕ7kwA Kțm3[-!8#'Xpꞯ֐l7N՞\?3n& !mRpBHiK. h@5lj!t 8'}rAd5W)L#pE W_@їä3H{53),HI3 MKtIĚGA?ݓ9Fm@vӄ`E, %CP M=u;KMkN2M/w+' HTA8L[S-;L,c[!py*3^/ o^ 'RSpJ;>F6)ᲓJМU;;8v'-cS+18OGB(r.!X} ̿h9.tzb@E-Jes#Ʒ 6?k8(Fy ۽bJmd'K]~ ,S/H,ևAIQj1F4Nh+bx̽a5߁t|H"ΕkJvsxg{(,h@~yE] h0U `4vܫ>Aza a$NW!`&+pr|B(sXZX"G]D`cbE$)pLX`[Ч M)zh6/PSg9L'.}kO&Z-$F"'׾!w{Rxx؆y`:,(ajp;םffs_#gql `8n@6r#ȥ{z^KDk^3ӌ/h]v_cчQ<;Oɫ,pBO[۫d Mz+a1{vnmx w܊)1+A HxuRzn?'A}\ 2XaVOv].|xsz-=Ȕ I9Bw{=)ݻG ;]iT\2xaۡDV.n/90"kbuPVId97ŏ}/=Q"q օŻru)7)?atpHoM"8Т$Wgw$ mH":B: TRQO\G!ʽ^ مm!hwO')9Ky5i.n,*h9=)wCGJ"<̀)@ rqxDÉTbhΙ3XI"23ٜF̱;_xu:,q='Cv@[^VYIe%̐`^u):EwF0I: J;pl-s5]Gk@-Rv8(|}H -B +("*gP/Y]D25dmŞ~us{F n^dp))[' IWn2_!KR 7Bz+=,鰏mbE G8P[:T6yU縷78m°0u)%{~/=⯷;EdDDPxVi3 pA3ğr2M):cnDiuiM_\)Bď({#!nߖvG-]srlּǪ|U.o QoO^M OkR$10:l6ʡ9EH2'$+P ؓEb6^ko& n%8 ~ a5,b}ȵt?I*&|WpB 8'%3jcQ8IGck#vpa+|`&cf#^gq-4\bؿѬA7F#Lʬ9H l?X!ySXOh.gr~ꊘ&Y94+&y뮣~ q \YE6HCҗ8VbZ?ҍa` kbmJ1s@@W:Y =ZǷ8iq]6X3ձ42鄇B(07XgoN %5zwDb;9pC_7C )`.CUiLq2 OWA4 /!boŸ;w1Yr\!|Pk1͙tqz~_}̣oqtH|n' AARm@#\#F6:}l6b 7E6o·c`L .=rS6N&id`i^kDz]aϾX[vZ`:ՇDsE +kOBT5 IDATaIīd`X |6d|~=!݁OSom(׀,Ph%Z_ʾ#>j@F)8/zu1ۀ)_rn=R4-8(88= ji:4q-Z@PBG*!#K>!&2av d,Y" ?#|(|7nj`:6phyc7VbG|Sxx7t~m!9j7f P^{ҽd@9ET/ze#H LtAn?fLt._M OWէ.M-_yR2OÖ/ߔ?2kXCt&= ۹Epn_v|ׯu)|3K#OA ]ZV@'5Oa^idჅ#ywK6 7BC7z:w*Q$GǮ!OG@r3Uj\(u'nO,V5m%i O!zm.+mԿkf,G5uL.,I$dbx^qtN.ʼ ]t bRܾ A٤3dgSY*ou0cFB$I:u9(kTL(Ig?Is}uz-'eYf]VFF>M2-XcrUM<x_T=P}WInצdv`og"p\"Cmh+.^[xx?vzLfmjmSoV7zMrHQ"3q ݚ3ʯ!c1˫ xz̢(^"ϋX搻XUՋsiS1\-w:hLlWuksx07a~`~<&r~@cUVZ޾>ud@!ErWá7 tuZXo04FDy^^?|` 6EEf"0˰a؜$hlj>h@18sŔM;H<>"O_|7re҃χj@pr@J[d>Q(9Tp=)X>Ղ,.|'a4q?y$V[ i hf"ڂRP9`-*eKbe3{=)ٮt_ً,#SC$r㣄6w]g[b& t C]GS'AyjĎ)\bWz7\1Jl8I f;[UUva,w]P7-P?DWdV?U~5)նjjR03Rd&=L2(m;) Yu~ߔe9*o+4|ٔDhqYBX'Ǘy)Lͧjoý_UsdodL-c<5& 1G533Q1͛Ǻ|Έ( (u)Hⵔu9[ei8ެ~BUUuuh~Sͫtq. ?77eYN_nkͺ.➀(~8].i9buSO﫪.зK=u*cpB.E9)72fߐWXhP (9@4oY`^9nmc56EXܭNRe )Ǜh?l-O!Nv;[8=fWQH|Auqa}f_).> @L- It@\NE!SrƑSעNwR;6嬹ַC#̷,!玂uFpT^/FvaFU y120zy~xUH=x try%*d?O"(y$#DmD.؁`ylQlN= b>:@D|SɅ{Y!K]>L:Ro:@Ǝ$/tG]! #ԌԆUƒDn u.KP{ǜW!B>ESEr<OEY q% 6ƂN7U}tzv8sջ6zP I11Ad'~jMʼ`i]-WP\p*(4.c3XOExj]y'Ϡ`m`ó UU%O.2~6"랻QxtASovuMK0w>.ݼy1QKP4~Wu}*է:#QvAHѴBsv2szšHsz=<5LURAO$1teI MN5m ^/`3O|l.GA\+X&GqK(;s_fF)/st웃0OK0{ :1} U{[07fmA    , p,00X @ `A   0X  ` `P` g]$}%QOMUuUMb/keX7~YẸCzv._0h+9H|1~pq^<;0HĴi@+n[Y8ė*ϫ

d#h/})eYv> Fm-xku(v" ݙ>F:^ Ǵ.ӗjI.4`O]ZnxwYUC{FKGja&OjRb&cgOL;htGIf K8q%FЗkb ix[FVT6 SGƗD넸23@22uw6ЊVg$ş(GKPv\hDzDZD{{pTD0GJ`crlɘ-fıL~\f eUrVR&eP=n4-9lv1 GƯ rlwBkՖ{{Advgrq'04! ՝ѻqRh>6<v v :;kCDÂ7DbUu]UC fi7s!yn.laE5,:su0n~,upC]yuQ]pͦi6"E:,3k_¡ ]xqcKDxvd0;=ȕoUQ\ F≀,-. wc% XwM@onD&|nuƱSAPCjtTjڌeՉͽp$zXdpȉ8`O@I7Nx~h `j&g4[MYjoq2 3 F``/>ǑS.EK@Gk _ng;RGXhn\VxbOy g|Ep]fUtYaSCUX:FKX~JN">ɍyq~iqۆ{ R-5>ֿSQnnp/k쉡6X"JHz)#RpXxF7k&oޯe1/c8ܼ"tXȆDGHa|rt\TG*|rqHtq/-8nYZ3Ϻp@~Rx13˻;;Jх!Kag\鏷!HGLԲ=׉%֜3#Ʃ\Ke_%tL"rYqJb9aXqOBÕRkO*~.l/3' J5E @s7#A8r2h#U6b7ʹG\[m [`9Z m QZb\aP.ANT֨?O7%: ;r}JQ B:dchjJvƸtH/ 6_b.Totd۝O# Ճ]=MaJ}El+ҹ@gTz:_C9>W:L<˭^9O4E8(Tr#O+o_6~6s0\<ѭtÄ\( cXq}*:<6hfonKf|KȬvolQV>à|X-(Ӽ^sCǴV3ZIsJ/at:akK:˚<}WJEZkԁqD>pQ#OFH2Q#ҝI? 8V2{:3oPO!\y#25)3^??7 Qq](>ViGJ bKDQ-Ay<'2j ;3A5iCנ",nIy񿗡hvx~DrLC! HYgԆF[%i6[4,pLs9zWqŷ!drU添ݮ[@u9&*!'G .N,3/=:92b(.G2Bzx7O65>REG5.e2ETA1=Ԛ*2aWtԫoeAKYX3J] +'ѮwIL2| @BS.;䞍\RQ%Pg:"JEN98dgdSpZO,zYIO☺8ưfrBp8?6Ψ o-Q.>52:-}C= ?L.Ĥ!4v+ow}G}Ϥhfeqh+Km& kMoZs|RbscYy{9 q$x`@YFHJ)LFzWpXfBr%t|{P1ao{pfw( 2؜"!G4 Kʉ=]%޽H+Eܱ"њ=龽fmk7醙YWd: z OQCUw?:QTS7!Y7 o]m](v Cw6(OZLn2dLe.RctFNTOJ;:(h]㟈Wp'f*{p;S$C/Sc6ȕRO*D$.#Y}JҾ %*,'eݞ>aj "q= ͜E6[Docl.&~;^#mLJRՁ܂6De%=Wak_4l"%>]m_𝐳nK`Ppi{A"=2q93|~Ee IkY|_jEJY=z#=a{b`{ UɪEd@$KEHs JIQcp=Qu;{]dYoXZ#<=ߧ}Aۍװ c&Tv9Ӻw@,}$5,Ӝz0;XIz'[Y 4*EΨc6}oGHk$ F#]ׁJ" ֞BuƂ/&k]3tǻ:Qy$=eZc[b "bQ XJ Kc)cb(RIo2wK7y13gAE1* `:#USP#"“*,p}s"6ht`"V;#n&^'s<UkEs BI*PVWXrJEI-S &)Aa4!ӹŒ7k-tJ<[w*aH I' 7{F & >.cH{?!3V1}*(&1qw`M)qֆHnZ]NLinOУq2dP2=]ȸ`Qm˅6T4(.h_8y/CJn SOge&2Y'ú}j8Q lD/;t[ʼnok[0LŎ EsKmw)FCuWzޭVG%֬2z71E܈o3Dz晔/> IDATYATŗ<:* ͳ!pHʍƖNlĩUX %J< {]7  MH&⧸|~,W̖r˹l1y`z_`<tF\c6Ar(ў֜`3O,L=o1 gVm߾S$`sc@t Ք.|-ee$6ą&hVrAu>4z*y^ v2@X0+oWFWW!|"6+Ц⠌=!3a:D oۉ#My^*6GtH{0{o1{S.@t&jmMkm/$:˿*ą#a~o9)? p{ }2K GwR_s^p5n}9PEGxڄduH%ÌVkqs8X«oH`;+b߬'p7:5!9j~_|w&NWb6j~v睽IvCMX(kKqؾ[QO(YGSgb,z("|JBgrSCv(]pZj|(Ƅl@HODH^@0rı'q&9\7bY\x6i!C8P=cHOwMFSB2XcH]7)K 521rpvߢ18P7c3 S3YQ> uUӢђ)9}(&\j[>YK`q擱-f̬릩[_4! :|K5N] a 2#,H '4u}Y= Āp Ga2N^󱱟:w)cTLɇAZ:Mn*d'{&l0Ydg0\Nŋ~X߯>" ɔ6 rȳ +i 0kNYaG"Vj$ \q\7 wpztRGDyM%$ywP-υSjr#&VN8oC'AǻE⣴-foʐkYDuׄ[7:̮{󾞿3 ̕rqWu6>^Sj|q9.c3|UnY?J`E1gT>dm,׊tm\əD/t'cn[\Od|M fn>&O9³9 fQ¡k~)y\JN8o6 fM3:u\{uyU2we6( l\7 ^,rK҅-SMӬuq5o#WV7Ӑmn,'TgtnM\/Lát9!GŋbZդT4z=.Хr0$LxQ,ɠ‡LZPXONkLhQhz'qQiۉϽS3LOɹ|5E5[|?|O4 dq|ZjzDݔ!?or4  Z ף틢25,2{~N8oҷ٨a9 ? ]p 6QNY?LKФey5%Gw⾙]UUURqY\oPU6a.q;~]B&r]e}̵JCt8h>6H1|5|zH沺%QWɏn|l7eN l,Fi6kr,ir};y߼Y&[`@DfnVy_7l7ǰfH_f<nW˲,/`S}Q]\| ˲,@g{Wh5'-eZx$" .L'p UoGX-K ˲,RJҹ0[LC*$Dy:@呙/r1h5ۣ%`Wk$( d~4ҏ9n~Dnk FGD|][$P`b* , ,G@]7x;?ӫJWCzwu@Z78Faw~y;fzuwH'T9ĥHf(~ϓ| ) Ϩ|]@>Ǟ3B{ '] R_':d5D>օiI|Lֺ/!Ԥ`nQڌ_Wri#_W>Тwy\WF,IugR dvd!o>5H`.oI?͞g}^/^OB}?/zyQ>\ ##Guv[H4>X.riOev\z\TcfsQ ^zv\oFݹhY(%E:;*GC@$qUA~+ҏ`pF.B>,oJ- ^h-yIQA>l8Z*~wfvF.Ç]'c@ʜ:}awN(#uccKI$85% Nq8i?o/* b\ g% N8OJ:=FfOC{l.‹FiC q9e`WIRs%fi1{Sz_v;.K|qߐuIf%$þf;emNvMf#}ahC"O;D]J& &phKD4ͧfͧsK`p%CUcj.1/r]˪]/sl'WƧx3ʵ'9s4x= r![ 4&=1KPdXɬRR'gGUiaǯu:Z]U-OM[~Az\O׿>fy)7uz׿>׿>/{ TzO[Kc(6[Fg rzLN8-^ۨЊ3WE~xr d,_24PWtf>آUUirj(C ``vYфˉJ=EC×CL*OgwG>e7ZvɵgR 55&aA Al|YVq|~(J*PW e}y(ʗw陥aFМuɭo$mcX09'|5'Ȏ]~P̎p xhߦrCSp5\4^$+ g{@f1Rě9,>4n Ԭ=)%Jm 8"6H`-v_IiD:yu=o҉A=]n1%롪k-<F6n@@׶rE!~%0JNBuTT _ gm:g&`p:O̭JR4-c;dHXY} 0c-AեQ(ql枹P1dnךo7ڤ2L%_ΐ񊀃B bt5,SLckhě̷$mpN84Javu5>~? ]+6Omzps‰+)qC7 C8ZZ.VڸVb"4-!L#~3an~F fIZ7 y(Lߓ ovsdC"LaZ0}05" L.|eWb[ a9f"RM=ȑQed4+fTVK?f\k!XN5py1$`[R&H*yxHD)Qwj3tűKVp'~l[Y6Y ^2|EhBf\'HуҞyBD~%nHܕ7,Ȫy#6B4g./ @GI9ٿ\i{)NK[LƔi_/VJjm`- m>!MiF ?P D{ֿre8ÞQ+ S}֗Ky%5szn]?yT8/;:v6_ŏ ؉̱ګ%gKiU_7o D:z}>IwF8l͖2œ¢Ls $nw8vGp?p^i/2K0ȥ9gX .8 $pg `AlIW)|r x&/DזdԐ}nyyZ> ?gUJx;} |I~%Iʇ[;ɯ/~S◉ ?E%芷MQǟHmۍZ(×6V;Q I' \v?4󮈫01~>Ee62֧E)O!J{PLD`fG/7<@2ߚk1kb?a@2#Hqʈo':\rxQEL܄i5֮cf}$3VFg;޼wZ2" %9(wu:3Q>]z`SW`x2E!,p(3{J(E)e4{>IHKi Si)(ֺV : 7xHD,e368;"C̡Ȏ9 $ Х&d01dkeI\X2b& zc'2BbGuƣKAle`i#ܮv'!W}2#OA#:>uٿ:E(as?hu)JFXmUp &c]U\|b0wbRco%S 3(m`fp3)/..z1^3 -cA MVg.Z@gOaZecיY` +x1C(Q=(8t4<APqʌBbMe !"uCl3iYQFX!/Ft@-va/Ax^÷VAzc Yl0On B'hqhcH. $|+X$kNOf]"tT Ό}WwQE0\%!IrNI [AaS=w+g] .'d/c' &}CِC7#jvqP#K{Mx5/X9]w(,W>?HKLg|t)== jo~:s&9oDqf/ϡǝU2Eyqx6l ym[ۯȌ9sx H>iRdUhP&2|T:@eL~JX g I:2@~>ܦh؈C+P:@[S6Rv\NMrPL^$RuaĀ;_Yb:Ub=̶t(Ne؂ iN f!,EkhE_sG?r za}Ė+ν0-%`Ҭ}$4Cg+M.h;숺/4d][5 ѐڰgoŪhibMo47h_|d#8B/x6ɴOc[m1SkfdȐinTRЉIJGcRM/3 Dvf*c@ԍ8O}TRf3_1m fvWC'R}/ D"9@3 !uJEex%Ah4)W/H񀌈tD(H2 B[y,B2\C%opuZ//>I)>ґf!2輡+{S]:|iMntNAE9L-䘆jƗog%3o>|߷%5rqr"4əw:ylqk)@IB-uiÎyMP5s r2-^ ~m<w|J1Q|D)˸4j@|8+cEG;wݦ*V&vM#|'szx1Y"!v쾳k(~t2dFn/My7( pidhG Cn׸a#VHHx?6~[*[ٳ6o>Nˢ*j/'V:Pkxg'vOwG>pL~CRqʧ׹豧z7nYcMv(+~> %O٨/6k8\'pD4Up\a? b UUxq~X7r+|6⮖6{KŪ0cNGFndOU9Yf6^(ʯ}@Bտ- BT7 @rP<}G'ޔG{|0QXth3|f#aOe!Z3,# ^DL<4g IDATF38[p:j5S }ȁ(v.W<\VDb0:=v-;(Pf>Oӛص/թ"'^eqFDDg930Z._ 0(qYkLZ<]UO{vy_ڏ_[xp}BAWӷWԖEG>Ij\\o7 Y.okӡ'zK/ٛjqW~PeTh+R.e9*FFtFk hD*Ԧ0$Z<@80Mc)mPJzZ'P1iC*}[q,9H3g<(Fgb"5aAW({W)W]*sdm5uaiYY=AǶ=6K[]@B)*Q[$s98T{+ YF 'X2# MWS@ 8LA`Y0"'C%:0I8Y>E1׿_O/{5=76>aDbe#oV53כ͸ĻCηUUrYU@ƯKn1*f* rYW 8>-KuqY٫ul>fnjdO|?.D:tY]*T[yQrYw=&!7i]ne 0H֔X!Fz؁,$gLpV5%ɭX$3>4 6l˘Vzzh"u |_t]Y%d/|`8'%+=,Gμ^>y|lfӉЯRṃ>?0(dDTULwF#ėvwNqVO&͜싳O#`#hzDO)(˗à^%U8TUUay%,hV^lbE钝^DZr3/U:6yyF|P[afFO %;O$_A[×8Qܽr N9J,)yM}6ΐ1l@Ϥ&AqN]f-;L)]T]J5c#8 $N^I"G$`JE_f7d<;rT9=WZ1h!8%I"g# YC `ZЧVz"zzs'vAdK;G~ G p$.'f$O;""cME!9W1kV?Ow lDGfu]ĸ@I.dS]]bf y95Ĭ e`M'7stuצypF $(O}gl.\`o%" ʦwb_gD(tIAй|2}tӋyZqyce³mz*IS:GOBnؑp8n>5e9֡(y;a /9'!BQq\M-i{rɻzJ3sXIzd-rrpf7]DZh'A2t%hkib}/:ʶݑGU8Iag":Á]-$]67G OƟ܁.}VMz$:t3*FdUXovr!wbQ70<(eH$LYt$e)IU"n7YY}ĥ):2j`SpP31>FJ]IAv%^g~ Vm{XV+wW_Mo EwL> _Q= 鏷Kxٔ1OA d^'ccQݩ^rdWP0xIIO݄w7CԜmz!NUpЃ  ] eq# H|- )d|M6芽Om%;mm} i#u3q@2״2W  rwf7aK )V׵4A?qQ]Chlq-foJY@[&N-j>4whlF-edTwzy''O}ah\JUM?m`n}>ﻔMnj2MU7NzF5WEA:i+"u8nO,\޽^JG#$yIrt["xBjxKj;"GQnw ! ]ZݧLvw 2v^l{] [k#{ ͔KGHu\^^Af |Od-'r}7sU{ mt>e~gDE冱*..NvE6\k!hDoMMֿvBGrbz\¥٦E3$ZNvF˟f˟^p?~As8@"$'=‡kO -z\UU]y[ܬUUŝIS Xjn9ΛUԵ *NGY,{*m1wB~m:=1g9[PꥄVR'_V+m}A տY U"1iM2>Tg)>_t[a-A ktYz h՚ym`tz `s3lVy$"}ϊ9`8l<~kJ9Ñi& /oƏ SE^1;:ln'3C akU:حq!,dOsŪ¢/1c 䕏j^X5]qG@w/B6Y'S:"Gy` xO}EժWgvݑTZ/ |$?|!0י~2_Mxr+ S+@2$Іt$um;Yʫ~L-28hj%VX~dID*]dv#v"HUQR0Ciz,4%h1W5n!fǵt9;" #V׮&j!m{Tzn (qDŖ9Ƭ?a6 elN7 ^P׈x  D` yOTS6[5ze D_<ŷ3`0 =>ORj:oWlu1;h;ʧ~) k)E!KuwSm'RzMTy-իBmBRJXvsn.f9Y֣|^bcN )%+7Ds:zz&pNbo29+AR!gxɡzxFD&Q>TۣiwMj|Pds<j×-尫<֙ ":5ț/4pm碓Aiy,r@G$1z¿]!QK\+&PnV"dj@ePgV"gAouf0ӹN(AZ @MqeR`=sm/bcsoGHU[tn((cV(k ;Ԙy+K=g$ie,0@`0$ D1ٳc5`_< %0Ti>A句~N+tHMU0o?UЭLG%]NS&T8WATkT2Wzi>3ǩ.X/et^>Ӹ]%Ee,;8[*h@+p.QcqfP%s(peqɖ@ä ta!^;Ҋ R%k%jaRY1#=_-:ԑ<>t>ZTcn胯(JZG&כ?vH=2ԧept L:u*GD&v}͖ z%޼uV4?O L揇&,*fFY"a]`9HObG܂310+y ;"$k})JE#3W#HKWDNFBNaqvoN9|®O/QD&m'$ f׽ȃmeZsrd>% ɫW::rf z">a%_R }C{cubn1~X-Ũ_ʊ/;=n2E~: 6(lM)Ca*[sKXR._b0 | ))YuӦ`Oў'jۂ( v;9%Z+ogRzU9}z*S}2QX*z&`8C3{* uA1 }RhkBPMäOƐ[.8#6MRA7[!6kzrfRN]Izn}2KpYmZaȏD{ƾ4 0P *L euFסEܥr:Aj&?֘Oܕ'%zknD@C\h`j50r2uqy5y7!_8]ٻ1Q^d/yZj]UAkV'8 DЋDn`@c34(4PR87ԭatFWC`hI8N`8f @ diN;)[ޤ'1-1Ϯ~*B<]eLf/jQYn%/ti4b'OxJcMoW`qvΩJJU&oWIOxZ\Oo.nfW G/'&;.rzI\fuT>DR!Zc.KVm{xWC5u3%ԔF5< 3}/l6v8Ņց@ LÁG($z>_}p~X3qZ';&SQ ™_Ai#G OM-ՍY?XG!Ron&&m5;g >LMq IDAT\8a,|fPX䭃>{eY~VA Y`psmy:_vNPp+<27ޜ-$ cp(_sPE5Xdv=^ϊb1vq.%QCQ? N^\CRv^ %UGz88vw/?{^;Db:n ~ PEm 3O(`80,hHKfɉG|;kauw¿&I->o%6M#ۋB/D4.+FL*8jKꏵdy"qch/Ϋ̚M|jh/h[^Zy`ۛ»nb6ni#=HvLn'!3vQ?m\yG`\,XPP `A|`gd;g=ڮ4uHH޽*WZ;/< ٢Qi?|>&(Dd PdD4t:j]EM6C* *+ +,MqA{_էTmhW>i;~q{pm Ӿ[Ev"ll- {JRvvm޿\ m6G2T\Y򗉝"|=mpzb/%aU{"yx?~9% ?G%};62)}`>Fx vQP}MU@ ">:"FyGn)TKj#C=^Yʼnח! 6tx΀7;-Z|` { jD'ź{;/UlwA[I5?,D> Li w;2z+ ',nI `=@ 0{tqlҍMd <{+ܮ;~_%7dJvcO8q&qb+1ڧ( Gt$úӆK\:6T(7 OU|d*˽P:jHtqnsU B}̉y0=Ḳ4e._ۢ0[0x5It4,l!?(2ȅ^3*De:Ѯ\ 82@~{ m={ѓJ]RA<&zrY D bZ<,Sy^?эJ™UK%Mz`K0 $)^u]Js UњUQ2s[<01@t:or^EΨtKe%>>T2;%-Z|cT4n;MȻOGh\:{wJ_8e!{wxjӳf͸ٛm>zWRslFl륙Pyv&Y2s]6jؕ&7^8Qs+뼇ܽK/+'  5QJJT{r]Mn1l?wpE\|!l/nѢŷs<L߮Ao7ӗ-cF-6*\ =0w㸥Mn @lU;!fLz ]x{=f\ p|.$c)3`F$IӔA81fj1=nF2~MуnD(Ϝ/͙ꕚMV _UTLLXf}E'z^ 'źݬyS?J@G(h6Bv?~wBH@l#oo-7-a-຾5u%U[\bQ**Cn'*U>O;p&y@Bɓʐu*,J5/˽q2{*,ehP ɓgB[Y g(0,Vx| <=m*K (}mw==X+i Pb^'j#l= [sxR`k!J{tnvde+ _a9 "UvWpJKN(ڈϮd͵*|fW,3b`iRhxEB@ YSBu X~!%UTcoqtX-;!/i#2.{X{C`홍6An&6QUG DX,JlȔ|D-LnhVX Nnu"Iߤ&"sJN I ;]ɾD.YU?WL@v2kyZDt] s֡;%2B(!NP: hW!W~-ZQٶ1D)kzø;n*K+?q,`K>'qJ`[0x*V3TiB }夞(jxqucf@M$ n4ڇOzYlr\@:FmϢJA/׻j> {lvj `=Df`=O^6;.|8--fwS\nB7Nb iFnOa_,Obt% %OON7%2uӮ7i'Ab}M$B`7 ˲@$yMnc?eUK[*(REihUOrU]mxKX'vUIj;5efi i'ANBKRkC;s-ZQ֢A ^M4 27p,/{nIՏȏcַad( Wt$}hb_o{6v|Zy޿͗e/Wn|z ٻeo\dy޿\]Vy9y>_w9?ȗ9v61:Ϻ{o6\vJ?哻 OhP2^zK>>͋89c!Tn,'wq 8ꮬFR\hrZ2D݂ĦtkUs8WBNW|esܢE@'@ᅢdȧ|:-`A38^c+yr``xGjZnmeLfyr$WmvnkBy+GM"unU~ZF~>xz|~0& 'l'دMGH0QyYRP̺?>%)%˲^H*փvD"4BQao&|&j2taLdK˸(:U88Af#גaPMUޣe\ojt-%: uu$)n'IDE&qύs<L߮A{IN_vZL顷w !Up ,$!KFb#xPr/Di?OPqO{_XiHT?`>R*~TB.?ݟvb㯶( 8ݟ&r6@7OhgfqgSErJWiӝLvkx<Sc!8Mha$0O}%;DOi@so=/N4OHtJc=Q,WTOWhpQ+>xlAw-yK=4зc=|=OG^OwGI2&w ;q O=[{C@/ϥ3$Y .̳w?%LMbXHبvՂvӷ[`^LYgN#/WxS5c?֔$"I;a˒M8q&qb+iqj7ZRoV._㸔m7L#Bݨ;mE2@ 6r:UgH)p%ʑl*jk%do-ز@bKl-T&=VΆb]np48ݟd?$*Lb@R)\FIX o* $:&%UbXBd3҇Cśu-u_5ai-Ch2G]Eutgw-i`WیQGyUvĖ79$f$O4Mm$İ 6zJRAT%ԏíΪFf?̱=L\2]Vj%@v5Ǯ4Fź 3h{sS/96G2vfoB/ qPJv`S-Z|?,oCy򦳔o̐|Ɠ'1YnĸqZ{Y4=C!!1[{q'̣hލ搴9ag?ieQ }ߥIoȷ:=7-S_5WpN,P{]XΏU?e38eZn~pQ}(w'*'0x3*n;0\ ph z2Bgڤ:Jy2/q^89ܩ-Z 'Zа(+! IDATX`"lW9Qd *rQOԴ1@ؘ۩H"6oB%c?1u598Vt:|`BI2$qdT?<ܒUka;X-鹕ww7kI05(bݕ%kF SXõ|JOv wM<G!Jemfw: dh[SVKPᶲϑ gJ DnSDS vi&)X^Y 5Q#C:]>Tbh 0lC0H;:pã%Oi V1X+{P S^#GDx#DaKr$:T|J{gtY|O(YRsF z/{!HKb0P-a?H7D/|ТEn,r?`7B jD$^0wue=SG2cbw$CSN,%t/Cꇬ7%w0&0' z&"gςTLr4jXhsa&O?`f}Ow %ݦy4&e]\XzG#Ŭ ;i$+M 3z_HnXDw]+1Ǯr h1 R"4>ٕ&dYL^woTw%ht *=h4x5It4,#36׉J|jiGKm,ǒ,W!pDP~,фN7n͚n~Ub#.~ ~ ~ ~ -.F6$$w `8"3-~0 yfl2YǛ t~xu_Qr_,/NW} @ Dvr`2%j ،tolГ.'.IOǯMہ]I2)#nB1K$.7t)$҄Ȟw&RP4 t$#4}LCl:f5dY)T|J>)(Vr|S59Jİ+?-}$t$٭E's@)ZN E燏ф˳_C#_C#_C#_CgcՎ뷳dXz9z;Mf08|,Q;9<6;O(vb>bσeZ~mW~7ZY/r[\J zlq?UI+'$g"n',nÑ;)"!/Geq*->Cz–%crc:Wʯ KsԟbZ'QiZ|,fR*ȷ{ҐwMr|Ub#.~ ~ ~ ~ -. D7qȼ[8 ϰ7}"t+!  ֿN&fw &a$b ɰ-bgEZPkg@a00,tuݎǪ˾4υOt!>5K *Y7>{gɓPNJdeYBb9~RuRq8s, We9.6bGTX@bxeEy%%ҧ uSʲXluA9VcS前<5t6و˳_Cg#.~ -B4zſ)'T|,d)fd[[Y]weru1) =a^Lf@J7|:07>H)JEO{V3aK>kOhgfqgSEr d$iu#"kqo-NDGyXG ,:oxJCAͣ)xkĬ(r<[FΤk&j=G?bKl쭕luD~?6P20 UH{DΡtF\::::[\7 >_Pz<8 ^ݱjJ>=E7}ځױgmAOֱND, X o@9犷٘-wa%g]} _[`M{W$uU_7tuuX $9U$Yd&yȩn$;) %nWbVZԧJyP`J5N+@j;c?H< Wz*yeueЉ>ocPYn.A\ڵf?ϳ@7dqyk|dk|dk|dklq 0K-Űw svf:t`}K/j~2iٻ-tevCw) v=17Pb|lU-aK.>­u%y*_}<:==@t{KIX귵t>_NuIiz'_/LOHT$@#+%xQq96(%u 7+Ҧ1V&[ği]GWA: u:{Qrvn&\a_t\_v|8_؈˳_C#_C#_C#_CgP9H߃&穎3;_=a3(%-v6`TOncӷ( ;WLnv;/ȡSJ"-pfNG6{zC{D Ǚĉ5iV$dD "5`f@%ɢ6 4zBBu([ tD8F[QgbBfфD@TF ;,F%k(ٸ~[B ؍C~H%:]T前<5t6و˳_Cg#.~ -.AljZ;o#ҚݩA)bHzUv(,z5L/Ld,lBҐf&!? ؼ r/:tDO.|"G8SJP[ ^uAr++*Qj !dg94[|vY!P(M!98%׆fN<;~9}@gNZt\ c4h!@_@o.dȍ.3 y [b)KB]E4Ny{'B[ɈGיmAt2)~1""QݟIm{Zh]` ni:doxv~wblC 6.F g iTdDl۪_+^Akci?qpu m3'I}g@v4WRP%N e!y;" (hk}"ӾQ=)?rAٮNEEHݩZF 8;(wr_U,rt5D.:)J0X߁ߥB[KV|SUv$<\;;M6s5f۹Ϙ>v|>W5\ ~@_܄2 #= O*N`ʮlYtԪIs6nzI\gE5ROj"\ 7}KX| ѷT7%8V;C|:kNu&pY1NPH8Z8,_ߧB[KV|KUql=&ix]OG4Mud\? "GJwdy5펉bG|yeᬵ?LXS뫪=%^LP(rEFD&̣;ϼc8dOpL!}咀c!ǽbɸ 'X*ȏݴ YI5ICuͩ+b$i6S#LVlr_"s>Vm0Vdi-Ц*9xq)Eh+-Z|#  Cՠ2tY'C@m `$hY0ֈ:&QBg R *֢)aT)Q/zC$p*Ŋ Ρ)8c߷JpGNy(>./'LKZBɠ|Jʑp.rIP t yn'Wxg'A,eWCKqQ[Y)BLJ! ۘeuTG3.ٜH~ ~ ~ -.A 5EOz!t:~Mom#xͶ  `(K%y'8UÁ]. -g!ODު rnlw|UUV[ 'H ҧiy}^`iYy<2m繽jRj6[*|& pMT=c8?ve8f_='9*wij>vH!ٽ7*2'u.9R@p4H.?̀=el&&^v28􁱣A> swS+_1::::[\;.ɱw$$wJ;ϳlR-\cٯٯٯe :,ߌoƕ -5p6a{G k_wFkYLi7M}ޘ)UǒOC轜*M~M_$%aV2 : hJ@d,!I*/u&{Tny/udNa2p%|y\Uv/[5UEuca+鵜D2C N4c%^u^^?:qyklٯC10&_k`E>Z`ܲ9QȠ.̑xq,{v˭Uĺ*g?ʺ*𐤪ph6A ad覲Cd25t>25tv$ii x3gWŠ>h&큰&/{.<|dwjL=AwrccD3ty?I/h*\ /eVK,D?O'xH4d*2B;25t>25tc-Z33VEWSm&ӷngϩNnugf~e;,-vvRɰ7WЕ |g/O8IJi?NŚ;JHҿș${^.]S0kKΞQX{SH!9-+'`X?ϝY "KB1EfB]Y.)0=U,}<2JG}k *U{</\b.ǣ6&>eplL|GfGfGfJ=RZ៷O&vC ;c`&: FPNq?Oݝ:x= %֋ Ej`H,s9lvE='gtk52vş wpVUTVVUe:߯8oZ5~XݾߝO0 IDAT=!tbzvbz00}h'Ca2b[bw y},y̲lLGŻe~zjgb+6IMPJ[p:iWnT)[9pڟF$  u%GNR:|[7"^<snu<,) guv=T N'~2H&D4-9lѢ7ˠ4mڽ^O;Ϻt_x{o`'D@ݾۣP`HAޓ!lɧ|m:LMӡ77S?۽*WPt%&t8<"#sJBVr$(ѻ}1IhQg8JJI*eibQh J*5&xvt2kh|HULKEp˲[ؼ-Z@j T.+?nQߛQ`Yr^gX1cd(v %yys:e`"uNC mrulN ^r*{֕ӕ])3t"3nRܻ ')Wҁγ[a9$dv;u;>̟g>1}tcjq>MjVK䘥%I38ulZ!MY rZ۷'b8~>? `2VsvE~>Xӷ0ixos=4**oW}0͸b},tC4ǖv,v(_xp~) }Xç˩. PxB%Ibz{Dă+ aS,x3RPp$+HAgMuz<BCˑ}(Tė!EEXI:Hb|QRK!kVh[ooMߗ@x4wh/!݀~Q2"Oa0u]̀.ݰӅھA~ko7Гr+~[e>UYz&@GztK݆l,'C\26klj3k<_I5OQI  $*yJ<.q҄ Ŵp+4k&ReaXԖ'UiaW(ydҹJ3UϨ*hBdO ߝL()*B"[?7 _'fyory1n92gٯٯٯ`7/_[;ir??v $n6Oo3'EnR#_C#_C#_CgKaGH3۷'mo/6n7lǁa#f&+s(V>$o`OG UTΪk`$iJ_ nC^bmS0Jp4sG.' DŽ[%BO&\c8p "$Z w-rݍ'+~1(Ғ9|3QCiLvIu`O\h۬q|ٯٯٯ%`9WbfA-0y`n0Iyp8|߱)8}Œg~ S*" 3N5jO919Hp)jk_edJL͊~V6űq|ݱF!0ڠeWrSNο mLLvƿ,A5H?:ٯٯٯ%p!-j{v܀ȃ%{4uE|>[]=1H9Vմ/94D D3@x:q>J}{dF\v%I~s8v칁8w.TWJ`K)<:R t  !OU%#u$JXJiNv-oWHgtj>Q:%>R#_C#_C#_CgP3Lo0`LZKÈXgHh..5bWҬO=4g*1qzE2>fx." ;>{W5^rе %ѸI*JBi_CTG%G< ?c?cg.6q^d 6"x</lcd2;E~1lǻϤQUm.7Ub\!K߮׀oZ5'` Ϻth /梚xyz:WY-*ž7OUODGyN`d-G ޯ-Zt;%2d" |F dXpptMwgLƮ[1_m"!!\R]\Dw <_b06iѢwJ%oM;d.[b6y8F֚Fm̀X,f^U꩛Z9u#Z^L j2P $ )U!w @sَ.ˑ]l t5E?aBQ˽\A%BF"fѺE5д,: d+mT6qr hf8]4Ud qԗf؟F 4ڃ6@s|-Zfc߾b0o?6 ^Fկ/vħZhI1 .kz(sU]MhH˟(<\aCsn+q{ZB:G*-\t\$UeRJŖsrIڊBrh[v$]Khk`Tcjb*57ݤ,:E,ZI`]>t kT8nö eo:m YʴhA<~h`7;M|:Xͧrv>CY˫j d` ݀<PT,NʬY4$5н55Xm4UQHJjN/Q!:e +<7C>z-/`.)p{XA@­Ʀ mQTOg)]6<[1o=@\ڝ!4]a>7|`"M}c:U2!/)(\}-Z C@?EnmIQv^WsV3"sx[șL@u_Q趱Ig׎ykST:ϡ䧫VtJXq2z{Ja?k;"pը46R~@t ȸiRJI2 I G׫G+WkLMTbeV]rȀ83N^uv vr1@ Ph[GO8}|:Mw ߟEmpn}CS:0iBdu3nZ6eA ,X` ` ,08 @`  0(0W3'KzG=U5==N 3jA( xbit+)i:pqFrd9ԭC%ACqZ(FU]l) ᐈ-zQ3"Qۤ^vN)؇Gy[Ibkst>T9,O Su"M_0lp]G3vHax:~uӪ{.Klw;R *]gl[&FrMW IG bHRIdHCK%*<WIxտD4|9>WhlG-hU:16xvn9vq9`+F8j6Y~SZe Q3cR&Z2uZ* v;7#R3|ܒf:f#_l2%vɉ86"OEHe2D l֔u< b&C쏮T^vw +S)ccl6YCivU,-h>߂fcP%`$pKy?J6es%0|6"ơOf\8%//$r=RaeN#h?PD$y&٨Ţ<-PEQ@k`*1p3{;gw-J@K 1ĸ"m ^+:0產3HQ*d(i3&D˃aوֲtJr AnJκ(7BCsObkſ,/-hfq|oA0ym vUUs[3Qn `auXQlu+E@n+Mʼ9ĵI y ێ8 %Dhm~8xiW׵VU\~2{P r .HQK<;S^&vD#pF 1 evgZ}=ꔨ ,5-^Na}ޑ`ۺ,-}Ш{WrٍD;tx+2,-h>߂fc@D](Mr /aL!7Ts`!A[=4fץcLcB\L`[R0I":!:ۘ&ϕK^pU#S# V Ft^>fFJv׬nyp>|aDJ(.M&Ak2Nr5jguu ƆneVZQŝp'W\Lte ^b]lj I)vc,M#(PQ:4Ϥ߂3 ,-ha.9/J*Ho-l "[I;@! ߂fcPol,nʣ7 KNz)dIO@gOqT{T+(wWލ+pMqxI= guY~n.} @i( hx 7и6)WIk; ԒЩVJ[Ze^0 g8 suHt?sMUտ䥈,2t{K۳t zwqv}[/]Ǘ?J]piٚIhj sa#Rt==᳅~vK))"L$Q@TBB.2EHH+H33d+9vld`6C{hb(ݴbOĵ.9d@ W ~T,]>3Dѣy[-GeGEaBC KvXu EQgP ox| 4:W ]ãI}l?'I0t -Cu'f.c@-@ e0ki2.In`3H6ݱТ D˖M&̗DYi'e{ y ''lsvE" ` fS.&f{#|SE)2Үg%B(@L8̭'C9%3qE ѐ#r) 29㙳gG6aÄV+W%nS IDAT&XF F۰B۲<2l6EA{{$+/;¯"|@u[F\"(WHewdAp*k V>3^i>#3DLi/C"WjcrOܬ>@#>w?ݴ4B*@sSƕxlVv+tMN2QAo$ @ qw @~9ꖸ)nADL您a&;m2iY~ع&f^V|<8ᴆ%W8ΐu tC[֐MN:|G.NiN\?JnkkV Dyl[Py-eV+ OEb2Շp#bqBmt1Rl@W[izgmڰ:-DV. J Lޙ)fFXeZqהebT\cL~=1[njK]oh&]VZF+ ftd^Vf&.ڃ@6E?;;`ΦehAkJ>1ďMfrM,as!oea':@NH2uF5=+ʴ]b$):Ù`LC*y!E]G =_ w ) Q>BZ*J?C)H70jX˥Mo\qp7@H܂ך S7-e6mlG,Y<FqY6Z#Hlp 㪠Œa,R0=UAgU-j~=2ųMNp̛$qrh,UBf6im2"%)h)]y%PQ"gĮUW < Av?OxkMs0S>`:Xa݀X bfŤʔ1)n*H3!JzBQ]r2aă]8_bIL0E I9␊+VT.JfoP֊I`)gM=fI'T_VC"+·ڔk SB8moцXTd]U!01ú $NɅZ I( }C-VA0ngw Xt)5|FAљ#{~H<{U^93my0G_[+ieCCcGh = `0V⚸⾴[YRkYʺ6d/ R~d}_ `5OZN  oBDxhQL63td{'t_}ha*~I k@~(FSwFD&)DtrKH" L] zK?[1aؗA&*69=FKcֱ.i\ ѧAPPu]jtۍ>WSΗr?FL{îOX;C&F$VB=@XQ)lΰR/DDƸLJ |9&eL!Rp&%^s–F1vqCLtR"c0xU !Mt}JA@`PIn`?Q!Ҟccϖ Wav]ѱpg$ :$ؑj.V2m 92izy?)Xnspl92i)9?{ ⌑:ZQyd$i|)z(@vlH摋C`xl .O6JP׺?WLck?zrW}N{WUfӡ0AOj aB%0-yBwS#6,y(blܒC3 >BKݽrV+يg "NN6Cu8y|4D˓^#S#x;euJF◶9Ȭ`_w^A7|'$1C^GS vq/Ak9'/g9換ǵSo†8]>'-K!w_e|d|~"[/n:\ܠE \YmqٺhP κA]K&B"W#s0R% !@DUGf$Dv92r EeMQrOWr&88d =!t-iygfTd`ʆ̬)\IIGxq9鵦-F=灧 gyA֏IPJ0+pH"\V6!']s&C hhPIVnYu@Ҵ?NN- pg5yj E4qX< hE1oxj_h HrHsP6#qr2uMFUof 0 ȉ9s29JIŕ,G E ؕ#nz.a .|ЏY~CT>Wݿ\z`G g> "Z .>EQoNa5O"9f ˳SUs@\xoQ].b\?F&)sjH$k*'!_)DmRH/"%LX97$eCL0N2Wj0,P֞WQ_Ko_: <}{3[a;3#p'tO9-ſ5R9O'.D> 3&6 #L'?M4޼[duՌUg&)r68vly3p%dNnv}Z(ڇx&v7tJfR6LM F1A6 YQX(v^i 7t0,WW@rv <gG1==bG` x IDAT{3[VX:>ZpDX0 P?SGrxZ~wWո(hz\*a!V׆(pp} W#ZTMpX7hhluvzt%d4F fLͿlT^D/tBWeBN+W$`wڋո(0iU5lt<nƯ>uVt:~*=rph`Ie4:kZuVw ȹnVViz0|YTmw<4{3lw]m8%bweN{BO=@d0@ķ.SolS%YD]:3:(˲,ˠ:Z9Q6=i9/wVt\P;鑐׳ZVwYo;~njϺnlpZ>n O#gfrp[O^br8LT&Ѵc/}O(eNӶ,̽]-vI8vCm,M]|rv7t5kЩZ8 Q}_ 9mA'.obO^Ɏ_ߍ^vuu_muվ4д#̜?]{\55'1MI]gwrgS?٠,5ތI?`L|5xY^ 0 \7dAMYQTyuSH; LX9 :j1UPL? u]Wn&HDPJ4i½Df8/NA@3_ ˠÆqOyas2Np>uu_s 5LJQ'J#91~eg|UՇ9 zk8B&S@á&7Շ|yu/ρ" hypZGû׋٠,!z`FBbM ѫ݅ )iqt\|"[|؂B!E98-wU<([]t2ՉGpI6e`k6ⴸf@jbhjRu-zjF<=X;7+@l6'E v;mlWx2W]4tR$ۇZs~y=tM]l[w{EE~pckx9 SG]i0[k̖ >J9\!f7x(h=aN@aȏNLcL/+ʹo&3r12+5E:K(b~s,G^}؎^n<,gP]؎f[-VLwMԼ3}Q\]{Z4@an(Jw[ #V(M]-՞9V[D^G6)Z 00_7`a; ;Շvd_dbyy6v۝\O#wWIW 7`ujkt9/O>9PH7|<Erz;~=7]庀7WogPJ8Avg`P2vW]q[ ] ttOgaAs̶zW>z6++f~;~5@dJ#NELRhm` r6Jc'k[K`i'wׁ81"|TִUXǎ>`LٱC4WcCʲQO^}NƅXu&8svM/׎j[T Xbfo,I>dY!''Bϝot,ʳ+TxΖhHOp讽p!c ?HZb1Q{B_.^9Zlf#(#ğ02'z T=52x*g-^V(i<]l)-C:>jDyUmhm54Y6F֒pz(Ogbp84Ii xФ qVH[[]`;YT@ps$_Co7@NVZ:f CdOhz<~g\Y/S(Zi\ {GAhA98`?>ch52spZ) >)N؛wpZNKᵻ{y0s );ZT]3M'e9 \ZDW.dht!vjwMtgZSQerfej۲^WG#߮jYl5vrPFj}MbYSYjPWuumD8LH2Ӛ/&69.<).FrK-].h$KglO' A&K_A\s ɣF+$iTdsA./J'g;TMIh3 /]}'o3VFIYv;RQNc <'hMφ~b*){\3?WfMRh/Yˁ٨!/6p Af2oXn"=X$%!ṷ\!RmSYMCg@ȷu`G@f)}_?TW^XokRsQ-]F!oMdญcҟ8v‚sv8U>~Clllى8l*<5~c-8kd|<$K/>X Og- !Fׄ6DP&%juL#lvpmE Zk6wD yez0lw5).&^{2vaӐl$L5U ^7R,>gCr.|k55^ߚ~/goMƷY[Ӈ.3ɧ&F\R@n}DZ6O:xo }Eg 5@𭾯ѣG=zO)TlP|v@ӷ)=}=X=zѣGvz`:ߣ+qV٠*] tI}hգG=z!voiyݿ% IDATyq1~="jb^9²OSAKlW:#/yۣG=z (Љ9^."wvE$5 u 3*ؑH޿mG=z7UM$B~ruX ʢX-ٹ˩sy^t(^EQj  ѣG=z|hAIM]C!s=A1z5٤hcx2۬3RRz 0VP=*݃uI=zѣGA'EU7eYv+!TSQUӌ^]ޖEa~-oeQE8a=pn=zѣn~IQ1y~OR4MyRԏ S5mewPcO >޵U]yhƖbn)JգG=zA^eINut~͇ͦP%:1wO~+Ll*( "4Uʠ9h߃գG=zaBxle<4,sS[`a×_fW׷>WΖeQs]Jo Blji}&8a=zFz.^F-s`*斫:zb~]?͟7j1Q/n6ي|T7=zѣG?ZHQݭ)ȩnmڝ>Wgo/k&8{ZZΗ @"X!'\MYw:|?G=zC~_5Ȝr;PE!-L 򘆂u +b/sهW=zѣG"\!-nһI&, tb]zs-w{N.e"lУG=z¦ǣQp(ÑE22A̦x7c,Bn:)[% Ȉ\besY=zѣw;omcɞ.0kuaY4(4=c=zѣGMQc 7$fglVW13ɲ"ѣG=zجO|h{ѣG=_3d`4ѣG=~$3ۭT /3QLDl7 3XqQɳ|N;q I,߉գG=z9 0G缛w  u6tb]UE9c2{ѣG=8hi7/QS`z G;m4SՉhTU{ќ(-jѣG=K!" 2X6݄GN"7YhY`g%'XSǘG=zǀl~ۤ b~g3 ¤%2ޣG=z!⹫{h2yP).p`vcr{ZѣG=~T2Y#"}-VuJC_t AG=zCvYg-vݎ(:0T/&b:2H#2W23{_FѣG=zZ'T7eY}%Rtb]JByU/ o&??l͙A ˿gtB-no6rscfI/NA%BExϲe=zѣw{Fhn桩Ƣ~Gׅbp<6c͏M~f.G?_I9yV?4C`0N.F?_ f}7?eVuԺjnꦩ~FW:;-`ѣG=舅N)_WnZEQ5HoN~ \_eA揫ws-OLòǓ\'(RT(ʢh녊[n~G=zc,N4MSq(6$nEQ?@u_'4<6勢a4M3/.ߞO]_v~עnP*n 5m%=X$~{=zGEДei0l:|Xtn`4,АBYMӠE08- 4e虰ˋht]I(O q(kf`B]f/{haѣG=~p ^uuC}n~y>MԏMQuSMzϖ5pϖPЛ_gwp48?8']UZԏTnX-֛EaˮV$S6bw AW`fõzѣG=؝5 lmO/ %oy{uC:2|X,֜\ZӰfz(,#:=T =zѣG$/yd BoǞuK B~z,MI(DDb6CqE=zѣG zU0wJ#DУG뉮)&6˷X_usy}-vPv\*G=z{{~hƯ66:4S *(\v`j(s]\^?ʍcyߧA  0X   ,h P` z>Z_k{5<ڪsSzaGDA勔 _wşfP .z+(\-e~h%I'm<uO. ='~<C e 8~T((rp^e8E Z%-}|q!ViEDD8((rl/h+((WgÞ3f40KQEQƗO7 Nw#|f&f/g<+c*kq, 9̦Fhh((J=0N|/:|"̀\=Ok-b>3g~JwIHo4Z$UEQE~v]5x={6jQDCሟ6h2~[_^t=P0E[ɮ)337.*(\"B$IPUI곂QtPyAs- HkEQE#Ut=hxʜ +|t<# }=(((Cr](ʜ{^o'N;^; > U}wnv1/KQEQJO7( EVwE ({dhn2V5(r-Wj9/]7t4"MY (1f /\~LQEQ9l\.ui!S37z=~SKz>7GQRElrn]ow^ݾKs{ÕB-rWEQ.0k)ypȯjr_t?;D7x-jXdL7q;NyϳfDYEZ/Zvq8X,o㩍,ʝF:4E+aEQEQ4@̛v0l.櫘(#un8zѯ=x:?l\qNYoo4up{b]KY W.*(r*kEQ,^>5#"cbqPa .u$uhݶf~kZ/G>/ ~ [SZk ²1g*(\!MԹ4}pc5X"n5ޑ]+?~Z~tc-qD P=qTVY-xzec˝Vs[v̎odfW`?N:ﻢ((ʵՂ[եs4Fx؏_4/wN/$IL|XQ_{?wItcOE/#|g!}ae?xEQEQk&?aWUml>l߭|ze5 `v9k2(n02|Lq^ԕVع TEQEZU˯<EOBȀ0`S -IF5q DB|;X",EQEQj(y?Q07yᑼ,>D((uIiڭAkϓQ0T􉨌3ESg@̙Phf@^"\ )(\'~%.fMLP!?%#n N<*&CB0ᰁ蹕@8((EqE<)KG)ҫ22ǓEQEQl{5^l*,EQEQ7B.*(Rt6nTΘ^BW\dDE)VPEg!{PŹT%̈́3@((JM0nn쾑|de@j Tx 2TF`E,u!Onq 3\((׌:1i40bi)mʮZW@S⣱AWKEQEQ2EeēU,s*U`3Þc6, QC)(|g0 + t3; Š7UQ++3sƜ\첥((rMqL9PSg=])`)(r኏[ ~=3ݬͪR0SVӎ92>Z6̟Y~-O*(\d_|NwxqNكQP5TOƪzJGGWܑ?((C ڥ<2`8Nvx$ @zpퟑ;<0Q]n^ofYp"Ly{((W!ιؒ{6hu@yNw^F=5n<ԥ9ff ?93Xg4REQmEQ|.u`Xbe.jɮ* 48sO'_Aچ JEQEQXv(u4e䜛7ՇMA|og۟:|Jqn=8eTNQEQ%.4Pn,@7h8x=mXd +bS*8Nk#ˈ z Z{cMD >,8 ((WH܃{pC>USI҇tv\yѕ/ۻԹԹrNX, ~όh[n <8|390_ HK(\5yA jg|kXNS\%LtT>L^q ,bmM>m-Έh^O,))|Pw*(RȔePE?u Fc_KUN\u,^-+QXl\,)Dʆ.BEQEQ grW5D &G`8WH'YBT~6;*(\7yR`f.BC՛:ATejYWEQENۭVd[T es[쓡pC+/B*AQ1((5a&IWabaG*Bslew#‘"K(x%cNb* l+|'%7lN(\;l׫{zF_)((_9jҥ((r7q2Eϓ+f&"LD\x0!@^1 b>z9˿LwF5REQŝv84 -ʫ&UD Q%ZR> cf0jZ'Kp=+(RN{0 F 'ݰTRqcj5[ɧ$M=PQEQڐEQh~99'aѼ_ Z9٬3-}bKY5EQEQk˔eXA u90GZg0n((׎9^;H]\CW+pɯeG˿d0XOQEQ)j~~vj›CS_0s)l6K:8˓] ^0((\!9>q><1yؔfCu8m/5 3x;y?GU6W3>X(\7y9I,lvg΅l7+żR+245gotJ7~kM>%?gI76N |!i((9郣|=GLƣaA4jxn{kLBIN9W_b1 l6~{ ~6DΥUT"d_ӥ5X(\9lŲ\Cߴc?Tu~@{Ͽ6,np2sz z klQ-(wi"IS7y3[ft%\Xw\9U9=y 2@TQEQjZ:EQ>" `f6 Gۭ ¬z,ۨ{?wL55B:k6Z S./,EQEQ\tcӇϢI\,~=qrflwPSB9/~G.>,gS3L((rdMvq n\Ml_턈 =ō \d_u(lpڦ:ޖh|W.I*(\Ah{tq>2t>Bs<&`ЀQwTN*(R;GN%DUT6䘎DciA!c߰ݷ6Z:EQEQ QYY!'g uX}8eb`)(r3N\!WŅ[A:2` X@2i<z((OlSrTAa0sR\yt4hfv8sk`S~k+(\+͆U9Gz8eFV,5O8w.?av8 ?(/EQEQ$m׫M|z^J{*(+ԗqձ:((׊M3rXPE`39^W mQJ"Ư6.-|6E!((OvQ 9lAe`/50Ft4\UPUYفʈ-M])EQEQl?:IH8quzrǬ2/uk8~C+I=o@WQEQERv =d*{bUdŸ9ܫO[`tG((5q'&\AEQ7p{b0u}VA937*(\1~e6lQ!x2; 8vm6|#]o8N "Tt PeJ?nqgͧZ˄49Xv43((\5E7) hOlԌA; mFA8l]g;UMqSjs]tcCBQEQ[ gn> 66!⛈2wXq"{pW1\RNů4u( D/#oۓ c'^0hTQEQkǯ5Xt2nZqg .^MF}2sιq)n oGQ3$Żݺ,_owŌȮɛEdmvW>XM?MLw=|-m+ƷM”ϟ[A}P?b>kut'9>YAf. :"Ʒ =o9-s:̜coSZJԷ4^wJ ՑR?HuuAjR~]+TG(πz)O C Q^r4^wJ ՑR?HuuAjR~]+TG(t0~bQ?A85"땗a8toƿ)R{~H!GS#H~~d\Q'L,;큾.*IbpBu?ISq,fbΥC(C#\t"WTr?Ϗ?ԟGYB. `Pk)@ЊTFʁA +"Θ 13;%̜gڻE7cgaxݑ+TG_J ՑR?HuuAjR :h((䙪jᯊ"FZy1Q4d`8xdYX W.OOOTEQE*R' Q7bV G(cb&u~$M6 ɺayPѰAQEQ)Aٟ]t':`uxP>S^t8MVE=DQb\L,ȸȇU My~}q-߯}Կ o՞ϟ[A}P?nc$&AMq_oll ~b|*u 0dל ' /Y^dm$ɔl8|GMC'vy>m & Utss?so_[_%Ϟo~|k}[S{]|CCJ6bH>&eAR:c4[OH::W|1h2Lr7|=<`i ((W/"JRP?&no<q|.|ս ǩ㸁|NDQ\4pHYkPd# 1ar4%6b4DEU^^Wn؛إt7.m x*ì%{O_J ՑR?HuuAjR<As6|*!pj`cr."W-3XY^jqsws=@Z'_s^E~c q"CΧ'?|dt^:R WT.HA#Wg`9t2nZq߫ɨOθKy)snѕO`挣~uw7~3];s19@qdxQFWW4`wM<_.+TG_J ՑR?HuuAjR*7束:|akFD'I,Vy)O–N2Xba{1}̝0,KTDhI=6,h6.T= .+TG_J ՑR?HuuAjRԛ/h3}_^tk {p82 ٬S/ohxW2*^tۋwzћs.2wjw/JO]0T*c8w{G {7PÖ2)ߔKJǟJrBrBrB_Qj WD@YE}D(;/ jAy>PiCd[5-y!*^קҏf]j6om9MdtR>T??2R?Ϗ+ʳTsB12?0p/&y-x/7Ⓗү R{~H^:R WT_QjMYg|ITK~\ی}jRߋ%@<* DA <)kXqNdtR~]+TG_J ՑR?HuR_(js%"\8&ʃ,οUA'GveEQEQkezӴٯ(_((W 6p}PQEQjk|40zkQg΋Rjn8(ʪN˳§8طah1`4dFEQEv|nڭqz=';$F~ WfP,(v?Fq2 = U5X((׍vN5c [Kz@7On n;~n^4Euଽ_1TuAjR~]+TG_J Ց+J))< 0gwj>Ư|& 9E$`'^!Usw)7qybǻfg%{㗐H^:R WT.HA#WZSSʗ-:m/5t2FVX.>CqtyU>@~ /b۰w ;۰hwz|:³KHuuAjR~]+TG_J Ց+J5(I]rQG~r+B8&׫8_qoޯ8ݯfݯ{wzmAFZko"۰hgs{G {7Púm26nu&\[|By8!o%{O?_?_?_(;k)MS2!(>8Kޯ[?-s?-WmN:|=O._{[Zpqs{{ECh4O?vyO|/!r_Iǿ)앎_B#ՇܟGAO# qEy+jqE@^PqWb {p V&eDv,x:wL ĭ{H)ĸ{A θcz7qǫŬ}ם]o>_]Յ~SR?HuuAjR~]+TG(x 9s>\\::CQw( n%Ijk[!s1;wѸn-.ens\cT\Kv8(viOO`TZy6Μ+ƟJ.HA#կ R{~H^:R}EyQL_ m}t>t;xj7~7sFY"S:}lEO Ɋ33o|((W!2"T `Lӓk> {" 2X|Ϙ T)扱=EQEQ*;No,1 '\d,8x^TL*(R%ypȎ >T  dC>@3 l(( vSY[Eb$xKE}cx)V؋O.:~?7Kzl;?[5l_Ss(:kW_B#կ R{~H^:R WT_QjOMb`fVP2Syd= ʸjP^}.pעhwMxܶ:w-w]{7Qd<-OF_O?78ҹdt髟諟~MϞou)OIjwML71tX2'չY H뒽o~|k}[뫟=Z~g7 ?7)iw?|e`Ɋ_5wr~3S&r^%:R WT.HA#կ R{~H0cb)͞ 1ת~BX~Pq,TzGK  /؆9Gw>]ܧնLԟKJ/!ՑR?HuuAjR~]+TG(π.<6>A (&ັ(A,YٰEDZj]RIQ'#԰nʩPBEpt˔ң ߔKJǟJrBrBrB_Qdn7ǓL1Qh&X~YnVDU3I;| /d~5[RyN>pKtjw(_rr^%:R}~~dT??2RWgWUUp8 ~q&vaf]D/daS~_!Bݼ?Qj10zhI0x璽ү R{~H^:R WT_QacO1ʊzBsˈbb> \ & @eWLWؼ_FK|],系^D.MVօKJǟJ.HA#կ R{~H^:R}E7&<, _ܫ<Or^ ȗ B$ɭGvϑ~%:R WT.HA#կ R{~H=?}3WPDY W7Y9`À?ɪ/((׋!1ya PIJȃӨN¡Ô9W5O@1 ,EQEQ+%caYkT˂٬3'<>~Pu~Ɉk~NUQEQ*p{X}|Dbe0,: vhKC0;vι=̙0>uQEQE6læEQy`l*\[<,6_p$kaa)L"BQEQav!ED2TfV'qRuxKycpV4REQEf|:9{c}/XRf8,@wRlإUG-O&vi|;]/lE:q^%:R WT.HA#կ R{~H޻Ȓ{p0eɪrLTaTƇR崣;sy.B0*`:$Py{Qi)®"!yTWZjd`p{G {7Pú0cz7m7b9*0϶IDATS.+*}~ }~ }~ }E5 tϭ紸j@'|WҠ*/'*'>g؄Yjo?m;кmn۽T[ܧS7咽KHuS#H~~dT??2(ϒ2KdYv* NUcGYV4:+"_ݼ? f?m^x\W:TuAjR~]+TG_J Ց+3cvw͞ݞ۳3˧WUDDy\aPUtu&Ee~/B-Kv8(viO^KJǟJ.HA#կ R{~H^:R}Ey6k׻cn8[gf?JY 连~n_\[뫟=Z_gϷW?{MyJv׷Qlp|((rRp2e>xL9eUA=Ѵ /i|; \45FEQEQkgKfX+ȗEe8V,̨2gÂ*fXC1ֹ32f](((׀]tڭ3 L/DՌ4,j{YQEQz(gUe* N,F`TTg1Akga* (("<|CTXG⭃yOA2栤˯WG`((@,]0alf'@l$^En؛إt\hKHuuAjR~]+TG_J Ց+Jɏ)B3Xso+f/ m^r!Ba3ݹ _s{6U%{O_J ՑR?HuuAjRԛ") ԕifbt\M2`E1tztȐ%{O_J ՑR?HuuAjR(I6XEFEQ-msݛ%{|Y۰a=tca%%Bf dU VߔV7qgbFezdtR~]+TG_J ՑR?HuRo2pu\=!u.-n{=o4\w`qNwCffܡsn'pREG6,h6,m4k__UB.+TG_J ՑR?HuuAjRԛ2KE2|RV +u|ړ{k<,%Q + Q'#԰n ̼L_'r>eʺ-7咽҇ܟЇܟЇܟWgstcWcQW@Q]nʳO :PPu~5[퇕ڼm'w8.N:MdtR>T??2R?Ϗ+36(}X <ڈ zTת?چe.Eݽvwp7[0pZm`7yldt^:R WT.HA#Wgsd)}p*u.}H`US?sU^B,ϫ"af&ڼ_FK|` 'Ozx_W:TuAjR~]+TG_J Ց+sIhpɪqn~7?ܬWgOY{bR~]+TG_J ՑR?HuRw⪲'p&ǪB": NK%<l9{/EQEQSdꠛ Zeʃr$E섃 Ј@y$)Wgaח8*(&*O2a8 bAvas!CV|awREQEy" ۭ 7'Q?)+{cvZ>((unpGI]ur3Nm:^N2gM:Yyϗ앎_B#կ R{~H^:R WT_Q̅luh|ƯZ9]r>];F/_Gk%{O_J ՑR?HuuAjR0"{p931P`z Ab9, &k[fC>b^ C:|:Nv[dHvtt@w0Z̦=Ȑ&ԟKJǟJ.HA#կ R{~H^:R}Ey$ӣ"  `Pm$CA+(!Kd-%"" /[ DI _5w~3} u~XO(nK7W7c^=dtR~]+TG_J ՑR?Hu %I9Gê2t54U4_54A=`qJayGwaXk۝~wF_B#կ R{~H^:R WT_QU(jp0S\`t! V ~ ;jX޻ds6s\0<5dq ߌS.+*}~ }~ }~ }EyEhQA۬ ¨Y| PM].g5oBz7咽KHuS#H~~dT??2(?d+:|d"(|9r%02,ynf qWr1Ϣ1Ed^HՏ ǟJ.HA#կ R{~H^:R}E9E$-+GA&9N8 N /:z2-y$kb& ;.˘n~OE{O_J ՑR?HuuAjR< 8n8 :qg;{fʴٱtXqj1{蒽KHuuAjR~]+TG_J Ց+J6(IQX\uU 8bK}AER &ϥ [((WYr. UO@ s:BtT!-9sU((W C郳6"s|q n֗FGZ۰lإ㆙ EQEQ뜃p)_4?*L}qp_͡`Ae{v{'-EQEQk#c{c+G??l:Ԣنu^w7~BUEQEjrW3|a ]ZI }g%rE+cfpȐE(fc\cx|SsrSEQEQgT4Tl妀,>7%ʟdΊ.S~\1EQEQkee JgsIO6 S~S)#Uaorg((ߘ"laVX2>8t, : ޜJH8[e3EQEQ++Vr:Ӑ}Evav0o],N;[Fn O0(00( ,(0(0(0(08`Php 0@a@@AA Վ>k(O笵jF;~XMלs!npZԐ?A{HAա^:T\KU T{~P |яcË+42x_zu ;e]-vOz#==,כ?oWėgbRJ?R@ա^:T\KU_(dR 9BXV\^\pnn $V!B72R? l9waܮ]h<6SV⥾k=}dG^j[^:T\KU T{~P 3XB px9b8Wh!!Ze= *Q6~x5f쟷*>>+9pg#{CT?Pu@U?R@ա yj㥮_ZGBԢrGp:N|DPZZUzJR\WkG6_{܏//KKMAա^:T\KU T{~P ZH9L!]LfA0gt'nmesY; ʦ6PAgqlQl+`.7w\<Y֡r=J^j[ݟgݟgݟg y/1&\n0d^ 4nmI 흌RD~<&+ .J^jz U,~N'9Dz! v>z+&nPkڒ=|̗7*'gG6|V@U?R@ա^:TB!k<+XvuHo0701bOXzCw/`7|Ñonսb>? 1{o T{~Psj/T~.PC/ U[BQҟ ^#(bȶwY s:3yRH3cPu@U?R@ա^:TB!{amjX0RO5ȂUނ Y BP(%O# ? :}!3qZt BP(/mXs8NE/tuZOO-&r=o}P( ;xv35^]dt2}U( B!m_7EqxғP( BMYVox2}.+-6/ui}OUv2;&5$v.ҔPM9&OԹ^z/tc':i=E8e9g@k:,{t,§1$&,jPN N6oD30Uq{=io+s=ڶl$ :&qݫ1ޞ$:Оd1 Xu M[v%٦"e[2&;"pio٩N;}6 M!yd1V{xw60ifis}>tH]N{eGj4vk}}@(M'aF3Mz 1l=$:U/x۰ }ZZ֏Sqm`E24HIW{ǤMM |UbpГJ'1C,hIl[`5M9Ƒ\'mGΑCkMSwJoKpyu9O~DuLő$yяȶ?.A]nsoM2޾RjPY䯗.k.(/At9)p8\x٦+&xE].䯗,pmx_VWiNaK,ܹx9Y[vܴ]U1jj.CzK3_$L`=M8S)Tфw# FqT ; 4Oq8Q(b1@ Fq8Q(ʈN4h s_/S(b1@ Fq8Q(b1Q Y.?@ Fq8Q"Nk"Oq8Qz_7☧kOzEo=fFz_ FqoqC_K}Iz!{Rrd䯗q8}{Z|e1@_!tϿOZAq8Q(b1@ Fq8Q>Hq.3_/o)Dq8Q(b1@ Fq8QĶTƎQn=v h/r{ekYUD3_/WeKQ<=}.5M;^9rq3qz'Brc^߲^cXpIkUR0db7\ݠ䯗)8qϮd?óf036zWm+Q'ϕRVrX*cAz6RӃs/,$I<ϋ~DDZ8N+·Q ǟZn}䯗?U#K]n 1df|h2֍۲p$fXVq=8Q(b1]x* 0M `C_y ,IU..;N9~ߏ繵om)>{ 4u]5}Yf*t-]$7IzQ$j5DZ$U9y3m[&R{Yz7*dYvq>Nnͨg~5{Α{*xdoy>N32nzVOѦ^纮y{RQvį6AGvMpq.7:fERyٰ CsITkemۃK$I>-s?T8}#P fM^YXϻƮQhw:M};M/>wj]3wMЙeKwбlVAQܲuIsQWX} +ڠ(O*Ȳ,~_2=XE嵧¯atbU>haYyZ-sz+E~kelh3*bt3t:j ˶yAL&$I t4džaAPn]6e?R G@spOan$I (/}>w:}~QlcLIyФx*L|+dYnײٍU\^V7v]k[ul#9q|Fu<]ۭo;񦫜.>X(Jб,yU>oZkߚ<!ϲlʲ:m_eEI/˲( U~~fEQi?'t=#9(0It:TB-_cUVq]횻fߟ~Ǯiuk,m;=,ʧmMπǧ3n81 Fq8Q(b1@ Fq8Q(b߆~Rfn3IENDB`hivelytracker-0+git20180223/Docs/images/wave04.png0000775001024000102400000000704613042730153016357 0ustar PNG  IHDRhҫU/tEXtSoftwareSGrab. http://www.stephan-rupprecht.devB IDATx/TXAD "DbE"Q"bDĈ#"FT " DDEĈD"Dn e.m#_t@@@@@@@@@@@@@@@@@@@@@@@@@ZMš) SKQA>4]CUzBx#̟oR;LIhj٫\|/ƣQ=!un:)۾\zy820:xqסikoyb@5@!ήfT*4_aڶu]_=>>ԍ#_Y[dn hV}}bƿzZ}F뚜qL.&Y0{>UQ'&r^UxsvPާŻ6'}3 ^32f[_[^!iiiiiiiiiiiiiiiiiiiiiiiiiiMVcڢc;ڒYOqCڙ~~߬Vq.kHkY]OqT߬68ޫS":xߞ6_4GZ{_jg~::\":C;/Gf0?F\7)!j[~-~ _PހEKfoV(((9\j|I~ P 6/_4-hՆkumoV8H8ц{Y8^L7kcq䏹뺪z;v@c4 E4aNӦE+=u]}sD~8qd!Fm/}P*.87"kVEǪV9]%#7h]( Sv8Ԍ mGfL!Hq@GF_"Nq @NG)*صh|w"6Vy&GqfK.oVc@v8Efu= v8ޠL7elqtAfu=v8trAfu=rqtAfu=v8^ 7fNj 7[]\Y]f_/\4:q߬r;t߬.ڹY'\8Cܪ(@@@@@@@@@@@@@@@@@@@@@@@@ZMš) SKQA>4-qlZ꾪֩q=!RMő?7jL&iTUp84O8$6Msx6'$WuyA/s!(Y)v\vLiGxYٲxeYzN.&e5 V8h8ήf !OR!Dބڡ&;&TREе7Omw<98L9#!r~;`p6<}ZCl#Mk뺳`B]W8#6iƱvsQ|w}j6s4'_'Ƒȝntg6^=_fm?8206wunh8t@@@@ZW#]UUws]7?HY=,Y/]R4MCo_)Ȳ,u_tZkUU-wƟY; ohͣyx"e,\N.I4>VLg*F?8|Ixo县~|1gt}yuj !`Û/T;~8?^_VzjoQphYm}BƑ RU8E ،QycY$˯EÖu]'K(K/0 ӻT;}u0/Vq'f!Z̚r^!Wn}}%IF'wv5bbu@=PK p$wIu3)<^=d2)q>h.p} }'_'X|fM_e]UQU5Ib)2MӲ, U:g~,eY6: r0ŸT}[%ƑQUU5zB>5={(}_UϬZWoU#}~5볭}Vmk!TUug>/y/^k|ɣG+U1L53q*FoB;zK]-'w(ǽ/^Gܪrh!`ţϓORsbmǕ=%˳b$UE}#i8@ӴՃUPF8(ß/Cq$ɲlǎ{;L~1wv5;X5 }s鷧N[mO{,y=]cSS_G6VF;NU{#`,3ҘqFqFqFqFqFqFqFqFqFqFqFqFqFqoIENDB`hivelytracker-0+git20180223/Docs/images/wave06.png0000775001024000102400000000724313042730153016360 0ustar PNG  IHDRhҫU/tEXtSoftwareSGrab. http://www.stephan-rupprecht.devB/IDATx!T " D DDD+"XQXxbE "XQ@T "Ov ed2w{r[dR2OR~(((((((((((((((((((((((((*c(q잺at:ޙ7_i6;5TSrx~wiޙb4<6;ƞA(~EQtq[VO[8q9qh mTB6 >WW[*uG$E"۶_}GݗYYdml P~EwE"x 'e,sgp6h&$wjTӊs)Ba|>7 cjҖ*M8NguG|>u]hŌ#MmBws!D簓|OCKvL8+?y$E"ug4WS#S]j Ѳ5g]kqWrk!l^^㰏lU E/84.맷iT832uYFg|o54G$I `@98H8H8H8Hӵ8"4; j{TeݽHru}~V<ϭC_KiZɤWYYkqL7I']:MIT‹5ʺ{Tr|~̶ُ{Co1UȲχ||OߧՎ~t1_=ktsa螺B&{R5ɼ?>N;2nzvN?u]{RQݣnoH>Wtqdw7\c'KH*7_~,ZAahW< _t ðm{yTy$I֡e OsTi#+ c7G-MB6 >WWu ^forSe9xϺGj__4Y#r۲({MSȞ>/hMoolkEQB4}ߟN+^_\Ju b~#'ߓstoBu- pv?ٝ݋*ޙޥEQgaVrɣ`0HӴ(G6<ӯv| !>uױi^#ܳF8zN{u{^oANY\v*ُ(4Mol>/xuű^$IWN%:ώ6}s p~{ꚦy^ROh۶sۋȫ#=8U9жm߯k f;g/Øqƌ4444444444444̧IENDB`hivelytracker-0+git20180223/Docs/mixingnotes.html0000775001024000102400000001577613042730153016541 0ustar HivelyTracker Documentation

Mixing Notes

I have decided to include a very full description of sound, sound reproduction, how sound is generated digitally, and how digital audio is mixed, so that people can gain a full understanding of digital audio mixing in general, and HivelyTrackers "mix gain" and "autogain" features specifically. It starts off really basic, as in school science class, so most people will probably want to skip to the last parts.

1. Sound and sound reproduction

Sound is the compression and decompression of air, which creates a "sound wave". As a sound wave passes a point in space, the pressure of the air at this point fluctuates. This is detected by small hairs in our ears that move in reaction to this tiny fluctuation in air pressure.

Speakers create sound waves by moving a cone back and forth. When the cone moves forwards, it pushes the air infront of it forwards, creating a slightly higher pressure in the air immediately in front of it. This high pressure area dissipates into the surrounding atmosphere as a sound wave. Similarily, when the cone moves backwards, it creates a low pressure wave in front of the cone. If you move the cone forwards and back at a steady rate, it creates a regular pattern of pressure moving through the room. If the speaker is moving back and forth at a constant rate between 20 times a second and 20000 times a second, it creates a tone within the audible spectrum (IE it can be heard by humans as a constant tone).

Sound waves are communicated to the speakers as a voltage. If the voltage across the speaker terminals is constant, the speaker will be held at a constant position (and you will hear silence since there are no pressure changes occuring). If you increase the voltage across the terminals, the speaker cone will move one way. If you reduce the voltage, the cone will move the other way. So, fluctuations in air pressure are represented as fluctuations in voltage across the speaker terminals.

2. How computers create sound

Sound is stored and processed inside the computer as a list of numbers. Each number corresponds to a position for the speaker cone. An eight bit sample uses one byte per speaker position. A signed byte contains a number from -128 to 127. -128 might mean that the speaker should be pulled fully back, and 127 might mean push it fully forwards. The computer sends a sequence of these numbers through a digital to analogue converter which turns them into a voltage, which is amplified and passed to the speakers. The speed at which the computer sends numbers to the digital to analogue converter is the sample rate. A 44.1Khz sample sends 44100 samples to each speaker every second.

If you sent a sine wave to an 8-bit channel, with the peak values of 127 and -128, with a wavelength of 1/440th of a second, you'd hear a loud tone at A-4. It would be loud because the speaker is moving as far as possible (-127 to 128), and it would be "A-4" because the frequency for A4 is 440Hz. On a graph, the signal looks like this:



3. Mixing sound

So, each speaker is sent a sequence of numbers representing a sound wave. But each channel can only take one sequence of numbers; what if we want to play more than one sounds at the same time in the same channel? We need to combine the sounds. All you have to do is add the two signals together. Imagine we want to play a sine wave at 440Hz and another one at 880Hz at the same time. If they are at maximum volume, it would look like this:


+



=




But theres a problem! The resulting wave has numbers greater than 127, and smaller than -128. These numbers don't fit into the limits of the sound channel, and so the wave is clipped (the flat lines at the top and bottom). The solution is to half the volume of the source waveforms:


+



=




This works great! But there are a couple of downsides. Firstly, each individual sound is now half the volume. Secondly, each sample is effectively a 7bit sample instead of an 8bit sample, so its quality isn't as good.

4. What does this mean for HivelyTracker?

HivelyTracker modules can have up to multiple 16 channels. The simplest way to have 16 channels is to add the sample data for each channel and divide by the number of channels. The problem is, each extra channel you use degrades the overall sample quality, and makes each channel quieter. Also, not all channels are going to be at maximum volume all the time, so it may be possible to keep the channels slightly louder before mixing. How much louder depends on the module.

Instead of this method, Hively gives you a "Mix Gain" control, to manually set the volume of the channels when mixing. At "100", it keeps all the channels at the default volume for replaying AHX songs with 100% stereo seperation (see next section). You can set mix gain from 0% (silent) to 200% (twice as loud) to manually get the best balance between audio quality and clipping. The "Autogain" button scans through the entire module and calculates the highest setting for "Mix Gain" that doesn't have any clipping.

5. Mix Gain vs. Stereo Seperation

By default HivelyTracker pans each channel to the left or to the right in the same order as the Amiga sound chip (IE left, right, right, left). How far they are panned depends on the "Stereo Seperation" setting in prefs. With stereo seperation at 100%, for a 4 channel module, channels 1&4 only show up in the left channel, and channels 2&3 only show up in the right, and so each speaker-channel only has two waveforms added together. With stereo seperation at 0%, both stereo-channels contain all four module channels, and so could need a lower mix gain for the same module.

When you load an AHX module into HivelyTracker, the Mix Gain is set to a predefined level depending on your "Default stereo seperation" setting:
Default Stereo SeperationMix Gain Preset
0% (mono)73
25%74
50%79
75%86
100%100

HVL modules can have a variable number of channels, and each channel can be panned independantly, and so instead of using this kind of preset, the "default stereo seperation" value and mix gain value are saved with the module. When you load the HVL module in, the "default stereo seperation" value of the module is used instead of your own setting, so you hear the song how the author intended.
Back to index hivelytracker-0+git20180223/Docs/keyref.html0000775001024000102400000002147113042730153015447 0ustar HivelyTracker Documentation

Keyboard Reference

ShortcutDescription
EscSwap between track editor and instrument editor
Right AmigaPlay position
Right AltPlay song
SpaceToggle edit mode in track editor and performance list editor
EnterOn main screen: Switch between track editor and position editor
In instrument editor: Toggle fixed
TabMove to the leftmost column in the next channel
Shift + TabMove to the leftmost column in the previous channel
Ctrl + TabMove to the same column in the next channel
Ctrl + Shift + TabMove to the same column in the previous channel
Key next to left shiftStop and kill any sound
DelIn track editor: Delete note and commands at cursor
In pos editor: Delete track & transpose at cursor
In performance list editor: Delete note & commands at cursor
Shift + DelIn track editor: Delete note at cursor
Left Alt + DelIn track editor: Delete commands at cursor
Left Alt + -In track editor: Copy command above cursor and decrement
In pos editor: Copy track number or transpose above cursor and decrement
Left Alt + =In track editor: Copy command above cursor and increment
In pos editor: Copy track number or transpose above cursor and increment
Left Alt + \In track editor: Copy command above cursor
In pos editor: Copy track number or transpose above cursor
F1Select octaves 1 & 2
F2Select octaves 2 & 3
F3Select octaves 3 & 4
F4Select octaves 4 & 5
F5Select octave 5
Shift + F3Cut track
Shift + F4Copy track
Shift + F5Paste
Ctrl + F3Cut commands
Ctrl + F4Copy commands
Ctrl + F5Paste commands
F6Go to stored position 1 (default: top)
F7Go to stored position 2 (default: 1/4 down the track)
F8Go to stored position 3 (default: half way down the track)
F9Go to stored position 4 (default: 3/4 down the track)
F10Go to stored position 5 (default: end of track)
Shift + F6Store track position as position 1
Shift + F7Store track position as position 2
Shift + F8Store track position as position 3
Shift + F9Store track position as position 4
Shift + F10Store track position as position 5
Left Alt + F6Play position from stored position 1
Left Alt + F7Play position from stored position 2
Left Alt + F8Play position from stored position 3
Left Alt + F9Play position from stored position 4
Left Alt + F10Play position from stored position 5
Shift + BackspaceDelete row above in track editor, pos editor or performance list and scroll everything below up one
Shift + EnterInsert row in track editor, pos editor or performance list
Ctrl + BackspaceDelete commands from the note above in track editor or performance list and scroll all commands below up one
Ctrl + EnterInsert row in command columns of track editor or performance list
Cursor leftMove cursor left
Cursor rightMove cursor right
Cursor upMove up one note/position/performance slot
Cursor downMove down one note/position/performance slot
Shift + Cursor leftMove one position up
Shift + Cursor rightMove one position down
Shift + Cursor upIn instrument editor, move one page up
Shift + Cursor downIn instrument editor, move one page down
Ctrl + Cursor leftPrevious instrument
Ctrl + Cursor rightNext instrument
Left Alt + Cursor leftDecrement currently selected track number
Left Alt + Cursor rightIncrement currently selected track number
Ctrl + 0Set note jump to 0 (note jump is number of notes to move down after putting a note into a track)
Ctrl + 1Set note jump to 1
Ctrl + 2Set note jump to 2
Ctrl + 3Set note jump to 3
Ctrl + 4Set note jump to 4
Ctrl + 5Set note jump to 5
Ctrl + 6Set note jump to 6
Ctrl + 7Set note jump to 7
Ctrl + 8Set note jump to 8
Ctrl + 9Set note jump to 9
Ctrl + AToggle channel output
Ctrl + BMark block / stop marking block
Ctrl + CCopy marked block
Ctrl + DDelete marked block
Ctrl + HTranspose marked block up one semitone
Ctrl + IInsert clipboard
Ctrl + KKill to end of track
Ctrl + LTranspose marked block down one semitone
Ctrl + ODelete every second row from cursor to end of track
Ctrl + PPaste clipboard
Ctrl + QUnmute all tracks
Ctrl + RRestore all stored positions to defaults
Ctrl + UUndo
Ctrl + VPaste clipboard
Ctrl + XCut marked block
Ctrl + YReverse track or marked block
Left Alt + AMute all but the current channel
Left Alt + QMute all channels
Shift + Ctrl + KKill from start of track to current position
Shift + Ctrl + URedo
Keypad 0Go to base instrument + 0
Keypad 1Go to base instrument + 1
Keypad 2Go to base instrument + 2
Keypad 3Go to base instrument + 3
Keypad 4Go to base instrument + 4
Keypad 5Go to base instrument + 5
Keypad 6Go to base instrument + 6
Keypad 7Go to base instrument + 7
Keypad 8Go to base instrument + 8
Keypad 9Go to base instrument + 9
Keypad +Add 10 to base instrument
Keypad -Subtract 10 to base instrument
Ctrl + Keypad fullstopKill current instrument

Back to index hivelytracker-0+git20180223/Docs/getstarted.html0000775001024000102400000002715013042730153016330 0ustar HivelyTracker Documentation

HivelyTracker v1.0 from IRIS & Up Rough!

Getting Started

When you first start HivelyTracker, you should be presented with an interface like this:

Main GUI

REMEMBER: Almost every input area in the GUI works with the mousewheel. Often this is the fastest and easiest method!

Along the top row next to the logo is the button panel. The following table lists the button functions:
ButtonLeft clickRight click
New TabOpens a new tab. Right click to create a clone of the current tab.
Load ModLoads an AHX or HVL song into the current tab
Save AHXSaves the current song as AHX format
Save HVLSaves the current song as HVL format
Load InsLoads an AHX or HVL instrument
Save InsSaves an AHX or HVL instrument
PrefsOpens the preferences window
AutogainCalculates optimal mix gain for the current song (see mixing notes)
UndoUndoes the last thing you changed. Right click to redo changes.
AboutOpens the about window
Play SongPlays the song from the top of the current positionContinues the song from the current position
Play PosPlays the current position from the topContinues the current position
StopStops the song and any audioStops the song and any audio

To the right of the button panel are the stereo scopes.

Underneath that first row are the tabs. You can have multiple songs loaded into HivelyTracker, and each one will have a tab here. You simple click on the relevant tab to select the desired song.

Beneath the tabs is the tracker panel. Here is where you edit the songs. The fundamental building blocks of AHX and HVL tunes are "tracks". A track is a list of notes and commands that are parsed from top to bottom. Several of these tracks are parsed in parallel (4 for AHX songs, up to 16 for HVL songs). A song can contain up to 256 of these tracks, and they can be played in any order.

The "position editor" defines the order in which the tracks are played. A song can have up to 1000 positions, and each position records which track should be played, and a transpose value for each channel. The position editor shows the track number and transpose value for each channel as a 3 digit decimal number and 2 digit hexadecimal number respectively. The track number is simply 0 - 255 for the track number. The transpose value is 00 - 7F to transpose up "n" semitones, and 80-FF to transpose down "n" semitones (where FF is -1, FE is -2 etc. etc.)

To the left of the position editor are two columns of numberboxes. They are:

NameFunction
PositionThis is the current position in the song. Changing this numberbox will update the position editor and the track editor
LengthThis is the length of the song in positions. Once the end of position "Length - 1" is reached, the song jumps to the position in the restart box
RestartThis is the position number to jump to once the end of the song is reached
Track LenThis is the length of the tracks from 1 to 64
SubsongsThis is the number of "subsongs". An AHX or HVL song file can contain a number of extra songs after the main one. This is the number of extra subsongs to include, not including the main one
S.S. NumThis is the number of the subsong that you are currently editing. When you change this numberbox, the position editor is set to the starting position of this subsong
S.S. PosThis is the starting position of the current subsong. For example, the main song might be 24 positions long (0 to 23), and subsong 1 could start at 24
ChannelsThis is the number of channels in the song. AHX songs can only have 4 channels
Mix GainThis sets the mix gain (see mixing notes)

Below the position editor is the track editor. Unlike AHX, the track editor is ALWAYS synced to the current position in the song. So, if a 4 channel song had tracks "1", "20", "0" and "0" at the current position, the track editor will show tracks 1, 20, 0 and 0 in the four columns. You can change the track number at a given position by going to the position editor (press ENTER if the track editor is active, or click in it), using the cursor keys to get to the track number you wish to change and typing in a new track number on the keyboard. Alternatively, you can just use the mousewheel in the "track number" box to the right of the mute button for the given channel. The track editor has to be in edit mode before you can put notes or commands into a track. Space toggles edit mode.

Each track in the track editor is split into 3 columns; the note column and two command columns. In edit mode, you use the keyboard like a piano keyboard to write notes into the track. The bottom row starting from the bottom left alphabet key (Z on english and us keyboards) is the low octave keyboard (on GB keyboard, Z = C, S = C#, X = D and so on). The top alphabetical row and the numerical row act as the high octave keyboard (on GB keyboard, Q = C, 2 = C#, W = D etc.). AHX and HVL has 5 octaves. Pressing F1 sets the low octave keys as octave 1, and the high octave keys as octave 2. F2 sets octaves 2&3, F3 sets 3&4, F4 sets 4&5 and F5 sets 5.

Using the cursor keys, you can move the cursor over to the command rows to insert commands. Commands are 3-digit hexadecimal numbers, where the first digit is the command type, and the second two are the effect parameter. See the command reference for more information.

HivelyTracker won't make any noise without instruments. Songs can have up to 63 different instruments. Instruments can be loaded in from disk. The "Load Ins" button loads an instrument file into the current instrument slot. You can change the currently active instrument slot with the numerical keypad (see keyboard reference), or by simply rolling the mousewheel over the instrument list to the right of the position editor.

When you insert notes into tracks, usually it will have an instrument number next to it. For example, "C#1 01" means play C#1 on instrument one. There is an "empty" instrument, which is instrument 0. This shows up as "C#1" with no number next to it. This changes the note of any currently playing sound in the channel without starting any new instrument noises.

Pressing "Ins Ed" takes you into the instrument editor:

Instrument Editor

The instrument editor allows you to create or modify instruments. At the top is the slot number and name of the current instrument. Down the left hand side are a number of parameters you can change:

NameFunction
VolumeThis is the master volume for this instrument
WavelenThis is the wavelength of the oscillators for this instrument. The higher the value, the lower the pitch of the instrument
Envelope
AttackAll instruments start with a volume of 0, and build to the attack volume. The lower this value is, the faster it gets to the attack volume. A value of one means that it instantly reaches this volume
VolumeThis is the volume to reach after the attack period
DecayAfter the attack volume is reach it fades to the decay volume. The larger the decay value, the longer this takes
VolumeThis is the volume to reach after the decay period
SustainThis is how long to hold the decay volume level for
ReleaseAfter the sustain period, the volume fades to the release volume level. This sets how long that takes
VolumeThis is the volume to reach after the relase period
Vibrato
DelayThis is the amount of time to wait before activating the vibrato effect, if any
DepthThis is how much the vibrato modifies the frequency of the note (0 = no vibrato)
SpeedThis is how fast the vibrato effect is. The smaller the number, the faste it goes
Square
Lower L.Sets the lower limit for square relation modulation.
Upper L.Sets the upper limit for square relation modulation
SpeedSets the speed for square relation modulation
Filter
Lower L.Sets the lower limit for filter modulation
Upper L.Sets the upper limit for filter modulation
SpeedSets the speed for filter modulation
-
SpeedSets the speed at which the performance list is parsed
LengthSets the length of the performance list
Hard CutSets a cut-off time before a new note is played for a previous one to stop
Rel. CutToggles release cut

To the right of these parameters is the "performance list". This defines the waveforms and notes that make up the instrument. Like the tracks, it is made up of a list of notes and commands. The commands are similar, but slightly different to the track commands (see command reference).

Like in the track editor, Space toggles between "free play" and "edit" mode. In edit mode, you can insert notes into the performance list using the keyboard as a piano-style keyboard. You can toggle the "fixed" parameter next to any note by hitting enter while in edit mode. Fixed notes have an asterisk (*) next to them. Fixed notes always play at the frequency of the note in the performance list (so, if the performance list has "C#1*", that will always play the note C#1, no matter what note is in the main track. Unfixed notes are added to the main track note, for example, if an instrument has "C#1" at the first position of the performance list, and the track has "C-1 n", where n is the instrument number, the instrument will actually play "C#1". If the track had "D-3 n", the instrument would play "D#3", because C#1 is one semitone higher than C-1, so the final note is one semitone higher than the track note.


Back to index hivelytracker-0+git20180223/Docs/introduction.html0000775001024000102400000000236413042730153016703 0ustar HivelyTracker Documentation

HivelyTracker v1.8 from IRIS & Up Rough!

Introduction

HivelyTracker is a tracker program based upon the AHX format created in the mid '90s by Dexter and Pink of Abyss. The format was relatively popular, and many songs were created and used in scene productions and games. AHX was designed to create a very SID-like sound on the Amiga.

Once I had upgraded to an AmigaOne XE running OS4, I wanted to use it to create AHX tunes, but unfortunately, the AHX tracker was too heavily reliant on the classic Amiga hardware to run directly without using UAE. I decided to try and write my own editor for the AHX format, and that eventually evolved into HivelyTracker.

HivelyTracker can import and export modules and instruments in the AHX format, but it also improves on AHX in several ways and therefore has its own instrument and module formats. HivelyTracker offers the following features over AHX:

  • Multichannel (4 to 16 channels)
  • Per-channel stereo panning
  • Two commands per note instead of one
  • Ring modulation
  • A more feature rich editor

Back to index hivelytracker-0+git20180223/ht.c0000775001024000102400000000363613042730153013166 0ustar /* ** HivelyTracker !!!!11 */ #include #include #include #include #include #include #include "replay.h" #include "gui.h" #include "about.h" BOOL quitting = FALSE; extern uint32 gui_sigs; extern uint32 rp_sigs; extern uint32 about_sigs; extern struct List *rp_tunelist; TEXT __attribute((used)) ver[] = "$VER: HivelyTracker 1.8 (7.5.2013)"; void SetAmiUpdateENVVariable( TEXT *varname ) { BPTR lock; APTR oldwin; if(( lock = IDOS->GetProgramDir() )) { TEXT progpath[2048]; TEXT varpath[1024] = "AppPaths"; if( IDOS->DevNameFromLock( lock, progpath, sizeof( progpath ), DN_FULLPATH ) ) { oldwin = IDOS->SetProcWindow( (APTR)-1 ); IDOS->AddPart( varpath, varname, 1024 ); IDOS->SetVar( varpath, progpath, -1, GVF_GLOBAL_ONLY|GVF_SAVE_VAR ); IDOS->SetProcWindow( oldwin ); } } } BOOL init( void ) { SetAmiUpdateENVVariable( "HivelyTracker" ); gui_pre_init(); about_pre_init(); if( !rp_init() ) return FALSE; if( !gui_init() ) return FALSE; if( !about_init() ) return FALSE; return TRUE; } void shutdown( void ) { about_shutdown(); gui_shutdown(); rp_shutdown(); } int main( void ) { signal( SIGINT, SIG_IGN ); if( init() ) { uint32 gotsigs; while( !quitting ) { gotsigs = IExec->Wait( gui_sigs | rp_sigs | about_sigs | SIGBREAKF_CTRL_C ); if( gotsigs & rp_sigs ) { rp_handler( gotsigs ); if( quitting ) break; // If rp_handler() says we need to quit, don't do anything else but just QUIT... } if( gotsigs & SIGBREAKF_CTRL_C ) { signal( SIGINT, SIG_IGN ); quitting = TRUE; } if( gotsigs & about_sigs ) about_handler( gotsigs ); if( gotsigs & gui_sigs ) gui_handler( gotsigs ); } rp_stop(); } shutdown(); return 0; } hivelytracker-0+git20180223/about.h0000775001024000102400000000031013042730153013654 0ustar void about_pre_init( void ); BOOL about_init( void ); void about_shutdown( void ); void about_open( void ); void about_close( void ); void about_toggle( void ); void about_handler( uint32 gotsigs ); hivelytracker-0+git20180223/winicon.bmp0000664001024000102400000000607013042730153014545 0ustar BM8 6(    fDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDjGjGjGjGjGjGgDgEjGjGjGjGjGjGjGjGjGjGjGjGjGjGjGjGjGjGfDfDfDfDiFxZ<*   'wY( ĵӷۿvfۿδεȺȺȺȺȺȺŝoS7jGfDfDjGoS6޽} ʹܻ£tbS}x࿢ݽ      qU8jGfDfDjGoS6 ~yt|riXKhWKhWJm]Q{}J=4oliu|tgVJeUHfUHfUHfUHfUHlZMJ>5pU8jGfDfDjGoS6 pmhOB8[LC_OFfSHeSHfSHeRF[KA]NEWH?$ecaRE;ZLB_OFeSHeSHeSHeSHeSHeSHkXLI<4qT8jGfDfDjGoS6 pjf=-;+?2$^TI_TH^SH`UJF9,9*@/!gdaB2!;,=/!]RG_TH^SH^SH^SH^SHcXLD<4qT8jGfDfDjGoS6}k@@@:;=:;@')+oS7jGfDfDjGoS6#  勁vô 2$.!.!.!. 1#8*z\=hFfDfDjGoS6sV8eJ1򎊅^E-sLlHlHlHlHlHlHgEfDfDfDjGoS6ömQ5_F. XA+lHfDfDfDfDfDfDfDfDfDfDjGoT7QQQvsrurqYXWnR6`H/baasqowts?>>@@Awtssqoa_^YB,lHfDfDfDfDfDfDfDfDfDfDiFtX:   tW:iO4     cK2kGfDfDfDfDfDfDfDfDfDfDfDfDeCeDeDeDeDeCfDfDeCeDeDeDeDeDeDeDeDeCfDfDfDfDfDfDfDfDfDfDfDfDfDfDgEgEgEgEgEgEfDfDgEgEgEgEgEgEgEgEgEgEfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDfDhivelytracker-0+git20180223/about.c0000775001024000102400000003202713042730153013661 0ustar /* ** Theres not many "about" dialogs that get their own C file ;-) */ #include #include #include #include #include #include "replay.h" #include "gui.h" #define NUMSTARS 100 struct star { int16 x, y; int16 sp; }; struct star stars[NUMSTARS]; #ifndef __SDL_WRAPPER__ extern struct Screen *scr; struct Window *aboutwin = NULL; extern struct Window *mainwin; uint32 about_winsig = 0; uint32 about_sigs = 0; uint32 about_timesig; struct MsgPort *ab_tioport = NULL; struct TimeRequest *ab_tioreq = NULL; BOOL ab_tiopen = FALSE; BOOL ab_tiopending = FALSE; #else BOOL aboutwin_open; extern struct rawbm mainbm; extern SDL_Event event; extern struct ahx_tune *curtune; extern int32 wm_count; extern struct ahx_tune *rp_curtune; SDL_TimerID thetimer = 0; #endif int16 ab_x = -1, ab_y = -1; struct rawbm about_bm; struct rawbm afont_bm; struct rawbm stuff_bm; int16 slice[320/2]; float64 sinpos = 0, cospos = 0; const TEXT *scrtxt = " hivelytracker version 1.8 :: code by xeron of iris " ":: gui design and skins created by spot of up rough " ":: example tunes by xeron of iris, varthall of up rough, oxide of sonik and syphus " ":: hivelytracker is based on ahx by dexter and pink of abyss " ":: many thanks to pieknyman for helping me find and fix bugs in the replayer. you rule! " ":: large parts of ahxplay.library by ilkka lehtoranta and per johansson were referenced for the replayer " ":: thanks to passing by and kode54 for replayer improvements " ":: thanks to spot, varthall and the rest of up rough for their ideas, tunes and betatesting " ":: thanks to buzz for the aros and morphos ports, deltafire for the os x port, and pulkomandy for the haiku port " ":: thanks to the betatesters of the sdl port as well! " ":: greets... rno, tbl, tulou, scarab, nature, triad, dcs, fairlight, and yes, even loonies. " ":: remember kids, say no to blancmange! " ":: extra thanks to spot for his nagging and complaining, without " " whom hively would not be as cool as it now is! " ":: so... why did i write an ahx based tracker for os4? for one simple " "reason: i wanted to create ahx tunes on my amigaone without having to " "resort to uae. unfortunately ahx used the cia timers and paula directly " "and so it didnt work properly at all under os4. so, i sat down and made " "my own os4 ahx tracker, and i started adding new features as i did so. " "i hope you enjoy it and the tunes made in it! " "messages from buzz:iwouldliketothankxeronforthisgreattracker,andbeingpatientwithme " "whileiportit.also thankstoallthebetatestersonirc ... hisyphus. " "i wouldalsoliketothankstingrayforhisuselesstipsandcodeadvice.justkidding!" "heisrightaboutonething though,mycodedoessuck! finally hi to yaz: " "thanks for sending me that new tune ! "; const TEXT *scrtab = "abcdefghijklmnopqrstuvwxyz!,.:?1234567890"; int32 scro=0, scrs=30, scrc=0; int32 bx1=0, bx2=320; #ifdef __SDL_WRAPPER__ Uint32 about_timing( Uint32 interval, void *userdata ) { if( aboutwin_open ) { SDL_Event tmpevent; SDL_UserEvent userevent; userevent.type = SDL_USEREVENT; userevent.code = 88; userevent.data1 = NULL; userevent.data2 = NULL; tmpevent.type = SDL_USEREVENT; tmpevent.user = userevent; SDL_PushEvent( &tmpevent ); } return 16; } #endif void about_pre_init( void ) { int32 i; #ifndef __SDL_WRAPPER__ about_bm.bm = NULL; afont_bm.bm = NULL; stuff_bm.bm = NULL; #else about_bm.srf = NULL; afont_bm.srf = NULL; stuff_bm.srf = NULL; #endif for( i=0; i<160; i++ ) slice[i] = -1; } BOOL about_init( void ) { int32 r, g, b, i; #ifndef __SDL_WRAPPER__ ab_tioport = IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE); if( !ab_tioport ) { printf( "Unable to create message port\n" ); return FALSE; } about_timesig = 1L<mp_SigBit; ab_tioreq = (struct TimeRequest *)IExec->AllocSysObjectTags(ASOT_IOREQUEST, ASOIOR_ReplyPort, ab_tioport, ASOIOR_Size, sizeof( struct TimeRequest ), TAG_DONE); if( !ab_tioreq ) { printf( "Unable to create io request!\n" ); return FALSE; } // Get timer.device ab_tiopen = !IExec->OpenDevice( "timer.device", UNIT_MICROHZ, (struct IORequest *)ab_tioreq, 0 ); if( !ab_tiopen ) { printf( "Unable to open timer.device\n" ); return FALSE; } about_sigs = about_timesig; if( !make_image( &about_bm, 320, 240 ) ) return FALSE; #else SDL_Rect cliprect = { .x = 4, .y = 4, .w = 320, .h = 240 }; if( !make_image( &about_bm, 328, 248 ) ) return FALSE; about_bm.offx = 4; about_bm.offy = 4; SDL_SetClipRect(about_bm.srf, &cliprect); #endif if( !make_image( &stuff_bm, 640, 2 ) ) return FALSE; if( !open_image( "aboutfont", &afont_bm ) ) return FALSE; set_fcol(&stuff_bm, 0x000000); fillrect_xy(&stuff_bm, 0, 0, 639, 1); r = 255<<8; g = 0; b = 0; for( i=0; i<106; i++ ) { set_fcol(&stuff_bm, ((r&0xff00)<<8)|(g&0xff00)|((b&0xff00)>>8)); #ifndef __SDL_WRAPPER__ IGraphics->WritePixel( &stuff_bm.rp, i, 0 ); #else fillrect_xy(&stuff_bm, i, 0, i, 0); #endif r -= ((255*256)/106); g += ((255*256)/106); } r = 0; g = 255<<8; b = 0; for( i=106; i<213; i++ ) { set_fcol(&stuff_bm, ((r&0xff00)<<8)|(g&0xff00)|((b&0xff00)>>8)); #ifndef __SDL_WRAPPER__ IGraphics->WritePixel( &stuff_bm.rp, i, 0 ); #else fillrect_xy(&stuff_bm, i, 0, i, 0); #endif g -= ((255*256)/107); b += ((255*256)/107); } r = 0; g = 0; b = 255<<8; for( i=213; i<320; i++ ) { set_fcol(&stuff_bm, ((r&0xff00)<<8)|(g&0xff00)|((b&0xff00)>>8)); #ifndef __SDL_WRAPPER__ IGraphics->WritePixel( &stuff_bm.rp, i, 0 ); #else fillrect_xy(&stuff_bm, i, 0, i, 0); #endif b -= ((255*256)/107); r += ((255*256)/107); } set_fcol(&stuff_bm, 0xffffff); fillrect_xy(&stuff_bm, 0, 1, 0, 1); set_fcol(&stuff_bm, 0xaaaaaa); fillrect_xy(&stuff_bm, 1, 1, 1, 1); set_fcol(&stuff_bm, 0x888888); fillrect_xy(&stuff_bm, 2, 1, 2, 1); set_fcol(&stuff_bm, 0x444444); fillrect_xy(&stuff_bm, 3, 1, 3, 1); for( i=0; iLeftEdge; ab_y = aboutwin->TopEdge; IIntuition->CloseWindow( aboutwin ); aboutwin = NULL; about_sigs &= ~about_winsig; about_winsig = 0; if( ab_tiopending ) { IExec->AbortIO( (struct IORequest *)ab_tioreq ); IExec->WaitIO( (struct IORequest *)ab_tioreq ); ab_tiopending = FALSE; } #else aboutwin_open = FALSE; if (thetimer) SDL_RemoveTimer(thetimer); thetimer = 0; gui_render_everything(); #endif } void about_shutdown( void ) { about_close(); #ifndef __SDL_WRAPPER__ if( about_bm.bm ) IGraphics->FreeBitMap( about_bm.bm ); if( afont_bm.bm ) IGraphics->FreeBitMap( afont_bm.bm ); if( stuff_bm.bm ) IGraphics->FreeBitMap( stuff_bm.bm ); if( ab_tiopen ) { if( ab_tiopending ) { IExec->AbortIO( (struct IORequest *)ab_tioreq ); IExec->WaitIO( (struct IORequest *)ab_tioreq ); ab_tiopending = FALSE; } IExec->CloseDevice( (struct IORequest *)ab_tioreq ); } if( ab_tioreq ) IExec->FreeSysObject( ASOT_IOREQUEST, ab_tioreq ); if( ab_tioport ) IExec->FreeSysObject( ASOT_PORT, ab_tioport ); about_bm.bm = NULL; afont_bm.bm = NULL; stuff_bm.bm = NULL; #else if (about_bm.srf) SDL_FreeSurface(about_bm.srf); if (afont_bm.srf) SDL_FreeSurface(afont_bm.srf); if (stuff_bm.srf) SDL_FreeSurface(stuff_bm.srf); about_bm.srf = NULL; afont_bm.srf = NULL; stuff_bm.srf = NULL; #endif } void about_open( void ) { #ifndef __SDL_WRAPPER__ if( aboutwin ) return; if( ab_x == -1 ) { ab_x = mainwin->LeftEdge + 40; ab_y = mainwin->TopEdge + 40; } aboutwin = IIntuition->OpenWindowTags( NULL, WA_Left, ab_x, WA_Top, ab_y, WA_InnerWidth, 320, WA_InnerHeight, 240, WA_Title, "HivelyTracker v1.8", WA_ScreenTitle, "HivelyTracker (c)2013 IRIS & Up Rough! - http://www.uprough.net - http://www.hivelytracker.co.uk", WA_RMBTrap, TRUE, WA_IDCMP, IDCMP_CLOSEWINDOW, //|IDCMP_MOUSEBUTTONS, WA_Activate, TRUE, WA_DragBar, TRUE, WA_CloseGadget, TRUE, WA_DepthGadget, TRUE, scr ? WA_CustomScreen : TAG_IGNORE, scr, TAG_DONE ); if( !aboutwin ) return; about_winsig = (1L<UserPort->mp_SigBit); about_sigs |= about_winsig; if( ab_tiopending ) { IExec->AbortIO( (struct IORequest *)ab_tioreq ); IExec->WaitIO( (struct IORequest *)ab_tioreq ); ab_tiopending = FALSE; } ab_tioreq->Request.io_Command = TR_ADDREQUEST; ab_tioreq->Time.Seconds = 0; ab_tioreq->Time.Microseconds = 20000; IExec->SendIO( (struct IORequest *)ab_tioreq ); ab_tiopending = TRUE; #else thetimer = SDL_AddTimer( 20, (SDL_NewTimerCallback)about_timing, NULL ); aboutwin_open = TRUE; #endif } void about_toggle( void ) { #ifndef __SDL_WRAPPER__ if( aboutwin ) #else if( aboutwin_open ) #endif about_close(); else about_open(); } void about_frame( void ) { int32 i; float64 stmp, ctmp; #ifndef __SDL_WRAPPER__ if( aboutwin == NULL ) return; #else if( !aboutwin_open ) return; #endif set_fcol(&about_bm, 0x000000); fillrect_xy(&about_bm, 0, 0, 319, 239); for( i=0; i<158; i++ ) slice[i] = slice[i+2]; scrs+=4; if( scrs > 31 ) { scrs = 0; scro++; if( scrtxt[scro] == 0 ) scro = 0; for( scrc=0; scrc -1 ) { slice[158] = (scrc<<5)+scrs; slice[159] = (scrc<<5)+scrs+2; } else { slice[158] = slice[159] = -1; } for( i=0; iBltBitMapRastPort( about_bm.bm, 0, 0, aboutwin->RPort, aboutwin->BorderLeft, aboutwin->BorderTop, 320, 240, 0x0C0 ); #else bm_to_bm(&about_bm, -4, -4, &mainbm, 236, 176, 328, 248); #endif } //BOOL pause = FALSE; void about_handler( uint32 gotsigs ) { #ifndef __SDL_WRAPPER__ struct IntuiMessage *msg; BOOL closeme; closeme = FALSE; if( gotsigs & about_timesig ) { if( ab_tiopending ) { IExec->WaitIO( (struct IORequest *)ab_tioreq ); ab_tiopending = FALSE; } if( aboutwin != NULL ) { // if( !pause ) about_frame(); ab_tioreq->Request.io_Command = TR_ADDREQUEST; ab_tioreq->Time.Seconds = 0; ab_tioreq->Time.Microseconds = 28571; // 35 FPS IExec->SendIO( (struct IORequest *)ab_tioreq ); ab_tiopending = TRUE; } } if( gotsigs & about_winsig ) { while( ( msg = (struct IntuiMessage *)IExec->GetMsg( aboutwin->UserPort ) ) ) { switch( msg->Class ) { /* case IDCMP_MOUSEBUTTONS: if( msg->Code == SELECTUP ) pause ^= 1; break; */ case IDCMP_CLOSEWINDOW: closeme = TRUE; break; } IExec->ReplyMsg( (struct Message *)msg ); } } if( closeme ) about_close(); #else switch (event.type) { case SDL_USEREVENT: switch (event.user.code) { case 0: if( curtune == rp_curtune ) { gui_render_tunepanel( FALSE ); gui_render_vumeters(); } if( (wm_count++)&1 ) gui_render_wavemeter(); bm_to_bm(&about_bm, -4, -4, &mainbm, 236, 176, 328, 248); break; case 88: about_frame(); break; } break; case SDL_MOUSEBUTTONUP: if (event.button.button == SDL_BUTTON_LEFT) about_close(); break; case SDL_KEYUP: if (event.key.keysym.sym == SDLK_ESCAPE) about_close(); break; } #endif } hivelytracker-0+git20180223/tables.h0000664001024000102400000004441013042730153014022 0ustar #define WHITENOISELEN (0x280*3) #define WO_LOWPASSES 0 #define WO_TRIANGLE_04 (WO_LOWPASSES+((0xfc+0xfc+0x80*0x1f+0x80+3*0x280)*31)) #define WO_TRIANGLE_08 (WO_TRIANGLE_04+0x04) #define WO_TRIANGLE_10 (WO_TRIANGLE_08+0x08) #define WO_TRIANGLE_20 (WO_TRIANGLE_10+0x10) #define WO_TRIANGLE_40 (WO_TRIANGLE_20+0x20) #define WO_TRIANGLE_80 (WO_TRIANGLE_40+0x40) #define WO_SAWTOOTH_04 (WO_TRIANGLE_80+0x80) #define WO_SAWTOOTH_08 (WO_SAWTOOTH_04+0x04) #define WO_SAWTOOTH_10 (WO_SAWTOOTH_08+0x08) #define WO_SAWTOOTH_20 (WO_SAWTOOTH_10+0x10) #define WO_SAWTOOTH_40 (WO_SAWTOOTH_20+0x20) #define WO_SAWTOOTH_80 (WO_SAWTOOTH_40+0x40) #define WO_SQUARES (WO_SAWTOOTH_80+0x80) #define WO_WHITENOISE (WO_SQUARES+(0x80*0x20)) #define WO_HIGHPASSES (WO_WHITENOISE+WHITENOISELEN) #define WAVES_SIZE (WO_HIGHPASSES+((0xfc+0xfc+0x80*0x1f+0x80+3*0x280)*31)) uint16 pt_import_period_tab[] = { 0, 856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453, 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, 214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113 }; static const uint16 lentab[45] = { 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, (0x280*3)-1 }; static const int16 vib_tab[] = { 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253,255, 253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, 0,-24,-49,-74,-97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253,-255, -253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120,-97,-74,-49,-24 }; static const uint16 period_tab[] = { 0x0000, 0x0D60, 0x0CA0, 0x0BE8, 0x0B40, 0x0A98, 0x0A00, 0x0970, 0x08E8, 0x0868, 0x07F0, 0x0780, 0x0714, 0x06B0, 0x0650, 0x05F4, 0x05A0, 0x054C, 0x0500, 0x04B8, 0x0474, 0x0434, 0x03F8, 0x03C0, 0x038A, 0x0358, 0x0328, 0x02FA, 0x02D0, 0x02A6, 0x0280, 0x025C, 0x023A, 0x021A, 0x01FC, 0x01E0, 0x01C5, 0x01AC, 0x0194, 0x017D, 0x0168, 0x0153, 0x0140, 0x012E, 0x011D, 0x010D, 0x00FE, 0x00F0, 0x00E2, 0x00D6, 0x00CA, 0x00BE, 0x00B4, 0x00AA, 0x00A0, 0x0097, 0x008F, 0x0087, 0x007F, 0x0078, 0x0071 }; static const int32 stereopan_left[] = { 128, 96, 64, 32, 0 }; static const int32 stereopan_right[] = { 128, 160, 193, 225, 255 }; static const int16 filter_thing[] = { -1161, -4413, -7161, -13094, 635, 13255, 2189, 6401, 9041, 16130, 13460, 5360, 6349, 12699, 19049, 25398, 30464, 32512, 32512, 32515, 31625, 29756, 27158, 24060, 20667, 17156, 13970, 11375, 9263, 7543, 6142, 5002, 4074, 3318, 2702, 2178, 1755, 1415, 1141, 909, 716, 563, 444, 331, -665, -2082, -6170, -9235, -13622, 12545, 9617, 3951, 8345, 11246, 18486, 6917, 3848, 8635, 17271, 25907, 32163, 32512, 32455, 30734, 27424, 23137, 18397, 13869, 10429, 7843, 5897, 4435, 3335, 2507, 1885, 1389, 1023, 720, 530, 353, 260, 173, 96, 32, -18, -55, -79, -92, -95, -838, -3229, -7298, -12386, -7107, 13946, 6501, 5970, 9133, 14947, 16881, 6081, 3048, 10921, 21843, 31371, 32512, 32068, 28864, 23686, 17672, 12233, 8469, 5862, 4058, 2809, 1944, 1346, 900, 601, 371, 223, 137, 64, 7, -34, -58, -69, -70, -63, -52, -39, -26, -14, -5, 4984, -4476, -8102, -14892, 2894, 12723, 4883, 8010, 9750, 17887, 11790, 5099, 2520, 13207, 26415, 32512, 32457, 28690, 22093, 14665, 9312, 5913, 3754, 2384, 1513, 911, 548, 330, 143, 3, -86, -130, -139, -125, -97, -65, -35, -11, 6, 15, 19, 19, 16, 12, 8, 6877, -5755, -9129, -15709, 9705, 10893, 4157, 9882, 10897, 19236, 8153, 4285, 2149, 15493, 30618, 32512, 30220, 22942, 14203, 8241, 4781, 2774, 1609, 933, 501, 220, 81, 35, 2, -18, -26, -25, -20, -13, -7, -1, 2, 4, 4, 3, 2, 1, 0, 0, -1, 2431, -6956, -10698, -14594, 12720, 8980, 3714, 10892, 12622, 19554, 6915, 3745, 1872, 17779, 32512, 32622, 26286, 16302, 8605, 4542, 2397, 1265, 599, 283, 45, -92, -141, -131, -93, -49, -14, 8, 18, 18, 14, 8, 3, 0, -2, -3, -2, -2, -1, 0, 0, -3654, -8008, -12743, -11088, 13625, 7342, 3330, 11330, 14859, 18769, 6484, 3319, 1660, 20065, 32512, 30699, 21108, 10616, 5075, 2425, 1159, 477, 196, 1, -93, -109, -82, -44, -12, 7, 14, 13, 9, 4, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, -7765, -8867, -14957, -5862, 13550, 6139, 2988, 11284, 17054, 16602, 6017, 2979, 1489, 22351, 32512, 28083, 15576, 6708, 2888, 1243, 535, 188, 32, -47, -64, -47, -22, -3, 7, 8, 5, 3, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9079, -9532, -16960, -335, 13001, 5333, 2704, 11192, 18742, 13697, 5457, 2703, 1351, 24637, 32512, 24556, 10851, 4185, 1614, 622, 184, 15, -57, -59, -34, -9, 5, 8, 6, 2, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -8576, -10043, -18551, 4372, 12190, 4809, 2472, 11230, 19803, 11170, 4953, 2473, 1236, 26923, 32512, 20567, 7430, 2550, 875, 212, 51, -30, -43, -25, -6, 3, 5, 3, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6960, -10485, -19740, 7864, 11223, 4449, 2279, 11623, 20380, 9488, 4553, 2280, 1140, 29209, 31829, 16235, 4924, 1493, 452, 86, -7, -32, -20, -5, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4739, -10974, -19831, 10240, 10190, 4169, 2114, 12524, 20649, 8531, 4226, 2114, 1057, 31495, 29672, 11916, 3168, 841, 121, 17, -22, -18, -5, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2333, -11641, -19288, 11765, 9175, 3923, 1971, 13889, 20646, 8007, 3942, 1971, 985, 32512, 27426, 8446, 1949, 449, 45, -11, -16, -5, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, -12616, -17971, 12690, 8247, 3693, 1846, 15662, 20271, 7658, 3692, 1846, 923, 32512, 25132, 6284, 1245, 246, -71, -78, -17, 8, 7, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2232, -14001, -15234, 13198, 7447, 3478, 1736, 17409, 19411, 7332, 3472, 1736, 868, 32512, 22545, 4352, 731, 18, -117, -40, 8, 9, 2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4197, -15836, -11480, 13408, 6791, 3281, 1639, 19224, 18074, 6978, 3276, 1639, 819, 32512, 19657, 2706, 380, -148, -86, 2, 13, 3, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5863, -17878, -9460, 13389, 6270, 3104, 1551, 20996, 16431, 6616, 3102, 1551, 776, 32512, 16633, 1921, 221, -95, -39, 5, 5, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7180, -20270, -6194, 13181, 5866, 2946, 1473, 22548, 14746, 6273, 2946, 1473, 737, 32512, 13621, 1263, 116, -53, -15, 4, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8117, -21129, -2795, 12809, 5550, 2804, 1402, 23717, 13326, 5962, 2804, 1402, 701, 32512, 10687, 776, -56, -56, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8560, -19953, 508, 12299, 5295, 2675, 1337, 25109, 12263, 5684, 2675, 1338, 669, 32512, 7905, 433, -36, -22, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8488, -18731, 3672, 11679, 5080, 2558, 1279, 26855, 11480, 5434, 2557, 1279, 639, 32512, 5357, 212, -95, 0, 4, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7977, -24055, 6537, 10986, 4883, 2450, 1225, 28611, 10918, 5206, 2450, 1225, 612, 32512, 3131, 83, -35, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7088, -30584, 9054, 10265, 4696, 2351, 1176, 28707, 10494, 4996, 2351, 1175, 588, 32512, 1920, -155, -13, 4, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5952, -32627, 11249, 9564, 4519, 2260, 1130, 28678, 10113, 4803, 2260, 1130, 565, 32512, 1059, -73, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4629, -32753, 13199, 8934, 4351, 2175, 1088, 28446, 9775, 4623, 2175, 1087, 544, 32512, 434, -22, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3132, -32768, 15225, 8430, 4194, 2097, 1049, 30732, 9439, 4456, 2097, 1049, 524, 32512, 75, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, -32768, 16765, 8107, 4048, 2025, 1012, 32512, 9112, 4302, 2025, 1012, 506, 32385, 392, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -706, -32768, 17879, 8005, 3913, 1956, 978, 32512, 8843, 4157, 1957, 978, 489, 31184, 1671, 122, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3050, -32768, 18923, 8163, 3799, 1893, 946, 32512, 8613, 4022, 1893, 945, 473, 29903, 3074, 316, 52, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5812, -32768, 19851, 8626, 3739, 1833, 917, 32512, 7982, 3889, 1833, 916, 459, 28541, 4567, 731, 206, 66, 23, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9235, -32768, 20587, 9408, 3841, 1784, 889, 32512, 6486, 3688, 1776, 889, 447, 27099, 6112, 1379, 313, 135, 65, 33, 17, 7, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -12713, 1188, 1318, -1178, -4304, -26320, -14931, -1716, -1486, 2494, 3611, 22275, 27450, -31839, -29668, -26258, -21608, -15880, -9560, -3211, 3138, 9369, 15281, 20717, 25571, 29774, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32748, 32600, 32750, 32566, 32659, 32730, 8886, 1762, 506, -1665, -12112, -24641, -8513, -2224, 247, 3288, 9926, 25787, 28909, -31048, -27034, -20726, -12532, -3896, 4733, 13043, 20568, 27010, 32215, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32762, 32696, 32647, 32512, 32665, 32512, 32587, 32638, 32669, 32681, 32679, 32667, 32648, 32624, 32598, 6183, 2141, -630, -2674, -21856, -18306, -5711, -2161, 2207, 4247, 17616, 26475, 29719, -30017, -23596, -13741, -2819, 8029, 18049, 26470, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32738, 32663, 32612, 32756, 32549, 32602, 32629, 32636, 32628, 32610, 32588, 32564, 32542, 32524, 32510, 32500, 32494, 32492, 3604, 2248, -1495, -5612, -26800, -13545, -4745, -1390, 3443, 6973, 23495, 27724, 30246, -28745, -19355, -6335, 6861, 19001, 28690, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32667, 32743, 32757, 32730, 32681, 32624, 32572, 32529, 32500, 32482, 32476, 32477, 32482, 32489, 32497, 32504, 32509, 32513, 7977, 1975, -1861, -9752, -25893, -10150, -4241, 86, 4190, 10643, 25235, 28481, 30618, -27231, -14398, 1096, 15982, 27872, 32512, 32512, 32512, 32512, 32512, 32734, 32631, 32767, 32531, 32553, 32557, 32551, 32539, 32527, 32516, 32509, 32505, 32504, 32505, 32506, 32508, 32510, 32511, 32512, 32512, 32512, 32511, 14529, 1389, -2028, -14813, -22765, -7845, -3774, 1986, 4706, 14562, 25541, 29019, 30894, -25476, -9294, 8516, 23979, 32512, 32512, 32512, 32512, 32512, 32512, 32708, 32762, 32727, 32654, 32579, 32522, 32490, 32478, 32480, 32488, 32498, 32507, 32512, 32515, 32515, 32514, 32513, 32512, 32510, 32510, 32510, 32510, 17663, 557, -2504, -19988, -19501, -6436, -3340, 4135, 5461, 18788, 26016, 29448, 31107, -23481, -4160, 15347, 30045, 32512, 32512, 32512, 32512, 32512, 32674, 32700, 32654, 32586, 32531, 32498, 32486, 32488, 32496, 32504, 32510, 32513, 32514, 32513, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 16286, -402, -3522, -23951, -16641, -5631, -2983, 6251, 6837, 22781, 26712, 29788, 31277, -21244, 1108, 21806, 32512, 32512, 32512, 32512, 32695, 32576, 32622, 32600, 32557, 32520, 32501, 32496, 32500, 32505, 32509, 32512, 32512, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 13436, -1351, -4793, -25948, -14224, -5151, -2702, 7687, 8805, 25705, 27348, 30064, 31415, -18766, 5872, 26652, 32512, 32512, 32512, 32747, 32581, 32620, 32586, 32540, 32508, 32497, 32499, 32505, 32510, 32512, 32512, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10427, -2162, -7136, -26147, -12195, -4810, -2474, 8723, 11098, 27251, 27832, 30293, 31530, -16047, 10877, 30990, 32512, 32512, 32512, 32512, 32584, 32571, 32536, 32511, 32502, 32503, 32507, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 7797, -2748, -10188, -25174, -10519, -4515, -2281, 9397, 13473, 27937, 28213, 30487, 31627, -13087, 15816, 32512, 32512, 32512, 32715, 32550, 32560, 32534, 32512, 32505, 32506, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 5840, -3084, -13327, -23617, -9177, -4231, -2116, 9892, 15843, 28292, 28538, 30652, 31710, -9886, 20235, 32512, 32512, 32512, 32512, 32550, 32534, 32514, 32507, 32507, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 4592, -3215, -15898, -21856, -8141, -3958, -1972, 10401, 18229, 28612, 28824, 30796, 31781, -7103, 24037, 32512, 32512, 32745, 32535, 32534, 32517, 32508, 32508, 32509, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 3964, -3262, -18721, -20087, -7368, -3705, -1847, 11014, 20634, 28996, 29075, 30920, 31843, -4732, 27243, 32512, 32512, 32648, 32627, 32530, 32495, 32500, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 3858, -3404, -21965, -18398, -6801, -3479, -1738, 12009, 22960, 29429, 29294, 31030, 31898, -2281, 30194, 32512, 32512, 32699, 32569, 32496, 32496, 32509, 32513, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 4177, -3869, -24180, -16820, -6380, -3280, -1640, 13235, 25035, 29863, 29490, 31128, 31947, 251, 32758, 32512, 32749, 32652, 32508, 32490, 32507, 32513, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 4837, -4913, -26436, -15364, -6056, -3103, -1553, 14759, 26704, 30256, 29664, 31215, 31991, 2863, 32512, 32512, 32657, 32580, 32503, 32501, 32510, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 5755, -6290, -27702, -14036, -5788, -2947, -1474, 16549, 27912, 30602, 29821, 31294, 32030, 5555, 32512, 32512, 32592, 32541, 32505, 32507, 32511, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 6898, -8911, -27788, -12841, -5550, -2805, -1403, 18509, 28687, 30906, 29963, 31364, 32066, 8328, 32512, 32512, 32623, 32511, 32502, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 8107, -11465, -27077, -11789, -5325, -2676, -1339, 19833, 29213, 31179, 30092, 31429, 32098, 11181, 32512, 32512, 32561, 32508, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 9247, -13203, -25808, -10886, -5109, -2559, -1280, 21060, 29636, 31428, 30209, 31488, 32127, 14114, 32512, 32681, 32529, 32502, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 10252, -16863, -24251, -10137, -4902, -2451, -1226, 21937, 30022, 31656, 30317, 31542, 32154, 17128, 32512, 32581, 32514, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11032, -22427, -22598, -9535, -4705, -2353, -1177, 20999, 30406, 31867, 30415, 31591, 32179, 20222, 32512, 32591, 32501, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11539, -19778, -20962, -9060, -4522, -2261, -1131, 19486, 30789, 32061, 30507, 31637, 32201, 23396, 32512, 32535, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11803, -12759, -19353, -8690, -4353, -2177, -1089, 18499, 31165, 32240, 30591, 31678, 32222, 26651, 32512, 32514, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11826, -7586, -17510, -8384, -4196, -2099, -1050, 26861, 31521, 32406, 30669, 31718, 32241, 29986, 32585, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11599, -2848, -15807, -8097, -4051, -2025, -1014, 30693, 31850, 32561, 30743, 31755, 32261, 32512, 32524, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 11037, -5302, -14051, -7770, -3913, -1958, -980, 28033, 32165, 32705, 30810, 31789, 32278, 32512, 32729, 32536, 32513, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10114, -7837, -12293, -7348, -3782, -1894, -948, 24926, 32473, 32512, 30873, 31819, 32294, 32512, 32512, 32580, 32527, 32515, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 8759, -10456, -10591, -6766, -3638, -1835, -917, 24058, 32600, 32512, 30934, 31850, 32309, 32512, 32512, 32729, 32591, 32537, 32520, 32514, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 6811, -13156, -9045, -5965, -3421, -1776, -890, 31582, 32246, 32512, 30988, 31878, 32324, 32512, 32512, 32512, 32628, 32573, 32541, 32526, 32518, 32514, 32513, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 4835 }; hivelytracker-0+git20180223/Replayer_Windows/0000775001024000102400000000000013042730153015671 5ustar hivelytracker-0+git20180223/Replayer_Windows/ChangeLog.txt0000664001024000102400000000134213042730153020261 0ustar Hively Tracker Windows Command Line Player Change Log 1.8 === + 1.8 replayer bug fixes 1.6 === + 1.6 replayer bug fixes 1.5 === + Plays files that require HT1.5 features 1.4 === + Added support for 9xx instrument playlist panning command + Fixed a really stupid bug 1.3 === + Should now play all AHX tunes properly + Shouldn't clip with loud tunes and stereo mixing levels below 100% any more. 1.1 === + Added 4xx command that was missing from both HivelyTracker and WinAHX for some unknown reason. The code was always there in WinAHX for all these years, but the 4xx command wasn't implimented so it was never called. 1.0 === + Bodged together this crappy example hivelytracker-0+git20180223/Replayer_Windows/hvl_tables.c0000664001024000102400000005150013042730153020161 0ustar #include #include "hvl_replay.h" #include "hvl_tables.h" const uint16 lentab[45] = { 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, (0x280*3)-1 }; const int16 vib_tab[64] = { 0,24,49,74,97,120,141,161,180,197,212,224,235,244,250,253, 255,253,250,244,235,224,212,197,180,161,141,120,97,74,49,24, 0,-24,-49,-74,-97,-120,-141,-161,-180,-197,-212,-224,-235,-244,-250,-253, -255,-253,-250,-244,-235,-224,-212,-197,-180,-161,-141,-120,-97,-74,-49,-24 }; const uint16 period_tab[61] = { 0x0000, 0x0D60, 0x0CA0, 0x0BE8, 0x0B40, 0x0A98, 0x0A00, 0x0970, 0x08E8, 0x0868, 0x07F0, 0x0780, 0x0714, 0x06B0, 0x0650, 0x05F4, 0x05A0, 0x054C, 0x0500, 0x04B8, 0x0474, 0x0434, 0x03F8, 0x03C0, 0x038A, 0x0358, 0x0328, 0x02FA, 0x02D0, 0x02A6, 0x0280, 0x025C, 0x023A, 0x021A, 0x01FC, 0x01E0, 0x01C5, 0x01AC, 0x0194, 0x017D, 0x0168, 0x0153, 0x0140, 0x012E, 0x011D, 0x010D, 0x00FE, 0x00F0, 0x00E2, 0x00D6, 0x00CA, 0x00BE, 0x00B4, 0x00AA, 0x00A0, 0x0097, 0x008F, 0x0087, 0x007F, 0x0078, 0x0071 }; const int32 stereopan_left[5] = { 128, 96, 64, 32, 0 }; const int32 stereopan_right[5] = { 128, 160, 193, 225, 255 }; const int16 filter_thing[2790] = { -1161, -4413, -7161, -13094, 635, 13255, 2189, 6401, 9041, 16130, 13460, 5360, 6349, 12699, 19049, 25398, 30464, 32512, 32512, 32515, 31625, 29756, 27158, 24060, 20667, 17156, 13970, 11375, 9263, 7543, 6142, 5002, 4074, 3318, 2702, 2178, 1755, 1415, 1141, 909, 716, 563, 444, 331, -665, -2082, -6170, -9235, -13622, 12545, 9617, 3951, 8345, 11246, 18486, 6917, 3848, 8635, 17271, 25907, 32163, 32512, 32455, 30734, 27424, 23137, 18397, 13869, 10429, 7843, 5897, 4435, 3335, 2507, 1885, 1389, 1023, 720, 530, 353, 260, 173, 96, 32, -18, -55, -79, -92, -95, -838, -3229, -7298, -12386, -7107, 13946, 6501, 5970, 9133, 14947, 16881, 6081, 3048, 10921, 21843, 31371, 32512, 32068, 28864, 23686, 17672, 12233, 8469, 5862, 4058, 2809, 1944, 1346, 900, 601, 371, 223, 137, 64, 7, -34, -58, -69, -70, -63, -52, -39, -26, -14, -5, 4984, -4476, -8102, -14892, 2894, 12723, 4883, 8010, 9750, 17887, 11790, 5099, 2520, 13207, 26415, 32512, 32457, 28690, 22093, 14665, 9312, 5913, 3754, 2384, 1513, 911, 548, 330, 143, 3, -86, -130, -139, -125, -97, -65, -35, -11, 6, 15, 19, 19, 16, 12, 8, 6877, -5755, -9129, -15709, 9705, 10893, 4157, 9882, 10897, 19236, 8153, 4285, 2149, 15493, 30618, 32512, 30220, 22942, 14203, 8241, 4781, 2774, 1609, 933, 501, 220, 81, 35, 2, -18, -26, -25, -20, -13, -7, -1, 2, 4, 4, 3, 2, 1, 0, 0, -1, 2431, -6956, -10698, -14594, 12720, 8980, 3714, 10892, 12622, 19554, 6915, 3745, 1872, 17779, 32512, 32622, 26286, 16302, 8605, 4542, 2397, 1265, 599, 283, 45, -92, -141, -131, -93, -49, -14, 8, 18, 18, 14, 8, 3, 0, -2, -3, -2, -2, -1, 0, 0, -3654, -8008, -12743, -11088, 13625, 7342, 3330, 11330, 14859, 18769, 6484, 3319, 1660, 20065, 32512, 30699, 21108, 10616, 5075, 2425, 1159, 477, 196, 1, -93, -109, -82, -44, -12, 7, 14, 13, 9, 4, 0, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, -7765, -8867, -14957, -5862, 13550, 6139, 2988, 11284, 17054, 16602, 6017, 2979, 1489, 22351, 32512, 28083, 15576, 6708, 2888, 1243, 535, 188, 32, -47, -64, -47, -22, -3, 7, 8, 5, 3, 0, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9079, -9532, -16960, -335, 13001, 5333, 2704, 11192, 18742, 13697, 5457, 2703, 1351, 24637, 32512, 24556, 10851, 4185, 1614, 622, 184, 15, -57, -59, -34, -9, 5, 8, 6, 2, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -8576, -10043, -18551, 4372, 12190, 4809, 2472, 11230, 19803, 11170, 4953, 2473, 1236, 26923, 32512, 20567, 7430, 2550, 875, 212, 51, -30, -43, -25, -6, 3, 5, 3, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6960, -10485, -19740, 7864, 11223, 4449, 2279, 11623, 20380, 9488, 4553, 2280, 1140, 29209, 31829, 16235, 4924, 1493, 452, 86, -7, -32, -20, -5, 2, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4739, -10974, -19831, 10240, 10190, 4169, 2114, 12524, 20649, 8531, 4226, 2114, 1057, 31495, 29672, 11916, 3168, 841, 121, 17, -22, -18, -5, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2333, -11641, -19288, 11765, 9175, 3923, 1971, 13889, 20646, 8007, 3942, 1971, 985, 32512, 27426, 8446, 1949, 449, 45, -11, -16, -5, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, -12616, -17971, 12690, 8247, 3693, 1846, 15662, 20271, 7658, 3692, 1846, 923, 32512, 25132, 6284, 1245, 246, -71, -78, -17, 8, 7, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2232, -14001, -15234, 13198, 7447, 3478, 1736, 17409, 19411, 7332, 3472, 1736, 868, 32512, 22545, 4352, 731, 18, -117, -40, 8, 9, 2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4197, -15836, -11480, 13408, 6791, 3281, 1639, 19224, 18074, 6978, 3276, 1639, 819, 32512, 19657, 2706, 380, -148, -86, 2, 13, 3, -2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5863, -17878, -9460, 13389, 6270, 3104, 1551, 20996, 16431, 6616, 3102, 1551, 776, 32512, 16633, 1921, 221, -95, -39, 5, 5, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7180, -20270, -6194, 13181, 5866, 2946, 1473, 22548, 14746, 6273, 2946, 1473, 737, 32512, 13621, 1263, 116, -53, -15, 4, 2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8117, -21129, -2795, 12809, 5550, 2804, 1402, 23717, 13326, 5962, 2804, 1402, 701, 32512, 10687, 776, -56, -56, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8560, -19953, 508, 12299, 5295, 2675, 1337, 25109, 12263, 5684, 2675, 1338, 669, 32512, 7905, 433, -36, -22, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8488, -18731, 3672, 11679, 5080, 2558, 1279, 26855, 11480, 5434, 2557, 1279, 639, 32512, 5357, 212, -95, 0, 4, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7977, -24055, 6537, 10986, 4883, 2450, 1225, 28611, 10918, 5206, 2450, 1225, 612, 32512, 3131, 83, -35, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7088, -30584, 9054, 10265, 4696, 2351, 1176, 28707, 10494, 4996, 2351, 1175, 588, 32512, 1920, -155, -13, 4, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5952, -32627, 11249, 9564, 4519, 2260, 1130, 28678, 10113, 4803, 2260, 1130, 565, 32512, 1059, -73, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4629, -32753, 13199, 8934, 4351, 2175, 1088, 28446, 9775, 4623, 2175, 1087, 544, 32512, 434, -22, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3132, -32768, 15225, 8430, 4194, 2097, 1049, 30732, 9439, 4456, 2097, 1049, 524, 32512, 75, -6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1345, -32768, 16765, 8107, 4048, 2025, 1012, 32512, 9112, 4302, 2025, 1012, 506, 32385, 392, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -706, -32768, 17879, 8005, 3913, 1956, 978, 32512, 8843, 4157, 1957, 978, 489, 31184, 1671, 122, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3050, -32768, 18923, 8163, 3799, 1893, 946, 32512, 8613, 4022, 1893, 945, 473, 29903, 3074, 316, 52, 11, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -5812, -32768, 19851, 8626, 3739, 1833, 917, 32512, 7982, 3889, 1833, 916, 459, 28541, 4567, 731, 206, 66, 23, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -9235, -32768, 20587, 9408, 3841, 1784, 889, 32512, 6486, 3688, 1776, 889, 447, 27099, 6112, 1379, 313, 135, 65, 33, 17, 7, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, -12713, 1188, 1318, -1178, -4304, -26320, -14931, -1716, -1486, 2494, 3611, 22275, 27450, -31839, -29668, -26258, -21608, -15880, -9560, -3211, 3138, 9369, 15281, 20717, 25571, 29774, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32748, 32600, 32750, 32566, 32659, 32730, 8886, 1762, 506, -1665, -12112, -24641, -8513, -2224, 247, 3288, 9926, 25787, 28909, -31048, -27034, -20726, -12532, -3896, 4733, 13043, 20568, 27010, 32215, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32762, 32696, 32647, 32512, 32665, 32512, 32587, 32638, 32669, 32681, 32679, 32667, 32648, 32624, 32598, 6183, 2141, -630, -2674, -21856, -18306, -5711, -2161, 2207, 4247, 17616, 26475, 29719, -30017, -23596, -13741, -2819, 8029, 18049, 26470, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32738, 32663, 32612, 32756, 32549, 32602, 32629, 32636, 32628, 32610, 32588, 32564, 32542, 32524, 32510, 32500, 32494, 32492, 3604, 2248, -1495, -5612, -26800, -13545, -4745, -1390, 3443, 6973, 23495, 27724, 30246, -28745, -19355, -6335, 6861, 19001, 28690, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32667, 32743, 32757, 32730, 32681, 32624, 32572, 32529, 32500, 32482, 32476, 32477, 32482, 32489, 32497, 32504, 32509, 32513, 7977, 1975, -1861, -9752, -25893, -10150, -4241, 86, 4190, 10643, 25235, 28481, 30618, -27231, -14398, 1096, 15982, 27872, 32512, 32512, 32512, 32512, 32512, 32734, 32631, 32767, 32531, 32553, 32557, 32551, 32539, 32527, 32516, 32509, 32505, 32504, 32505, 32506, 32508, 32510, 32511, 32512, 32512, 32512, 32511, 14529, 1389, -2028, -14813, -22765, -7845, -3774, 1986, 4706, 14562, 25541, 29019, 30894, -25476, -9294, 8516, 23979, 32512, 32512, 32512, 32512, 32512, 32512, 32708, 32762, 32727, 32654, 32579, 32522, 32490, 32478, 32480, 32488, 32498, 32507, 32512, 32515, 32515, 32514, 32513, 32512, 32510, 32510, 32510, 32510, 17663, 557, -2504, -19988, -19501, -6436, -3340, 4135, 5461, 18788, 26016, 29448, 31107, -23481, -4160, 15347, 30045, 32512, 32512, 32512, 32512, 32512, 32674, 32700, 32654, 32586, 32531, 32498, 32486, 32488, 32496, 32504, 32510, 32513, 32514, 32513, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 16286, -402, -3522, -23951, -16641, -5631, -2983, 6251, 6837, 22781, 26712, 29788, 31277, -21244, 1108, 21806, 32512, 32512, 32512, 32512, 32695, 32576, 32622, 32600, 32557, 32520, 32501, 32496, 32500, 32505, 32509, 32512, 32512, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 13436, -1351, -4793, -25948, -14224, -5151, -2702, 7687, 8805, 25705, 27348, 30064, 31415, -18766, 5872, 26652, 32512, 32512, 32512, 32747, 32581, 32620, 32586, 32540, 32508, 32497, 32499, 32505, 32510, 32512, 32512, 32512, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10427, -2162, -7136, -26147, -12195, -4810, -2474, 8723, 11098, 27251, 27832, 30293, 31530, -16047, 10877, 30990, 32512, 32512, 32512, 32512, 32584, 32571, 32536, 32511, 32502, 32503, 32507, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 7797, -2748, -10188, -25174, -10519, -4515, -2281, 9397, 13473, 27937, 28213, 30487, 31627, -13087, 15816, 32512, 32512, 32512, 32715, 32550, 32560, 32534, 32512, 32505, 32506, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 5840, -3084, -13327, -23617, -9177, -4231, -2116, 9892, 15843, 28292, 28538, 30652, 31710, -9886, 20235, 32512, 32512, 32512, 32512, 32550, 32534, 32514, 32507, 32507, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 4592, -3215, -15898, -21856, -8141, -3958, -1972, 10401, 18229, 28612, 28824, 30796, 31781, -7103, 24037, 32512, 32512, 32745, 32535, 32534, 32517, 32508, 32508, 32509, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 3964, -3262, -18721, -20087, -7368, -3705, -1847, 11014, 20634, 28996, 29075, 30920, 31843, -4732, 27243, 32512, 32512, 32648, 32627, 32530, 32495, 32500, 32510, 32512, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 3858, -3404, -21965, -18398, -6801, -3479, -1738, 12009, 22960, 29429, 29294, 31030, 31898, -2281, 30194, 32512, 32512, 32699, 32569, 32496, 32496, 32509, 32513, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 4177, -3869, -24180, -16820, -6380, -3280, -1640, 13235, 25035, 29863, 29490, 31128, 31947, 251, 32758, 32512, 32749, 32652, 32508, 32490, 32507, 32513, 32512, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 4837, -4913, -26436, -15364, -6056, -3103, -1553, 14759, 26704, 30256, 29664, 31215, 31991, 2863, 32512, 32512, 32657, 32580, 32503, 32501, 32510, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 5755, -6290, -27702, -14036, -5788, -2947, -1474, 16549, 27912, 30602, 29821, 31294, 32030, 5555, 32512, 32512, 32592, 32541, 32505, 32507, 32511, 32511, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 6898, -8911, -27788, -12841, -5550, -2805, -1403, 18509, 28687, 30906, 29963, 31364, 32066, 8328, 32512, 32512, 32623, 32511, 32502, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 8107, -11465, -27077, -11789, -5325, -2676, -1339, 19833, 29213, 31179, 30092, 31429, 32098, 11181, 32512, 32512, 32561, 32508, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 9247, -13203, -25808, -10886, -5109, -2559, -1280, 21060, 29636, 31428, 30209, 31488, 32127, 14114, 32512, 32681, 32529, 32502, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 10252, -16863, -24251, -10137, -4902, -2451, -1226, 21937, 30022, 31656, 30317, 31542, 32154, 17128, 32512, 32581, 32514, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11032, -22427, -22598, -9535, -4705, -2353, -1177, 20999, 30406, 31867, 30415, 31591, 32179, 20222, 32512, 32591, 32501, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11539, -19778, -20962, -9060, -4522, -2261, -1131, 19486, 30789, 32061, 30507, 31637, 32201, 23396, 32512, 32535, 32508, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11803, -12759, -19353, -8690, -4353, -2177, -1089, 18499, 31165, 32240, 30591, 31678, 32222, 26651, 32512, 32514, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11826, -7586, -17510, -8384, -4196, -2099, -1050, 26861, 31521, 32406, 30669, 31718, 32241, 29986, 32585, 32510, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 32511, 11599, -2848, -15807, -8097, -4051, -2025, -1014, 30693, 31850, 32561, 30743, 31755, 32261, 32512, 32524, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 11037, -5302, -14051, -7770, -3913, -1958, -980, 28033, 32165, 32705, 30810, 31789, 32278, 32512, 32729, 32536, 32513, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 10114, -7837, -12293, -7348, -3782, -1894, -948, 24926, 32473, 32512, 30873, 31819, 32294, 32512, 32512, 32580, 32527, 32515, 32512, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 8759, -10456, -10591, -6766, -3638, -1835, -917, 24058, 32600, 32512, 30934, 31850, 32309, 32512, 32512, 32729, 32591, 32537, 32520, 32514, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 32510, 6811, -13156, -9045, -5965, -3421, -1776, -890, 31582, 32246, 32512, 30988, 31878, 32324, 32512, 32512, 32512, 32628, 32573, 32541, 32526, 32518, 32514, 32513, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 32512, 4835 }; void hvl_GenPanningTables( void ) { uint32 i; float64 aa, ab; // Sine based panning table aa = (3.14159265f*2.0f)/4.0f; // Quarter of the way through the sinewave == top peak ab = 0.0f; // Start of the climb from zero for( i=0; i<256; i++ ) { panning_left[i] = (uint32)(sin(aa)*255.0f); panning_right[i] = (uint32)(sin(ab)*255.0f); aa += (3.14159265*2.0f/4.0f)/256.0f; ab += (3.14159265*2.0f/4.0f)/256.0f; } panning_left[255] = 0; panning_right[0] = 0; } void hvl_GenSawtooth( int8 *buf, uint32 len ) { uint32 i; int32 val, add; add = 256 / (len-1); val = -128; for( i=0; i> 2; d1 = 128/d5; d4 = -(d2 >> 1); val = 0; for( i=0; i> 16); if (top > 127) in = 127 << 16; else if (top < -128) in = -(128 << 16); return in; } void hvl_GenFilterWaves( const int8 *buf, int8 *lowbuf, int8 *highbuf ) { const int16 * mid_table = &filter_thing[0]; const int16 * low_table = &filter_thing[1395]; int32 freq; int32 i; for( i=0, freq = 25; i<31; i++, freq += 9 ) { uint32 wv; const int8 *a0 = buf; for( wv=0; wv<6+6+0x20+1; wv++ ) { int32 in, fre, high, mid, low; uint32 j; mid = *mid_table++ << 8; low = *low_table++ << 8; for( j=0; j<=lentab[wv]; j++ ) { in = a0[j] << 16; high = clipshifted8( in - mid - low ); fre = (high >> 8) * freq; mid = clipshifted8(mid + fre); fre = (mid >> 8) * freq; low = clipshifted8(low + fre); *highbuf++ = high >> 16; *lowbuf++ = low >> 16; } a0 += lentab[wv]+1; } } } void hvl_GenWhiteNoise( int8 *buf, uint32 len ) { uint32 ays; ays = 0x41595321; do { uint16 ax, bx; int8 s; s = ays; if( ays & 0x100 ) { s = 0x7f; if( ays & 0x8000 ) s = 0x80; } *buf++ = s; len--; ays = (ays >> 5) | (ays << 27); ays = (ays & 0xffffff00) | ((ays & 0xff) ^ 0x9a); bx = ays; ays = (ays << 2) | (ays >> 30); ax = ays; bx += ax; ax ^= bx; ays = (ays & 0xffff0000) | ax; ays = (ays >> 3) | (ays << 29); } while( len ); } void hvl_GenTables( void ) { hvl_GenPanningTables(); hvl_GenSawtooth( &waves[WO_SAWTOOTH_04], 0x04 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_08], 0x08 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_10], 0x10 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_20], 0x20 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_40], 0x40 ); hvl_GenSawtooth( &waves[WO_SAWTOOTH_80], 0x80 ); hvl_GenTriangle( &waves[WO_TRIANGLE_04], 0x04 ); hvl_GenTriangle( &waves[WO_TRIANGLE_08], 0x08 ); hvl_GenTriangle( &waves[WO_TRIANGLE_10], 0x10 ); hvl_GenTriangle( &waves[WO_TRIANGLE_20], 0x20 ); hvl_GenTriangle( &waves[WO_TRIANGLE_40], 0x40 ); hvl_GenTriangle( &waves[WO_TRIANGLE_80], 0x80 ); hvl_GenSquare( &waves[WO_SQUARES] ); hvl_GenWhiteNoise( &waves[WO_WHITENOISE], WHITENOISELEN ); hvl_GenFilterWaves( &waves[WO_TRIANGLE_04], &waves[WO_LOWPASSES], &waves[WO_HIGHPASSES] ); } hivelytracker-0+git20180223/Replayer_Windows/hvl_tables.h0000664001024000102400000000215713042730153020172 0ustar #define WHITENOISELEN (0x280*3) #define WO_LOWPASSES 0 #define WO_TRIANGLE_04 (WO_LOWPASSES+((0xfc+0xfc+0x80*0x1f+0x80+3*0x280)*31)) #define WO_TRIANGLE_08 (WO_TRIANGLE_04+0x04) #define WO_TRIANGLE_10 (WO_TRIANGLE_08+0x08) #define WO_TRIANGLE_20 (WO_TRIANGLE_10+0x10) #define WO_TRIANGLE_40 (WO_TRIANGLE_20+0x20) #define WO_TRIANGLE_80 (WO_TRIANGLE_40+0x40) #define WO_SAWTOOTH_04 (WO_TRIANGLE_80+0x80) #define WO_SAWTOOTH_08 (WO_SAWTOOTH_04+0x04) #define WO_SAWTOOTH_10 (WO_SAWTOOTH_08+0x08) #define WO_SAWTOOTH_20 (WO_SAWTOOTH_10+0x10) #define WO_SAWTOOTH_40 (WO_SAWTOOTH_20+0x20) #define WO_SAWTOOTH_80 (WO_SAWTOOTH_40+0x40) #define WO_SQUARES (WO_SAWTOOTH_80+0x80) #define WO_WHITENOISE (WO_SQUARES+(0x80*0x20)) #define WO_HIGHPASSES (WO_WHITENOISE+WHITENOISELEN) #define WAVES_SIZE (WO_HIGHPASSES+((0xfc+0xfc+0x80*0x1f+0x80+3*0x280)*31)) const uint16 lentab[45]; const int16 vib_tab[64]; const uint16 period_tab[61]; const int32 stereopan_left[5]; const int32 stereopan_right[5]; const int16 filter_thing[2790]; int8 waves[WAVES_SIZE]; uint32 panning_left[256], panning_right[256]; void hvl_GenTables( void ); hivelytracker-0+git20180223/Replayer_Windows/main.c0000664001024000102400000000727413042730153016773 0ustar /* ** HivelyTracker Windows Commandline Replayer ** ** Just quickly bodged together as an example. */ #define WIN32_LEAN_AND_MEAN // for stripping windows.h include #include #include #include #include #include // for mixer stream #include // for mixer stream #include "hvl_replay.h" #define BUFFNUM 8 HWAVEOUT hWaveOut = INVALID_HANDLE_VALUE; /* Device handle */ WAVEFORMATEX wfx; LPSTR audblock; char audiobuffer[BUFFNUM][((44100*2*2)/50)]; struct hvl_tune *ht = NULL; HANDLE eventh; BOOL init( char *name ) { //MMRESULT result; wfx.nSamplesPerSec = 44100; wfx.wBitsPerSample = 16; wfx.nChannels = 2; wfx.cbSize = 0; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; hvl_InitReplayer(); ht = hvl_LoadTune( name, 44100, 2 ); if( !ht ) return FALSE; eventh = CreateEvent( NULL, // default security attributes TRUE, // manual-reset event FALSE, // initial state is nonsignaled TEXT("WriteEvent") // object name ); if( waveOutOpen( &hWaveOut, WAVE_MAPPER, &wfx, (unsigned int)eventh, 0, CALLBACK_EVENT ) != MMSYSERR_NOERROR ) { printf( "Unable to open waveout\n" ); return FALSE; } return TRUE; } void shut( void ) { if( ht ) hvl_FreeTune( ht ); if( hWaveOut != INVALID_HANDLE_VALUE ) waveOutClose( hWaveOut ); } int main(int argc, char *argv[]) { WAVEHDR header[BUFFNUM]; int nextbuf = 0; //sigset_t base_mask, waiting_mask; printf( "Hively Tracker command line player 1.8\n" ); if( argc < 2 ) { printf( "Usage: play_hvl \n" ); return 0; } if( init( argv[1] ) ) { int i; printf( "Tune: '%s'\n", ht->ht_Name ); printf( "Instruments:\n" ); for( i=1; i<=ht->ht_InstrumentNr; i++ ) printf( "%02i: %s\n", i, ht->ht_Instruments[i].ins_Name ); for ( i=0; i #include #include #include "hvl_replay.h" #include "hvl_tables.h" /* ** Waves */ void hvl_reset_some_stuff( struct hvl_tune *ht ) { uint32 i; for( i=0; iht_Voices[i].vc_Delta=1; ht->ht_Voices[i].vc_OverrideTranspose=1000; // 1.5 ht->ht_Voices[i].vc_SamplePos=ht->ht_Voices[i].vc_Track=ht->ht_Voices[i].vc_Transpose=ht->ht_Voices[i].vc_NextTrack = ht->ht_Voices[i].vc_NextTranspose = 0; ht->ht_Voices[i].vc_ADSRVolume=ht->ht_Voices[i].vc_InstrPeriod=ht->ht_Voices[i].vc_TrackPeriod=ht->ht_Voices[i].vc_VibratoPeriod=ht->ht_Voices[i].vc_NoteMaxVolume=ht->ht_Voices[i].vc_PerfSubVolume=ht->ht_Voices[i].vc_TrackMasterVolume=0; ht->ht_Voices[i].vc_NewWaveform=ht->ht_Voices[i].vc_Waveform=ht->ht_Voices[i].vc_PlantSquare=ht->ht_Voices[i].vc_PlantPeriod=ht->ht_Voices[i].vc_IgnoreSquare=0; ht->ht_Voices[i].vc_TrackOn=ht->ht_Voices[i].vc_FixedNote=ht->ht_Voices[i].vc_VolumeSlideUp=ht->ht_Voices[i].vc_VolumeSlideDown=ht->ht_Voices[i].vc_HardCut=ht->ht_Voices[i].vc_HardCutRelease=ht->ht_Voices[i].vc_HardCutReleaseF=0; ht->ht_Voices[i].vc_PeriodSlideSpeed=ht->ht_Voices[i].vc_PeriodSlidePeriod=ht->ht_Voices[i].vc_PeriodSlideLimit=ht->ht_Voices[i].vc_PeriodSlideOn=ht->ht_Voices[i].vc_PeriodSlideWithLimit=0; ht->ht_Voices[i].vc_PeriodPerfSlideSpeed=ht->ht_Voices[i].vc_PeriodPerfSlidePeriod=ht->ht_Voices[i].vc_PeriodPerfSlideOn=ht->ht_Voices[i].vc_VibratoDelay=ht->ht_Voices[i].vc_VibratoCurrent=ht->ht_Voices[i].vc_VibratoDepth=ht->ht_Voices[i].vc_VibratoSpeed=0; ht->ht_Voices[i].vc_SquareOn=ht->ht_Voices[i].vc_SquareInit=ht->ht_Voices[i].vc_SquareLowerLimit=ht->ht_Voices[i].vc_SquareUpperLimit=ht->ht_Voices[i].vc_SquarePos=ht->ht_Voices[i].vc_SquareSign=ht->ht_Voices[i].vc_SquareSlidingIn=ht->ht_Voices[i].vc_SquareReverse=0; ht->ht_Voices[i].vc_FilterOn=ht->ht_Voices[i].vc_FilterInit=ht->ht_Voices[i].vc_FilterLowerLimit=ht->ht_Voices[i].vc_FilterUpperLimit=ht->ht_Voices[i].vc_FilterPos=ht->ht_Voices[i].vc_FilterSign=ht->ht_Voices[i].vc_FilterSpeed=ht->ht_Voices[i].vc_FilterSlidingIn=ht->ht_Voices[i].vc_IgnoreFilter=0; ht->ht_Voices[i].vc_PerfCurrent=ht->ht_Voices[i].vc_PerfSpeed=ht->ht_Voices[i].vc_WaveLength=ht->ht_Voices[i].vc_NoteDelayOn=ht->ht_Voices[i].vc_NoteCutOn=0; ht->ht_Voices[i].vc_AudioPeriod=ht->ht_Voices[i].vc_AudioVolume=ht->ht_Voices[i].vc_VoiceVolume=ht->ht_Voices[i].vc_VoicePeriod=ht->ht_Voices[i].vc_VoiceNum=ht->ht_Voices[i].vc_WNRandom=0; ht->ht_Voices[i].vc_SquareWait=ht->ht_Voices[i].vc_FilterWait=ht->ht_Voices[i].vc_PerfWait=ht->ht_Voices[i].vc_NoteDelayWait=ht->ht_Voices[i].vc_NoteCutWait=0; ht->ht_Voices[i].vc_PerfList=0; ht->ht_Voices[i].vc_RingSamplePos=ht->ht_Voices[i].vc_RingDelta=ht->ht_Voices[i].vc_RingPlantPeriod=ht->ht_Voices[i].vc_RingAudioPeriod=ht->ht_Voices[i].vc_RingNewWaveform=ht->ht_Voices[i].vc_RingWaveform=ht->ht_Voices[i].vc_RingFixedPeriod=ht->ht_Voices[i].vc_RingBasePeriod=0; ht->ht_Voices[i].vc_RingMixSource = NULL; ht->ht_Voices[i].vc_RingAudioSource = NULL; memset(&ht->ht_Voices[i].vc_SquareTempBuffer,0,0x80); memset(&ht->ht_Voices[i].vc_ADSR,0,sizeof(struct hvl_envelope)); memset(&ht->ht_Voices[i].vc_VoiceBuffer,0,0x281); memset(&ht->ht_Voices[i].vc_RingVoiceBuffer,0,0x281); } for( i=0; iht_Voices[i].vc_WNRandom = 0x280; ht->ht_Voices[i].vc_VoiceNum = i; ht->ht_Voices[i].vc_TrackMasterVolume = 0x40; ht->ht_Voices[i].vc_TrackOn = 1; ht->ht_Voices[i].vc_MixSource = ht->ht_Voices[i].vc_VoiceBuffer; } } BOOL hvl_InitSubsong( struct hvl_tune *ht, uint32 nr ) { uint32 PosNr, i; if( nr > ht->ht_SubsongNr ) return FALSE; ht->ht_SongNum = nr; PosNr = 0; if( nr ) PosNr = ht->ht_Subsongs[nr-1]; ht->ht_PosNr = PosNr; ht->ht_PosJump = 0; ht->ht_PatternBreak = 0; ht->ht_NoteNr = 0; ht->ht_PosJumpNote = 0; ht->ht_Tempo = 6; ht->ht_StepWaitFrames = 0; ht->ht_GetNewPosition = 1; ht->ht_SongEndReached = 0; ht->ht_PlayingTime = 0; for( i=0; iht_Voices[i+0].vc_Pan = ht->ht_defpanleft; ht->ht_Voices[i+0].vc_SetPan = ht->ht_defpanleft; // 1.4 ht->ht_Voices[i+0].vc_PanMultLeft = panning_left[ht->ht_defpanleft]; ht->ht_Voices[i+0].vc_PanMultRight = panning_right[ht->ht_defpanleft]; ht->ht_Voices[i+1].vc_Pan = ht->ht_defpanright; ht->ht_Voices[i+1].vc_SetPan = ht->ht_defpanright; // 1.4 ht->ht_Voices[i+1].vc_PanMultLeft = panning_left[ht->ht_defpanright]; ht->ht_Voices[i+1].vc_PanMultRight = panning_right[ht->ht_defpanright]; ht->ht_Voices[i+2].vc_Pan = ht->ht_defpanright; ht->ht_Voices[i+2].vc_SetPan = ht->ht_defpanright; // 1.4 ht->ht_Voices[i+2].vc_PanMultLeft = panning_left[ht->ht_defpanright]; ht->ht_Voices[i+2].vc_PanMultRight = panning_right[ht->ht_defpanright]; ht->ht_Voices[i+3].vc_Pan = ht->ht_defpanleft; ht->ht_Voices[i+3].vc_SetPan = ht->ht_defpanleft; // 1.4 ht->ht_Voices[i+3].vc_PanMultLeft = panning_left[ht->ht_defpanleft]; ht->ht_Voices[i+3].vc_PanMultRight = panning_right[ht->ht_defpanleft]; } hvl_reset_some_stuff( ht ); return TRUE; } void hvl_InitReplayer( void ) { hvl_GenTables(); } struct hvl_tune *hvl_load_ahx( const uint8 *buf, uint32 buflen, uint32 defstereo, uint32 freq ) { const uint8 *bptr; const TEXT *nptr; uint32 i, j, k, l, posn, insn, ssn, hs, trkn, trkl; struct hvl_tune *ht; struct hvl_plsentry *ple; const int32 defgain[] = { 71, 72, 76, 85, 100 }; posn = ((buf[6]&0x0f)<<8)|buf[7]; insn = buf[12]; ssn = buf[13]; trkl = buf[10]; trkn = buf[11]; hs = sizeof( struct hvl_tune ); hs += sizeof( struct hvl_position ) * posn; hs += sizeof( struct hvl_instrument ) * (insn+1); hs += sizeof( uint16 ) * ssn; // Calculate the size of all instrument PList buffers bptr = &buf[14]; bptr += ssn*2; // Skip past the subsong list bptr += posn*4*2; // Skip past the positions bptr += trkn*trkl*3; if((buf[6]&0x80)==0) bptr += trkl*3; // *NOW* we can finally calculate PList space for( i=1; i<=insn; i++ ) { hs += bptr[21] * sizeof( struct hvl_plsentry ); bptr += 22 + bptr[21]*4; } ht = malloc( hs ); if( !ht ) { printf( "Out of memory!\n" ); return NULL; } ht->ht_Frequency = freq; ht->ht_FreqF = (float64)freq; ht->ht_Positions = (struct hvl_position *)(&ht[1]); ht->ht_Instruments = (struct hvl_instrument *)(&ht->ht_Positions[posn]); ht->ht_Subsongs = (uint16 *)(&ht->ht_Instruments[(insn+1)]); ple = (struct hvl_plsentry *)(&ht->ht_Subsongs[ssn]); ht->ht_WaveformTab[0] = &waves[WO_TRIANGLE_04]; ht->ht_WaveformTab[1] = &waves[WO_SAWTOOTH_04]; ht->ht_WaveformTab[3] = &waves[WO_WHITENOISE]; ht->ht_Channels = 4; ht->ht_PositionNr = posn; ht->ht_Restart = (buf[8]<<8)|buf[9]; ht->ht_SpeedMultiplier = ((buf[6]>>5)&3)+1; ht->ht_TrackLength = trkl; ht->ht_TrackNr = trkn; ht->ht_InstrumentNr = insn; ht->ht_SubsongNr = ssn; ht->ht_defstereo = defstereo; ht->ht_defpanleft = stereopan_left[ht->ht_defstereo]; ht->ht_defpanright = stereopan_right[ht->ht_defstereo]; ht->ht_mixgain = (defgain[ht->ht_defstereo]*256)/100; if( ht->ht_Restart >= ht->ht_PositionNr ) ht->ht_Restart = ht->ht_PositionNr-1; // Do some validation if( ( ht->ht_PositionNr > 1000 ) || ( ht->ht_TrackLength > 64 ) || ( ht->ht_InstrumentNr > 64 ) ) { printf( "%d,%d,%d\n", ht->ht_PositionNr, ht->ht_TrackLength, ht->ht_InstrumentNr ); free( ht ); printf( "Invalid file.\n" ); return NULL; } strncpy( ht->ht_Name, (TEXT *)&buf[(buf[4]<<8)|buf[5]], 128 ); nptr = (TEXT *)&buf[((buf[4]<<8)|buf[5])+strlen( ht->ht_Name )+1]; bptr = &buf[14]; // Subsongs for( i=0; iht_SubsongNr; i++ ) { ht->ht_Subsongs[i] = (bptr[0]<<8)|bptr[1]; if( ht->ht_Subsongs[i] >= ht->ht_PositionNr ) ht->ht_Subsongs[i] = 0; bptr += 2; } // Position list for( i=0; iht_PositionNr; i++ ) { for( j=0; j<4; j++ ) { ht->ht_Positions[i].pos_Track[j] = *bptr++; ht->ht_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; } } // Tracks for( i=0; i<=ht->ht_TrackNr; i++ ) { if( ( ( buf[6]&0x80 ) == 0x80 ) && ( i == 0 ) ) { for( j=0; jht_TrackLength; j++ ) { ht->ht_Tracks[i][j].stp_Note = 0; ht->ht_Tracks[i][j].stp_Instrument = 0; ht->ht_Tracks[i][j].stp_FX = 0; ht->ht_Tracks[i][j].stp_FXParam = 0; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; } continue; } for( j=0; jht_TrackLength; j++ ) { ht->ht_Tracks[i][j].stp_Note = (bptr[0]>>2)&0x3f; ht->ht_Tracks[i][j].stp_Instrument = ((bptr[0]&0x3)<<4) | (bptr[1]>>4); ht->ht_Tracks[i][j].stp_FX = bptr[1]&0xf; ht->ht_Tracks[i][j].stp_FXParam = bptr[2]; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; bptr += 3; } } // Instruments for( i=1; i<=ht->ht_InstrumentNr; i++ ) { if( nptr < (TEXT *)(buf+buflen) ) { strncpy( ht->ht_Instruments[i].ins_Name, nptr, 128 ); nptr += strlen( nptr )+1; } else { ht->ht_Instruments[i].ins_Name[0] = 0; } ht->ht_Instruments[i].ins_Volume = bptr[0]; ht->ht_Instruments[i].ins_FilterSpeed = ((bptr[1]>>3)&0x1f)|((bptr[12]>>2)&0x20); ht->ht_Instruments[i].ins_WaveLength = bptr[1]&0x07; ht->ht_Instruments[i].ins_Envelope.aFrames = bptr[2]; ht->ht_Instruments[i].ins_Envelope.aVolume = bptr[3]; ht->ht_Instruments[i].ins_Envelope.dFrames = bptr[4]; ht->ht_Instruments[i].ins_Envelope.dVolume = bptr[5]; ht->ht_Instruments[i].ins_Envelope.sFrames = bptr[6]; ht->ht_Instruments[i].ins_Envelope.rFrames = bptr[7]; ht->ht_Instruments[i].ins_Envelope.rVolume = bptr[8]; ht->ht_Instruments[i].ins_FilterLowerLimit = bptr[12]&0x7f; ht->ht_Instruments[i].ins_VibratoDelay = bptr[13]; ht->ht_Instruments[i].ins_HardCutReleaseFrames = (bptr[14]>>4)&0x07; ht->ht_Instruments[i].ins_HardCutRelease = bptr[14]&0x80?1:0; ht->ht_Instruments[i].ins_VibratoDepth = bptr[14]&0x0f; ht->ht_Instruments[i].ins_VibratoSpeed = bptr[15]; ht->ht_Instruments[i].ins_SquareLowerLimit = bptr[16]; ht->ht_Instruments[i].ins_SquareUpperLimit = bptr[17]; ht->ht_Instruments[i].ins_SquareSpeed = bptr[18]; ht->ht_Instruments[i].ins_FilterUpperLimit = bptr[19]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Speed = bptr[20]; ht->ht_Instruments[i].ins_PList.pls_Length = bptr[21]; ht->ht_Instruments[i].ins_PList.pls_Entries = ple; ple += bptr[21]; bptr += 22; for( j=0; jht_Instruments[i].ins_PList.pls_Length; j++ ) { k = (bptr[0]>>5)&7; if( k == 6 ) k = 12; if( k == 7 ) k = 15; l = (bptr[0]>>2)&7; if( l == 6 ) l = 12; if( l == 7 ) l = 15; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = k; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = l; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = ((bptr[0]<<1)&6) | (bptr[1]>>7); ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[1]>>6)&1; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[1]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[2]; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[3]; // 1.6: Strip "toggle filter" commands if the module is // version 0 (pre-filters). This is what AHX also does. if( ( buf[3] == 0 ) && ( l == 4 ) && ( (bptr[2]&0xf0) != 0 ) ) ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] &= 0x0f; if( ( buf[3] == 0 ) && ( k == 4 ) && ( (bptr[3]&0xf0) != 0 ) ) ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] &= 0x0f; // 1.8 bptr += 4; } } hvl_InitSubsong( ht, 0 ); return ht; } struct hvl_tune *hvl_load_hvl( const uint8 *buf, uint32 buflen, uint32 defstereo, uint32 freq ) { const uint8 *bptr; const TEXT *nptr; uint32 i, j, posn, insn, ssn, chnn, hs, trkl, trkn; struct hvl_tune *ht; struct hvl_plsentry *ple; posn = ((buf[6]&0x0f)<<8)|buf[7]; insn = buf[12]; ssn = buf[13]; chnn = (buf[8]>>2)+4; trkl = buf[10]; trkn = buf[11]; hs = sizeof( struct hvl_tune ); hs += sizeof( struct hvl_position ) * posn; hs += sizeof( struct hvl_instrument ) * (insn+1); hs += sizeof( uint16 ) * ssn; // Calculate the size of all instrument PList buffers bptr = &buf[16]; bptr += ssn*2; // Skip past the subsong list bptr += posn*chnn*2; // Skip past the positions // Skip past the tracks // 1.4: Fixed two really stupid bugs that cancelled each other // out if the module had a blank first track (which is how // come they were missed. for( i=((buf[6]&0x80)==0x80)?1:0; i<=trkn; i++ ) for( j=0; jht_Version = buf[3]; // 1.5 ht->ht_Frequency = freq; ht->ht_FreqF = (float64)freq; ht->ht_Positions = (struct hvl_position *)(&ht[1]); ht->ht_Instruments = (struct hvl_instrument *)(&ht->ht_Positions[posn]); ht->ht_Subsongs = (uint16 *)(&ht->ht_Instruments[(insn+1)]); ple = (struct hvl_plsentry *)(&ht->ht_Subsongs[ssn]); ht->ht_WaveformTab[0] = &waves[WO_TRIANGLE_04]; ht->ht_WaveformTab[1] = &waves[WO_SAWTOOTH_04]; ht->ht_WaveformTab[3] = &waves[WO_WHITENOISE]; ht->ht_PositionNr = posn; ht->ht_Channels = (buf[8]>>2)+4; ht->ht_Restart = ((buf[8]&3)<<8)|buf[9]; ht->ht_SpeedMultiplier = ((buf[6]>>5)&3)+1; ht->ht_TrackLength = buf[10]; ht->ht_TrackNr = buf[11]; ht->ht_InstrumentNr = insn; ht->ht_SubsongNr = ssn; ht->ht_mixgain = (buf[14]<<8)/100; ht->ht_defstereo = buf[15]; ht->ht_defpanleft = stereopan_left[ht->ht_defstereo]; ht->ht_defpanright = stereopan_right[ht->ht_defstereo]; if( ht->ht_Restart >= ht->ht_PositionNr ) ht->ht_Restart = ht->ht_PositionNr-1; // Do some validation if( ( ht->ht_PositionNr > 1000 ) || ( ht->ht_TrackLength > 64 ) || ( ht->ht_InstrumentNr > 64 ) ) { printf( "%d,%d,%d\n", ht->ht_PositionNr, ht->ht_TrackLength, ht->ht_InstrumentNr ); free( ht ); printf( "Invalid file.\n" ); return NULL; } strncpy( ht->ht_Name, (TEXT *)&buf[(buf[4]<<8)|buf[5]], 128 ); nptr = (TEXT *)&buf[((buf[4]<<8)|buf[5])+strlen( ht->ht_Name )+1]; bptr = &buf[16]; // Subsongs for( i=0; iht_SubsongNr; i++ ) { ht->ht_Subsongs[i] = (bptr[0]<<8)|bptr[1]; bptr += 2; } // Position list for( i=0; iht_PositionNr; i++ ) { for( j=0; jht_Channels; j++ ) { ht->ht_Positions[i].pos_Track[j] = *bptr++; ht->ht_Positions[i].pos_Transpose[j] = *(int8 *)bptr++; } } // Tracks for( i=0; i<=ht->ht_TrackNr; i++ ) { if( ( ( buf[6]&0x80 ) == 0x80 ) && ( i == 0 ) ) { for( j=0; jht_TrackLength; j++ ) { ht->ht_Tracks[i][j].stp_Note = 0; ht->ht_Tracks[i][j].stp_Instrument = 0; ht->ht_Tracks[i][j].stp_FX = 0; ht->ht_Tracks[i][j].stp_FXParam = 0; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; } continue; } for( j=0; jht_TrackLength; j++ ) { if( bptr[0] == 0x3f ) { ht->ht_Tracks[i][j].stp_Note = 0; ht->ht_Tracks[i][j].stp_Instrument = 0; ht->ht_Tracks[i][j].stp_FX = 0; ht->ht_Tracks[i][j].stp_FXParam = 0; ht->ht_Tracks[i][j].stp_FXb = 0; ht->ht_Tracks[i][j].stp_FXbParam = 0; bptr++; continue; } ht->ht_Tracks[i][j].stp_Note = bptr[0]; ht->ht_Tracks[i][j].stp_Instrument = bptr[1]; ht->ht_Tracks[i][j].stp_FX = bptr[2]>>4; ht->ht_Tracks[i][j].stp_FXParam = bptr[3]; ht->ht_Tracks[i][j].stp_FXb = bptr[2]&0xf; ht->ht_Tracks[i][j].stp_FXbParam = bptr[4]; bptr += 5; } } // Instruments for( i=1; i<=ht->ht_InstrumentNr; i++ ) { if( nptr < (TEXT *)(buf+buflen) ) { strncpy( ht->ht_Instruments[i].ins_Name, nptr, 128 ); nptr += strlen( nptr )+1; } else { ht->ht_Instruments[i].ins_Name[0] = 0; } ht->ht_Instruments[i].ins_Volume = bptr[0]; ht->ht_Instruments[i].ins_FilterSpeed = ((bptr[1]>>3)&0x1f)|((bptr[12]>>2)&0x20); ht->ht_Instruments[i].ins_WaveLength = bptr[1]&0x07; ht->ht_Instruments[i].ins_Envelope.aFrames = bptr[2]; ht->ht_Instruments[i].ins_Envelope.aVolume = bptr[3]; ht->ht_Instruments[i].ins_Envelope.dFrames = bptr[4]; ht->ht_Instruments[i].ins_Envelope.dVolume = bptr[5]; ht->ht_Instruments[i].ins_Envelope.sFrames = bptr[6]; ht->ht_Instruments[i].ins_Envelope.rFrames = bptr[7]; ht->ht_Instruments[i].ins_Envelope.rVolume = bptr[8]; ht->ht_Instruments[i].ins_FilterLowerLimit = bptr[12]&0x7f; ht->ht_Instruments[i].ins_VibratoDelay = bptr[13]; ht->ht_Instruments[i].ins_HardCutReleaseFrames = (bptr[14]>>4)&0x07; ht->ht_Instruments[i].ins_HardCutRelease = bptr[14]&0x80?1:0; ht->ht_Instruments[i].ins_VibratoDepth = bptr[14]&0x0f; ht->ht_Instruments[i].ins_VibratoSpeed = bptr[15]; ht->ht_Instruments[i].ins_SquareLowerLimit = bptr[16]; ht->ht_Instruments[i].ins_SquareUpperLimit = bptr[17]; ht->ht_Instruments[i].ins_SquareSpeed = bptr[18]; ht->ht_Instruments[i].ins_FilterUpperLimit = bptr[19]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Speed = bptr[20]; ht->ht_Instruments[i].ins_PList.pls_Length = bptr[21]; ht->ht_Instruments[i].ins_PList.pls_Entries = ple; ple += bptr[21]; bptr += 22; for( j=0; jht_Instruments[i].ins_PList.pls_Length; j++ ) { ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[0] = bptr[0]&0xf; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FX[1] = (bptr[1]>>3)&0xf; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Waveform = bptr[1]&7; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Fixed = (bptr[2]>>6)&1; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_Note = bptr[2]&0x3f; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[0] = bptr[3]; ht->ht_Instruments[i].ins_PList.pls_Entries[j].ple_FXParam[1] = bptr[4]; bptr += 5; } } hvl_InitSubsong( ht, 0 ); return ht; } struct hvl_tune *hvl_ParseTune( const uint8 *buf, uint32 buflen, uint32 freq, uint32 defstereo ) { struct hvl_tune *ht = NULL; if( ( buf[0] == 'T' ) && ( buf[1] == 'H' ) && ( buf[2] == 'X' ) && ( buf[3] < 3 ) ) { ht = hvl_load_ahx( buf, buflen, defstereo, freq ); } else if( ( buf[0] == 'H' ) && ( buf[1] == 'V' ) && ( buf[2] == 'L' ) && ( buf[3] < 2 ) ) { ht = hvl_load_hvl( buf, buflen, defstereo, freq ); } else { printf( "Invalid file.\n" ); } return ht; } struct hvl_tune *hvl_LoadTune( const TEXT *name, uint32 freq, uint32 defstereo ) { struct hvl_tune *ht = NULL; uint8 *buf; uint32 buflen; FILE *fh; fh = fopen( name, "rb" ); if( !fh ) { printf( "Can't open file\n" ); return NULL; } fseek( fh, 0, SEEK_END ); buflen = ftell( fh ); fseek( fh, 0, SEEK_SET ); buf = malloc( buflen ); if( !buf ) { fclose( fh ); printf( "Out of memory!\n" ); return NULL; } if( fread( buf, 1, buflen, fh ) != buflen ) { fclose( fh ); free( buf ); printf( "Unable to read from file!\n" ); return NULL; } fclose( fh ); ht = hvl_ParseTune( buf, buflen, freq, defstereo ); free( buf ); return ht; } void hvl_FreeTune( struct hvl_tune *ht ) { if( !ht ) return; free( ht ); } void hvl_process_stepfx_1( struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam ) { switch( FX ) { case 0x0: // Position Jump HI if( ((FXParam&0x0f) > 0) && ((FXParam&0x0f) <= 9) ) ht->ht_PosJump = FXParam & 0xf; break; case 0x5: // Volume Slide + Tone Portamento case 0xa: // Volume Slide voice->vc_VolumeSlideDown = FXParam & 0x0f; voice->vc_VolumeSlideUp = FXParam >> 4; break; case 0x7: // Panning if( FXParam > 127 ) FXParam -= 256; voice->vc_Pan = (FXParam+128); voice->vc_SetPan = (FXParam+128); // 1.4 voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; break; case 0xb: // Position jump ht->ht_PosJump = ht->ht_PosJump*100 + (FXParam & 0x0f) + (FXParam >> 4)*10; ht->ht_PatternBreak = 1; if( ht->ht_PosJump <= ht->ht_PosNr ) ht->ht_SongEndReached = 1; break; case 0xd: // Pattern break ht->ht_PosJump = ht->ht_PosNr+1; ht->ht_PosJumpNote = (FXParam & 0x0f) + (FXParam>>4)*10; ht->ht_PatternBreak = 1; if( ht->ht_PosJumpNote > ht->ht_TrackLength ) ht->ht_PosJumpNote = 0; break; case 0xe: // Extended commands switch( FXParam >> 4 ) { case 0xc: // Note cut if( (FXParam & 0x0f) < ht->ht_Tempo ) { voice->vc_NoteCutWait = FXParam & 0x0f; if( voice->vc_NoteCutWait ) { voice->vc_NoteCutOn = 1; voice->vc_HardCutRelease = 0; } } break; // 1.6: 0xd case removed } break; case 0xf: // Speed ht->ht_Tempo = FXParam; if( FXParam == 0 ) ht->ht_SongEndReached = 1; break; } } void hvl_process_stepfx_2( const struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam, int32 *Note ) { switch( FX ) { case 0x9: // Set squarewave offset voice->vc_SquarePos = FXParam >> (5 - voice->vc_WaveLength); // voice->vc_PlantSquare = 1; voice->vc_IgnoreSquare = 1; break; case 0x3: // Tone portamento if( FXParam != 0 ) voice->vc_PeriodSlideSpeed = FXParam; case 0x5: // Tone portamento + volume slide if( *Note ) { int32 new, diff; new = period_tab[*Note]; diff = period_tab[voice->vc_TrackPeriod]; diff -= new; new = diff + voice->vc_PeriodSlidePeriod; if( new ) voice->vc_PeriodSlideLimit = -diff; } voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 1; *Note = 0; break; } } void hvl_process_stepfx_3( struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam ) { int32 i; switch( FX ) { case 0x01: // Portamento up (period slide down) voice->vc_PeriodSlideSpeed = -FXParam; voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 0; break; case 0x02: // Portamento down voice->vc_PeriodSlideSpeed = FXParam; voice->vc_PeriodSlideOn = 1; voice->vc_PeriodSlideWithLimit = 0; break; case 0x04: // Filter override if( ( FXParam == 0 ) || ( FXParam == 0x40 ) ) break; if( FXParam < 0x40 ) { voice->vc_IgnoreFilter = FXParam; break; } if( FXParam > 0x7f ) break; voice->vc_FilterPos = FXParam - 0x40; break; case 0x0c: // Volume FXParam &= 0xff; if( FXParam <= 0x40 ) { voice->vc_NoteMaxVolume = FXParam; break; } if( (FXParam -= 0x50) < 0 ) break; // 1.6 if( FXParam <= 0x40 ) { for( i=0; iht_Channels; i++ ) ht->ht_Voices[i].vc_TrackMasterVolume = FXParam; break; } if( (FXParam -= 0xa0-0x50) < 0 ) break; // 1.6 if( FXParam <= 0x40 ) voice->vc_TrackMasterVolume = FXParam; break; case 0xe: // Extended commands; switch( FXParam >> 4 ) { case 0x1: // Fineslide up voice->vc_PeriodSlidePeriod -= (FXParam & 0x0f); // 1.8 voice->vc_PlantPeriod = 1; break; case 0x2: // Fineslide down voice->vc_PeriodSlidePeriod += (FXParam & 0x0f); // 1.8 voice->vc_PlantPeriod = 1; break; case 0x4: // Vibrato control voice->vc_VibratoDepth = FXParam & 0x0f; break; case 0x0a: // Fine volume up voice->vc_NoteMaxVolume += FXParam & 0x0f; if( voice->vc_NoteMaxVolume > 0x40 ) voice->vc_NoteMaxVolume = 0x40; break; case 0x0b: // Fine volume down voice->vc_NoteMaxVolume -= FXParam & 0x0f; if( voice->vc_NoteMaxVolume < 0 ) voice->vc_NoteMaxVolume = 0; break; case 0x0f: // Misc flags (1.5) if( ht->ht_Version < 1 ) break; switch( FXParam & 0xf ) { case 1: voice->vc_OverrideTranspose = voice->vc_Transpose; break; } break; } break; } } void hvl_process_step( struct hvl_tune *ht, struct hvl_voice *voice ) { int32 Note, Instr, donenotedel; const struct hvl_step *Step; if( voice->vc_TrackOn == 0 ) return; voice->vc_VolumeSlideUp = voice->vc_VolumeSlideDown = 0; Step = &ht->ht_Tracks[ht->ht_Positions[ht->ht_PosNr].pos_Track[voice->vc_VoiceNum]][ht->ht_NoteNr]; Note = Step->stp_Note; Instr = Step->stp_Instrument; // --------- 1.6: from here -------------- donenotedel = 0; // Do notedelay here if( ((Step->stp_FX&0xf)==0xe) && ((Step->stp_FXParam&0xf0)==0xd0) ) { if( voice->vc_NoteDelayOn ) { voice->vc_NoteDelayOn = 0; donenotedel = 1; } else { if( (Step->stp_FXParam&0x0f) < ht->ht_Tempo ) { voice->vc_NoteDelayWait = Step->stp_FXParam & 0x0f; if( voice->vc_NoteDelayWait ) { voice->vc_NoteDelayOn = 1; return; } } } } if( (donenotedel==0) && ((Step->stp_FXb&0xf)==0xe) && ((Step->stp_FXbParam&0xf0)==0xd0) ) { if( voice->vc_NoteDelayOn ) { voice->vc_NoteDelayOn = 0; } else { if( (Step->stp_FXbParam&0x0f) < ht->ht_Tempo ) { voice->vc_NoteDelayWait = Step->stp_FXbParam & 0x0f; if( voice->vc_NoteDelayWait ) { voice->vc_NoteDelayOn = 1; return; } } } } // --------- 1.6: to here -------------- if( Note ) voice->vc_OverrideTranspose = 1000; // 1.5 hvl_process_stepfx_1( ht, voice, Step->stp_FX&0xf, Step->stp_FXParam ); hvl_process_stepfx_1( ht, voice, Step->stp_FXb&0xf, Step->stp_FXbParam ); if( ( Instr ) && ( Instr <= ht->ht_InstrumentNr ) ) { struct hvl_instrument *Ins; int16 SquareLower, SquareUpper, d6, d3, d4; /* 1.4: Reset panning to last set position */ voice->vc_Pan = voice->vc_SetPan; voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; voice->vc_PeriodSlideSpeed = voice->vc_PeriodSlidePeriod = voice->vc_PeriodSlideLimit = 0; voice->vc_PerfSubVolume = 0x40; voice->vc_ADSRVolume = 0; voice->vc_Instrument = Ins = &ht->ht_Instruments[Instr]; voice->vc_SamplePos = 0; voice->vc_ADSR.aFrames = Ins->ins_Envelope.aFrames; voice->vc_ADSR.aVolume = voice->vc_ADSR.aFrames ? Ins->ins_Envelope.aVolume*256/voice->vc_ADSR.aFrames : Ins->ins_Envelope.aVolume * 256; // XXX voice->vc_ADSR.dFrames = Ins->ins_Envelope.dFrames; voice->vc_ADSR.dVolume = voice->vc_ADSR.dFrames ? (Ins->ins_Envelope.dVolume-Ins->ins_Envelope.aVolume)*256/voice->vc_ADSR.dFrames : Ins->ins_Envelope.dVolume * 256; // XXX voice->vc_ADSR.sFrames = Ins->ins_Envelope.sFrames; voice->vc_ADSR.rFrames = Ins->ins_Envelope.rFrames; voice->vc_ADSR.rVolume = voice->vc_ADSR.rFrames ? (Ins->ins_Envelope.rVolume-Ins->ins_Envelope.dVolume)*256/voice->vc_ADSR.rFrames : Ins->ins_Envelope.rVolume * 256; // XXX voice->vc_WaveLength = Ins->ins_WaveLength; voice->vc_NoteMaxVolume = Ins->ins_Volume; voice->vc_VibratoCurrent = 0; voice->vc_VibratoDelay = Ins->ins_VibratoDelay; voice->vc_VibratoDepth = Ins->ins_VibratoDepth; voice->vc_VibratoSpeed = Ins->ins_VibratoSpeed; voice->vc_VibratoPeriod = 0; voice->vc_HardCutRelease = Ins->ins_HardCutRelease; voice->vc_HardCut = Ins->ins_HardCutReleaseFrames; voice->vc_IgnoreSquare = voice->vc_SquareSlidingIn = 0; voice->vc_SquareWait = voice->vc_SquareOn = 0; SquareLower = Ins->ins_SquareLowerLimit >> (5 - voice->vc_WaveLength); SquareUpper = Ins->ins_SquareUpperLimit >> (5 - voice->vc_WaveLength); if( SquareUpper < SquareLower ) { int16 t = SquareUpper; SquareUpper = SquareLower; SquareLower = t; } voice->vc_SquareUpperLimit = SquareUpper; voice->vc_SquareLowerLimit = SquareLower; voice->vc_IgnoreFilter = voice->vc_FilterWait = voice->vc_FilterOn = 0; voice->vc_FilterSlidingIn = 0; d6 = Ins->ins_FilterSpeed; d3 = Ins->ins_FilterLowerLimit; d4 = Ins->ins_FilterUpperLimit; if( d3 & 0x80 ) d6 |= 0x20; if( d4 & 0x80 ) d6 |= 0x40; voice->vc_FilterSpeed = d6; d3 &= ~0x80; d4 &= ~0x80; if( d3 > d4 ) { int16 t = d3; d3 = d4; d4 = t; } voice->vc_FilterUpperLimit = d4; voice->vc_FilterLowerLimit = d3; voice->vc_FilterPos = 32; voice->vc_PerfWait = voice->vc_PerfCurrent = 0; voice->vc_PerfSpeed = Ins->ins_PList.pls_Speed; voice->vc_PerfList = &voice->vc_Instrument->ins_PList; voice->vc_RingMixSource = NULL; // No ring modulation voice->vc_RingSamplePos = 0; voice->vc_RingPlantPeriod = 0; voice->vc_RingNewWaveform = 0; } voice->vc_PeriodSlideOn = 0; hvl_process_stepfx_2( ht, voice, Step->stp_FX&0xf, Step->stp_FXParam, &Note ); hvl_process_stepfx_2( ht, voice, Step->stp_FXb&0xf, Step->stp_FXbParam, &Note ); if( Note ) { voice->vc_TrackPeriod = Note; voice->vc_PlantPeriod = 1; } hvl_process_stepfx_3( ht, voice, Step->stp_FX&0xf, Step->stp_FXParam ); hvl_process_stepfx_3( ht, voice, Step->stp_FXb&0xf, Step->stp_FXbParam ); } void hvl_plist_command_parse( const struct hvl_tune *ht, struct hvl_voice *voice, int32 FX, int32 FXParam ) { switch( FX ) { case 0: if( ( FXParam > 0 ) && ( FXParam < 0x40 ) ) { if( voice->vc_IgnoreFilter ) { voice->vc_FilterPos = voice->vc_IgnoreFilter; voice->vc_IgnoreFilter = 0; } else { voice->vc_FilterPos = FXParam; } voice->vc_NewWaveform = 1; } break; case 1: voice->vc_PeriodPerfSlideSpeed = FXParam; voice->vc_PeriodPerfSlideOn = 1; break; case 2: voice->vc_PeriodPerfSlideSpeed = -FXParam; voice->vc_PeriodPerfSlideOn = 1; break; case 3: if( voice->vc_IgnoreSquare == 0 ) voice->vc_SquarePos = FXParam >> (5-voice->vc_WaveLength); else voice->vc_IgnoreSquare = 0; break; case 4: if( FXParam == 0 ) { voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); voice->vc_SquareSign = 1; } else { if( FXParam & 0x0f ) { voice->vc_SquareInit = (voice->vc_SquareOn ^= 1); voice->vc_SquareSign = 1; if(( FXParam & 0x0f ) == 0x0f ) voice->vc_SquareSign = -1; } if( FXParam & 0xf0 ) { voice->vc_FilterInit = (voice->vc_FilterOn ^= 1); voice->vc_FilterSign = 1; if(( FXParam & 0xf0 ) == 0xf0 ) voice->vc_FilterSign = -1; } } break; case 5: voice->vc_PerfCurrent = FXParam; break; case 7: // Ring modulate with triangle if(( FXParam >= 1 ) && ( FXParam <= 0x3C )) { voice->vc_RingBasePeriod = FXParam; voice->vc_RingFixedPeriod = 1; } else if(( FXParam >= 0x81 ) && ( FXParam <= 0xBC )) { voice->vc_RingBasePeriod = FXParam-0x80; voice->vc_RingFixedPeriod = 0; } else { voice->vc_RingBasePeriod = 0; voice->vc_RingFixedPeriod = 0; voice->vc_RingNewWaveform = 0; voice->vc_RingAudioSource = NULL; // turn it off voice->vc_RingMixSource = NULL; break; } voice->vc_RingWaveform = 0; voice->vc_RingNewWaveform = 1; voice->vc_RingPlantPeriod = 1; break; case 8: // Ring modulate with sawtooth if(( FXParam >= 1 ) && ( FXParam <= 0x3C )) { voice->vc_RingBasePeriod = FXParam; voice->vc_RingFixedPeriod = 1; } else if(( FXParam >= 0x81 ) && ( FXParam <= 0xBC )) { voice->vc_RingBasePeriod = FXParam-0x80; voice->vc_RingFixedPeriod = 0; } else { voice->vc_RingBasePeriod = 0; voice->vc_RingFixedPeriod = 0; voice->vc_RingNewWaveform = 0; voice->vc_RingAudioSource = NULL; voice->vc_RingMixSource = NULL; break; } voice->vc_RingWaveform = 1; voice->vc_RingNewWaveform = 1; voice->vc_RingPlantPeriod = 1; break; /* New in HivelyTracker 1.4 */ case 9: if( FXParam > 127 ) FXParam -= 256; voice->vc_Pan = (FXParam+128); voice->vc_PanMultLeft = panning_left[voice->vc_Pan]; voice->vc_PanMultRight = panning_right[voice->vc_Pan]; break; case 12: if( FXParam <= 0x40 ) { voice->vc_NoteMaxVolume = FXParam; break; } if( (FXParam -= 0x50) < 0 ) break; if( FXParam <= 0x40 ) { voice->vc_PerfSubVolume = FXParam; break; } if( (FXParam -= 0xa0-0x50) < 0 ) break; if( FXParam <= 0x40 ) voice->vc_TrackMasterVolume = FXParam; break; case 15: voice->vc_PerfSpeed = voice->vc_PerfWait = FXParam; break; } } void hvl_process_frame( struct hvl_tune *ht, struct hvl_voice *voice ) { static const uint8 Offsets[] = {0x00,0x04,0x04+0x08,0x04+0x08+0x10,0x04+0x08+0x10+0x20,0x04+0x08+0x10+0x20+0x40}; if( voice->vc_TrackOn == 0 ) return; if( voice->vc_NoteDelayOn ) { if( voice->vc_NoteDelayWait <= 0 ) hvl_process_step( ht, voice ); else voice->vc_NoteDelayWait--; } if( voice->vc_HardCut ) { int32 nextinst; if( ht->ht_NoteNr+1 < ht->ht_TrackLength ) nextinst = ht->ht_Tracks[voice->vc_Track][ht->ht_NoteNr+1].stp_Instrument; else nextinst = ht->ht_Tracks[voice->vc_NextTrack][0].stp_Instrument; if( nextinst ) { int32 d1; d1 = ht->ht_Tempo - voice->vc_HardCut; if( d1 < 0 ) d1 = 0; if( !voice->vc_NoteCutOn ) { voice->vc_NoteCutOn = 1; voice->vc_NoteCutWait = d1; voice->vc_HardCutReleaseF = -(d1-ht->ht_Tempo); } else { voice->vc_HardCut = 0; } } } if( voice->vc_NoteCutOn ) { if( voice->vc_NoteCutWait <= 0 ) { voice->vc_NoteCutOn = 0; if( voice->vc_HardCutRelease ) { voice->vc_ADSR.rVolume = -(voice->vc_ADSRVolume - (voice->vc_Instrument->ins_Envelope.rVolume << 8)) / voice->vc_HardCutReleaseF; voice->vc_ADSR.rFrames = voice->vc_HardCutReleaseF; voice->vc_ADSR.aFrames = voice->vc_ADSR.dFrames = voice->vc_ADSR.sFrames = 0; } else { voice->vc_NoteMaxVolume = 0; } } else { voice->vc_NoteCutWait--; } } // ADSR envelope if( voice->vc_ADSR.aFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.aVolume; if( --voice->vc_ADSR.aFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.aVolume << 8; } else if( voice->vc_ADSR.dFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.dVolume; if( --voice->vc_ADSR.dFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.dVolume << 8; } else if( voice->vc_ADSR.sFrames ) { voice->vc_ADSR.sFrames--; } else if( voice->vc_ADSR.rFrames ) { voice->vc_ADSRVolume += voice->vc_ADSR.rVolume; if( --voice->vc_ADSR.rFrames <= 0 ) voice->vc_ADSRVolume = voice->vc_Instrument->ins_Envelope.rVolume << 8; } // VolumeSlide voice->vc_NoteMaxVolume = voice->vc_NoteMaxVolume + voice->vc_VolumeSlideUp - voice->vc_VolumeSlideDown; if( voice->vc_NoteMaxVolume < 0 ) voice->vc_NoteMaxVolume = 0; else if( voice->vc_NoteMaxVolume > 0x40 ) voice->vc_NoteMaxVolume = 0x40; // Portamento if( voice->vc_PeriodSlideOn ) { if( voice->vc_PeriodSlideWithLimit ) { int32 d0, d2; d0 = voice->vc_PeriodSlidePeriod - voice->vc_PeriodSlideLimit; d2 = voice->vc_PeriodSlideSpeed; if( d0 > 0 ) d2 = -d2; if( d0 ) { int32 d3; d3 = (d0 + d2) ^ d0; if( d3 >= 0 ) d0 = voice->vc_PeriodSlidePeriod + d2; else d0 = voice->vc_PeriodSlideLimit; voice->vc_PeriodSlidePeriod = d0; voice->vc_PlantPeriod = 1; } } else { voice->vc_PeriodSlidePeriod += voice->vc_PeriodSlideSpeed; voice->vc_PlantPeriod = 1; } } // Vibrato if( voice->vc_VibratoDepth ) { if( voice->vc_VibratoDelay <= 0 ) { voice->vc_VibratoPeriod = (vib_tab[voice->vc_VibratoCurrent] * voice->vc_VibratoDepth) >> 7; voice->vc_PlantPeriod = 1; voice->vc_VibratoCurrent = (voice->vc_VibratoCurrent + voice->vc_VibratoSpeed) & 0x3f; } else { voice->vc_VibratoDelay--; } } // PList if( voice->vc_PerfList != 0 ) { if( voice->vc_Instrument && voice->vc_PerfCurrent < voice->vc_Instrument->ins_PList.pls_Length ) { if( --voice->vc_PerfWait <= 0 ) { uint32 i; int32 cur; cur = voice->vc_PerfCurrent++; voice->vc_PerfWait = voice->vc_PerfSpeed; if( voice->vc_PerfList->pls_Entries[cur].ple_Waveform ) { voice->vc_Waveform = voice->vc_PerfList->pls_Entries[cur].ple_Waveform-1; voice->vc_NewWaveform = 1; voice->vc_PeriodPerfSlideSpeed = voice->vc_PeriodPerfSlidePeriod = 0; } // Holdwave voice->vc_PeriodPerfSlideOn = 0; for( i=0; i<2; i++ ) hvl_plist_command_parse( ht, voice, voice->vc_PerfList->pls_Entries[cur].ple_FX[i]&0xff, voice->vc_PerfList->pls_Entries[cur].ple_FXParam[i]&0xff ); // GetNote if( voice->vc_PerfList->pls_Entries[cur].ple_Note ) { voice->vc_InstrPeriod = voice->vc_PerfList->pls_Entries[cur].ple_Note; voice->vc_PlantPeriod = 1; voice->vc_FixedNote = voice->vc_PerfList->pls_Entries[cur].ple_Fixed; } } } else { if( voice->vc_PerfWait ) voice->vc_PerfWait--; else voice->vc_PeriodPerfSlideSpeed = 0; } } // PerfPortamento if( voice->vc_PeriodPerfSlideOn ) { voice->vc_PeriodPerfSlidePeriod -= voice->vc_PeriodPerfSlideSpeed; if( voice->vc_PeriodPerfSlidePeriod ) voice->vc_PlantPeriod = 1; } if( voice->vc_Waveform == 3-1 && voice->vc_SquareOn ) { if( --voice->vc_SquareWait <= 0 ) { int32 d1, d2, d3; d1 = voice->vc_SquareLowerLimit; d2 = voice->vc_SquareUpperLimit; d3 = voice->vc_SquarePos; if( voice->vc_SquareInit ) { voice->vc_SquareInit = 0; if( d3 <= d1 ) { voice->vc_SquareSlidingIn = 1; voice->vc_SquareSign = 1; } else if( d3 >= d2 ) { voice->vc_SquareSlidingIn = 1; voice->vc_SquareSign = -1; } } // NoSquareInit if( d1 == d3 || d2 == d3 ) { if( voice->vc_SquareSlidingIn ) voice->vc_SquareSlidingIn = 0; else voice->vc_SquareSign = -voice->vc_SquareSign; } d3 += voice->vc_SquareSign; voice->vc_SquarePos = d3; voice->vc_PlantSquare = 1; voice->vc_SquareWait = voice->vc_Instrument->ins_SquareSpeed; } } if( voice->vc_FilterOn && --voice->vc_FilterWait <= 0 ) { uint32 i, FMax; int32 d1, d2, d3; d1 = voice->vc_FilterLowerLimit; d2 = voice->vc_FilterUpperLimit; d3 = voice->vc_FilterPos; if( voice->vc_FilterInit ) { voice->vc_FilterInit = 0; if( d3 <= d1 ) { voice->vc_FilterSlidingIn = 1; voice->vc_FilterSign = 1; } else if( d3 >= d2 ) { voice->vc_FilterSlidingIn = 1; voice->vc_FilterSign = -1; } } // NoFilterInit FMax = (voice->vc_FilterSpeed < 3) ? (5-voice->vc_FilterSpeed) : 1; for( i=0; ivc_FilterSlidingIn ) voice->vc_FilterSlidingIn = 0; else voice->vc_FilterSign = -voice->vc_FilterSign; } d3 += voice->vc_FilterSign; } if( d3 < 1 ) d3 = 1; if( d3 > 63 ) d3 = 63; voice->vc_FilterPos = d3; voice->vc_NewWaveform = 1; voice->vc_FilterWait = voice->vc_FilterSpeed - 3; if( voice->vc_FilterWait < 1 ) voice->vc_FilterWait = 1; } if( voice->vc_Waveform == 3-1 || voice->vc_PlantSquare ) { // CalcSquare uint32 i; int32 Delta; const int8 *SquarePtr; int32 X; SquarePtr = &waves[WO_SQUARES+(voice->vc_FilterPos-0x20)*(0xfc+0xfc+0x80*0x1f+0x80+0x280*3)]; X = voice->vc_SquarePos << (5 - voice->vc_WaveLength); if( X > 0x20 ) { X = 0x40 - X; voice->vc_SquareReverse = 1; } // OkDownSquare if( X > 0 ) SquarePtr += (X-1) << 7; Delta = 32 >> voice->vc_WaveLength; ht->ht_WaveformTab[2] = voice->vc_SquareTempBuffer; for( i=0; i<(1<vc_WaveLength)*4; i++ ) { voice->vc_SquareTempBuffer[i] = *SquarePtr; SquarePtr += Delta; } voice->vc_NewWaveform = 1; voice->vc_Waveform = 3-1; voice->vc_PlantSquare = 0; } if( voice->vc_Waveform == 4-1 ) voice->vc_NewWaveform = 1; if( voice->vc_RingNewWaveform ) { const int8 *rasrc; if( voice->vc_RingWaveform > 1 ) voice->vc_RingWaveform = 1; rasrc = ht->ht_WaveformTab[voice->vc_RingWaveform]; rasrc += Offsets[voice->vc_WaveLength]; voice->vc_RingAudioSource = rasrc; } if( voice->vc_NewWaveform ) { const int8 *AudioSource; AudioSource = ht->ht_WaveformTab[voice->vc_Waveform]; if( voice->vc_Waveform != 3-1 ) AudioSource += (voice->vc_FilterPos-0x20)*(0xfc+0xfc+0x80*0x1f+0x80+0x280*3); if( voice->vc_Waveform < 3-1) { // GetWLWaveformlor2 AudioSource += Offsets[voice->vc_WaveLength]; } if( voice->vc_Waveform == 4-1 ) { // AddRandomMoving AudioSource += ( voice->vc_WNRandom & (2*0x280-1) ) & ~1; // GoOnRandom voice->vc_WNRandom += 2239384; voice->vc_WNRandom = ((((voice->vc_WNRandom >> 8) | (voice->vc_WNRandom << 24)) + 782323) ^ 75) - 6735; } voice->vc_AudioSource = AudioSource; } // Ring modulation period calculation if( voice->vc_RingAudioSource ) { voice->vc_RingAudioPeriod = voice->vc_RingBasePeriod; if( !(voice->vc_RingFixedPeriod) ) { if( voice->vc_OverrideTranspose != 1000 ) // 1.5 voice->vc_RingAudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; else voice->vc_RingAudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; } if( voice->vc_RingAudioPeriod > 5*12 ) voice->vc_RingAudioPeriod = 5*12; if( voice->vc_RingAudioPeriod < 0 ) voice->vc_RingAudioPeriod = 0; voice->vc_RingAudioPeriod = period_tab[voice->vc_RingAudioPeriod]; if( !(voice->vc_RingFixedPeriod) ) voice->vc_RingAudioPeriod += voice->vc_PeriodSlidePeriod; voice->vc_RingAudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; if( voice->vc_RingAudioPeriod > 0x0d60 ) voice->vc_RingAudioPeriod = 0x0d60; if( voice->vc_RingAudioPeriod < 0x0071 ) voice->vc_RingAudioPeriod = 0x0071; } // Normal period calculation voice->vc_AudioPeriod = voice->vc_InstrPeriod; if( !(voice->vc_FixedNote) ) { if( voice->vc_OverrideTranspose != 1000 ) // 1.5 voice->vc_AudioPeriod += voice->vc_OverrideTranspose + voice->vc_TrackPeriod - 1; else voice->vc_AudioPeriod += voice->vc_Transpose + voice->vc_TrackPeriod - 1; } if( voice->vc_AudioPeriod > 5*12 ) voice->vc_AudioPeriod = 5*12; if( voice->vc_AudioPeriod < 0 ) voice->vc_AudioPeriod = 0; voice->vc_AudioPeriod = period_tab[voice->vc_AudioPeriod]; if( !(voice->vc_FixedNote) ) voice->vc_AudioPeriod += voice->vc_PeriodSlidePeriod; voice->vc_AudioPeriod += voice->vc_PeriodPerfSlidePeriod + voice->vc_VibratoPeriod; if( voice->vc_AudioPeriod > 0x0d60 ) voice->vc_AudioPeriod = 0x0d60; if( voice->vc_AudioPeriod < 0x0071 ) voice->vc_AudioPeriod = 0x0071; voice->vc_AudioVolume = (((((((voice->vc_ADSRVolume >> 8) * voice->vc_NoteMaxVolume) >> 6) * voice->vc_PerfSubVolume) >> 6) * voice->vc_TrackMasterVolume) >> 6); } void hvl_set_audio( struct hvl_voice *voice, float64 freqf ) { if( voice->vc_TrackOn == 0 ) { voice->vc_VoiceVolume = 0; return; } voice->vc_VoiceVolume = voice->vc_AudioVolume; if( voice->vc_PlantPeriod ) { float64 freq2; uint32 delta; voice->vc_PlantPeriod = 0; voice->vc_VoicePeriod = voice->vc_AudioPeriod; freq2 = Period2Freq( voice->vc_AudioPeriod ); delta = (uint32)(freq2 / freqf); if( delta > (0x280<<16) ) delta -= (0x280<<16); if( delta == 0 ) delta = 1; voice->vc_Delta = delta; } if( voice->vc_NewWaveform ) { const int8 *src; src = voice->vc_AudioSource; if( voice->vc_Waveform == 4-1 ) { memcpy( &voice->vc_VoiceBuffer[0], src, 0x280 ); } else { uint32 i, WaveLoops; WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; for( i=0; ivc_VoiceBuffer[i*4*(1<vc_WaveLength)], src, 4*(1<vc_WaveLength) ); } voice->vc_VoiceBuffer[0x280] = voice->vc_VoiceBuffer[0]; voice->vc_MixSource = voice->vc_VoiceBuffer; } /* Ring Modulation */ if( voice->vc_RingPlantPeriod ) { float64 freq2; uint32 delta; voice->vc_RingPlantPeriod = 0; freq2 = Period2Freq( voice->vc_RingAudioPeriod ); delta = (uint32)(freq2 / freqf); if( delta > (0x280<<16) ) delta -= (0x280<<16); if( delta == 0 ) delta = 1; voice->vc_RingDelta = delta; } if( voice->vc_RingNewWaveform ) { const int8 *src; uint32 i, WaveLoops; src = voice->vc_RingAudioSource; WaveLoops = (1 << (5 - voice->vc_WaveLength)) * 5; for( i=0; ivc_RingVoiceBuffer[i*4*(1<vc_WaveLength)], src, 4*(1<vc_WaveLength) ); voice->vc_RingVoiceBuffer[0x280] = voice->vc_RingVoiceBuffer[0]; voice->vc_RingMixSource = voice->vc_RingVoiceBuffer; } } void hvl_play_irq( struct hvl_tune *ht ) { uint32 i; if( ht->ht_StepWaitFrames <= 0 ) { if( ht->ht_GetNewPosition ) { int32 nextpos = (ht->ht_PosNr+1==ht->ht_PositionNr)?0:(ht->ht_PosNr+1); for( i=0; iht_Channels; i++ ) { ht->ht_Voices[i].vc_Track = ht->ht_Positions[ht->ht_PosNr].pos_Track[i]; ht->ht_Voices[i].vc_Transpose = ht->ht_Positions[ht->ht_PosNr].pos_Transpose[i]; ht->ht_Voices[i].vc_NextTrack = ht->ht_Positions[nextpos].pos_Track[i]; ht->ht_Voices[i].vc_NextTranspose = ht->ht_Positions[nextpos].pos_Transpose[i]; } ht->ht_GetNewPosition = 0; } for( i=0; iht_Channels; i++ ) hvl_process_step( ht, &ht->ht_Voices[i] ); ht->ht_StepWaitFrames = ht->ht_Tempo; } for( i=0; iht_Channels; i++ ) hvl_process_frame( ht, &ht->ht_Voices[i] ); ht->ht_PlayingTime++; if( ht->ht_Tempo > 0 && --ht->ht_StepWaitFrames <= 0 ) { if( !ht->ht_PatternBreak ) { ht->ht_NoteNr++; if( ht->ht_NoteNr >= ht->ht_TrackLength ) { ht->ht_PosJump = ht->ht_PosNr+1; ht->ht_PosJumpNote = 0; ht->ht_PatternBreak = 1; } } if( ht->ht_PatternBreak ) { ht->ht_PatternBreak = 0; ht->ht_PosNr = ht->ht_PosJump; ht->ht_NoteNr = ht->ht_PosJumpNote; if( ht->ht_PosNr == ht->ht_PositionNr ) { ht->ht_SongEndReached = 1; ht->ht_PosNr = ht->ht_Restart; } ht->ht_PosJumpNote = 0; ht->ht_PosJump = 0; ht->ht_GetNewPosition = 1; } } for( i=0; iht_Channels; i++ ) hvl_set_audio( &ht->ht_Voices[i], ht->ht_Frequency ); } void hvl_mixchunk( struct hvl_tune *ht, uint32 samples, int8 *buf1, int8 *buf2, int32 bufmod ) { const int8 *src[MAX_CHANNELS]; const int8 *rsrc[MAX_CHANNELS]; uint32 delta[MAX_CHANNELS]; uint32 rdelta[MAX_CHANNELS]; int32 vol[MAX_CHANNELS]; uint32 pos[MAX_CHANNELS]; uint32 rpos[MAX_CHANNELS]; uint32 cnt; int32 panl[MAX_CHANNELS]; int32 panr[MAX_CHANNELS]; // uint32 vu[MAX_CHANNELS]; int32 a=0, b=0, j; uint32 i, chans, loops; chans = ht->ht_Channels; for( i=0; iht_Voices[i].vc_Delta; vol[i] = ht->ht_Voices[i].vc_VoiceVolume; pos[i] = ht->ht_Voices[i].vc_SamplePos; src[i] = ht->ht_Voices[i].vc_MixSource; panl[i] = ht->ht_Voices[i].vc_PanMultLeft; panr[i] = ht->ht_Voices[i].vc_PanMultRight; /* Ring Modulation */ rdelta[i]= ht->ht_Voices[i].vc_RingDelta; rpos[i] = ht->ht_Voices[i].vc_RingSamplePos; rsrc[i] = ht->ht_Voices[i].vc_RingMixSource; // vu[i] = 0; } do { loops = samples; for( i=0; i= (0x280 << 16)) pos[i] -= 0x280<<16; cnt = ((0x280<<16) - pos[i] - 1) / delta[i] + 1; if( cnt < loops ) loops = cnt; if( rsrc[i] ) { if( rpos[i] >= (0x280<<16)) rpos[i] -= 0x280<<16; cnt = ((0x280<<16) - rpos[i] - 1) / rdelta[i] + 1; if( cnt < loops ) loops = cnt; } } samples -= loops; // Inner loop do { a=0; b=0; for( i=0; i>16]*rsrc[i][rpos[i]>>16])>>7)*vol[i]; rpos[i] += rdelta[i]; } else { j = src[i][pos[i]>>16]*vol[i]; } // if( abs( j ) > vu[i] ) vu[i] = abs( j ); a += (j * panl[i]) >> 7; b += (j * panr[i]) >> 7; pos[i] += delta[i]; } a = (a*ht->ht_mixgain)>>8; b = (b*ht->ht_mixgain)>>8; if (a<-0x8000) a=-0x8000; if (a> 0x7fff) a= 0x7fff; if (b<-0x8000) b=-0x8000; if (b> 0x7fff) b= 0x7fff; *(int16 *)buf1 = a; *(int16 *)buf2 = b; loops--; buf1 += bufmod; buf2 += bufmod; } while( loops > 0 ); } while( samples > 0 ); for( i=0; iht_Voices[i].vc_SamplePos = pos[i]; ht->ht_Voices[i].vc_RingSamplePos = rpos[i]; // ht->ht_Voices[i].vc_VUMeter = vu[i]; } } void hvl_DecodeFrame( struct hvl_tune *ht, int8 *buf1, int8 *buf2, int32 bufmod ) { uint32 samples, loops; samples = ht->ht_Frequency/50/ht->ht_SpeedMultiplier; loops = ht->ht_SpeedMultiplier; do { hvl_play_irq( ht ); hvl_mixchunk( ht, samples, buf1, buf2, bufmod ); buf1 += samples * bufmod; buf2 += samples * bufmod; loops--; } while( loops ); } hivelytracker-0+git20180223/Replayer_Windows/Makefile0000664001024000102400000000043013042730153017326 0ustar CC = gcc CFLAGS = -g LFLAGS += -g -lm -lwinmm TARGET = play_hvl.exe OBJECTS = hvl_replay.o \ main.o $(TARGET): $(OBJECTS) $(CC) -o $(TARGET) $(OBJECTS) $(LFLAGS) $(OBJECTS): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ @$(CC) -MM $(CFLAGS) $< > $*.d hivelytracker-0+git20180223/undo.c0000775001024000102400000015552413042730153013524 0ustar #include #include #include #include #include "util.h" #include "replay.h" #include "gui.h" #include "undo.h" extern struct ahx_tune *curtune; extern struct textbox tbx[]; extern int32 pref_maxundobuf; extern struct ahx_instrument *perf_lastinst; extern int16 insls_lastcuri; extern int16 inslsb_lastcuri; extern int16 posed_lastposnr; extern int16 trked_lastposnr; extern struct rawbm mainbm; struct textbox *show_tbox; uint32 mubsizes[] = { 0, 262144, 524288, 1048576, 2097152, 4194304 }; /*****************************************************/ void free_undolists( struct ahx_tune *at ) { struct undonode *tn, *nn; // Clear undo system lists tn = (struct undonode *)IExec->GetHead(at->at_undolist); while( tn ) { nn = (struct undonode *)IExec->GetSucc(&tn->un_ln); IExec->Remove( (struct Node *)tn ); IExec->FreeSysObject( ASOT_NODE, tn ); tn = nn; } tn = (struct undonode *)IExec->GetHead(at->at_redolist); while( tn ) { nn = (struct undonode *)IExec->GetSucc(&tn->un_ln); IExec->Remove( (struct Node *)tn ); IExec->FreeSysObject( ASOT_NODE, tn ); tn = nn; } at->at_undomem = 0; } void clear_redolist( struct ahx_tune *at ) { struct undonode *tn, *nn; tn = (struct undonode *)IExec->GetHead(at->at_redolist); while( tn ) { nn = (struct undonode *)IExec->GetSucc(&tn->un_ln); at->at_undomem -= tn->un_size; IExec->Remove( (struct Node *)tn ); IExec->FreeSysObject( ASOT_NODE, tn ); tn = nn; } } void trim_undolist( struct ahx_tune *at ) { struct undonode *un; if( mubsizes[pref_maxundobuf] == 0 ) return; while( at->at_undomem > mubsizes[pref_maxundobuf] ) { un = (struct undonode *)IExec->RemHead( at->at_undolist ); if( !un ) { // WTF? clear_redolist( at ); at->at_undomem = 0; return; } at->at_undomem -= un->un_size; IExec->FreeSysObject( ASOT_NODE, un ); } } struct undonode *alloc_undonode( struct ahx_tune *at, uint32 size ) { struct undonode *un; un = (struct undonode *)allocnode(size); if( !un ) return NULL; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_size = size; at->at_undomem += size; trim_undolist( at ); return un; } /*****************************************************/ void setbefore_posregion( struct ahx_tune *at, int32 left, int32 pos, int32 chans, int32 rows ) { int32 size; int32 x, y, i; size = chans*rows*2; at->at_rem_pbleft = left; at->at_rem_pbpos = pos; at->at_rem_pbchans = chans; at->at_rem_pbrows = rows; i=0; for( y=pos; y<(pos+rows); y++ ) { for( x=left; x<(left+chans); x++ ) { at->at_rem_posbuf[i++] = at->at_Positions[y].pos_Track[x]; at->at_rem_posbuf[i++] = at->at_Positions[y].pos_Transpose[x]; } } } void setafter_posregion( struct ahx_tune *at, int32 left, int32 pos, int32 chans, int32 rows ) { struct undonode *un; struct udat_pos_region *ud; int32 x,y,i,size; // Ensure we're talking about the same region if( ( left != at->at_rem_pbleft ) || ( pos != at->at_rem_pbpos ) || ( chans != at->at_rem_pbchans ) || ( rows != at->at_rem_pbrows ) ) return; size = chans*rows*2; un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_pos_region )+(size*2) ); if( !un ) return; ud = (struct udat_pos_region *)&un[1]; ud->before = (uint8 *)&ud[1]; ud->after = (uint8 *)&ud->before[size]; un->un_type = UNT_POS_REGION; un->un_data = ud; ud->left = left; ud->pos = pos; ud->chans = chans; ud->rows = rows; ud->posnr = at->at_PosNr; ud->posed_curs = at->at_posed_curs; ud->curlch = at->at_curlch; i=0; for( y=pos; y<(pos+rows); y++ ) { for( x=left; x<(left+chans); x++ ) { ud->before[i] = at->at_rem_posbuf[i]; ud->after[i++] = at->at_Positions[y].pos_Track[x]; ud->before[i] = at->at_rem_posbuf[i]; ud->after[i++] = at->at_Positions[y].pos_Transpose[x]; } } IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); } void setbefore_track( struct ahx_tune *at, int32 trk ) { int32 i; for( i=0; i<64; i++ ) at->at_rem_track[i] = at->at_Tracks[trk][i]; } void setafter_track( struct ahx_tune *at, int32 trk ) { struct undonode *un; struct udat_track *ud; int32 i; un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_track ) ); if( !un ) return; ud = (struct udat_track *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = UNT_TRACK; un->un_data = ud; ud->track = trk; ud->posnr = at->at_PosNr; ud->notenr = at->at_NoteNr; ud->tracked_curs = at->at_tracked_curs; ud->curlch = at->at_curlch; for( i=0; i<64; i++ ) { ud->before[i] = at->at_rem_track[i]; ud->after[i] = at->at_Tracks[trk][i]; } IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); } void setbefore_string( struct ahx_tune *at, TEXT *ptr ) { if( ptr == NULL ) return; strcpy( at->at_rem_string, ptr ); } void setafter_string( int32 which, struct ahx_tune *at, TEXT *ptr ) { struct undonode *un; struct udat_string *ud; if( ptr == NULL ) return; un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_string )+strlen( ptr )+strlen( at->at_rem_string )+2 ); if( !un ) return; ud = (struct udat_string *)&un[1]; ud->before = (TEXT *)&ud[1]; ud->after = &ud->before[strlen(at->at_rem_string)+1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = which; un->un_data = ud; ud->ptr = ptr; strcpy( ud->before, at->at_rem_string ); strcpy( ud->after, ptr ); switch( which ) { case UNT_STRING_INSNAME: ud->extra = at->at_curins; break; } IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); } void setbefore_plist( struct ahx_tune *at, struct ahx_plsentry *ptr ) { int32 i; for( i=0; i<256; i++ ) at->at_rem_pls_Entries[i] = ptr[i]; } void setafter_plist( struct ahx_tune *at, struct ahx_plsentry *ptr ) { struct undonode *un; struct udat_whole_plist *ud; int32 i; un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_whole_plist ) ); if( !un ) return; ud = (struct udat_whole_plist *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = UNT_PLE; un->un_data = ud; ud->ptr = ptr; ud->insnum = at->at_curins; for( i=0; i<256; i++ ) { ud->before[i] = at->at_rem_pls_Entries[i]; ud->after[i] = ptr[i]; } IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); } /*****************************************************/ // Simple modifications void modify_env_w( struct ahx_tune *at, struct ahx_envelope *ptr, uint32 field, int16 new ) { struct undonode *un; struct udat_env_w *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_env_w *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_env_w *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_env_w ) ); if( un ) { ud = (struct udat_env_w *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->insnum = at->at_curins; ud->after = new; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_ENV_AFRAMES: if( rembef ) ud->before = ptr->aFrames; ptr->aFrames = new; break; case UNT_ENV_AVOLUME: if( rembef ) ud->before = ptr->aVolume; ptr->aVolume = new; break; case UNT_ENV_DFRAMES: if( rembef ) ud->before = ptr->dFrames; ptr->dFrames = new; break; case UNT_ENV_DVOLUME: if( rembef ) ud->before = ptr->dVolume; ptr->dVolume = new; break; case UNT_ENV_SFRAMES: if( rembef ) ud->before = ptr->sFrames; ptr->sFrames = new; break; case UNT_ENV_RFRAMES: if( rembef ) ud->before = ptr->rFrames; ptr->rFrames = new; break; case UNT_ENV_RVOLUME: if( rembef ) ud->before = ptr->rVolume; ptr->rVolume = new; break; } } void modify_ins_b( struct ahx_tune *at, struct ahx_instrument *ptr, uint32 field, uint8 new ) { struct undonode *un; struct udat_ins_b *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_ins_b *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_ins_b *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_ins_b ) ); if( un ) { ud = (struct udat_ins_b *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->insnum = at->at_curins; ud->after = new; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_INS_VOLUME: if( rembef ) ud->before = ptr->ins_Volume; ptr->ins_Volume = new; break; case UNT_INS_WAVELENGTH: if( rembef ) ud->before = ptr->ins_WaveLength; ptr->ins_WaveLength = new; break; case UNT_INS_FILTERLOWERLIMIT: if( rembef ) ud->before = ptr->ins_FilterLowerLimit; ptr->ins_FilterLowerLimit = new; break; case UNT_INS_FILTERUPPERLIMIT: if( rembef ) ud->before = ptr->ins_FilterUpperLimit; ptr->ins_FilterUpperLimit = new; break; case UNT_INS_FILTERSPEED: if( rembef ) ud->before = ptr->ins_FilterSpeed; ptr->ins_FilterSpeed = new; break; case UNT_INS_SQUARELOWERLIMIT: if( rembef ) ud->before = ptr->ins_SquareLowerLimit; ptr->ins_SquareLowerLimit = new; break; case UNT_INS_SQUAREUPPERLIMIT: if( rembef ) ud->before = ptr->ins_SquareUpperLimit; ptr->ins_SquareUpperLimit = new; break; case UNT_INS_SQUARESPEED: if( rembef ) ud->before = ptr->ins_SquareSpeed; ptr->ins_SquareSpeed = new; break; case UNT_INS_VIBRATODELAY: if( rembef ) ud->before = ptr->ins_VibratoDelay; ptr->ins_VibratoDelay = new; break; case UNT_INS_VIBRATOSPEED: if( rembef ) ud->before = ptr->ins_VibratoSpeed; ptr->ins_VibratoSpeed = new; break; case UNT_INS_VIBRATODEPTH: if( rembef ) ud->before = ptr->ins_VibratoDepth; ptr->ins_VibratoDepth = new; break; case UNT_INS_HARDCUTRELEASE: if( rembef ) ud->before = ptr->ins_HardCutRelease; ptr->ins_HardCutRelease = new; break; case UNT_INS_HARDCUTRELEASEFRAMES: if( rembef ) ud->before = ptr->ins_HardCutReleaseFrames; ptr->ins_HardCutReleaseFrames = new; break; } } void modify_ple_b( struct ahx_tune *at, struct ahx_instrument *ins, struct ahx_plsentry *ptr, uint32 field, int8 new ) { struct undonode *un; struct udat_ple_b *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_ple_b *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_ple_b *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_ple_b ) ); if( un ) { ud = (struct udat_ple_b *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->pins = ins; ud->insnum = at->at_curins; ud->after = new; ud->ptop = ins->ins_ptop; ud->pcurx = ins->ins_pcurx; ud->pcury = ins->ins_pcury; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_PLE_NOTE: if( rembef ) ud->before = ptr->ple_Note; ptr->ple_Note = new; break; case UNT_PLE_WAVEFORM: if( rembef ) ud->before = ptr->ple_Waveform; ptr->ple_Waveform = new; break; case UNT_PLE_FX0: if( rembef ) ud->before = ptr->ple_FX[0]; ptr->ple_FX[0] = new; break; case UNT_PLE_FX1: if( rembef ) ud->before = ptr->ple_FX[1]; ptr->ple_FX[1] = new; break; case UNT_PLE_FXPARAM0: if( rembef ) ud->before = ptr->ple_FXParam[0]; ptr->ple_FXParam[0] = new; break; case UNT_PLE_FXPARAM1: if( rembef ) ud->before = ptr->ple_FXParam[1]; ptr->ple_FXParam[1] = new; break; } } void modify_ple_w( struct ahx_tune *at, struct ahx_instrument *ins, struct ahx_plsentry *ptr, uint32 field, int16 new ) { struct undonode *un; struct udat_ple_w *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_ple_w *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_ple_w *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_ple_w ) ); if( un ) { ud = (struct udat_ple_w *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->pins = ins; ud->insnum = at->at_curins; ud->after = new; ud->ptop = ins->ins_ptop; ud->pcurx = ins->ins_pcurx; ud->pcury = ins->ins_pcury; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_PLE_FIXED: if( rembef ) ud->before = ptr->ple_Fixed; ptr->ple_Fixed = new; break; } } void modify_pls_w( struct ahx_tune *at, struct ahx_plist *ptr, uint32 field, int16 new ) { struct undonode *un; struct udat_pls_w *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_pls_w *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_pls_w *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_pls_w ) ); if( un ) { ud = (struct udat_pls_w *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->insnum = at->at_curins; ud->after = new; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_PLS_SPEED: if( rembef ) ud->before = ptr->pls_Speed; ptr->pls_Speed = new; break; case UNT_PLS_LENGTH: if( rembef ) ud->before = ptr->pls_Length; ptr->pls_Length = new; break; } } void modify_stp_b( struct ahx_tune *at, struct ahx_step *ptr, uint32 field, int8 new ) { struct undonode *un; struct udat_stp_b *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_stp_b *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_stp_b *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_stp_b ) ); if( un ) { ud = (struct udat_stp_b *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->after = new; ud->posnr = at->at_PosNr; ud->notenr = at->at_NoteNr; ud->tracked_curs = at->at_tracked_curs; ud->curlch = at->at_curlch; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_STP_NOTE: if( rembef ) ud->before = ptr->stp_Note; ptr->stp_Note = new; break; case UNT_STP_INSTRUMENT: if( rembef ) ud->before = ptr->stp_Instrument; ptr->stp_Instrument = new; break; case UNT_STP_FX: if( rembef ) ud->before = ptr->stp_FX; ptr->stp_FX = new; break; case UNT_STP_FXB: if( rembef ) ud->before = ptr->stp_FXb; ptr->stp_FXb = new; break; case UNT_STP_FXPARAM: if( rembef ) ud->before = ptr->stp_FXParam; ptr->stp_FXParam = new; break; case UNT_STP_FXBPARAM: if( rembef ) ud->before = ptr->stp_FXbParam; ptr->stp_FXbParam = new; break; } } void modify_stp_w( struct ahx_tune *at, struct ahx_step *ptr, uint32 field, int16 new ) { struct undonode *un; struct udat_stp_w *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_stp_w *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_stp_w *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_stp_w ) ); if( un ) { ud = (struct udat_stp_w *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->after = new; ud->posnr = at->at_PosNr; ud->notenr = at->at_NoteNr; ud->tracked_curs = at->at_tracked_curs; ud->curlch = at->at_curlch; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_STP_NOTEANDINS: if( rembef ) ud->before = (ptr->stp_Note<<8)|(ptr->stp_Instrument); ptr->stp_Note = new>>8; ptr->stp_Instrument = new & 0xff; break; case UNT_STP_FXANDPARAM: if( rembef ) ud->before = (ptr->stp_FX<<8)|(ptr->stp_FXParam); ptr->stp_FX = new>>8; ptr->stp_FXParam = new & 0xff; break; case UNT_STP_FXBANDPARAM: if( rembef ) ud->before = (ptr->stp_FXb<<8)|(ptr->stp_FXbParam); ptr->stp_FXb = new>>8; ptr->stp_FXbParam = new & 0xff; break; } } void modify_pos_b( struct ahx_tune *at, struct ahx_position *ptr, int32 chan, uint32 field, int8 new ) { struct undonode *un; struct udat_pos_b *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) && ( ((struct udat_pos_b *)un->un_data)->chan == chan ) && ( ((struct udat_pos_b *)un->un_data)->ptr == ptr ) ) { ud = (struct udat_pos_b *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_pos_b ) ); if( un ) { ud = (struct udat_pos_b *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->ptr = ptr; ud->chan = chan; ud->after = new; ud->posnr = at->at_PosNr; ud->posed_curs = at->at_posed_curs; ud->curlch = at->at_curlch; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_POS_TRACK: if( rembef ) ud->before = ptr->pos_Track[chan]; ptr->pos_Track[chan] = new; break; case UNT_POS_TRANS: if( rembef ) ud->before = ptr->pos_Transpose[chan]; ptr->pos_Transpose[chan] = new; break; } } void modify_tune_b( struct ahx_tune *at, uint32 field, int8 new ) { struct undonode *un; struct udat_tune_b *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) ) { ud = (struct udat_tune_b *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_tune_b ) ); if( un ) { ud = (struct udat_tune_b *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->after = new; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_TRACKLEN: if( rembef ) ud->before = at->at_TrackLength; at->at_TrackLength = new; break; case UNT_CHANNELS: if( rembef ) ud->before = at->at_Channels; at->at_Channels = new; break; case UNT_MIXGAIN: if( rembef ) ud->before = at->at_mixgainP; at->at_mixgainP = new; break; case UNT_SPMUL: if( rembef ) ud->before = at->at_SpeedMultiplier; at->at_SpeedMultiplier = new; break; } } void modify_tune_w( struct ahx_tune *at, uint32 field, int16 new ) { struct undonode *un; struct udat_tune_w *ud = NULL; BOOL rembef; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == field ) ) { ud = (struct udat_tune_w *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_tune_w ) ); if( un ) { ud = (struct udat_tune_w *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = field; un->un_data = ud; ud->after = new; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } switch( field ) { case UNT_POSITIONNR: if( rembef ) ud->before = at->at_PositionNr; at->at_PositionNr = new; break; case UNT_RESTART: if( rembef ) ud->before = at->at_Restart; at->at_Restart = new; break; case UNT_SUBSONGS: if( rembef ) ud->before = at->at_SubsongNr; at->at_SubsongNr = new; break; } } void modify_sspos( struct ahx_tune *at, int16 new ) { struct undonode *un; struct udat_subsongpos *ud; BOOL rembef; if( at->at_curss == 0 ) return; rembef = FALSE; // Check for strings of the same action un = (struct undonode *)IExec->GetTail( at->at_undolist ); if( ( un != NULL ) && ( un->un_type == UNT_SSPOS ) && ( ((struct udat_subsongpos *)un->un_data)->subsong == at->at_curss ) ) { ud = (struct udat_subsongpos *)un->un_data; ud->after = new; } else { un = alloc_undonode( at, sizeof( struct undonode )+sizeof( struct udat_subsongpos ) ); if( un ) { ud = (struct udat_subsongpos *)&un[1]; un->un_ln.ln_Succ = NULL; un->un_ln.ln_Pred = NULL; un->un_type = UNT_SSPOS; un->un_data = ud; ud->after = new; ud->subsong = at->at_curss; IExec->AddTail( at->at_undolist, (struct Node *)un ); clear_redolist( at ); rembef = TRUE; } } if( rembef ) ud->before = at->at_Subsongs[at->at_curss-1]; at->at_Subsongs[at->at_curss-1] = new; } /*****************************************************/ void show_changed( struct ahx_tune *at, int32 wpanel, BOOL forceall ) { if( at != curtune ) return; if( wpanel == -1 ) return; if( wpanel != at->at_curpanel ) { // Switch panel and render whole thing at->at_curpanel = wpanel; gui_render_tunepanel( TRUE ); return; } // Try and be semi-smart about re-rendering parts of the panel switch( wpanel ) { case PN_TRACKER: gui_render_tracker( forceall ); gui_render_inslist( forceall ); break; case PN_INSED: gui_render_perf( at, &at->at_Instruments[at->at_curins], forceall ); gui_render_inslistb( forceall ); break; } if( show_tbox ) { gui_render_tbox( &mainbm, show_tbox ); gui_render_tabs(); } gui_set_various_things( at ); } void undo( struct ahx_tune *at ) { struct undonode *un; struct udat_env_w *env_w; struct udat_whole_plist *whole_plist; struct udat_ple_b *ple_b; struct udat_ple_w *ple_w; struct udat_pls_w *pls_w; struct udat_ins_b *ins_b; struct udat_track *rtrk; struct udat_stp_b *stp_b; struct udat_stp_w *stp_w; struct udat_string *rstr; struct udat_pos_b *pos_b; struct udat_pos_region *pos_r; int32 i, wpanel, x, y; BOOL forceall = FALSE; wpanel = -1; show_tbox = NULL; // Get last thing in the undo list un = (struct undonode *)IExec->RemTail( at->at_undolist ); if( !un ) return; // Undo it! switch( un->un_type ) { case UNT_POSITIONNR: at->at_PositionNr = ((struct udat_tune_w *)un->un_data)->before; wpanel = PN_TRACKER; break; case UNT_RESTART: at->at_Restart = ((struct udat_tune_w *)un->un_data)->before; wpanel = PN_TRACKER; break; case UNT_TRACKLEN: at->at_TrackLength = ((struct udat_tune_b *)un->un_data)->before; wpanel = PN_TRACKER; break; case UNT_SUBSONGS: at->at_SubsongNr = ((struct udat_tune_w *)un->un_data)->before; wpanel = PN_TRACKER; break; case UNT_SSPOS: at->at_curss = ((struct udat_subsongpos *)un->un_data)->subsong; at->at_Subsongs[at->at_curss-1] = ((struct udat_tune_b *)un->un_data)->before; wpanel = PN_TRACKER; break; case UNT_CHANNELS: at->at_Channels = ((struct udat_tune_b *)un->un_data)->before; at->at_curlch = 0; wpanel = PN_TRACKER; forceall = TRUE; break; case UNT_MIXGAIN: at->at_mixgainP = ((struct udat_tune_b *)un->un_data)->before; at->at_mixgain = (at->at_mixgainP*256)/100; wpanel = PN_TRACKER; break; case UNT_SPMUL: at->at_SpeedMultiplier = ((struct udat_tune_b *)un->un_data)->before; wpanel = PN_TRACKER; break; case UNT_ENV_AFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->aFrames = env_w->before; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_AVOLUME: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->aVolume = env_w->before; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_DFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->dFrames = env_w->before; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_DVOLUME: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->aVolume = env_w->before; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_SFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->sFrames = env_w->before; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_RFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->rFrames = env_w->before; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_RVOLUME: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->rVolume = env_w->before; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_PLE: whole_plist = (struct udat_whole_plist *)un->un_data; for( i=0; i<256; i++ ) whole_plist->ptr[i] = whole_plist->before[i]; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = whole_plist->insnum; break; case UNT_PLE_NOTE: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_Note = ple_b->before; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_WAVEFORM: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_Waveform = ple_b->before; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FIXED: ple_w = (struct udat_ple_w *)un->un_data; ple_w->ptr->ple_Fixed = ple_w->before; ple_w->pins->ins_ptop = ple_w->ptop; ple_w->pins->ins_pcurx = ple_w->pcurx; ple_w->pins->ins_pcury = ple_w->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_w->insnum; break; case UNT_PLE_FX0: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FX[0] = ple_b->before; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FX1: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FX[1] = ple_b->before; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FXPARAM0: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FXParam[0] = ple_b->before; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FXPARAM1: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FXParam[1] = ple_b->before; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLS_SPEED: pls_w = (struct udat_pls_w *)un->un_data; pls_w->ptr->pls_Speed = pls_w->before; wpanel = PN_INSED; at->at_curins = pls_w->insnum; break; case UNT_PLS_LENGTH: pls_w = (struct udat_pls_w *)un->un_data; pls_w->ptr->pls_Length = pls_w->before; wpanel = PN_INSED; at->at_curins = pls_w->insnum; break; case UNT_INS_VOLUME: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_Volume = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_WAVELENGTH: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_WaveLength = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_FILTERLOWERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_FilterLowerLimit = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_FILTERUPPERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_FilterUpperLimit = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_FILTERSPEED: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_FilterSpeed = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_SQUARELOWERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_SquareLowerLimit = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_SQUAREUPPERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_SquareUpperLimit = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_SQUARESPEED: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_SquareSpeed = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_VIBRATODELAY: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_VibratoDelay = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_VIBRATOSPEED: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_VibratoSpeed = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_VIBRATODEPTH: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_VibratoDepth = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_HARDCUTRELEASE: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_HardCutRelease = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_HARDCUTRELEASEFRAMES: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_HardCutReleaseFrames = ins_b->before; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_STRING_INSNAME: rstr = (struct udat_string *)un->un_data; strcpy( rstr->ptr, rstr->before ); show_tbox = &tbx[TB_INSNAME]; wpanel = PN_INSED; at->at_curins = rstr->extra; inslsb_lastcuri = -1; break; case UNT_STRING_INSNAME2: rstr = (struct udat_string *)un->un_data; strcpy( rstr->ptr, rstr->before ); wpanel = PN_TRACKER; insls_lastcuri = -1; break; case UNT_STRING_SONGNAME: rstr = (struct udat_string *)un->un_data; strcpy( rstr->ptr, rstr->before ); show_tbox = &tbx[TB_SONGNAME]; wpanel = PN_TRACKER; break; case UNT_TRACK: rtrk = (struct udat_track *)un->un_data; for( i=0; i<64; i++ ) at->at_Tracks[rtrk->track][i] = rtrk->before[i]; at->at_PosNr = rtrk->posnr; at->at_NoteNr = rtrk->notenr; at->at_tracked_curs = rtrk->tracked_curs; at->at_curlch = rtrk->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_NOTEANDINS: stp_w = (struct udat_stp_w *)un->un_data; stp_w->ptr->stp_Note = (stp_w->before>>8); stp_w->ptr->stp_Instrument = stp_w->before & 0xff; at->at_PosNr = stp_w->posnr; at->at_NoteNr = stp_w->notenr; at->at_tracked_curs = stp_w->tracked_curs; at->at_curlch = stp_w->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXANDPARAM: stp_w = (struct udat_stp_w *)un->un_data; stp_w->ptr->stp_FX = (stp_w->before>>8); stp_w->ptr->stp_FXParam = stp_w->before & 0xff; at->at_PosNr = stp_w->posnr; at->at_NoteNr = stp_w->notenr; at->at_tracked_curs = stp_w->tracked_curs; at->at_curlch = stp_w->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXBANDPARAM: stp_w = (struct udat_stp_w *)un->un_data; stp_w->ptr->stp_FXb = (stp_w->before>>8); stp_w->ptr->stp_FXbParam = stp_w->before & 0xff; at->at_PosNr = stp_w->posnr; at->at_NoteNr = stp_w->notenr; at->at_tracked_curs = stp_w->tracked_curs; at->at_curlch = stp_w->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_NOTE: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_Note = stp_b->before; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_INSTRUMENT: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_Instrument = stp_b->before; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FX: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FX = stp_b->before; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXPARAM: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FXParam = stp_b->before; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXB: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FXb = stp_b->before; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXBPARAM: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FXbParam = stp_b->before; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_POS_TRACK: pos_b = (struct udat_pos_b *)un->un_data; pos_b->ptr->pos_Track[pos_b->chan] = pos_b->before; at->at_PosNr = pos_b->posnr; at->at_posed_curs = pos_b->posed_curs; at->at_curlch = pos_b->curlch; wpanel = PN_TRACKER; posed_lastposnr = 1000; trked_lastposnr = 1000; break; case UNT_POS_TRANS: pos_b = (struct udat_pos_b *)un->un_data; pos_b->ptr->pos_Transpose[pos_b->chan] = pos_b->before; at->at_PosNr = pos_b->posnr; at->at_posed_curs = pos_b->posed_curs; at->at_curlch = pos_b->curlch; wpanel = PN_TRACKER; posed_lastposnr = 1000; trked_lastposnr = 1000; break; case UNT_POS_REGION: pos_r = (struct udat_pos_region *)un->un_data; i=0; for( y=pos_r->pos; y<(pos_r->pos+pos_r->rows); y++ ) { for( x=pos_r->left; x<(pos_r->left+pos_r->chans); x++ ) { at->at_Positions[y].pos_Track[x] = pos_r->before[i++]; at->at_Positions[y].pos_Transpose[x] = pos_r->before[i++]; } } at->at_PosNr = pos_r->posnr; at->at_posed_curs = pos_r->posed_curs; at->at_curlch = pos_r->curlch; wpanel = PN_TRACKER; posed_lastposnr = 1000; trked_lastposnr = 1000; break; } at->at_modified = TRUE; // Place it into the redo list IExec->AddTail( at->at_redolist, (struct Node *)un ); show_changed( at, wpanel, forceall ); } void redo( struct ahx_tune *at ) { struct undonode *un; struct udat_env_w *env_w; struct udat_whole_plist *whole_plist; struct udat_ple_b *ple_b; struct udat_ple_w *ple_w; struct udat_pls_w *pls_w; struct udat_ins_b *ins_b; struct udat_track *rtrk; struct udat_stp_b *stp_b; struct udat_stp_w *stp_w; struct udat_string *rstr; struct udat_pos_b *pos_b; struct udat_pos_region *pos_r; int32 i, wpanel, x, y; BOOL forceall = FALSE; wpanel = -1; show_tbox = NULL; // Get last thing in the redo list un = (struct undonode *)IExec->RemTail( at->at_redolist ); if( !un ) return; // Redo it! switch( un->un_type ) { case UNT_POSITIONNR: at->at_PositionNr = ((struct udat_tune_w *)un->un_data)->after; wpanel = PN_TRACKER; break; case UNT_RESTART: at->at_Restart = ((struct udat_tune_w *)un->un_data)->after; wpanel = PN_TRACKER; break; case UNT_TRACKLEN: at->at_TrackLength = ((struct udat_tune_b *)un->un_data)->after; wpanel = PN_TRACKER; break; case UNT_SUBSONGS: at->at_SubsongNr = ((struct udat_tune_w *)un->un_data)->after; wpanel = PN_TRACKER; break; case UNT_SSPOS: at->at_curss = ((struct udat_subsongpos *)un->un_data)->subsong; at->at_Subsongs[at->at_curss-1] = ((struct udat_tune_b *)un->un_data)->after; wpanel = PN_TRACKER; break; case UNT_CHANNELS: at->at_Channels = ((struct udat_tune_b *)un->un_data)->after; at->at_curlch = 0; wpanel = PN_TRACKER; forceall = TRUE; break; case UNT_MIXGAIN: at->at_mixgainP = ((struct udat_tune_b *)un->un_data)->after; at->at_mixgain = (at->at_mixgainP*256)/100; wpanel = PN_TRACKER; break; case UNT_SPMUL: at->at_SpeedMultiplier = ((struct udat_tune_b *)un->un_data)->after; wpanel = PN_TRACKER; break; case UNT_ENV_AFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->aFrames = env_w->after; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_AVOLUME: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->aVolume = env_w->after; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_DFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->dFrames = env_w->after; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_DVOLUME: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->aVolume = env_w->after; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_SFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->sFrames = env_w->after; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_RFRAMES: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->rFrames = env_w->after; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_ENV_RVOLUME: env_w = (struct udat_env_w *)un->un_data; env_w->ptr->rVolume = env_w->after; wpanel = PN_INSED; at->at_curins = env_w->insnum; break; case UNT_PLE: whole_plist = (struct udat_whole_plist *)un->un_data; for( i=0; i<256; i++ ) whole_plist->ptr[i] = whole_plist->after[i]; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = whole_plist->insnum; break; case UNT_PLE_NOTE: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_Note = ple_b->after; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_WAVEFORM: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_Waveform = ple_b->after; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FIXED: ple_w = (struct udat_ple_w *)un->un_data; ple_w->ptr->ple_Fixed = ple_w->after; ple_w->pins->ins_ptop = ple_w->ptop; ple_w->pins->ins_pcurx = ple_w->pcurx; ple_w->pins->ins_pcury = ple_w->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_w->insnum; break; case UNT_PLE_FX0: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FX[0] = ple_b->after; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FX1: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FX[1] = ple_b->after; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FXPARAM0: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FXParam[0] = ple_b->after; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLE_FXPARAM1: ple_b = (struct udat_ple_b *)un->un_data; ple_b->ptr->ple_FXParam[1] = ple_b->after; ple_b->pins->ins_ptop = ple_b->ptop; ple_b->pins->ins_pcurx = ple_b->pcurx; ple_b->pins->ins_pcury = ple_b->pcury; wpanel = PN_INSED; perf_lastinst = NULL; at->at_curins = ple_b->insnum; break; case UNT_PLS_SPEED: pls_w = (struct udat_pls_w *)un->un_data; pls_w->ptr->pls_Speed = pls_w->after; wpanel = PN_INSED; at->at_curins = pls_w->insnum; break; case UNT_PLS_LENGTH: pls_w = (struct udat_pls_w *)un->un_data; pls_w->ptr->pls_Length = pls_w->after; wpanel = PN_INSED; at->at_curins = pls_w->insnum; break; case UNT_INS_VOLUME: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_Volume = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_WAVELENGTH: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_WaveLength = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_FILTERLOWERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_FilterLowerLimit = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_FILTERUPPERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_FilterUpperLimit = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_FILTERSPEED: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_FilterSpeed = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_SQUARELOWERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_SquareLowerLimit = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_SQUAREUPPERLIMIT: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_SquareUpperLimit = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_SQUARESPEED: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_SquareSpeed = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_VIBRATODELAY: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_VibratoDelay = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_VIBRATOSPEED: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_VibratoSpeed = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_VIBRATODEPTH: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_VibratoDepth = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_HARDCUTRELEASE: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_HardCutRelease = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_INS_HARDCUTRELEASEFRAMES: ins_b = (struct udat_ins_b *)un->un_data; ins_b->ptr->ins_HardCutReleaseFrames = ins_b->after; wpanel = PN_INSED; at->at_curins = ins_b->insnum; break; case UNT_STRING_INSNAME: rstr = (struct udat_string *)un->un_data; strcpy( rstr->ptr, rstr->after ); show_tbox = &tbx[TB_INSNAME]; wpanel = PN_INSED; at->at_curins = rstr->extra; inslsb_lastcuri = -1; break; case UNT_STRING_INSNAME2: rstr = (struct udat_string *)un->un_data; strcpy( rstr->ptr, rstr->after ); wpanel = PN_TRACKER; insls_lastcuri = -1; break; case UNT_STRING_SONGNAME: rstr = (struct udat_string *)un->un_data; strcpy( rstr->ptr, rstr->after ); show_tbox = &tbx[TB_SONGNAME]; wpanel = PN_TRACKER; break; case UNT_TRACK: rtrk = (struct udat_track *)un->un_data; for( i=0; i<64; i++ ) at->at_Tracks[rtrk->track][i] = rtrk->after[i]; at->at_PosNr = rtrk->posnr; at->at_NoteNr = rtrk->notenr; at->at_tracked_curs = rtrk->tracked_curs; at->at_curlch = rtrk->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_NOTEANDINS: stp_w = (struct udat_stp_w *)un->un_data; stp_w->ptr->stp_Note = (stp_w->after>>8); stp_w->ptr->stp_Instrument = stp_w->after & 0xff; at->at_PosNr = stp_w->posnr; at->at_NoteNr = stp_w->notenr; at->at_tracked_curs = stp_w->tracked_curs; at->at_curlch = stp_w->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXANDPARAM: stp_w = (struct udat_stp_w *)un->un_data; stp_w->ptr->stp_FX = (stp_w->after>>8); stp_w->ptr->stp_FXParam = stp_w->after & 0xff; at->at_PosNr = stp_w->posnr; at->at_NoteNr = stp_w->notenr; at->at_tracked_curs = stp_w->tracked_curs; at->at_curlch = stp_w->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXBANDPARAM: stp_w = (struct udat_stp_w *)un->un_data; stp_w->ptr->stp_FXb = (stp_w->after>>8); stp_w->ptr->stp_FXbParam = stp_w->after & 0xff; at->at_PosNr = stp_w->posnr; at->at_NoteNr = stp_w->notenr; at->at_tracked_curs = stp_w->tracked_curs; at->at_curlch = stp_w->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_NOTE: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_Note = stp_b->after; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_INSTRUMENT: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_Instrument = stp_b->after; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FX: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FX = stp_b->after; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXPARAM: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FXParam = stp_b->after; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXB: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FXb = stp_b->after; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_STP_FXBPARAM: stp_b = (struct udat_stp_b *)un->un_data; stp_b->ptr->stp_FXbParam = stp_b->after; at->at_PosNr = stp_b->posnr; at->at_NoteNr = stp_b->notenr; at->at_tracked_curs = stp_b->tracked_curs; at->at_curlch = stp_b->curlch; wpanel = PN_TRACKER; trked_lastposnr = 1000; break; case UNT_POS_TRACK: pos_b = (struct udat_pos_b *)un->un_data; pos_b->ptr->pos_Track[pos_b->chan] = pos_b->after; at->at_PosNr = pos_b->posnr; at->at_posed_curs = pos_b->posed_curs; at->at_curlch = pos_b->curlch; wpanel = PN_TRACKER; posed_lastposnr = 1000; trked_lastposnr = 1000; break; case UNT_POS_TRANS: pos_b = (struct udat_pos_b *)un->un_data; pos_b->ptr->pos_Transpose[pos_b->chan] = pos_b->after; at->at_PosNr = pos_b->posnr; at->at_posed_curs = pos_b->posed_curs; at->at_curlch = pos_b->curlch; wpanel = PN_TRACKER; posed_lastposnr = 1000; trked_lastposnr = 1000; break; case UNT_POS_REGION: pos_r = (struct udat_pos_region *)un->un_data; i=0; for( y=pos_r->pos; y<(pos_r->pos+pos_r->rows); y++ ) { for( x=pos_r->left; x<(pos_r->left+pos_r->chans); x++ ) { at->at_Positions[y].pos_Track[x] = pos_r->after[i++]; at->at_Positions[y].pos_Transpose[x] = pos_r->after[i++]; } } at->at_PosNr = pos_r->posnr; at->at_posed_curs = pos_r->posed_curs; at->at_curlch = pos_r->curlch; wpanel = PN_TRACKER; posed_lastposnr = 1000; trked_lastposnr = 1000; break; } at->at_modified = TRUE; // Place it back into the undo list IExec->AddTail( at->at_undolist, (struct Node *)un ); show_changed( at, wpanel, forceall ); } hivelytracker-0+git20180223/ht.info0000775001024000102400000000536613042730153013701 0ustar @@@@D8?  'g&nl&ol&m'& l& g  ?3wّ7ِْ7377wFORMICONFACE}#IMAG #!! #31 /PDDA$DA$DA$DB@q$DADD$DAPUUQ%UQ%UQ%URPq%UQUU%UQPB$$"A/B$B$!$"O B$B"$"AB$&Do&DodFdF!&Do &Da&DaBdFbdF''q/xx!' 'q'qBxbx))))q)B/P!)))q)/)"!)//)P!(((q(* *** *BBPBB!+Q+Q+A&faff`/6!/&ff q&faDD$DA/@"@/$DD/     //     //  /  //  /  / Q Dfhxz`wǥIMAGM # .?0 "005UQPP?%UU5UQ"?%_6fa``?&ff6fa"?&o53QCS50E3Q03Q53_cS553Q53_ S5S513QuWCuWCuWuWuWSuW7Ur!7U 7Uq7U7UqCCS8!8 8880Q:?:333Q:?*?*?*?*?*q:q:??)?)?)?)CA;CBA@@|@@@@@??????}>=====>?}??????hivelytracker-0+git20180223/Skins/Vintage/tab_left.png0000775001024000102400000000020013042730153017341 0ustar PNG  IHDR PLTEU"P/IDATxcXjժ 8iZA$$^0I0h7gIENDB`hivelytracker-0+git20180223/Skins/Vintage/bg_insed.png0000775001024000102400000001175413042730153017353 0ustar PNG  IHDR  if PLTEU"wE0IDATxOo:ijx؁O!|r79Y E&'&Ir(ˑm%颛-_HVQo sv.n"Yxx4ā@;OCnѯpR>Nr 'ٝӁxޞ (!J Df;i7 D烌I!W{ȧ{sMJ6S~$OUJ*Z)^*/o&Tcr=o4>P@W1ugL%Da6[l^/a)4܆fKXȕ{Hn{9ȷYg̦9f=- c)ۀ~sMq8$\ ?#y03 /29#oq ; (j0M[A].iqԹo[-,Rxj0R' b~\V+L&K8F~.("Ln qu܌& WAvu ā89A '#yu dha%Gew ā ^ݨ& 7HE7}%@4I?Ȯ9g Biud ș6՘K5Ћ3rE=4Y`E#2` Tc.] # (T!9H QH U&#|qCߌtL.LY3_f̥h-JQ2i&uqn7lىq ?r)աXk^ ˩āg= e}DI Ac u!͒1rъvX3K^\A+rLy1Wbc#K:Aoy|mLvtHJ<lA|4hh o*W>WäVJAيq ?r)աXk 2<e:ryS(Z]Ajwg ZoU$Br)uߝ \zx%WMi/Ze5Ζxo2ctGHDq4 ˰I2Ѧ`8ܥ*ZbF/LA%^ Fwy.>'Ӣ(Eh~Gp9G.Lmy ^ H2R:·#Y au9wT[ --I) !AGm1ZEaj˥7m;icR媂WH_}oIS r= d{ D>'XCV)sѺWenMuifOZ@4PZf? D,|炱~Ⱦ869Kİr6q {vQ^ {zeQ {zs}ֳ~o?{@D(QB)g 2L:_Sx1ƻïh  P`x~yր|8aE!\ypW !Wc79'g8݁8 R_ H`"5} "IFZKFd(^ =M.RPP4ܨ@ Q-d5$#7U0*ī?O~;o-oD,Տ'H-~Vb. ;?~ Ȩ#o!h!|HՏBL\au(g-/1KJz88+H+lӟ ttU FfTBȽ -PkT wM@H= D)& t)|ZId Ju9_eu19%Jy-+b29%JyO&@NRn)QʭRԶҒ1DN ͤc26A'P>*%tJ/>Y}p6b8NZrlXa d> :P.SNXS@F4Q&鞴u!L[3)!˽2-Ro\-cdgO2r6c rt K Ivby>:"Uh қ SK =A.h]New @207375 WoH3Ɂ8r)ա8WXrǁR]ArkAe1p@ ZEB6?%}/nOʽ,l^)oL5EVϽDcugua b=@؃\Lt9 T#PA(d6 A5"{mvۃ{[$6޺#M m5 ?GyK1Łl)ZS |ӓvѼo ߒōݽAo߾Q~A->XEXzSa;(3Mqc-10&(lq bX S.; jn!fzZ:Q lQIgzN_ :-gla=ͩXͅ?׍ i@P .,@NαG r2Ȱkʎu9 A-V\Av"Wyи\<@2 JtyC{0 PJf%bc -{)=cwm0KH:]Xs-ZX$ʮ:Ĕ>o|,qj_t  b=@؃\Lt9 a^bʽq|@ׄcO9'jrQ@dKm~FiHh!6PTف` \YxE ]޽\\uLW )P,Z. 7Lۊ䁨 ȃdLWֵ2 m'jZ<> eʶ(壅#Ǐ+Zhqrn̕]|/Ɂ8A% [?LOPpбذ纘CDPkmȡ~=KY[ FÒ-*emYDh@,@YȄA,aMu ďhdArրI.riƻ=vϸ:ke%H3oCS*iAXZrk7oP&LA.e0Pij!ReH+k@|CT><Ӳ= k!B"W_{@Ī18IP'Hx cDeYe$۔6׽1DF1\R#n{cPCp?N ȯUI"F2ƷrV n{̵ԕ״"ތYFגqO7a~+)skCKG rlX]WB~beJm9jrϞVO+3WKDTZmj+BV !_f䴳g@bv<WްJ$"W^ bEvmˌ)E徭[w;iiѬxӻռ-. VwI>WJ<-O<}V󶸀@xY٥H'Yp)+Œ)RSD/xH n3<.?4; d %th q-!'~'ռ-& Ҁ Wx ^nu32{\U͎`rqVBL[$CX֯ VM_z4vWk@hJWsB5a 1/D"i4Q+]rqxWYY~a^.{@B/Xgsҵj׺" ji,@!+sU\VJ1iYx"{4sq=Zbtc'2=!>!L{O2=mȐnZկI d{̍ujNVHV CӲk)ߴ ӄ> I Ե A=k=aS{@htQztSZ"е qEkezo` rlXBԵF_&7ԫE}[>[Cx 3f]kE%4mrX IUj;_sRZ(t+!i|lDl^򞦧w12d = P1 =ȼb{7ud{f^[$_yt #~L؀ yO[2=m ȐE ͤcUe= ~Ch3Z=z96@ _րLqI ![b~#SGɹYsTh[sAvd@- - /{O$O]Nٻ3A2ߏOwˋh, >Yd@Nҿ˛ ,rȩ@<9Uș'v I Mq;t3~ULIENDB`hivelytracker-0+git20180223/Skins/Vintage/trkbankr.png0000775001024000102400000000023313042730153017405 0ustar PNG  IHDR4 PLTEU"PJIDATxС rBHYɞۜfi_Wv -i)HKAZ R Bp-桨LRoIENDB`hivelytracker-0+git20180223/Skins/Vintage/bg_tracker.png0000775001024000102400000000467313042730153017706 0ustar PNG  IHDR  if PLTEU? jIDATxݽn S-$,@U+  ;WA)\q9 C<3TH#y;LJ68 ZP j "=oLٚ}_2@[o}u͚oxo*.f"=Ylp#F47(\\bRj0q.D:A֏;b3aV{ Y;&M'Ŷre&,=;]i/$yuqjT"Mz"H-nзkqnXĢ2|ZzbQR)Yz n\,n~һTEs?}L'm:ܷk5~tX7ЏA /B-R&; rVEFǑ"@Vz8duqe} }=$> 7a)烸F4B5JXyNz8aʷEvDQHD2<(h .e$mHmWS|ZRInr0q6.ʗ\ jwjNH.xeFH?kUvO; %>On痂H.*Ň]ݪp-Sԙ&1-4MD.ҤkB96.׆!;-ZZvr/-א|QKcrI"aY+n1-HlGz2d" ,byZ9D¶keXLn}z aE2Nwo; .ȿ[eHC3X=2@ r]gkėkBdErG-DvbuvU[H؉ʝݞ-.{ eԲ;;^ֽ;\櫳ÝsQz,{| <d0# ٵsc!t^W!^b6.{">f3QJًH-n_ J+B7Dε}^ClאÐ V g f̍@:fvHfokH;Ye@ڙӛ+H;&"+sU̮97޵V3{Iqw @9 `@@@dhQnOz ZPh r]sc/ t~:ۋkBVjBZ@] kv- Ԯڵ?tڟbԥ^Y_i܂=w_ B-@ ZP jB-@5WQnOjB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jBBr ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@ ZP jB-@{g%i|+3;9rٜ1IENDB`hivelytracker-0+git20180223/Skins/Vintage/butbankp.png0000775001024000102400000000026713042730153017404 0ustar PNG  IHDR@`%C PLTEU"PfIDATxա D.f) < PMOVRqtNY?7H^`[+Ol #U?xe)mA|<^ vIENDB`hivelytracker-0+git20180223/Skins/Vintage/tab_mid.png0000775001024000102400000000015013042730153017164 0ustar PNG  IHDR  PLTEU"PIDATxcX xb̡MEIENDB`hivelytracker-0+git20180223/Skins/Vintage/dir_popup.png0000775001024000102400000000025613042730153017575 0ustar PNG  IHDR$3 PLTEU"wE0]IDATxMͱ @RROƉe1ADDzQdVw}mlI5<UξT%E܇n|yL'IENDB`hivelytracker-0+git20180223/Skins/Vintage/tab_right.png0000775001024000102400000000015713042730153017537 0ustar PNG  IHDR4 PLTEU"PIDATxcXjUA`SGb F%QIENDB`hivelytracker-0+git20180223/Skins/Vintage/vumeter.png0000775001024000102400000000020113042730153017251 0ustar PNG  IHDR @= PLTEUU<.0IDATxc P`F<'* J x`J zIUaaIENDB`hivelytracker-0+git20180223/Skins/Vintage/itab_right.png0000775001024000102400000000016313042730153017705 0ustar PNG  IHDRΕ PLTEU"P"IDATxcXjU%6u$)&؅-?mIENDB`hivelytracker-0+git20180223/Skins/Vintage/aboutfont.png0000775001024000102400000000600613042730153017574 0ustar PNG  IHDR"&_PLTE"3.8AV^rhxzǥ44tRNS@f oFFs*IDATx]ے;dM>'k=مD4HFTeYՐe)(]x(_Gvg)gf z$w.{8|VM3)+z#W#f{F߼El_[.|׵Ew_Pƿ/DY2u .gLSM\Qu¦z%#ƟP7~hlvxr -2yp} 1$ dϐ9t7=d}c?/Wj⊪}X-60ztF]o7Due  [->CY nmHDWek Q-#}UPHjY5tX}^/kɟZm.͐/r17m Rյw|oV<&Z"ܙiK>U;ϐ56~4/mEtjtzZg͹r!q lፑRn@!}Pz6G=s?Cܦ>|X2;|>[b62o XVpp%+ZHk-rJ<ϐ56~4Vn8M)FWZoUʼ)`YmZ;Ϝpڕy^т5ㇿ0=S?C\6~4k;tzZƗm >͐)G]'-X#/;~+P&7}=Kk)_=cE_)s{;?6?u.Q;S9]2O^+ZFz_4wWPž',-ғ7h">ߵ+kEs*?U|xv_,b2Z5Q|?Q=:Ji1pO: lqE6@HY/ z-?[6Q|?Q=EN੒- -*>ڎk!qE[ !U#ss™j‡S? cBnŏ^fǏHEn#sMla"I|Sŧ[~-#߲4NhkΈ2Z5Q|?Q=:J),Q?+hU P%Vb"p#gF">7?--UvRw oi —=Q nB_We>eu/V_z"QZ1sEƅAG(Tx-'-ҋ-}Hd"\eg!~@6V_ | ,~}-NG7R)a`ߋD`P}]5R.e>"m&F$ܳEf4jTEbH?H.}L FF__kE~2_BQ% U#/vdDLIgi`l^} P!MnB؇⏾2l *Ad|uםB~_#Y?Agq*!e>"; K'㫻c%?[Wi4A@?nPh9~3QwG"ƇW`H e?[?c'[-RɟE|z`g*ŷQ%Dg8=}="[ 4>|ŚOH8xEL#L%HE?xz*+ߺE.OmWf!a>`qiU =.?La:+@X=E. xL},c-_Eb\%lo [B&:J=D?7_E._Ow\XZ$R2 ?`J:J\RRRB6r7+&?*qsO{ WbU$0JIENDB`hivelytracker-0+git20180223/Skins/Vintage/prefs_os4.png0000775001024000102400000000254613042730153017504 0ustar PNG  IHDR,fo PLTEU"wE0IDATxAo8_hzJ؟ ͅS%Ҋ~ z(ld)9uJb v $$_$Ϳ/`W 1X~O-la,ʊ'U-2Mu-34j6R+Z5-8'!ήhimnFp5H_F3j%(t\KVs-la [r+"m[N[/}IK,*#GXaZ팢&lQA5D<ζ8o#9>Pam]ݛ$}ovF,-2nUݶU~+ X|Ftb=k"2Ԙy*б6)=xmu^:HA^B5MiBQTPrJu6N̪-x>[[BKi/sٰ c.M%=juhc]N@-m˖r)=)Sz~1qb-(&zlHIz6!KΖy1c_8L]j]g|̋rtX&J-[ns6-r O\-d0)TپB6客KtJcru). `( "YTRgªoqFThiG-} j%dA@oYad YN)z9̣2F@^%(/( cE5%=Q*\y+Hge\&{cOcUa "cc}"䱦kҭɻ%_v1)"eS"qʤq<&H$,zݪvjk0yL%D O7-0yIWKn媝wyc"6QVOdqw®u~$eYUY(IQYR(+ylyc̏籟ʢV6cx cDŽic70t}w6CǎnozcHce5*&y-Ǿz sG+o1O0WQh/cاzK/OE eiCV,H\"NfYljU<<3/Ǟ,~uh/\І .o\u YuKL*^<5ô:VpNiFy!Y0б/Zmuͧvw J}+Gܰu|3Qy5V{+VUSlո4l,f^ɻr)R +̴'ZbDZ֥;3C/ukd!9E[*0弗Dugk-`Pse?*އpKYDO]Pt^`_ϳy]y(JTɳYJbYɋzl\A"A+q?x oH0x2K<u4}~*VYf*\X % yLE+9L3P܂zъXT"3!}!?( y!!g=O`KUDv$kڛ;8gףQ^g<ξ:X}nYG3"'A "xM9bS_.w-+4Y(q&.x y^j!w ~Ԃ{37Umw;x]+@ Hjv]yIC+$7bt_ۛݥ.V]+ߛW0mm@T𮦪O]5ylP*zBE+w rew3EK: ?&"q4$mY|i1HH: cww& $q4g3E zR]o0 -%-Y+&[6N<xÏK^~/ nHl2 Y9+=nd/ {SpK`rZj1|}{ 0` UPɰ[0˝ ^٥[ se-%e 1+Mt!vWx^d(b-+o&S84y͌ r'AB !AB !AB'C]N.o8`IENDB`hivelytracker-0+git20180223/Skins/Vintage/logo.png0000775001024000102400000000277113042730153016540 0ustar PNG  IHDR`/\PLTEU"w"UfwU3fwwDUUw"fD"wf̻wUU݈DffDwwU333fffwww"Df"UwUUD3U3"Uf333U"DwDDU""w3f3"DwU3Dw3fU3D"wU"fD"DUD3D3"U33"wwwwwffUUDD33""wfUD3"wfUD3"wfUD3"wwffUUDD33""̻wwffUUDD33""wfUD3"̻wwwfffUUUDDD333"""wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"Uwf3DfwD"3UR& IDATx휁v E1&IHh-V_6qv-,@-paO >H?ro0r   `n__혂z#ٮve0!=A37qXz3fT<)3v E-UM#7fi4*qx z*qRdx5{(ʽ #:tm[qZ@{S 04 ' %LzJKf5L5ZʟoG>>*ey>Y\#\H~)GE(`x3`,65=yWmȧx+%V0{\];4ToYkTBl^ 0[Ta0|WañzSbc9 2ow7x&!b9_̑=r\E?׋BH4b%GjwOyϒ/;@,*k_,uRFm^r=s BYc za=8 _,`l[o?~sML#^GIENDB`hivelytracker-0+git20180223/Skins/Vintage/chanmute.png0000775001024000102400000000021213042730153017370 0ustar PNG  IHDR%M0 PLTEU"P9IDATxcXjH0. ˂ա`CZA-xp4×IENDB`hivelytracker-0+git20180223/Skins/Vintage/trkbankp.png0000775001024000102400000000027413042730153017410 0ustar PNG  IHDR4 PLTEU"PtRNS@f oFFsIf+IIDATxС _RxO^DlD+ =)HOAz S ==9F MRIENDB`hivelytracker-0+git20180223/Skins/Vintage/Settings_os40000775001024000102400000000262213042730153017375 0ustar # # format is "RRGGBB" hex values # any line starting with # is a comment # # 16 pixel tall proportional font font1 = 'BitStream Vera Sans.font' # 16x8 pixel fixed width font (MUST be 16x8) font2 = 'BitStream Vera Sans Mono.font' # 14x7 pixel fixed width font (MUST be 14x7) font3 = 'BitStream Vera Sans Mono.font' # "Yes" if you want a different tab background behind # the tab label text, "No" for ordinary (defaults to No if omitted) tabtextback = 'Yes' # "Yes" if you want shadows behind tab label text # "No" if you don't (defaults to Yes if omitted) tabtextshad = 'No' # first is the background to the track, pos & # performance editors, and the number & text gadgets 000022 # next is the "dark" colour underneath the position bars # in the track, pos and performance list editors 804400 # position bar colour cc8200 # "light" colour above position bars ffcc44 # text colour ffffff # disabled text colour 333333 # wavemeter colour cc8200 # normal cursor in track, pos & perf editors ffffff # edit mode cursor in track, pos & perf editors 000022 # text in buttons and tabs ffffff # shadow in buttons and tabs 000022 # track F-key position highlight colour ffffff # Position editor channel number highlight colour d6e6ff # Tab text colour (if you stop at the colour above, it'll use button text colour) 0055aa # Tab shadow colour (if you stop before tab colours, it'll use button shadow colour) 0055aa hivelytracker-0+git20180223/Skins/Vintage/itab_mid.png0000775001024000102400000000015713042730153017344 0ustar PNG  IHDR = PLTEU"PIDATxcX x\P`,sn n7ZQaNIENDB`hivelytracker-0+git20180223/Skins/Vintage/Settings_os30000775001024000102400000000262313042730153017375 0ustar # # format is "RRGGBB" hex values # any line starting with # is a comment # # 16 pixel tall proportional font font1 = 'BitStream Vera Sans.font' # 16x8 pixel fixed width font (MUST be 16x8) font2 = 'BitStream Vera Sans Mono.font' # 14x7 pixel fixed width font (MUST be 14x7) font3 = 'BitStream Vera Sans Mono.font' # "Yes" if you want a different tab background behind # the tab label text, "No" for ordinary (defaults to No if omitted) tabtextback = 'Yes' # "Yes" if you want shadows behind tab label text # "No" if you don't (defaults to Yes if omitted) tabtextshad = 'No' # first is the background to the track, pos & # performance editors, and the number & text gadgets 000022 # next is the "dark" colour underneath the position bars # in the track, pos and performance list editors 804400 # position bar colour cc8200 # "light" colour above position bars ffcc44 # text colour ffffff # disabled text colour 333333 # wavemeter colour cc8200 # normal cursor in track, pos & perf editors ffffff # edit mode cursor in track, pos & perf editors 000022 # text in buttons and tabs ffffff # shadow in buttons and tabs 000022 # track F-key position highlight colour ffffff # Position editor channel number highlight colour d6e6ff # Tab text colour (if you stop at the colour above, it'll use button text colour) 0055aa # Tab shadow colour (if you stop before tab colours, it'll use button shadow colour) 0055aa hivelytracker-0+git20180223/Skins/Vintage/blank.png0000775001024000102400000000155113042730153016662 0ustar PNG  IHDRH'=PLTEU"p$IDATxc`pJJJJJJ b &]7NIENDB`hivelytracker-0+git20180223/Skins/Vintage/itab_left.png0000775001024000102400000000017613042730153017526 0ustar PNG  IHDR%5 PLTEU"P-IDATxcXjժ 8iZA$$^H6p:IENDB`hivelytracker-0+git20180223/Skins/Vintage/tab_text.png0000775001024000102400000000014213042730153017400 0ustar PNG  IHDR  PLTEU"PIDATxcX CA foRIENDB`hivelytracker-0+git20180223/Skins/Vintage/itab_text.png0000775001024000102400000000014213042730153017551 0ustar PNG  IHDR = PLTEU"PIDATxcX Co~`rIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/0000775001024000102400000000000013042730153016517 5ustar hivelytracker-0+git20180223/Skins/SIDMonster-Light/plusminus.png0000775001024000102400000000245013042730153021270 0ustar PNG  IHDR&2 mPLTE"""33U6X9[;]>`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ/Mb+tRNS@f oFFs*IDATx}ю0C" $i,h mfPAwxV7{n[OkYa *x|ݧ8~_|(`~Y˜jިvG%cQeF19W[lYT0߷}7mbFJ#ӼM.I*Ж1QM -ϼV.NZW20 :ClYTnqR*P)neB'j*b*Pשׁx8 ;zezc*PVJ`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3Uʑɏǎƌŋĉˆ~|{yxvutrqonlki~h}f{ezLIDATx[o@;d"(]@P7Zzab&Be['>W$0ec=w=?C4K @纞A0ph4O& v+ $+t{O?H* o:r .>3꽑4&x/"Bi(=$"H4QrwOl-Vk3]gTpOQBuE@sA8 uj0*8 rSqX 3|XEgC4Сl駶r iL0$ Dq9ge!H֞0MӲdQXo&ò;V*׶B G?D&X\ 5x% n'x4Bh8 |\ .|beJu 'r8g [)eAhsts18hqA9q~<Vkff \ ($l4Ͳn?D\pnGx%ڤ`YѸ`kSp\)aL _ʝ6L"av") -`Β n"ߒ% sAȋ1M˰6YE4?U[bN@.K# ?ϙP6O#Si54mL-[uI% E͊]qA䕤vtNB0m%Fc^).*?(q9|`u!-i^畤8nein6IQj AuƪK孙wAb#u}bfQQ,nIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/tab_left.png0000775001024000102400000000220713042730153021011 0ustar PNG  IHDR͕-DPLTE"""33U6X9[;]>`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣN*0tRNS@f oFFsaR IDATxj0MH F%x,Jfc41&׎^f_ OȁŻɎ;ư.MhMC;eD=e};s__(xOH%ܾgk',w%FN5 HΗ(厈xCO "bj!CE_˙@ 10^(xє#[.PȮ ăYUZ>mgEeU׈8$"y.DQRHH%RWDDuP׵zg3`Ac"Df%Gi(Jl+Lo-Oq0Rt3Uw6Xz9[};]>`D\tAcDfJiOmUoUpZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ.8AV^rhxzǥMVi?UkQjr囷ՅnhxBKZ -:=Mb_nGPa^l:BO>GUCM\\TIDATx} w6vںe=RH(Pչ{˰Y/-y˒lcGOcKgُ`^;i'O!\U! 3ƂFYru`:8A~1F-! _-䵺,i,`-Z\8Z}F.l EY⚻#qѧyF>s^V[fGW+t0պ54e>\L~71:h|>V;f'U4Yk`oiw[F&io}FV?n;J%@X=]a͚ZӍø*LL( 7;<@gDÚ<_='Meul^і=Yú5ra@)Y&A(ڃ>g7pVޯ#TV$c^++{[jWT>f `Euaf `@5" 3ܖI8Ó|ψt^xK^#OKpj[֮VL-Ԃ^ϸg-4B@ӍlVAig8D_ !\\v$/-PڕچΤBν)쯶iu@LG[%G!z2T:mنS_nVЗ~oF~P믵S{ǧٟol&<J=ѧ mr"Kζ#;™p} lfGZ`:ttIm8|*Q 0м)Sf6E3ȜfU]s03!'A*?E~HVYD})1o|zЯŲP3p{~ÓG?~/ݝ޽{$~{csyP<<?'yyz*~~)~`h/5^I(s~#{)xZsNh6D)6 GN/36̌[{QQ>i& _ 5Av!<%\88€Kp KAb8uWA}ͦq0XaK&Cm2Ȉ?)nn߾啪 FAհ K&+KBܹsg~K߱gX#om/G~S> mLP ;?RK&Ⱥ8@0 "ƕ|ƿ`O8~qW`.9XWSdۘ zH?Ts@ ث)sScU4b& .ؿ޼yͿ#  #2L3Eh |>Ns?:Ӽ,Rk K]qAU2L}ݖCU0ni>Cp3k ޼}v"t.ðN,Rc Y{O]> l|$< [Hmnw$HP-I 6ǓEn)S! W < l|\aN~Cp)u9"x @Lfɧd@ 匉,R\KMw?ݯ?X| W'ɧ4*ANT1tCp)1/:I'?3%AըNF Yxd\;[>[w8D7PD r?Pnvm! pojFu&];3%A؉7*gtCp)5 ><ۀ5uMɿO0$|M7d]WPMo7SG51|󠲖;!L|X#$w1t)R\TþOB̓[ܹx~v2_S˺a: k/=;n 0^ҝ6 AC'*HW޻Zl7H n6 x`-Hhĝ Z'x~S)a{isO5ļb߄= "C*TD8XNCPASR쵂L%iI R2l*` >dr\W0q_UbJ9A2R= =t CsRWZ*pV;ZqZS3HАRdd lj\,A xMـWA4HPxTC[ 4|O|jfLA£壼NA5SFwX!H4a{i/! W!0`;4_ R4r0`_"3ĔU;$h<ȶm.[!&0qYx$'D(S4&$z 2|  Yx$ v#DF":H]U|z.|!EXzQ Y*A70)6%`ꅊ@5(u2>]bzL &;~.¦>jLzf A *L B?ĭ tV`P3GA{he Y*AQ*(ǪYAuU:G7!yb{So ci;FXQ @dˆeGoejҪaNxGw秇 ' YAbA3:WAc-D A(=UA1\t=P 5&0jI;N.aLtf+d(z+Ce:GOU`(!KE+.&)HI/ b-[Lzbҟd[,v w:PS/fM*H0IPAdnwOL:N_pL< Rc5F`Hr`@51cΨ S N<n.; qƤQ ϟsq|×Ͼ  G_$( _Ƥcbݍg !dzL.UAč>CNEPc5F.=%]%wJCNWCNb$P!K)H[Hź.;XcU2x'H}>'RNE f5&\idjǤ;RŊsWK^&w!A%Bn˿cCX@"]\BJ)zvr% Qaw\W^c5FXޘIkԇjD }tHz IjLzfȥ xQSA8?Xsc| 7D']ޯM 2"wPi Qo"FV-` FV']Զ"zEf =gZ!Ô!?\c&&1 Ԙrͦ$f`,^#BXĚʝ\e!͋#118%a{c Ւ3l GAdk~PQWO?"r%N DDzI/lჹ݊wj jb 6}.ײfݗh ,cV0}{aX Nbt ] ¥Ĥi; a(< & V8Nvt) ғ 2=&}V $2Xݣƍ_-oѧT[䖮kz ѱu$J xZ% r. riN3ܶ2JvkܒuMSrg%JWވ^4" _/)yK! M#A[1Jnc8"=KLzc @M5DM,=X 24AdL(]:=2"=Kmk ATĂI`$g!^)1{Z% ^ 瘂|U ȷ-lV bajIG |"HI/ @'FA& $l =T)p, rTJLzk łjb%az ёc[֭ǟ|O9Apo%yHȇU$><%xڎ$ؒDd5hÐ0՜8%E!:Ohoݺ}镎 ϋ4ҧ7F`2aþa%b٘ C<> oJUm# dhvD"'AqJ4YV^܁gUld:<ڥ{ B:׮kL@I}G H2Ѹ}w ^q}X m"ayʩaxά&\! $p.Y< Bp!$| `O8&yz [>l0VJ/ŔO-l1Tfb/xVu+S2+n+zN|yR$*ᗌ/UA8؅/Upĺc2FG "b)H0Źw\*KA SLX1I-.aeSx'ler*N$}! H8+lKg:JPV)&VC9J` _ ȿ 71lAtSA 9߸N0-@AL7C\h-V2*>taJ$Sm$\'l#ժ;΍_1y-VUd՛dD`2Le"@>AVP\΍/. u;ċo hڌ2l? ,R\ 0ʼn2eD)P iWPxX?}ںG\q2IQ`ycE S{w*><ހx g7[_UKAQayc$t q}3r|ІUAfFXW0Y8l**GФ1C:Pr{.zuSFS- ȥ "HBXY?HGѰdw5ajcczL:(H۞.F+ajCz4ȑ\ǸN&caĂQ*Af@ AbALY!&\ca*Af@ S N 2e?l&oV̀\09d T<رq0,V% E'Ô SA)rF݌E8r0CT1 iJ9 N T1JA?F$T́\o S! [MB(r; _%dNo^H6J `t*A@t'Td< )AX= s A Sqt>S x52fM!kdCiO-l჉#ajؐɓ(dM!rvRdR'p1[tAElbD,b[Dˠ%bԯJ9 N:vDy_ЯӰ"`&Fh&Gb980;̚'E A$+XVdP=! ߱gv':3ȼyߋS Jj"fnļ rVͦs0@5nP̚'=CFf0ziCp)5 q3Lq=ctgir'2u"o3k|`;& bnXF-yEǐ\؀j S1Uc}r՘-rϙ g9dW>l y LYxƅ Pa*?l i !nw PaʇIy !.; $90C7Ô SPAp3c QE?FRp) 8LIϨ ćL S#Oz!,=UdzP4x2Cȭ"5߄WF/ŤOo6z S&\/Ct;rr :Wj6\ 0 DVqޖ# 4enRQS8(1 Y=l*Ô*rLPB\x6h4OQWP:YK)HPͦ蜟*CG}jert7#CLyHgSB *ɎDQţ>́ AA B_P!O: aN}V4$1d 6)(\L&L ʕnnLATD2GE$"> s l!'n<;`zL:nIw^dy cZ***+`jT) @tdcұȷ&|eqrEGPddjq3N٦|1n'ӤXDt_,fk è 26KdgJ%];kOzE2 G@U9>qSD #%OtCDXTRO ۍGbzLzQr7UA@qղ٦ <5$UA""MAy?lUAiK(\x$T Ի'$ȓ$4CiO-ljq2H8r*lVAjUZ]@'u T)pvB_/vt3c ɀjH8jr*(`JGTv^Ĭxh@i Y*0Z&N]_PͰudʑ']GnWUݢAyU+ju ZQkeTtT}1S%TC^/5Zܚ|ҍ"ǣVBVTxCqM)~/liKqUa@L曚:(tnMt%)lQ][ûnR)]FU9q0P-XҽL`Y &`tjd$~wF\wMAoT~8Z"ȓ>)k{+TJfH\wLAHr 1ecAB MtUT4" -iBtF)A0e#H)RȓΉU/ Q?fZ'W71M)HV2]Mk*MzNq_\XcWZ_!QdlJAruf=z4QǤ{ =TRL k.ZQ4c+Ac?} bRD^ #Șv qDmF"LǮ$C_PpW4Tl jJ`Lz%H<*A1u l JxT c*A! {0 s`FbzLDDLeA%[Lȩ S !ހ)BpXCi;Gv<~Xȩ S 乇 ^%zJ]O1ZFr LI*H%ul暊 YUMj&! W13qѨ`&J|0.1IF̦> A ($CPAlzȭ_A\K('ԈRƐϊ4[4&ޫ=F? $AEfNT %&` Dq٥! Wq 7q_Dw[ :Qz2wA-ՍR!)P 2"&ԛayg3ꔎ S T5AY PIOi# s vo¬*jm(>=YrW1ა)fͦs00}%n{RiZ{:=&=}8MK)HjSi9l휶~P 21cUA@*=GL:narbx:=&a)|jfӈ9a bгĤ? ҉|aVVtW^PiLP ݏ~wNf(A@+EĬQѷV j{}ڡh s //|>ILAnbŧ(%zje{[߱', R9}/%VJJ|K JP {uFG"HqGw\Oh!LJ|KTT^}A9`*3*%{dRU%EP`r#Ï _׫_9KćP@ooƙ a¤N*A@.'3Pqq^;/_Y Q$AT M#AqQl\E󮌯o ) G)l⌠ƍE Tppd  bN)W+%E; s A k\lQNzOh*%N0|O|jfӈ9^¬LF%* >A C>[((T́\ u 9) "# MTXO4ҧ7FհvG W^`~ 83 BYFe'* ajtpj) A>sY!"nbm s Ab2L{Xs [4Kd =M#`j2Ly%/CC)&~؅0A b0Lc%аaAJ$A-~]ĻͲKqUPMlԖ3aJ LJ)V,Q SOgo s fJ싥X#dntUE" G=ՠ $UAl@5AKI*\ yA3)Ibҧ7FՄҿl9knL rtxQ_8)׫$* Q)l@(PIlQH9=+.UA!MlS(mo*@k% zwUH\WMlSl}[D;y_{V,qM*T)y lI7>kBT T>6 lS0A" & ИSQ GX6bME)߂`'Yn>E{;iO-l1T2J렌L~D+H-Lr X|gT>r)dц06 g ⌢lkxW:E{ 6Q aʗm*-]h;IGzErX2zëT>r)L0eCdeiI4eXhәFDQ$Z ff7{ M\؀jK oѺlS9 hEi> ̰AAJZ(o\ LٸDRM% my!N,b%* }XIXR`1Zm* /z9k+ȡE*HJT:C<.*Al@5* ~0.۔MqAmn SrJTb>DʒJ9 _'DĤtV Կ!a"AiOo6 _)?Aa HIG_T9R*~ w(!S:cWiLP {O#iy\HLCNSlR6NJ=o6 ADLIX+16eAUMA^a*IW}m#CnI1'+́\ oLRb^ S ")[rÍ\ $%E0GU&&H vObE/ŤOo6 9LV)0 # Ej9[@,sǐ F.9tV)>?a*-Wg*DBҭ'"djLrGxG7~SiGǣ*ȥ "*A t{JbjL:Ų kt]Gd"{yJy2L`+Hj( *ȃ3 LdHeb,*XXM \pB*A@.7'T;0BuSc|yQi&t^8!`n,ǐiO-l1T~I)BpAʄt>NA1{( APDK mУ*<Ȧ FH$% bJy2LsBUh5 h1#  s @ *0 #mJㄢrSCnU b QdRe_^QV)_[#6FݦcÉ\ J1{>J2L rfI sBKb%HETe0IkLIEuhFF KՀEIQ 2rJ1u,T8a* =1$AiOo6 aNeWM8X1%KqUP {a* 8E6QAʿWհɤRC"ƌ. ʿS>iLP L*5aj-WEd 6:`@;C'HB#QdRȊ2<\T C#R*#E\gr1RSZCcR )nBVtCfM!|O#/M#`Y2LEC 70;V:E)'84 pC!++\4Ҥ3KqU&:?T;\mťWbb%Z[L~vaCkUb"GҢڪ hyb܁/]⢀\+QUKD¢LxYtFi =D)- HA~]1K(H66@rXgO|?* s`ND<ѣ#?xEd"դC.@^BZɫ! rZ>U4b&Q A;ZeC _(>`?8AC~4AʿWq jbEkLajV=[: s 8~evTi s}hH0Tq rg氂0(>"`T0R_9YnD]u2vz$p}O  R* aBŪ_vKU><ko޴IgFX0G )GLL0L[TR7Ô左#a}lz%T[WAXf b5lR\ fs|iA:PSkuU/rYצf+H06׹G`ߪ s /rL};(PU/U65k-*ȥ (GY<݁?JmT2L R5A&crJT؀jP)A U ǻ!Lr[ JP 0%HJ1O(w@ oS~wR JP 02/-4Lr)HhQ 2rAyxv 3eAr:g@O#}|iLP sƿb2-%}nx M KqUP /2- y}8|#awPA)\6M#{́\ d`tm$؛[,+`JG=1,:JI _nbTTPf́\ d`tr7 àZtt^UKFPR}Dd 22)H,nia”;b DARQf`S0TU9IAbKzeG &HSu! &[ale _HU9$/O}AVw֭|) \40x&=[,=̇Pdl$2T2^DBOW`L?ݳXm &JEP5V5xFp) b g `Tn" BGCQM3R* &_a*iDTz0pP&IMT T-6nECgd 6SP NQ!CARaАxZ% o];1\)XJ*ȋTDT3 Io{aQbsu )ls$1} *=Cm)&i 6q-z(aHY&*Al@5€nE$> yg@O#}|iLT<m 6 )'A1 g s`CEƩ~)ht?Atvr m3d 6jy)"mh0 8#H&z,R\U-dy2a n:۫Rӫk= UYz@Ma +O}gDCٻ0NmhrА\ UY0`y|-Y !&Y 4ΔVTmbˤ3RUT˳Jh`y|-{DN_4&| EsM9H3R*0ZUHw8N#ǻ2zb I6KqUT2/<[L}; C:ADv1,eMdSIňZ3R* ZU r qK&H8NX%3SRgKqUT*B/[[,M5tAMvbI:{R*ANr_L;ȓgV<ȚE7S*:KLzsaqG%D&on$9 C܇t6>ZA1#^\03MJ9 _ۡݢSn(:I)H1oP n M!CQHOAǤҤOհoh_vSD<h}~RJe@O#}|iLP/`:ۨg~(5 UAQTY:_To_(z;!UAQT KjѦza: 11m۹UA@.2C`LGAzὫ́\ 7ǤʢIUխVܔ=אϪ́\ 3 b!>LIBy)n j)7t*iUAl|]L}C0cx. v~Ho۴w =Fq}k^eO{a +[ R2ly98xg_vx18NOH+1 b:-d# e~1 ݍg DhEDA4RSd."*[U S rt .zu*>fg- [ѷXA;*D{{SM#`bJ)u4YpyA^6 kUp VI8;diм O*NUKAުRB5MV)\żł["H\VpB# %EzL|Erg _yu*Hr\A:Xئ?r A@pKz+6JIAIƍ̭ 2r)+HJ$a* +D@n&HIT\؀j:'q GvୠH 'IWhaذ3W_BFʿWհtR)N*,c˓3aJ䎒9mZ$k_`' SV{T*EenD@.L~6zA#cٿYV!Ci 6aʝni:&霶j\ڪ6SuJK0F&F1h96;uD<ЄF5f&C,*Al|- /#C[)P) OհvhTe p\/ *AAb3Lż cӳzFwwAV`p-;L=ͦs0@5R Si 2=m-pGBR* &_a*z9 Ғ LP){}UKA z-XAgȀ)_WcciP/́ G& +A#͇>\}!:8 L2T@*BĆ\ R6Ay ` "2`*d<T$aj;D '!C"xe<0!a*-2 SX#L't38ߠ*@*a* /ٙM KAdL'HRVRW*MɬR S-$Ado]=mѼE%6L!&COoJdd {O" Mzc w\;Cx, BL#B|S*a* "AȩEPArƣdL'< yMݢB)ʙFD# }H7wvjZW R BfBWwJF>[,{ -JJV(; s? EEHA1JE`:ADKJ0ec(T;l 6 N8T́}T)C"܁$H7yǤAi#Cs"+) s k^``;1ޑ+9#K=xS{XT́\yCcZlk,5Be6J9 DFPvCnn<FR*T/TZK~Y{ƣFR*T/6 2=&*H<*Al+ç` m^$ 6,Fy!D%Rz,X6L$9R 1T s`3!65xˤX>k4A"Bn! WK4ڧ7F$*K2L06 E#ȱ H&i\*AfBep^<ȚE7S*:GLOALiyF*Aď4wACG+H0&5 4E"'ȵ=SM#`0ui )HR)PtS1'a.*\Vj6 |Ҧ($~cO{qQcϧ62Q "APt4>LI)ȧ EaQz:D3"BR*O-l=>jm r]?Cѭh ƤҺ4. cjQ Er&S:\JEig%yZ%XDk<,AV 2I"BR*O-l[=INѸT̎%dT1^w<Ş! JK1.e62l O-l[=md|L& s (OVI|%mܘ9Z!jHܧc6P;eF޺$cjjۧ6FiclAٖ)<3 2'Ǜkhȵxp3zf{Vm`jڐ;~j=LJ]*ϦPE#?kGfOcf š[?C{_IENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/trkbankr.png0000775001024000102400000000264613042730153021056 0ustar PNG  IHDR4S}[PLTEUwDf3Uw"Df3U6Xz9[};^>`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3UutRNS" oFFs*IDATxr@E (8& M3Ġ!AKʀVYٸy'r_aD1KxEY;I8b,I8O,5Iq<3+L$ {jcn$%L:]C$Δ;*p$拐D;..aAd$iլwqO wZE&K/1!ףѦPM %J"eЏF\ݱFlF$ܢrOܠt;I`B*%t1KvTNB tJHjQm,<ϲ4`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ"3.8AV^rhxzǥ~7IDATxko۸@IT)v~w|(ŋ-KM$eE\l-qgG+c.y }ݭ(777ע\_]_]]ry!L,Dɴdg'x4ͯ+HDwn!yʻGΣFt.i4hD\O݉ "UCu4 ,Z^98h5BGPFS"4Jlbu{,ԙx`~4t> ole||,C`})3q??#Afp+G oXr[ԁ"{9B~3>,C8&B W?}ik*#bi;v@4:K]e|;.7:G], ?QXM D!)Ǔ\&6*.}c]ڐW±HED(.xodimt<)Gh>:o*!ޞyi${%::p/RBT@tF ĊMRm)U8ulUGȼP4?V8XJ} h5_1-١5X4[/WJ\+':*[ɷc+Af|hU+njFR $X<SAʏ>-kxlQ'Mȋ=&@`%jWO7r+6;soDXyuG}bU'6rPW?YۚOq\߿ͯ5ߞMYAUhbQrWhvjj[7_:̚{Q$H $o4?'kGX|U'&s{~b-3s~\q\[̟/SkZ}8Ƚ]y-UHgUdŁ~"9% df,G !Z%7>4yXԇ@v %[ QE ]y330"ˇ@M`vqoK< !}|ě@&6ggWW׷wwM ^y+g`8MDERnԭl}| K8A y &X7'|^A ^@ѫpQCbQNil ÉO+q~\Jrk $lQ3=Yz?)MiCK UTZ%':xv<* DCkS<"RMW yBQע ;16~Rnx y]ILz=2?)89B]jh?YOg( WH4CvI_@:#ht҇tON=lhp%!;uhp5鈟)$n]2NEKLsY/t%Jk]2nDɸN{Q2tx%Ig]2d%El(HJHXR ގ ^Q'ۏOvXm65=k}N}l۶ZiYAl(e'5Dy~ioҘ)'c?GG2+E<yGb"G䑷xܶMӴezpgC-4ֆ(oONm${%4cEkŲ +aj fiZ_OS}e0(A"ϻ4N6Dy#ru<ɑ*9ȧ>jKȅc[mr̓⦥f44+b' ERz1NzZ?9yK" $:;}3^jyUuά]:(n}mZ{Fn,1 y^;ȤIn&SNz ya'H&UKvsN>Yx6}Z\>G76۾b&؈Q 7%It5nONMD}]؟bq>o+  %F#I&9Omw=H~-L ʟ,,f3^ՠHyuY~uI% kq)'}&F\O r,t5tI>@ Rn Hw>x Js =5cn/McH0GЎxٹ! :`fH=~^!))':XDsC$b\?v8bwyN8߆x'" JwW%v3!+8B @d`Wi5?)@C!Z<X [}n/Mu C^swu,2S#oEc_;hvnD,3=d޹"W̶ >Y?2|"wP[9yaJWϳirqD,tz1H` aԻ;W^ɣ){IA|@$$U,u?9i5<.R*ifHivЃT5AVI?Ք| Vz5mSNNht.8LJb +=HqRGTu+]]s<X )OdNI7S}yfSZē:JL+ms<X .!WǙ9hvn&\O rWg@V'" d3)'}x dxٹ!@,Y@:O4;7D"ȹ'O&&)OQ8 r9N Tf^~!&&72B3`5pdD, HLo}W82Fby+'|tn?M*']?  AD~JS8|ݼ)# rCT>zyZBw2/IqE c$bW%֗~?("6 m!;0E;Nr.'#MiËz;b@@pD,oOxr@<@;bⅈ;xp/6 /''# ON:2FH+25^KPO\w:#i`"dFӆWE0GRNz=A]Srb&']Pz"gHIn 2E ,9IӐRFaR .𓓎[KJOTyҘb:I/X$EsNZONz%͡x@}M69Iȱы'zI_rN98X I iqGORWvN98Q_Eӆ7E:d j_#"12 Q@ t2FH+'uE4mx[5 S-#$@H Cϛ'"1 $#vh ^PEӆwEwIG cFi4mtKE #d$'m E0`INzQRn@䤧e\RiCK sқOiN5NuIϳAjxI7 {d0*(6|(zqt9g}t_9ǁ9NseUmtO98X %'}RWN:2>"Hv9'=5 <夛xq/d(*46|,z@vCNzjFKzKL d8@ LMnF rARo9d OEX@"@A<#]Gӆ\}N -7O4;7Dbs[:dhxٹ!<= #vh ^M : q / ,46= Z< ty3DsC$6Xr,/ɿ-ⅈ;xMi_EZ< /٦Ѵ!5%Nlb $#ăx!6y)z9B!b;HX2xL<ymM$Z< 9BlZ:dh /&`r)N7‰.|]2fdr~ť,Wd\q#Jƭ.wd܋Kƣ(O<=1%./dʢc趐mPlfV˷Kny2rW7!Eym $xފx6lCK~y>=`za:zǏ%vBm}<߃9?vBm)}~ +0ucz|ؚ9՟BXNNkL*nM)HPS<-ˀO$=|8|Wݜ> >e|Tۆu~|5xoO|dǏ^X,zfo|3;Ӳ Ov毂WqYD ]\;_e ϸ$ qav6P/sSL' 5Te1kz3bƵB*Vk^PlB5E=4ff=X缵hQ5m ^cSVv~~N)Hue)?PMb/Y_l:MvN^ ^4rH^4R7\?9#Z/MȪQܗ'i C@=5F>ȟcV,/3kw'}]t`;F>qbZLzwL _ǶS eFZ7z7z@^HP/W8I?X#rE\_AHP/Wٿ:߶!Rob]}_#J^ ޫ/ mQP/@:ȲDR|jې[S(2^ A^<J^ ^qg sۆS/W/^u@z7

`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3Uʑɏǎƌŋĉˆ~|{yxvutrqonlki~h}f{ezIDATxj`=1_ $б K ""(""o?iNĵ]Xk>65yE8n%ieU7m18uμ'&YEQUUMӶ]p9H9š:gyI3ΙDǯLLyO*fN~U37xYӲΙQ 4P{"m9^d\_9xПdT%:g"-vͅQ9LOs=_>~pè:gb3/ZmҩsY_d5FQ Ιغ/X4?ZsΙF:Oz/i9Eu&_36?;uμ7o{ Cw]6M]WUYEY9ӛmVya?9{L3q. yz . z . z . z . zz . z . z=]؅م؅A]؅A]؅A]؅A]؅م؅A]؅A]؅مممqم9{0 o 0 0 0 0 o 0 0 ?f -eStIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/tab_mid.png0000775001024000102400000000171713042730153020635 0ustar PNG  IHDR PLTE"""33U6X9[;]>`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣN*0tRNS@f oFFsvDhIDATxA 0 HA MH-Ap.a!,6y AD5=p;C#,l)MWJ`?3EUմ0EiJ$&AIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/dir_popup.png0000775001024000102400000000245013042730153021232 0ustar PNG  IHDR$-PLTE"33U6X9[;]>`Ac"Df%Gi(Jl+Lo-Oq0Rt3Uw6Xz9[};]>`D\tAcDfJiOmUoUpZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ.8AV^rhxzǥMVi?UkQjr囷ՅnhxBKZ -:=Mb_nGPa^l:BO>GUCM\wwffUUDD33""̻wwffUUDD33""wfUD3"̻wwwfffUUUDDD333"""wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"Uwf3DfwD"3U@C4tRNS@f oFFsGqo?IDATxmA0y$E M/N7XSt1CuMԎ#9?4gvaB!L(K&f?Cf9eˈRʥ1 &R@O&0ű<|\XPLTE"""33U6X9[;]>`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣN*0tRNS@f oFFsIDATx0 ^&d2E!J2nx;uz0@[I%\ι@*ak0y@ g+˞i qD [{%JP]ؠPoЃ0J03JC6Qeyꦽu~'@yu0}yR %4"~jIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/vumeter.png0000775001024000102400000000214613042730153020722 0ustar PNG  IHDR @LPPLTEUwDf3Uw"Df3UUfwDDU"3D"3D"3"3"  %+'.6.8A6AL>JWFSaM]lUfw]odylt|ڈwfUoD\t3I_"6K$6"wfUD3"䙹ړӎ̈ŀwof^xUoMf~D\tK џ,7}d,8F=4/WBȂ ΑUhPLKh,FCEoЦ 4VMTh[h*mo# Q U|@<2]W UzF=d Jh9@>PDU}c-rjM_(7J3d޼rIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/itab_right.png0000775001024000102400000000177713042730153021360 0ustar PNG  IHDR~>PLTEUwDf3Uw"Df3U6Xz9[};^>`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3Uʑɏǎƌŋĉˆ~|{yxvutrqonlki~h}f{eztRNS@f oFFs*IDATx? 0[," "Kxe)T!ZS]B.f-ue5'\$gRZA،`n2/ ìE~Cf#x3!p.۹i| ?$D|IENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/aboutfont.png0000775001024000102400000000600613042730153021233 0ustar PNG  IHDR"&_PLTE"3.8AV^rhxzǥ44tRNS@f oFFs*IDATx]ے;dM>'k=مD4HFTeYՐe)(]x(_Gvg)gf z$w.{8|VM3)+z#W#f{F߼El_[.|׵Ew_Pƿ/DY2u .gLSM\Qu¦z%#ƟP7~hlvxr -2yp} 1$ dϐ9t7=d}c?/Wj⊪}X-60ztF]o7Due  [->CY nmHDWek Q-#}UPHjY5tX}^/kɟZm.͐/r17m Rյw|oV<&Z"ܙiK>U;ϐ56~4/mEtjtzZg͹r!q lፑRn@!}Pz6G=s?Cܦ>|X2;|>[b62o XVpp%+ZHk-rJ<ϐ56~4Vn8M)FWZoUʼ)`YmZ;Ϝpڕy^т5ㇿ0=S?C\6~4k;tzZƗm >͐)G]'-X#/;~+P&7}=Kk)_=cE_)s{;?6?u.Q;S9]2O^+ZFz_4wWPž',-ғ7h">ߵ+kEs*?U|xv_,b2Z5Q|?Q=:Ji1pO: lqE6@HY/ z-?[6Q|?Q=EN੒- -*>ڎk!qE[ !U#ss™j‡S? cBnŏ^fǏHEn#sMla"I|Sŧ[~-#߲4NhkΈ2Z5Q|?Q=:J),Q?+hU P%Vb"p#gF">7?--UvRw oi —=Q nB_We>eu/V_z"QZ1sEƅAG(Tx-'-ҋ-}Hd"\eg!~@6V_ | ,~}-NG7R)a`ߋD`P}]5R.e>"m&F$ܳEf4jTEbH?H.}L FF__kE~2_BQ% U#/vdDLIgi`l^} P!MnB؇⏾2l *Ad|uםB~_#Y?Agq*!e>"; K'㫻c%?[Wi4A@?nPh9~3QwG"ƇW`H e?[?c'[-RɟE|z`g*ŷQ%Dg8=}="[ 4>|ŚOH8xEL#L%HE?xz*+ߺE.OmWf!a>`qiU =.?La:+@X=E. xL},c-_Eb\%lo [B&:J=D?7_E._Ow\XZ$R2 ?`J:J\RRRB6r7+&?*qsO{ WbU$0JIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/prefs_os4.png0000775001024000102400000000727513042730153021147 0ustar PNG  IHDR,iPLTE"""33U6X9[;]>`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ"3.8AV^rhxzǥ~7 xIDATxݮ6 XmZjn^wռ+m  g}p! M(ۗ/? A//ǯ+>?X~?"{]^*m(D={*ҚB~΁CWxǞW h&(Q愔 DuD!swi{ȲfgZac |~۟>C`ןNz@~4OһsL!2T2sbqO`B~Z@Za3gX< o2y$iX -V4̧,I~@ٟ3;澛(1e6ވ\(f Z /"+L"8P)dG( B0(*B W l(cdJ_4WaPeA$uML=klQ|+};.(i tQхL4֘rq?'ۦ.5eBbĤWDtV\bS!nƮ{Li,ǒDH8kN_l׬r96Ng럾X2lBk*l~̒ -wZX&heKΒ럌FUyBB.~;cVOoe.14B,7oBV:q,?T.ǰ M /^1TȂ y5!R$< 0( B8^S ,0( xB WaXL=% , yw1W(4<{9VD٫ٚr:2{Ms~|W͕o;4lq0j4G.)BJuQ=WyWB&[\!Ͷbت1m謅kf' ֦ )ms4  +GDe6^do:\TH]JWuYuqbXv )msLK^YWa-24ͺcmDB A!`NșBvB0( opBѺHӺ >3([*DgI'ӞIU rAYp|_B:1bX.D b;h{Ț8X9Nt+vY%*z%]wvU]q_?~+$BD\UwS ^c䡭rZ5d8w :ŚgUaޡd%bKV~++$Ih=1CJ lQwUS!;+$ª/07M{=d>~g+DtY%!+[t|-KZUV5S!oi K.bVYW#+{` M“Ж| W 5(7{*)$EwUR8B~gIR_}?߀y2<[\BA!`PѺMWe="9akbi.eM~w MWoY:ޙz(U1uD!rY:b%+6(&:sYQvÓnQXLp Von.++YV8ՎoTȦlN嬲䞈o[ͧNʹ$Da-sdIuNls uP޷pR![WH9% @BkR!= ֌oTȶ,HB+iP7AjP:)dc[8B^%uߢ0(sYځB0( Bh]Vejf>F#a\Lb: 7J1 mocԋQU=!ꐽ̗&]zZ1\SUa,wk= 9^چk=8R+e".cأ􂵞R'YGǨS!rY\jV>` "A'S/Xz,s = U])FUTf2^:ޫq8e5ѺL \!?B~0(-QIN!_YO(uU5'Dnx,]UkWBQ!UiOrZ: '`Ŷڑ.k)d=~b ze=3·n yh;Evv8u*4Ir/BnnR9wu) kV<VwZX!B\y(R>^_^k槻Zҡ}/ZB q{k#ӱX* qb~\%Rݺ!/VHZ!}m%swC6:fNC3k *$Tnrmퟋ2+?ebPAFkqG㪓_xa-wҞC!`PA!`T/zB7u!B0( B0( B0( B0( B0( B0( B0( B0( B0( B0( Bh7A!`PA!`PA!`PA!`PA!`PA!`PA!`PA!`PA!`PA!`PA!`PA!`PA!`Pr$( 0x`q! Cwcq,xBB8_(k'p,( cA!@^r~.{Ș76Bf>_(dg.ۼ ن(d8dNb9|~~~5BF#cVQd!4~)IENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/butbankr.png0000775001024000102400000000310213042730153021034 0ustar PNG  IHDR@`[[PLTEUwDf3Uw"Df3U6Xz9[};^>`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3UuIDATx[S@MN:M|V Hu Hk\l+f48"_QY^f ($I,(zoo?f'nC?O^j?g>D(K\~8F?ODA}}'m6:l,g>mD"ܸY' cj;;/:n܉~"y"u:i\ϓ'b7OjX?Oq} ('tl']D`48yLfaz0is Ӱx'̴s[LynÁlqEb 3?5j,< Ioū[M3P/OyZ_ߙ$xժ,"ϳ,M$( 󍽥'WQn:pԝuFVWPm/w_Η5%o''_@ݼ~n`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ/Mb+tRNS@f oFFsw IDATxu]k0@ń*R ͎fR #!u#o57: C3XN+mC%rۖ'`s`$񑋉 h !p1ZZ$Q/)(mg0nҕ;9 Aʠ*4  TI,CݴmہAQ/r=Op2z۲Cw@/#>AםZj+8X>e ؾ{`O+tp.)0\G)-0hX6X`8IENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/wavemeter.png0000775001024000102400000000301613042730153021227 0ustar PNG  IHDR`o/PLTE@bP`p6Xz 4BdIe"  GjDfώUvAQa(JlUo)2:`sPh^rUw,I4VA\.Pr$Fh;]"6K2T6Ys2Pt"Df2Tvf9Z}:\~Lnàe|>`-Lp+Lo,NpJh9DOnezi8Ty(Fj<^ Bd#*2Z|@bm5I\]u:\0EvNpNlaw{ `vIbD`?[?^~ x D\t5Vx嘶8ZRt>KX0:CZr3>HTp:FQ5@K /?$#3B$5F0Rt~ɔ>`w &?Rj"(f{)itn\#8+@HXhKZiXi{ CP]Sev{dy*G '-lr"e &"3\nt-6?b,:VxUnʆnjЂc~}iq5DUl)B "zv"pp~ =IU%+ER`Vgx0K,FXzFTb")0i~ /Zl~%6auÖљ<^2D$0=%-4 {{FtRNSiD oFFs*o}bIDATx1k0Rn%`^Z r4PY%K?32޽ޗNOdq8PC/&mRHuwG8nxPUEpY\,fRoERTW./s]WmYEҡ~zQL{0zz Nob벬mb8o\#=G y pZmUi=WqG[pF_t@^|GcNf{NhVkNmVq]z^~eov}Ɩ֥ܹ͝صfzIDATx뎤8h$F;+nL&98twCQ|$t%XPur p r<򪶁fjeYON eb/NY.XBИynϯ߿ W-~T*&G-u\{G)_s)yBI3=쎐GlЖZ-!dyqjBKsͷ F6Gbpב$b!Vae BK@Dv -oD|oi @4h|Cԫ)@#&&&&&&Ұ<;}ݸ-ߣ'BKYo͊;lS7RրiqF vvQցI^|R8d$۱4޼eqX \̊زdlL}WsRrcQ;l%,A*yb ?az>3BPtEyZ,D;hò#z4X;V+BrVZY!Η^3:edT˥Ӌ΢*FHe/7v,_jADVLȸ9&ܷӗ%+رB)e+qPe웢㋬T^!~7@^2n@@@@@@@@@@@4q: 2az Rny KV 6昺~\ifT(v3_j"Ubg Hk/&a<չxt-GSοN@MD8 .Po)\&x DJV eRor/Bsq>HV*!)\&x tɌUشaMKURorN=Uy_T"5I!iJ w|@t*u޲FbVrH|Ea DǼX$k~ޭ{vbT2>U,*imtoL]&B)ZI䕣@MNBp"ɇ3aI_*imtkN2Bǖ#&ĺJ@MF2@lg3B3aI1u6{c+K=*җ0 H{ir+5Əª$kĮ=IW L L L L L L L L L L Li jyLj ^cRk> ~<#,"Cr弡xK⺭X^`Ѻ'|Ye*]Xލe\?3d{9-Z@Z" ڒ} ֛r@MFޞEWk$vZ=2-x } _( Uh7ktxKy+?KSӖzE Hk_C/M֖RB:p72d:+m [ L L L L L L L L]& b `g|$KӚ4~Q@tcpZ?mv{ڂЁ]or Ŷ:Dx'9O$oV+񍆅#4v4$=ll@i]+')SVHm#TB;YX1{Sw!2by'FFNoI!Ñ;OZuƬ,Bv'ZQ:O=k%ɰy=@A3fgV0te-]4Bd8=1 79$ހl[xGMR)݊z4Y2Vh>34!tq/>4ן#>GAA_?Cv1x79in o7Z=_@h.JSA;V;I~YZ~YmC@c*剱8xM[Y: =l/f{Y|`7!qW{+R]]b}_eQmqˋcs* i_{NOdr0t%DτE:f| kldN qQI91Og+_U9ze5#I:UؤVRw1QɺR[֗d:0ٰDnҤ&R$J_iׄ@)vU C5Z Z\XqJjҵ%eʕ/_k dkL]h?zXj)%\<;t(lݔ~ ~:5kk7)|?%[Q^ZuCaȅU[df݊.-\qeql^kCxӕHAv'-Xұ"=^H5Ȓ:%FG}FB &/`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ"3.8AV^rhxzǥ~7tRNS@f oFFsY ZIDATx\msںqi'6A)pq79m93s/zdc$^VDz-[޽{CQ,ĭF"ί̯Wl rzyyb29| l|vf'_S@X*"`D!ZR ,.K* 1 c68WG@ez1W-|)?-v~8>^s LbqEZ?W( d b܊* H\teq.lg0zhh6c6}-!f{}M"@/b.)H_$>>üK!E{,{< Dh}IɾCQphg1j%Ȓ h'yX^ ٢1ukԟ "MK .*;]BScI@n(Vfn|()x<4l4[(< /Lrp<ԃaΨuUg=Y\Q!^;OQ$iH%k(]`ky+A<כAa Yv:|3v< b)ծv rcxcCAkXםκNl2y>=iK44M6lN=oϥ`(HB.ċ)MfXiˣ+HX: zuKbeHy(QKE :#FLSWP}ʳ/ ]Y,E"htVbbAeE<}O{NRKAfzA7_JP5&S+1]mWJ׿>9ɫl Ϧ + }PT}4yp2ϣJǝʰ*N Թom|bDEUK9y}jn:5ItKNMkW?I X ~>@VQX }nFB4&W$M})' O4pƈ)Q vu%c]gDx9DC")iʯWGSYrpTF R)6hLٹD*"Sa"⟝Vhe )xE p^-.+!)#y"_ j6}ڦ8os7h?\S89NOb:]]]sEosjTs$?>!/||xhi@ ƺGҪ>|^(9ݢ ^%w[IIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/chanmute.png0000775001024000102400000000227113042730153021036 0ustar PNG  IHDR&iZPLTE"""33U6X9[;]>`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣ6X,1VC+T[&Rr PNLJ HFD-tRNS@f oFFsg !RIDATxUaO0@HAHƭIa!l,(ZRw_x^ o_֎l`m>KKozV(upl['|l+uQ;ݡwΉoNthFUm8cQf^PvNQU-C jX2n6+VuO\Zyu y4,E$2\fk4 P~F *igry028k(RhDR6 L" -WZ:{ ؛P!V} W-NQFc]rʧCS"3MY_{<۶rIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/trkbankp.png0000775001024000102400000000756513042730153021061 0ustar PNG  IHDR5e pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3- cHRMz%u0`:o_FPLTEUwDf3Uw"Df3U6Xz9[};^>`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3Uʑɏǎƌŋĉˆ~|{yxvutrqonlki~h}f{ezIDATx10g `FgG"/ APDp`l布?$ժi^kFZCai_2@ץ?,==V@9ӷJaHC#}cLc$nJ To}N$I%wUY`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3Uʑɏǎƌŋĉˆ~|{yxvutrqonlki~h}f{eztRNS@f oFFs*fIDATxc$`ȰHv , ^`&):`&ذc l1@! Xr[&`(SrjaIENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/Settings_os30000775001024000102400000000163313042730153021034 0ustar # # format is "RRGGBB" hex values # any line starting with # is a comment # # 16 pixel tall proportional font font1 = 'BitStream Vera Sans.font' # 16x8 pixel fixed width font (MUST be 16x8) font2 = 'BitStream Vera Sans Mono.font' # 14x7 pixel fixed width font (MUST be 14x7) font3 = 'BitStream Vera Sans Mono.font' # first is the background to the track, pos & # performance editors, and the number & text gadgets 000000 # next is the "dark" colour underneath the position bars # in the track, pos and performance list editors 113355 # position bar colour 446688 # "light" colour above position bars 8899bb # text colour aaccee # disabled text colour 113355 # wavemeter colour aaccee # normal cursor in track, pos & perf editors aaccee # edit mode cursor in track, pos & perf editors ffffff # text in buttons and tabs ffffff # shadow in buttons and tabs 113355 # track F-key position highlight colour d6e6ff hivelytracker-0+git20180223/Skins/SIDMonster-Light/blank.png0000775001024000102400000000223713042730153020323 0ustar PNG  IHDRH'=PLTE"""33U6X9[;]>`Ac"3D"6K"Df%Gi(Jl+Lo-Oq3I_0Rt3Uw6Xz9[};];^>`D\tAcDfJiOmUoUpUwZs`wezfwƈŒʔ͗љ՛ؙڞܠߣN*0tRNS@f oFFs4r8IDATx]k0`o:Fi ~ cmZLz}8ƀL۶R] 6Fnr0|[zHDljH8^DXÐolhL{Atx;Qq~ծ B% {}1guxqжA87ojN &o *(,W!(sh" 5r@2)&*}/2앁 HQx)]R\=JWGelc|㴪;͖ç'3!LdeXJ,2yhԯL 8IENDB`hivelytracker-0+git20180223/Skins/SIDMonster-Light/itab_left.png0000775001024000102400000000213113042730153021156 0ustar PNG  IHDR͕-DPLTEUwDf3Uw"Df3U6Xz9[};^>`Ac9[>`"Df(Jl-Oq3UwDfJiOmUpZs`wez㠽ߞܛؙ՗є͒ʏƍŠDfAc>`;]9[}6Xz3Uw0Rt-Oq+Lo(Jl%Gi"DfAc>`;]9[6X3Uʑɏǎƌŋĉˆ~|{yxvutrqonlki~h}f{eztRNS@f oFFs*IDATx0EcXlf[BLaB$0WNER1Vq͇rqkB1KZf'L^^tJf1Z2SST%\/"\LMW B6MH.HFDH0)C6<"L`ǢCC=]EB#>Κuﲩt[:&ьh0k9]KlO?FI~@ l-@OMlgg';Dc,bIENDB`