ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

commit 4dfccacdd7d6538d2073a020a1b7f7d1fb4bf774
parent df5ceec1c72376898da165c8564299d680bb0cb3
Author: Olav Sørensen <olav.sorensen@live.no>
Date:   Tue, 17 Dec 2019 20:41:42 +0100

Pushed v1.04 code

- Fixed rare crash (or strange behaviors) when changing pattern and/or pattern
  length while the song is playing.
- Properly restore channel mute flags when loading a new song (fixes mute bugs)
- Fixed a few bugs with different pattern buttons (Ins./Del., Ln. up/down etc)
- Config: "Hardware mouse" was changed to "Software mouse" (and "Software mouse"
  is now disabled in the default config).
- Added a routine to create scaled FT2 mouse cursors for software mouse mode,
  though the "busy mouse" will stand still and not animate.
  Hopefully the new default "hardware mouse" mode will satisfy some people!
- MacOS: Pass NDEBUG to clang preprocessor defines, to prevent debug code
  from being compiled in release mode (performance increase).
- MacOS/Linux: make scripts had Windows linefeeds and would thus break!

- Add .gitignore to empty directories so that they get included (Silly git...)

Diffstat:
M.gitignore | 4++++
Mmake-linux.sh | 2+-
Mmake-macos.sh | 2+-
Arelease/macos/ft2-clone-macos.app/Contents/MacOS/.gitignore | 5+++++
Arelease/other/.gitignore | 5+++++
Msrc/ft2_checkboxes.c | 2+-
Msrc/ft2_checkboxes.h | 2+-
Msrc/ft2_config.c | 30++++++++++++++++++++++--------
Msrc/ft2_config.h | 2+-
Msrc/ft2_edit.c | 49++++++++++++++++++++++---------------------------
Msrc/ft2_header.h | 4++--
Msrc/ft2_inst_ed.c | 3++-
Msrc/ft2_keyboard.c | 2+-
Msrc/ft2_main.c | 18++++++++----------
Msrc/ft2_module_loader.c | 14+++++++-------
Msrc/ft2_mouse.c | 203++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/ft2_mouse.h | 4++--
Msrc/ft2_palette.c | 7+++++++
Msrc/ft2_pattern_draw.c | 25+++++++++++++++----------
Msrc/ft2_pattern_ed.c | 147++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/ft2_replayer.c | 95+++++++++++++++++++++++--------------------------------------------------------
Msrc/ft2_replayer.h | 4++--
Msrc/ft2_sample_ed.c | 2+-
Msrc/ft2_sample_ed_features.c | 24++++++++++++------------
Msrc/ft2_trim.c | 6+++---
Msrc/ft2_video.c | 18+++++++++++++++---
Msrc/ft2_video.h | 4++--
Msrc/ft2_wav_renderer.c | 2+-
Msrc/helpdata/FT2.HLP | 13++++++-------
Msrc/helpdata/ft2_help_data.h | 213+++++++++++++++++++++++++++++++++++++++----------------------------------------
30 files changed, 512 insertions(+), 399 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -11,3 +11,7 @@ *.cache vs2019_project/.vs/ft2-clone/v16/.suo *.ipch +.DS_Store +vs2019_project/ft2-clone/Release/ft2-clone.vcxproj.FileListAbsolute.txt +vs2019_project/ft2-clone/x64/Debug/ft2-clone.vcxproj.FileListAbsolute.txt +*.cod diff --git a/make-linux.sh b/make-linux.sh @@ -7,7 +7,7 @@ echo Compiling, please wait patiently... # This will activate 2-tap linear interpolation mixing (blurrier sound) instead # of 3-tap quadratic interpolation mixing (sharper sound) -gcc -D__LINUX_ALSA__ src/rtmidi/*.cpp src/gfxdata/*.c src/*.c -lSDL2 -lpthread -lasound -lstdc++ -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone +gcc -DNDEBUG -D__LINUX_ALSA__ src/rtmidi/*.cpp src/gfxdata/*.c src/*.c -lSDL2 -lpthread -lasound -lstdc++ -lm -Wshadow -Winit-self -Wall -Wno-missing-field-initializers -Wno-unused-result -Wno-strict-aliasing -Wextra -Wunused -Wunreachable-code -Wswitch-default -march=native -mtune=native -O3 -o release/other/ft2-clone rm src/rtmidi/*.o src/gfxdata/*.o src/*.o &> /dev/null diff --git a/make-macos.sh b/make-macos.sh @@ -8,7 +8,7 @@ else rm release/macos/ft2-clone-macos.app/Contents/MacOS/ft2-clone-macos &> /dev/null - clang -mmacosx-version-min=10.7 -arch x86_64 -mmmx -mfpmath=sse -msse2 -I/Library/Frameworks/SDL2.framework/Headers -F/Library/Frameworks -D__MACOSX_CORE__ -stdlib=libc++ src/rtmidi/*.cpp src/gfxdata/*.c src/*.c -O3 /usr/lib/libiconv.dylib -lm -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default -framework SDL2 -framework CoreMidi -framework CoreAudio -framework Cocoa -lpthread -lm -lstdc++ -o release/macos/ft2-clone-macos.app/Contents/MacOS/ft2-clone-macos + clang -mmacosx-version-min=10.7 -arch x86_64 -mmmx -mfpmath=sse -msse2 -I/Library/Frameworks/SDL2.framework/Headers -F/Library/Frameworks -g0 -DNDEBUG -D__MACOSX_CORE__ -stdlib=libc++ src/rtmidi/*.cpp src/gfxdata/*.c src/*.c -O3 /usr/lib/libiconv.dylib -lm -Winit-self -Wno-deprecated -Wextra -Wunused -mno-ms-bitfields -Wno-missing-field-initializers -Wswitch-default -framework SDL2 -framework CoreMidi -framework CoreAudio -framework Cocoa -lpthread -lm -lstdc++ -o release/macos/ft2-clone-macos.app/Contents/MacOS/ft2-clone-macos strip release/macos/ft2-clone-macos.app/Contents/MacOS/ft2-clone-macos install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2 release/macos/ft2-clone-macos.app/Contents/MacOS/ft2-clone-macos diff --git a/release/macos/ft2-clone-macos.app/Contents/MacOS/.gitignore b/release/macos/ft2-clone-macos.app/Contents/MacOS/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore +\ No newline at end of file diff --git a/release/other/.gitignore b/release/other/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore +\ No newline at end of file diff --git a/src/ft2_checkboxes.c b/src/ft2_checkboxes.c @@ -90,7 +90,7 @@ checkBox_t checkBoxes[NUM_CHECKBOXES] = { 113, 79, 128, 12, cbConfigLineColors }, { 113, 92, 126, 12, cbConfigChanNums }, { 255, 14, 136, 12, cbConfigShowVolCol }, - { 255, 158, 113, 12, cbHardwareMouse }, + { 255, 158, 111, 12, cbSoftwareMouse }, // --------------------------------- { 212, 2, 150, 12, cbSampCutToBuff }, { 212, 15, 153, 12, cbPattCutToBuff }, diff --git a/src/ft2_checkboxes.h b/src/ft2_checkboxes.h @@ -67,7 +67,7 @@ enum // CHECKBOXES CB_CONF_LINECOLORS, CB_CONF_CHANNUMS, CB_CONF_SHOW_VOLCOL, - CB_CONF_HARDWARE_MOUSE, + CB_CONF_SOFTWARE_MOUSE, // CONFIG MISCELLANEOUS CB_CONF_SAMP_CUT_TO_BUF, diff --git a/src/ft2_config.c b/src/ft2_config.c @@ -885,7 +885,7 @@ static void setConfigLayoutCheckButtonStates(void) checkBoxes[CB_CONF_LINECOLORS].checked = config.ptnLineLight; checkBoxes[CB_CONF_CHANNUMS].checked = config.ptnChnNumbers; checkBoxes[CB_CONF_SHOW_VOLCOL].checked = config.ptnS3M; - checkBoxes[CB_CONF_HARDWARE_MOUSE].checked = (config.specialFlags2 & HARDWARE_MOUSE) ? true : false; + checkBoxes[CB_CONF_SOFTWARE_MOUSE].checked = (config.specialFlags2 & HARDWARE_MOUSE) ? false : true; showCheckBox(CB_CONF_PATTSTRETCH); showCheckBox(CB_CONF_HEXCOUNT); @@ -895,7 +895,7 @@ static void setConfigLayoutCheckButtonStates(void) showCheckBox(CB_CONF_LINECOLORS); showCheckBox(CB_CONF_CHANNUMS); showCheckBox(CB_CONF_SHOW_VOLCOL); - showCheckBox(CB_CONF_HARDWARE_MOUSE); + showCheckBox(CB_CONF_SOFTWARE_MOUSE); } static void setConfigLayoutRadioButtonStates(void) @@ -1237,7 +1237,7 @@ void showConfigScreen(void) textOutShadow(319, 146, PAL_FORGRND, PAL_DSKTOP2, "Std."); textOutShadow(360, 146, PAL_FORGRND, PAL_DSKTOP2, "Lined"); - textOutShadow(272, 160, PAL_FORGRND, PAL_DSKTOP2, "Hardware mouse"); + textOutShadow(272, 160, PAL_FORGRND, PAL_DSKTOP2, "Software mouse"); textOutShadow(414, 3, PAL_FORGRND, PAL_DSKTOP2, "Pattern text"); textOutShadow(414, 17, PAL_FORGRND, PAL_DSKTOP2, "Block mark"); @@ -1438,7 +1438,7 @@ void hideConfigScreen(void) hideCheckBox(CB_CONF_LINECOLORS); hideCheckBox(CB_CONF_CHANNUMS); hideCheckBox(CB_CONF_SHOW_VOLCOL); - hideCheckBox(CB_CONF_HARDWARE_MOUSE); + hideCheckBox(CB_CONF_SOFTWARE_MOUSE); hidePushButton(PB_CONFIG_PAL_R_DOWN); hidePushButton(PB_CONFIG_PAL_R_UP); hidePushButton(PB_CONFIG_PAL_G_DOWN); @@ -1743,20 +1743,31 @@ void cbConfigShowVolCol(void) redrawPatternEditor(); } -void cbHardwareMouse(void) +void cbSoftwareMouse(void) { config.specialFlags2 ^= HARDWARE_MOUSE; + if (!createMouseCursors()) + okBox(0, "System message", "Error: Couldn't create/show mouse cursor!"); if (config.specialFlags2 & HARDWARE_MOUSE) - SDL_ShowCursor(true); + { + checkBoxes[CB_CONF_SOFTWARE_MOUSE].checked = false; + drawCheckBox(CB_CONF_SOFTWARE_MOUSE); + SDL_ShowCursor(SDL_TRUE); + } else - SDL_ShowCursor(false); + { + checkBoxes[CB_CONF_SOFTWARE_MOUSE].checked = true; + drawCheckBox(CB_CONF_SOFTWARE_MOUSE); + SDL_ShowCursor(SDL_FALSE); + } } void rbConfigMouseNice(void) { config.mouseType = MOUSE_IDLE_SHAPE_NICE; checkRadioButton(RB_CONFIG_MOUSE_NICE); + createMouseCursors(); setMouseShape(config.mouseType); } @@ -1764,6 +1775,7 @@ void rbConfigMouseUgly(void) { config.mouseType = MOUSE_IDLE_SHAPE_UGLY; checkRadioButton(RB_CONFIG_MOUSE_UGLY); + createMouseCursors(); setMouseShape(config.mouseType); } @@ -1771,6 +1783,7 @@ void rbConfigMouseAwful(void) { config.mouseType = MOUSE_IDLE_SHAPE_AWFUL; checkRadioButton(RB_CONFIG_MOUSE_AWFUL); + createMouseCursors(); setMouseShape(config.mouseType); } @@ -1778,6 +1791,7 @@ void rbConfigMouseUsable(void) { config.mouseType = MOUSE_IDLE_SHAPE_USABLE; checkRadioButton(RB_CONFIG_MOUSE_USABLE); + createMouseCursors(); setMouseShape(config.mouseType); } @@ -2175,7 +2189,7 @@ const uint8_t defConfigData[CONFIG_FILE_SIZE] = { 0x46,0x61,0x73,0x74,0x54,0x72,0x61,0x63,0x6B,0x65,0x72,0x20,0x32,0x2E,0x30,0x20,0x63,0x6F,0x6E,0x66, 0x69,0x67,0x75,0x72,0x61,0x74,0x69,0x6F,0x6E,0x20,0x66,0x69,0x6C,0x65,0x1A,0x01,0x01,0x80,0xBB,0x00, - 0x00,0xFF,0x00,0x00,0x01,0xDC,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x00,0xFF,0x00,0x20,0x02,0x01,0x00, + 0x00,0xFF,0x00,0x00,0x01,0xDC,0x00,0x00,0x00,0x01,0x01,0x00,0x02,0x00,0xFF,0x00,0x20,0x02,0x01,0x00, 0x05,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x01,0x01,0x01,0x01,0x04,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x24,0x2F,0x3F,0x09,0x09,0x10,0x3F,0x3F,0x3F,0x13,0x18,0x26,0x3F,0x3F,0x3F,0x27,0x27, 0x27,0x00,0x00,0x00,0x08,0x0A,0x0F,0x20,0x29,0x3F,0x0F,0x0F,0x0F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, diff --git a/src/ft2_config.h b/src/ft2_config.h @@ -246,7 +246,7 @@ void cbConfigFramework(void); void cbConfigLineColors(void); void cbConfigChanNums(void); void cbConfigShowVolCol(void); -void cbHardwareMouse(void); +void cbSoftwareMouse(void); void cbSampCutToBuff(void); void cbPattCutToBuff(void); void cbKillNotesAtStop(void); diff --git a/src/ft2_edit.c b/src/ft2_edit.c @@ -252,7 +252,7 @@ static bool testEditKeys(SDL_Scancode scancode, SDL_Keycode keycode) pattLen = pattLens[editor.editPattern]; if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen); + setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); if (i == 0) // if we inserted a zero, check if pattern is empty, for killing killPatternIfUnused(editor.editPattern); @@ -459,7 +459,7 @@ void recordNote(uint8_t note, int8_t vol) { // increase row (only in edit mode) if (pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen); + setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); } else { @@ -524,7 +524,7 @@ void recordNote(uint8_t note, int8_t vol) { // increase row (only in edit mode) if (pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen); + setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); } else { @@ -598,7 +598,7 @@ bool handleEditKeys(SDL_Keycode keycode, SDL_Scancode scancode) // increase row (only in edit mode) pattLen = pattLens[editor.editPattern]; if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen); + setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); editor.ui.updatePatternEditor = true; setSongModifiedFlag(); @@ -673,7 +673,7 @@ void writeFromMacroSlot(uint8_t slot) } if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen); + setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); killPatternIfUnused(editor.editPattern); @@ -723,35 +723,30 @@ void insertPatternLine(void) nr = editor.editPattern; - if (setPatternLen(nr, pattLens[nr] + config.recTrueInsert)) // config.recTrueInsert is 0 or 1 + setPatternLen(nr, pattLens[nr] + config.recTrueInsert); // config.recTrueInsert is 0 or 1 + + pattPtr = patt[nr]; + if (pattPtr != NULL) { - pattPtr = patt[nr]; - if (pattPtr != NULL) - { - pattPos = editor.pattPos; - pattLen = pattLens[nr]; + pattPos = editor.pattPos; + pattLen = pattLens[nr]; - if (pattLen > 1) + if (pattLen > 1) + { + for (int32_t i = pattLen-2; i >= pattPos; i--) { - for (int32_t i = pattLen-2; i >= pattPos; i--) - { - for (int32_t j = 0; j < MAX_VOICES; j++) - pattPtr[((i + 1) * MAX_VOICES) + j] = pattPtr[(i * MAX_VOICES) + j]; - } + for (int32_t j = 0; j < MAX_VOICES; j++) + pattPtr[((i + 1) * MAX_VOICES) + j] = pattPtr[(i * MAX_VOICES) + j]; } - - memset(&pattPtr[pattPos * MAX_VOICES], 0, TRACK_WIDTH); - - killPatternIfUnused(nr); } - editor.ui.updatePatternEditor = true; - setSongModifiedFlag(); - } - else - { - okBox(0, "System message", "Not enough memory!"); + memset(&pattPtr[pattPos * MAX_VOICES], 0, TRACK_WIDTH); + + killPatternIfUnused(nr); } + + editor.ui.updatePatternEditor = true; + setSongModifiedFlag(); } void deletePatternNote(void) diff --git a/src/ft2_header.h b/src/ft2_header.h @@ -12,7 +12,7 @@ #endif #include "ft2_replayer.h" -#define PROG_VER_STR "1.03" +#define PROG_VER_STR "1.04" // do NOT change these! It will only mess things up... @@ -90,7 +90,7 @@ struct editor_t bool scopesShown, diskOpShown, nibblesShown, transposeShown, instEditorExtShown; bool sampleEditorExtShown, advEditShown, wavRendererShown, trimScreenShown; bool drawBPMFlag, drawSpeedFlag, drawGlobVolFlag, drawPosEdFlag, drawPattNumLenFlag; - bool updatePosSections; + bool updatePosSections, updatePosEdScrollBar; uint8_t oldTopLeftScreen; // bottom screens diff --git a/src/ft2_inst_ed.c b/src/ft2_inst_ed.c @@ -220,7 +220,8 @@ static void drawMIDICh(void) instrTyp *ins = getCurDispInstr(); assert(ins->midiChannel <= 15); - sprintf(str, "%02d", ins->midiChannel + 1); + uint8_t disp = ins->midiChannel + 1; + sprintf(str, "%02d", disp); textOutFixed(156, 132, PAL_FORGRND, PAL_DESKTOP, str); } diff --git a/src/ft2_keyboard.c b/src/ft2_keyboard.c @@ -347,7 +347,7 @@ static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey) pattLen = pattLens[editor.editPattern]; if (playMode == PLAYMODE_EDIT && pattLen >= 1) - setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen); + setPos(-1, (editor.pattPos + editor.ID_Add) % pattLen, true); editor.ui.updatePatternEditor = true; setSongModifiedFlag(); diff --git a/src/ft2_main.c b/src/ft2_main.c @@ -120,8 +120,6 @@ int main(int argc, char *argv[]) } SDL_EventState(SDL_DROPFILE, SDL_ENABLE); - createSDL2Cursors(); - /* Text input is started by default in SDL2, turn it off to remove ~2ms spikes per key press. ** We manuallay start it again when a text edit box is activated, and stop it when done. ** Ref.: https://bugzilla.libsdl.org/show_bug.cgi?id=4166 */ @@ -233,18 +231,18 @@ static void initializeVars(void) { int32_t i; - cpu.hasSSE = SDL_HasSSE(); + cpu.hasSSE = SDL_HasSSE(); cpu.hasSSE2 = SDL_HasSSE2(); // clear common structs - memset(&video, 0, sizeof (video)); - memset(&keyb, 0, sizeof (keyb)); - memset(&mouse, 0, sizeof (mouse)); - memset(&editor, 0, sizeof (editor)); + memset(&video, 0, sizeof (video)); + memset(&keyb, 0, sizeof (keyb)); + memset(&mouse, 0, sizeof (mouse)); + memset(&editor, 0, sizeof (editor)); memset(&pattMark, 0, sizeof (pattMark)); memset(&pattSync, 0, sizeof (pattSync)); - memset(&chSync, 0, sizeof (chSync)); - memset(&song, 0, sizeof (song)); + memset(&chSync, 0, sizeof (chSync)); + memset(&song, 0, sizeof (song)); for (i = 0; i < MAX_VOICES; i++) { @@ -307,7 +305,7 @@ static void cleanUpAndExit(void) // never call this inside the main loop! freeMidiInputDeviceList(); windUpFTHelp(); freeTextBoxes(); - freeSDL2Cursors(); + freeMouseCursors(); if (midi.inputDeviceName != NULL) { diff --git a/src/ft2_module_loader.c b/src/ft2_module_loader.c @@ -349,7 +349,7 @@ static bool loadMusicMOD(FILE *f, uint32_t fileLength) for (a = 0; a <= b; a++) { - pattTmp[a] = (tonTyp *)calloc(64 * MAX_VOICES, sizeof (tonTyp)); + pattTmp[a] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); if (pattTmp[a] == NULL) { okBoxThreadSafe(0, "System message", "Not enough memory!"); @@ -674,7 +674,7 @@ static bool loadMusicSTM(FILE *f, uint32_t fileLength) ap = h_STM.ap; for (i = 0; i < ap; i++) { - pattTmp[i] = (tonTyp *)calloc(64 * MAX_VOICES, sizeof (tonTyp)); + pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); if (pattTmp[i] == NULL) { okBoxThreadSafe(0, "System message", "Not enough memory!"); @@ -1099,7 +1099,7 @@ static bool loadMusicS3M(FILE *f, uint32_t dataLength) if (j > 0 && j <= 12288) { - pattTmp[i] = (tonTyp *)calloc(64 * MAX_VOICES, sizeof (tonTyp)); + pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); if (pattTmp[i] == NULL) { okBoxThreadSafe(0, "System message", "Not enough memory!"); @@ -2215,15 +2215,15 @@ static bool loadPatterns(FILE *f, uint16_t antPtn) if (ph.dataLen > 0) { - a = ph.pattLen * TRACK_WIDTH; - - pattTmp[i] = (tonTyp *)malloc(a + 16); // + 16 = a little extra for safety + pattTmp[i] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); if (pattTmp[i] == NULL) { okBoxThreadSafe(0, "System message", "Not enough memory!"); return false; } + a = ph.pattLen * TRACK_WIDTH; + pattPtr = (uint8_t *)pattTmp[i]; memset(pattPtr, 0, a); @@ -2321,7 +2321,7 @@ static void setupLoadedModule(void) resetChannels(); refreshScopes(); - setPos(0, 0); + setPos(0, 0, false); setSpeed(song.speed); editor.tmpPattern = editor.editPattern; // set kludge variable diff --git a/src/ft2_mouse.c b/src/ft2_mouse.c @@ -19,40 +19,145 @@ #include "ft2_gfxdata.h" #include "ft2_audioselector.h" #include "ft2_midi.h" +#include "ft2_gfxdata.h" + +#define NUM_CURSORS 6 static bool mouseBusyGfxBackwards; static int16_t mouseShape; static int32_t mouseModeGfxOffs, mouseBusyGfxFrame; +static SDL_Cursor *cursors[NUM_CURSORS]; -static SDL_Cursor *cArrow, *cIBeam, *cBusy; +static bool setSystemCursor(SDL_Cursor *cursor) +{ + if (cursor == NULL) + { + SDL_SetCursor(SDL_GetDefaultCursor()); + return false; + } + + SDL_SetCursor(cursor); + return true; +} -void freeSDL2Cursors(void) +void freeMouseCursors(void) { - if (cArrow != NULL) + SDL_SetCursor(SDL_GetDefaultCursor()); + for (uint32_t i = 0; i < NUM_CURSORS; i++) { - SDL_FreeCursor(cArrow); - cArrow = NULL; + if (cursors[i] != NULL) + { + SDL_FreeCursor(cursors[i]); + cursors[i] = NULL; + } } +} - if (cIBeam != NULL) +bool createMouseCursors(void) // creates scaled SDL surfaces for current mouse pointer shape +{ + freeMouseCursors(); + + const uint8_t *cursorsSrc = mouseCursors; + switch (config.mouseType) { - SDL_FreeCursor(cIBeam); - cIBeam = NULL; + case MOUSE_IDLE_SHAPE_NICE: cursorsSrc += 0 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_UGLY: cursorsSrc += 1 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_AWFUL: cursorsSrc += 2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_USABLE: cursorsSrc += 3 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + default: break; } - if (cBusy != NULL) + for (uint32_t i = 0; i < NUM_CURSORS; i++) { - SDL_FreeCursor(cBusy); - cBusy = NULL; + SDL_Surface *surface = SDL_CreateRGBSurface(0, MOUSE_CURSOR_W*video.yScale, MOUSE_CURSOR_H*video.yScale, 32, 0, 0, 0, 0); + if (surface == NULL) + { + freeMouseCursors(); + config.specialFlags2 &= ~HARDWARE_MOUSE; // enable software mouse + return false; + } + + uint32_t colorkey = SDL_MapRGB(surface->format, 0x00, 0xFF, 0x00); // colorkey + uint32_t fg = SDL_MapRGB(surface->format, 0xFF, 0xFF, 0xFF); // foreground + uint32_t border = SDL_MapRGB(surface->format, 0x00, 0x00, 0x00); // border + + SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE); + SDL_SetColorKey(surface, SDL_TRUE, colorkey); + SDL_SetSurfaceRLE(surface, SDL_TRUE); + + const uint8_t *srcPixels8; + if (i == 3) // text edit cursor + srcPixels8 = &mouseCursors[12 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H)]; + else if (i == 4) // mouse busy (wall clock) + srcPixels8 = &mouseCursorBusyClock[2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H)]; // pick a good still-frame + else if (i == 5) // mouse busy (hourglass) + srcPixels8 = &mouseCursorBusyGlass[2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H)]; // pick a good still-frame + else // normal idle cursor + disk op. "delete/rename" cursors + srcPixels8 = &cursorsSrc[i * (4 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H))]; + + SDL_LockSurface(surface); + + uint32_t *dstPixels32 = (uint32_t *)surface->pixels; + + for (int32_t k = 0; k < surface->w*surface->h; k++) // fill surface with colorkey pixels + dstPixels32[k] = colorkey; + + // blit upscaled cursor to surface + for (uint32_t y = 0; y < MOUSE_CURSOR_H; y++) + { + uint32_t *outX = &dstPixels32[(y * video.yScale) * surface->w]; + for (uint32_t yScale = 0; yScale < video.yScale; yScale++) + { + for (uint32_t x = 0; x < MOUSE_CURSOR_W; x++) + { + uint8_t srcPix = srcPixels8[(y * MOUSE_CURSOR_W) + x]; + if (srcPix != PAL_TRANSPR) + { + uint32_t pixel = colorkey; + if (srcPix == PAL_MOUSEPT) + pixel = fg; + else if (srcPix == PAL_BCKGRND) + pixel = border; + + for (uint32_t xScale = 0; xScale < video.yScale; xScale++) + outX[xScale] = pixel; + } + + outX += video.xScale; + } + } + } + SDL_UnlockSurface(surface); + + uint32_t hotX = 0; + uint32_t hotY = 0; + + if (i == 3) // text edit cursor bias + { + hotX = 2 * video.xScale; + hotY = 6 * video.yScale; + } + + cursors[i] = SDL_CreateColorCursor(surface, hotX, hotY); + if (cursors[i] == NULL) + { + SDL_FreeSurface(surface); + freeMouseCursors(); + config.specialFlags2 &= ~HARDWARE_MOUSE; // enable software mouse + return false; + } + + SDL_FreeSurface(surface); } -} + if (config.specialFlags2 & HARDWARE_MOUSE) + { + if (mouse.mode == MOUSE_MODE_NORMAL) setSystemCursor(cursors[0]); + else if (mouse.mode == MOUSE_MODE_DELETE) setSystemCursor(cursors[1]); + else if (mouse.mode == MOUSE_MODE_RENAME) setSystemCursor(cursors[2]); + } -void createSDL2Cursors(void) -{ - cArrow = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - cIBeam = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); - cBusy = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT); + return true; } void setMousePosToCenter(void) @@ -75,6 +180,12 @@ void animateBusyMouse(void) { if (config.mouseAnimType == MOUSE_BUSY_SHAPE_CLOCK) { + if (config.specialFlags2 & HARDWARE_MOUSE) + { + setSystemCursor(cursors[4]); + return; + } + if ((editor.framesPassed % 7) == 6) { if (mouseBusyGfxBackwards) @@ -100,6 +211,12 @@ void animateBusyMouse(void) } else { + if (config.specialFlags2 & HARDWARE_MOUSE) + { + setSystemCursor(cursors[5]); + return; + } + if ((editor.framesPassed % 5) == 4) { mouseBusyGfxFrame = (mouseBusyGfxFrame + 1) % MOUSE_GLASS_ANI_FRAMES; @@ -126,17 +243,24 @@ void setMouseShape(int16_t shape) gfxPtr = &mouseCursors[mouseModeGfxOffs]; switch (shape) { - case MOUSE_IDLE_SHAPE_NICE: gfxPtr += 0 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_UGLY: gfxPtr += 1 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_AWFUL: gfxPtr += 2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_SHAPE_USABLE: gfxPtr += 3 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; - case MOUSE_IDLE_TEXT_EDIT: gfxPtr += 12 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_NICE: gfxPtr += 0 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_UGLY: gfxPtr += 1 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_AWFUL: gfxPtr += 2 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_SHAPE_USABLE: gfxPtr += 3 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; + case MOUSE_IDLE_TEXT_EDIT: gfxPtr += 12 * (MOUSE_CURSOR_W * MOUSE_CURSOR_H); break; default: return; } } mouseShape = shape; changeSpriteData(SPRITE_MOUSE_POINTER, gfxPtr); + + if (config.specialFlags2 & HARDWARE_MOUSE) + { + if (mouse.mode == MOUSE_MODE_NORMAL) setSystemCursor(cursors[0]); + else if (mouse.mode == MOUSE_MODE_DELETE) setSystemCursor(cursors[1]); + else if (mouse.mode == MOUSE_MODE_RENAME) setSystemCursor(cursors[2]); + } } static void setTextEditMouse(void) @@ -145,8 +269,8 @@ static void setTextEditMouse(void) mouse.xBias = -2; mouse.yBias = -6; - if (config.specialFlags2 & HARDWARE_MOUSE && cIBeam != NULL) - SDL_SetCursor(cIBeam); + if (config.specialFlags2 & HARDWARE_MOUSE) + setSystemCursor(cursors[3]); } static void clearTextEditMouse(void) @@ -155,8 +279,8 @@ static void clearTextEditMouse(void) mouse.xBias = 0; mouse.yBias = 0; - if (config.specialFlags2 & HARDWARE_MOUSE && cArrow != NULL) - SDL_SetCursor(cArrow); + if (config.specialFlags2 & HARDWARE_MOUSE) + setSystemCursor(cursors[0]); } static void changeCursorIfOverTextBoxes(void) @@ -238,8 +362,8 @@ void mouseAnimOn(void) editor.busy = true; setMouseShape(config.mouseAnimType); - if (config.specialFlags2 & HARDWARE_MOUSE && cBusy != NULL) - SDL_SetCursor(cBusy); + //if (config.specialFlags2 & HARDWARE_MOUSE && cBusy != NULL) + // SDL_SetCursor(cBusy); } void mouseAnimOff(void) @@ -250,8 +374,8 @@ void mouseAnimOff(void) editor.busy = false; setMouseShape(config.mouseType); - if (config.specialFlags2 & HARDWARE_MOUSE && cArrow != NULL) - SDL_SetCursor(cArrow); + //if (config.specialFlags2 & HARDWARE_MOUSE && cArrow != NULL) + // SDL_SetCursor(cArrow); } static void mouseWheelDecRow(void) @@ -265,7 +389,7 @@ static void mouseWheelDecRow(void) if (pattPos < 0) pattPos = pattLens[editor.editPattern] - 1; - setPos(-1, pattPos); + setPos(-1, pattPos, true); } static void mouseWheelIncRow(void) @@ -279,7 +403,7 @@ static void mouseWheelIncRow(void) if (pattPos > (pattLens[editor.editPattern] - 1)) pattPos = 0; - setPos(-1, pattPos); + setPos(-1, pattPos, true); } void mouseWheelHandler(bool directionUp) @@ -633,13 +757,8 @@ void handleLastGUIObjectDown(void) void updateMouseScaling(void) { - double dScaleX, dScaleY; - - dScaleX = video.renderW / (double)SCREEN_W; - dScaleY = video.renderH / (double)SCREEN_H; - - video.xScaleMul = (dScaleX == 0.0) ? 65536 : (uint32_t)round(65536.0 / dScaleX); - video.yScaleMul = (dScaleY == 0.0) ? 65536 : (uint32_t)round(65536.0 / dScaleY); + video.dMouseXMul = (double)SCREEN_W / video.renderW; + video.dMouseYMul = (double)SCREEN_H / video.renderH; } void readMouseXY(void) @@ -693,9 +812,9 @@ void readMouseXY(void) if (mx < 0) mx = 0; if (my < 0) mx = 0; - // multiply coords by video scaling factors - mx = (((uint32_t)mx * video.xScaleMul) + (1 << 15)) >> 16; // rounded - my = (((uint32_t)my * video.yScaleMul) + (1 << 15)) >> 16; + // multiply coords by video upscaling factors (don't round) + mx = (uint32_t)(mx * video.dMouseXMul); + my = (uint32_t)(my * video.dMouseYMul); if (mx >= SCREEN_W) mx = SCREEN_W - 1; if (my >= SCREEN_H) my = SCREEN_H - 1; diff --git a/src/ft2_mouse.h b/src/ft2_mouse.h @@ -30,8 +30,8 @@ struct mouse_t #define MOUSE_GLASS_ANI_FRAMES 22 #define MOUSE_CLOCK_ANI_FRAMES 5 -void freeSDL2Cursors(void); -void createSDL2Cursors(void); +void freeMouseCursors(void); +bool createMouseCursors(void); void setMousePosToCenter(void); void setMouseShape(int16_t shape); diff --git a/src/ft2_palette.c b/src/ft2_palette.c @@ -129,6 +129,13 @@ static void paletteDragMoved(void) return; } + if ((config.specialFlags2 & HARDWARE_MOUSE) && cfg_ColorNr == 3) + { + updatePaletteEditor(); // resets colors/contrast vars + okBox(0, "System message", "Mouse color can only be changed when \"Software mouse\" is enabled."); + return; + } + nr = FTC_EditOrder[cfg_ColorNr]; p = (uint8_t)config.cfg_StdPalNr; diff --git a/src/ft2_pattern_draw.c b/src/ft2_pattern_draw.c @@ -75,6 +75,8 @@ void drawPatternBorders(void) else if (chans == 10 && !config.ptnS3M) chans = 12; + assert(chans >= 2 && chans <= 12); + chanWidth = chanWidths[(chans / 2) - 1] + 2; // fill scrollbar framework (if needed) @@ -359,9 +361,11 @@ static void showNoteNum(uint8_t pal, uint16_t xPos, uint16_t yPos, int16_t ton) { xPos += 3; + assert(ton >= 0 && ton <= 97); + if (editor.ui.numChannelsShown <= 4) { - if (ton == 0) + if (ton <= 0 || ton > 97) drawEmptyNoteBig(xPos, yPos, pal); else if (ton == 97) drawKeyOffBig(xPos, yPos, pal); @@ -370,7 +374,7 @@ static void showNoteNum(uint8_t pal, uint16_t xPos, uint16_t yPos, int16_t ton) } else { - if (ton == 0) + if (ton <= 0 || ton > 97) drawEmptyNoteMedium(xPos, yPos, pal); else if (ton == 97) drawKeyOffMedium(xPos, yPos, pal); @@ -499,9 +503,11 @@ static void showNoteNumNoVolColumn(uint8_t pal, uint16_t xPos, uint16_t yPos, in { xPos += 3; + assert(ton >= 0 && ton <= 97); + if (editor.ui.numChannelsShown <= 6) { - if (ton == 0) + if (ton <= 0 || ton > 97) drawEmptyNoteBig(xPos, yPos, pal); else if (ton == 97) drawKeyOffBig(xPos, yPos, pal); @@ -510,7 +516,7 @@ static void showNoteNumNoVolColumn(uint8_t pal, uint16_t xPos, uint16_t yPos, in } else if (editor.ui.numChannelsShown <= 8) { - if (ton == 0) + if (ton <= 0 || ton > 97) drawEmptyNoteMedium(xPos, yPos, pal); else if (ton == 97) drawKeyOffMedium(xPos, yPos, pal); @@ -519,7 +525,7 @@ static void showNoteNumNoVolColumn(uint8_t pal, uint16_t xPos, uint16_t yPos, in } else { - if (ton == 0) + if (ton <= 0 || ton > 97) drawEmptyNoteSmall(xPos, yPos, pal); else if (ton == 97) drawKeyOffSmall(xPos, yPos, pal); @@ -668,13 +674,16 @@ void writePattern(int16_t currRow, int16_t pattern) void (*drawVolEfx)(uint8_t, uint16_t, uint16_t, uint8_t); void (*drawEfx)(uint8_t, uint16_t, uint16_t, uint8_t, uint8_t); - // we're too lazy to erase things, just render the whole pattern framework first (fast enough on modern PCs) + /* We're too lazy to carefully erase things as needed, just render + ** the whole pattern framework first (fast enough on modern PCs) */ drawPatternBorders(); chans = editor.ui.numChannelsShown; if (chans > editor.ui.maxVisibleChannels) chans = editor.ui.maxVisibleChannels; + assert(chans >= 2 && chans <= 12); + // get channel width chanWidth = chanWidths[(chans / 2) - 1]; editor.ui.patternChannelWidth = chanWidth + 3; @@ -1067,8 +1076,6 @@ static void drawNoteMedium(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, i uint8_t note; uint32_t *dstPtr, pixVal, fontOffset, char1, char2, char3; - assert(ton >= 1 && ton <= 97); - ton--; note = ton % 12; @@ -1158,8 +1165,6 @@ static void drawNoteBig(uint16_t xPos, uint16_t yPos, uint8_t paletteIndex, int1 uint8_t note; uint32_t *dstPtr, pixVal, fontOffset, char1, char2, char3; - assert(ton >= 1 && ton <= 97); - ton--; note = ton % 12; diff --git a/src/ft2_pattern_ed.c b/src/ft2_pattern_ed.c @@ -46,7 +46,6 @@ const uint16_t chanWidths[6] = { 141, 141, 93, 69, 45, 45 }; bool allocatePattern(uint16_t nr) // for tracker use only, not in loader! { bool audioWasntLocked; - int16_t pattLen; if (patt[nr] == NULL) { @@ -54,9 +53,13 @@ bool allocatePattern(uint16_t nr) // for tracker use only, not in loader! if (audioWasntLocked) lockAudio(); - pattLen = pattLens[nr]; + /* Original FT2 allocates only the amount of rows needed, but we don't + * do that to avoid out of bondary row look-up between out-of-sync replayer + * state and tracker state (yes it used to happen, rarely). We're not wasting + * too much RAM for a modern computer anyway. Worst case: 256 allocated + * patterns would be ~10MB. */ - patt[nr] = (tonTyp *)calloc(pattLen * TRACK_WIDTH, sizeof (tonTyp)); + patt[nr] = (tonTyp *)calloc((MAX_PATT_LEN * TRACK_WIDTH) + 16, 1); if (patt[nr] == NULL) { if (audioWasntLocked) @@ -65,8 +68,8 @@ bool allocatePattern(uint16_t nr) // for tracker use only, not in loader! return false; } - if (song.pattNr == nr) - song.pattLen = pattLen; + // XXX: Do we really need this? Sounds redundant. + song.pattLen = pattLens[nr]; if (audioWasntLocked) unlockAudio(); @@ -122,6 +125,8 @@ void updatePatternWidth(void) if (editor.ui.numChannelsShown > editor.ui.maxVisibleChannels) editor.ui.numChannelsShown = editor.ui.maxVisibleChannels; + assert(editor.ui.numChannelsShown >= 2 && editor.ui.numChannelsShown <= 12); + editor.ui.patternChannelWidth = chanWidths[(editor.ui.numChannelsShown / 2) - 1] + 3; } @@ -925,7 +930,7 @@ void handlePatternDataMouseDown(bool mouseButtonHeld) { pattLen = pattLens[editor.editPattern]; if (editor.pattPos > 0) - setPos(-1, editor.pattPos - 1); + setPos(-1, editor.pattPos - 1, true); forceMarking = true; editor.ui.updatePatternEditor = true; @@ -934,7 +939,7 @@ void handlePatternDataMouseDown(bool mouseButtonHeld) { pattLen = pattLens[editor.editPattern]; if (editor.pattPos < (pattLen - 1)) - setPos(-1, editor.pattPos + 1); + setPos(-1, editor.pattPos + 1, true); forceMarking = true; editor.ui.updatePatternEditor = true; @@ -1521,7 +1526,7 @@ void sbPosEdPos(uint32_t pos) lockAudio(); if (song.songPos != (int16_t)pos) - setPos((int16_t)pos, 0); + setPos((int16_t)pos, 0, true); if (audioWasntLocked) unlockAudio(); @@ -1534,7 +1539,7 @@ void pbPosEdPosUp(void) lockAudio(); if (song.songPos < song.len-1) - setPos(song.songPos + 1, 0); + setPos(song.songPos + 1, 0, true); if (audioWasntLocked) unlockAudio(); @@ -1547,7 +1552,7 @@ void pbPosEdPosDown(void) lockAudio(); if (song.songPos > 0) - setPos(song.songPos - 1, 0); + setPos(song.songPos - 1, 0, true); if (audioWasntLocked) unlockAudio(); @@ -1568,12 +1573,9 @@ void pbPosEdIns(void) song.songTab[song.songPos] = oldPatt; song.len++; - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); - - drawPosEdNums(song.songPos); - drawSongLength(); editor.ui.updatePosSections = true; + editor.ui.updatePosEdScrollBar = true; setSongModifiedFlag(); unlockMixerCallback(); @@ -1581,34 +1583,29 @@ void pbPosEdIns(void) void pbPosEdDel(void) { - if (song.len == 0) + if (song.len <= 1) return; lockMixerCallback(); - for (uint16_t i = 0; i < 254-song.songPos; i++) - song.songTab[song.songPos+i] = song.songTab[song.songPos+1+i]; - song.len--; - - if (song.repS >= song.len) + if (song.songPos < 254) { - song.repS = song.len - 1; - drawSongRepS(); + for (uint16_t i = 0; i < 254-song.songPos; i++) + song.songTab[song.songPos+i] = song.songTab[song.songPos+1+i]; } + song.len--; + if (song.repS >= song.len) + song.repS = song.len - 1; + if (song.songPos > song.len-1) { - song.songPos = song.len-1; - setPos(song.songPos, -1); - + editor.songPos = song.songPos = song.len-1; + setPos(song.songPos, -1, false); } - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); - - drawPosEdNums(song.songPos); - drawSongLength(); - - setPos(song.songPos, -1); + editor.ui.updatePosSections = true; + editor.ui.updatePosEdScrollBar = true; setSongModifiedFlag(); unlockMixerCallback(); @@ -1616,6 +1613,9 @@ void pbPosEdDel(void) void pbPosEdPattUp(void) { + if (song.songTab[song.songPos] == 255) + return; + lockMixerCallback(); if (song.songTab[song.songPos] < 255) { @@ -1624,11 +1624,8 @@ void pbPosEdPattUp(void) editor.editPattern = (uint8_t)song.pattNr; song.pattLen = pattLens[editor.editPattern]; - drawPosEdNums(song.songPos); - drawEditPattern(editor.editPattern); - drawPatternLength(editor.editPattern); - editor.ui.updatePatternEditor = true; + editor.ui.updatePosSections = true; setSongModifiedFlag(); } unlockMixerCallback(); @@ -1636,6 +1633,9 @@ void pbPosEdPattUp(void) void pbPosEdPattDown(void) { + if (song.songTab[song.songPos] == 0) + return; + lockMixerCallback(); if (song.songTab[song.songPos] > 0) { @@ -1644,11 +1644,8 @@ void pbPosEdPattDown(void) editor.editPattern = (uint8_t)song.pattNr; song.pattLen = pattLens[editor.editPattern]; - drawPosEdNums(song.songPos); - drawEditPattern(editor.editPattern); - drawPatternLength(editor.editPattern); - editor.ui.updatePatternEditor = true; + editor.ui.updatePosSections = true; setSongModifiedFlag(); } unlockMixerCallback(); @@ -1667,10 +1664,8 @@ void pbPosEdLenUp(void) song.len++; - drawPosEdNums(song.songPos); - drawSongLength(); - - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); + editor.ui.updatePosSections = true; + editor.ui.updatePosEdScrollBar = true; setSongModifiedFlag(); if (audioWasntLocked) @@ -1691,22 +1686,16 @@ void pbPosEdLenDown(void) song.len--; if (song.repS >= song.len) - { song.repS = song.len - 1; - drawSongRepS(); - } if (song.songPos >= song.len) { song.songPos = song.len - 1; - setScrollBarPos(SB_POS_ED, song.songPos, false); - setPos(song.songPos, -1); + setPos(song.songPos, -1, false); } - drawPosEdNums(song.songPos); - drawSongLength(); - - setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); + editor.ui.updatePosSections = true; + editor.ui.updatePosEdScrollBar = true; setSongModifiedFlag(); if (audioWasntLocked) @@ -1722,7 +1711,7 @@ void pbPosEdRepSUp(void) if (song.repS < song.len-1) { song.repS++; - drawSongRepS(); + editor.ui.updatePosSections = true; setSongModifiedFlag(); } @@ -1739,7 +1728,7 @@ void pbPosEdRepSDown(void) if (song.repS > 0) { song.repS--; - drawSongRepS(); + editor.ui.updatePosSections = true; setSongModifiedFlag(); } @@ -1749,6 +1738,9 @@ void pbPosEdRepSDown(void) void pbBPMUp(void) { + if (song.speed == 255) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -1772,6 +1764,9 @@ void pbBPMUp(void) void pbBPMDown(void) { + if (song.speed == 32) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -1795,6 +1790,9 @@ void pbBPMDown(void) void pbSpeedUp(void) { + if (song.tempo == 31) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -1817,6 +1815,9 @@ void pbSpeedUp(void) void pbSpeedDown(void) { + if (song.tempo == 0) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -1910,6 +1911,9 @@ static void updatePtnLen(void) void pbEditPattUp(void) { + if (editor.editPattern == 255) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -1921,15 +1925,8 @@ void pbEditPattUp(void) song.pattNr = editor.editPattern; updatePtnLen(); - if (!editor.ui.aboutScreenShown && !editor.ui.configScreenShown && - !editor.ui.diskOpShown && !editor.ui.helpScreenShown && - !editor.ui.nibblesShown) - { - drawEditPattern(editor.editPattern); - drawPatternLength(editor.editPattern); - } - editor.ui.updatePatternEditor = true; + editor.ui.updatePosSections = true; } if (audioWasntLocked) @@ -1938,6 +1935,9 @@ void pbEditPattUp(void) void pbEditPattDown(void) { + if (editor.editPattern == 0) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -1949,15 +1949,8 @@ void pbEditPattDown(void) song.pattNr = editor.editPattern; updatePtnLen(); - if (!editor.ui.aboutScreenShown && !editor.ui.configScreenShown && - !editor.ui.diskOpShown && !editor.ui.helpScreenShown && - !editor.ui.nibblesShown) - { - drawEditPattern(editor.editPattern); - drawPatternLength(editor.editPattern); - } - editor.ui.updatePatternEditor = true; + editor.ui.updatePosSections = true; } if (audioWasntLocked) @@ -1969,6 +1962,9 @@ void pbPattLenUp(void) bool audioWasntLocked; uint16_t pattLen; + if (pattLens[editor.editPattern] >= 256) + return; + audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -1979,8 +1975,8 @@ void pbPattLenUp(void) setPatternLen(editor.editPattern, pattLen + 1); checkMarkLimits(); - drawPatternLength(editor.editPattern); editor.ui.updatePatternEditor = true; + editor.ui.updatePosSections = true; setSongModifiedFlag(); } @@ -1993,6 +1989,9 @@ void pbPattLenDown(void) bool audioWasntLocked; uint16_t pattLen; + if (pattLens[editor.editPattern] <= 1) + return; + audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); @@ -2003,12 +2002,8 @@ void pbPattLenDown(void) setPatternLen(editor.editPattern, pattLen - 1); checkMarkLimits(); - drawPatternLength(editor.editPattern); - - if (song.pattPos >= song.pattLen) - song.pattPos--; - editor.ui.updatePatternEditor = true; + editor.ui.updatePosSections = true; setSongModifiedFlag(); } diff --git a/src/ft2_replayer.c b/src/ft2_replayer.c @@ -111,6 +111,8 @@ void resetChannels(void) ch->oldPan = 128; ch->outPan = 128; ch->finalPan = 128; + + ch->stOff = !editor.chnMode[i]; // set channel mute flag from global mute flag } if (audioWasntLocked) @@ -148,79 +150,31 @@ void tuneSample(sampleTyp *s, uint32_t midCFreq) } } -bool setPatternLen(uint16_t nr, int16_t len) +void setPatternLen(uint16_t nr, int16_t len) { bool audioWasntLocked; - tonTyp *newPtr; assert(nr < MAX_PATTERNS); - len = CLAMP(len, 1, MAX_PATT_LEN); - if (len == pattLens[nr]) - return true; + if ((len < 1 || len > MAX_PATT_LEN) || len == pattLens[nr]) + return; audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); - if (patt[nr] == NULL) - { - pattLens[nr] = len; - - if (editor.editPattern == song.pattNr) - song.pattLen = pattLens[nr]; - - if (song.pattPos >= pattLens[nr]) - song.pattPos = pattLens[nr] - 1; - - editor.pattPos = song.pattPos; - - checkMarkLimits(); - - if (audioWasntLocked) - unlockAudio(); - - editor.ui.updatePatternEditor = true; - editor.ui.updatePosSections = true; - - return true; - } - - newPtr = (tonTyp *)realloc(patt[nr], len * TRACK_WIDTH); - if (newPtr == NULL) - { - okBox(0, "Status message", "Not enough memory!"); - - if (audioWasntLocked) - unlockAudio(); + pattLens[nr] = len; - return false; - } - - patt[nr] = newPtr; - - // if we enlarged the pattern length, wipe the new data - if (len >= pattLens[nr]) - { - if (len > pattLens[nr]) - memset(&patt[nr][pattLens[nr] * MAX_VOICES], 0, (len - pattLens[nr]) * TRACK_WIDTH); + if (patt[nr] != NULL) + killPatternIfUnused(nr); - pattLens[nr] = len; - } - else + song.pattLen = pattLens[nr]; + if (song.pattPos >= song.pattLen) { - pattLens[nr] = len; - killPatternIfUnused(nr); + song.pattPos = song.pattLen - 1; + editor.pattPos = song.pattPos; } - if (editor.editPattern == song.pattNr) - song.pattLen = pattLens[nr]; - - if (song.pattPos >= pattLens[nr]) - song.pattPos = pattLens[nr] - 1; - - editor.pattPos = song.pattPos; - checkMarkLimits(); if (audioWasntLocked) @@ -228,8 +182,6 @@ bool setPatternLen(uint16_t nr, int16_t len) editor.ui.updatePatternEditor = true; editor.ui.updatePosSections = true; - - return true; } int16_t getUsedSamples(int16_t nr) @@ -2203,7 +2155,7 @@ void resetMusic(void) if (audioWasntLocked) unlockAudio(); - setPos(0, 0); + setPos(0, 0, false); if (!songPlaying) { @@ -2212,7 +2164,7 @@ void resetMusic(void) } } -void setPos(int16_t songPos, int16_t pattPos) +void setPos(int16_t songPos, int16_t pattPos, bool resetTimer) { bool audioWasntLocked = !audio.locked; if (audioWasntLocked) @@ -2255,7 +2207,8 @@ void setPos(int16_t songPos, int16_t pattPos) } } - song.timer = 1; + if (resetTimer) + song.timer = 1; if (audioWasntLocked) unlockAudio(); @@ -2836,7 +2789,7 @@ bool setupReplayer(void) song.initialTempo = song.tempo; setFrqTab(true); - setPos(0, 0); + setPos(0, 0, true); if (!allocateInstr(0)) { @@ -2876,9 +2829,9 @@ void startPlaying(int8_t mode, int16_t row) assert(mode != PLAYMODE_IDLE && mode != PLAYMODE_EDIT); if (mode == PLAYMODE_PATT || mode == PLAYMODE_RECPATT) - setPos(-1, row); + setPos(-1, row, true); else - setPos(editor.songPos, row); + setPos(editor.songPos, row, true); playMode = mode; songPlaying = true; @@ -3181,12 +3134,15 @@ void stopVoices(void) void decSongPos(void) { + if (song.songPos == 0) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); if (song.songPos > 0) - setPos(song.songPos - 1, 0); + setPos(song.songPos - 1, 0, true); if (audioWasntLocked) unlockAudio(); @@ -3194,12 +3150,15 @@ void decSongPos(void) void incSongPos(void) { + if (song.songPos == song.len-1) + return; + bool audioWasntLocked = !audio.locked; if (audioWasntLocked) lockAudio(); if (song.songPos < song.len-1) - setPos(song.songPos + 1, 0); + setPos(song.songPos + 1, 0, true); if (audioWasntLocked) unlockAudio(); diff --git a/src/ft2_replayer.h b/src/ft2_replayer.h @@ -278,7 +278,7 @@ void resetMusic(void); void startPlaying(int8_t mode, int16_t row); void stopPlaying(void); void stopVoices(void); -void setPos(int16_t songPos, int16_t pattPos); +void setPos(int16_t songPos, int16_t pattPos, bool resetTimer); void pauseMusic(void); // stops reading pattern data void resumeMusic(void); // starts reading pattern data void setSongModifiedFlag(void); @@ -291,7 +291,7 @@ void conv8BitSample(int8_t *p, int32_t len, bool stereo); void conv16BitSample(int8_t *p, int32_t len, bool stereo); void delta2Samp(int8_t *p, int32_t len, uint8_t typ); void samp2Delta(int8_t *p, int32_t len, uint8_t typ); -bool setPatternLen(uint16_t nr, int16_t len); +void setPatternLen(uint16_t nr, int16_t len); void setFrqTab(bool linear); void mainPlayer(void); // periodically called from audio callback void resetChannels(void); diff --git a/src/ft2_sample_ed.c b/src/ft2_sample_ed.c @@ -300,7 +300,7 @@ uint32_t getSampleMiddleCRate(sampleTyp *s) // replayer is shifting the finetune to the right by 3 dFTune = (s->fine >> 3) / (128.0 / (double)(1 << 3)); - return (uint32_t)round(8363.0 * pow(2.0, (s->relTon + dFTune) / 12.0)); + return (uint32_t)round(8363.0 * exp2((s->relTon + dFTune) / 12.0)); } int32_t getSampleRangeStart(void) diff --git a/src/ft2_sample_ed_features.c b/src/ft2_sample_ed_features.c @@ -84,7 +84,7 @@ static int32_t SDLCALL resampleThread(void *ptr) int8_t *p1, *p2, *src8, *dst8; int16_t *src16, *dst16; uint32_t newLen, mask, resampleLen; - uint64_t posfrac64, delta64; + uint64_t posFrac64, delta64; double dNewLen, dLenMul; sampleTyp *s; @@ -96,7 +96,7 @@ static int32_t SDLCALL resampleThread(void *ptr) s = &instr[editor.curInstr]->samp[editor.curSmp]; mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF; - dLenMul = pow(2.0, smpEd_RelReSmp * (1.0 / 12.0)); + dLenMul = exp2(smpEd_RelReSmp / 12.0); dNewLen = s->len * dLenMul; if (dNewLen > (double)MAX_SAMPLE_LEN) @@ -116,9 +116,9 @@ static int32_t SDLCALL resampleThread(void *ptr) p1 = s->pek; // don't use the potentially clamped newLen value here - delta64 = ((uint64_t)s->len << 32) / (uint64_t)(s->len * dLenMul); + delta64 = ((uint64_t)s->len << 32) / (uint64_t)(s->len * dLenMul); // 32.32 fixed point delta - posfrac64 = 0; + posFrac64 = 0; // 32.32 fixed point position.fraction pauseAudio(); restoreSample(s); @@ -133,8 +133,8 @@ static int32_t SDLCALL resampleThread(void *ptr) resampleLen = newLen / 2; for (uint32_t i = 0; i < resampleLen; i++) { - dst16[i] = src16[posfrac64 >> 32]; - posfrac64 += delta64; + dst16[i] = src16[posFrac64 >> 32]; + posFrac64 += delta64; } } else @@ -144,8 +144,8 @@ static int32_t SDLCALL resampleThread(void *ptr) for (uint32_t i = 0; i < newLen; i++) { - dst8[i] = src8[posfrac64 >> 32]; - posfrac64 += delta64; + dst8[i] = src8[posFrac64 >> 32]; + posFrac64 += delta64; } } } @@ -159,8 +159,8 @@ static int32_t SDLCALL resampleThread(void *ptr) s->repS = (int32_t)(s->repS * dLenMul) & mask; s->repL = (int32_t)(s->repL * dLenMul) & mask; - if (s->repS > s->len) - s->repS = s->len; + if (s->repS >= s->len) + s->repS = s->len - 1; if (s->repS+s->repL > s->len) s->repL = s->len - s->repS; @@ -172,7 +172,7 @@ static int32_t SDLCALL resampleThread(void *ptr) s->repL &= 0xFFFFFFFE; } - if (s->repL == 0) + if (s->repL <= 0) s->typ &= ~3; // disable loop fixSample(s); @@ -228,7 +228,7 @@ static void drawResampleBox(void) s = &instr[editor.curInstr]->samp[editor.curSmp]; mask = (s->typ & 16) ? 0xFFFFFFFE : 0xFFFFFFFF; - dLenMul = pow(2.0, smpEd_RelReSmp * (1.0 / 12.0)); + dLenMul = exp2(smpEd_RelReSmp / 12.0); dNewLen = s->len * dLenMul; if (dNewLen > (double)MAX_SAMPLE_LEN) diff --git a/src/ft2_trim.c b/src/ft2_trim.c @@ -99,7 +99,7 @@ static int16_t getUsedTempSamples(uint16_t nr) i--; /* Yes, 'i' can be -1 here, and will be set to at least 0 - ** because of ins->ta values. Possibly an FT2 bug... */ + * because of ins->ta values. Possibly an FT2 bug... */ for (j = 0; j < 96; j++) { if (ins->ta[j] > i) @@ -655,7 +655,7 @@ static bool tmpPatternEmpty(uint16_t nr, uint8_t antChn) return false; } - scanPtr += sizeof (tonTyp) * MAX_VOICES; + scanPtr += TRACK_WIDTH; } return true; @@ -970,7 +970,7 @@ static int32_t SDLCALL trimThreadFunc(void *ptr) void trimThreadDone(void) { if (removePatt) - setPos(song.songPos, song.pattPos); + setPos(song.songPos, song.pattPos, false); if (removeInst) { diff --git a/src/ft2_video.c b/src/ft2_video.c @@ -266,6 +266,11 @@ void updateRenderSizeVars(void) video.renderX = 0; video.renderY = 0; } + + // for mouse cursor creation + video.xScale = (uint32_t)round(video.renderW / (double)SCREEN_W); + video.yScale = (uint32_t)round(video.renderH / (double)SCREEN_H); + createMouseCursors(); } void enterFullscreen(void) @@ -887,8 +892,8 @@ bool setupWindow(void) video.vsync60HzPresent = false; video.window = SDL_CreateWindow("", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - SCREEN_W * video.upscaleFactor, SCREEN_H * video.upscaleFactor, - windowFlags); + SCREEN_W * video.upscaleFactor, SCREEN_H * video.upscaleFactor, + windowFlags); if (video.window == NULL) { @@ -909,7 +914,7 @@ bool setupRenderer(void) { uint32_t rendererFlags; - rendererFlags = 0; + rendererFlags = SDL_RENDERER_ACCELERATED; if (video.vsync60HzPresent) rendererFlags |= SDL_RENDERER_PRESENTVSYNC; @@ -1018,6 +1023,13 @@ void handleRedrawing(void) } } + if (editor.ui.updatePosEdScrollBar) + { + editor.ui.updatePosEdScrollBar = false; + setScrollBarPos(SB_POS_ED, song.songPos, false); + setScrollBarEnd(SB_POS_ED, (song.len - 1) + 5); + } + if (!editor.ui.extended) { if (!editor.ui.diskOpShown) diff --git a/src/ft2_video.h b/src/ft2_video.h @@ -22,8 +22,8 @@ struct video_t bool fullscreen, vsync60HzPresent, showFPSCounter; int32_t renderX, renderY, renderW, renderH, displayW, displayH; uint32_t *frameBuffer, palette[PAL_NUM], vblankTimeLen, vblankTimeLenFrac; - uint32_t xScaleMul, yScaleMul; - double dMonitorRefreshRate; + uint32_t xScale, yScale; + double dMonitorRefreshRate, dMouseXMul, dMouseYMul; #ifdef _WIN32 HWND hWnd; #endif diff --git a/src/ft2_wav_renderer.c b/src/ft2_wav_renderer.c @@ -169,7 +169,7 @@ static bool dump_Init(uint32_t frq, int16_t amp, int16_t songPos) editor.wavIsRendering = true; - setPos(songPos, 0); + setPos(songPos, 0, true); playMode = PLAYMODE_SONG; songPlaying = true; diff --git a/src/helpdata/FT2.HLP b/src/helpdata/FT2.HLP @@ -864,9 +864,10 @@ END macOS/OS X: Change ALT+F4/ALT+F5 keys in the OS to something else. Also for GNU/Linux. >@X020 >@C001Q: The mouse cursor is delayed/laggy! ->@C002A: Enable "VSync off" in Config -> Miscellaneous. This however, will ->@X035introduce stuttering because the rendering rate is not exact to your ->monitor's rate. Alternatively, enable "Hardware mouse" in Config -> Layout. +>@C002A: Make sure "Software mouse" is disabled in Config -> Layout. +>@X035Alternatively, you can enable "VSync off" in Config -> Miscellaneous. +>This however, will introduce stuttering because the rendering rate is +>not exact to your monitor's rate. >@X020 >@C001Q: Will you implement MIDI out functionality? >@C002A: No, sorry. This is very difficult to implement correctly when having @@ -911,6 +912,8 @@ see the individual sample points, it will look like normal. I only poll input once per frame (60Hz), so the frequency is a tad low. It has to be like this for several reasons, though... >@X010 +>@C002- macOS / OS X: "Hardware mode" mouse looks blurry on retina Macs +>@X010 >- macOS / OS X: Holding down a mouse button won't trap the mouse cursor >@X021inside the window. This is related to a kludge that simply doesn't work very well in macOS. The mouse movement would freeze for some time after a mouse @@ -929,9 +932,5 @@ button was held down. >@X021you may experience visual stuttering because VSync will not be used then. I highly recommend running your monitor at 60Hz if you're a hardcore user of this program. -> ->@X010- macOS / OS X: If "Hardware mouse" is enabled, the mouse cursor won't ->@X021change when the program is busy. The macOS "wait" cursor is missing in -SDL2... END diff --git a/src/helpdata/ft2_help_data.h b/src/helpdata/ft2_help_data.h @@ -3,9 +3,9 @@ #include <stdint.h> -#define HELP_DATA_LEN 27975 +#define HELP_DATA_LEN 27914 -const uint8_t helpData[27975] = +const uint8_t helpData[27914] = { 0x4C,0x3B,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, 0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A, @@ -2060,26 +2060,28 @@ const uint8_t helpData[27975] = 0x30,0x31,0x51,0x3A,0x20,0x54,0x68,0x65,0x20,0x6D,0x6F,0x75, 0x73,0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x20,0x69,0x73, 0x20,0x64,0x65,0x6C,0x61,0x79,0x65,0x64,0x2F,0x6C,0x61,0x67, - 0x67,0x79,0x21,0x4A,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A, - 0x20,0x45,0x6E,0x61,0x62,0x6C,0x65,0x20,0x22,0x56,0x53,0x79, - 0x6E,0x63,0x20,0x6F,0x66,0x66,0x22,0x20,0x69,0x6E,0x20,0x43, - 0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D,0x3E,0x20,0x4D,0x69,0x73, - 0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65,0x6F,0x75,0x73,0x2E,0x20, - 0x54,0x68,0x69,0x73,0x20,0x68,0x6F,0x77,0x65,0x76,0x65,0x72, - 0x2C,0x20,0x77,0x69,0x6C,0x6C,0x4A,0x3E,0x40,0x58,0x30,0x33, - 0x35,0x69,0x6E,0x74,0x72,0x6F,0x64,0x75,0x63,0x65,0x20,0x73, + 0x67,0x79,0x21,0x44,0x3E,0x40,0x43,0x30,0x30,0x32,0x41,0x3A, + 0x20,0x4D,0x61,0x6B,0x65,0x20,0x73,0x75,0x72,0x65,0x20,0x22, + 0x53,0x6F,0x66,0x74,0x77,0x61,0x72,0x65,0x20,0x6D,0x6F,0x75, + 0x73,0x65,0x22,0x20,0x69,0x73,0x20,0x64,0x69,0x73,0x61,0x62, + 0x6C,0x65,0x64,0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69, + 0x67,0x20,0x2D,0x3E,0x20,0x4C,0x61,0x79,0x6F,0x75,0x74,0x2E, + 0x4B,0x3E,0x40,0x58,0x30,0x33,0x35,0x41,0x6C,0x74,0x65,0x72, + 0x6E,0x61,0x74,0x69,0x76,0x65,0x6C,0x79,0x2C,0x20,0x79,0x6F, + 0x75,0x20,0x63,0x61,0x6E,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65, + 0x20,0x22,0x56,0x53,0x79,0x6E,0x63,0x20,0x6F,0x66,0x66,0x22, + 0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E,0x66,0x69,0x67,0x20,0x2D, + 0x3E,0x20,0x4D,0x69,0x73,0x63,0x65,0x6C,0x6C,0x61,0x6E,0x65, + 0x6F,0x75,0x73,0x2E,0x46,0x3E,0x54,0x68,0x69,0x73,0x20,0x68, + 0x6F,0x77,0x65,0x76,0x65,0x72,0x2C,0x20,0x77,0x69,0x6C,0x6C, + 0x20,0x69,0x6E,0x74,0x72,0x6F,0x64,0x75,0x63,0x65,0x20,0x73, 0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E,0x67,0x20,0x62,0x65, 0x63,0x61,0x75,0x73,0x65,0x20,0x74,0x68,0x65,0x20,0x72,0x65, 0x6E,0x64,0x65,0x72,0x69,0x6E,0x67,0x20,0x72,0x61,0x74,0x65, - 0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x65,0x78,0x61,0x63, - 0x74,0x20,0x74,0x6F,0x20,0x79,0x6F,0x75,0x72,0x4C,0x3E,0x6D, + 0x20,0x69,0x73,0x22,0x3E,0x6E,0x6F,0x74,0x20,0x65,0x78,0x61, + 0x63,0x74,0x20,0x74,0x6F,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D, 0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73,0x20,0x72,0x61,0x74, - 0x65,0x2E,0x20,0x41,0x6C,0x74,0x65,0x72,0x6E,0x61,0x74,0x69, - 0x76,0x65,0x6C,0x79,0x2C,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65, - 0x20,0x22,0x48,0x61,0x72,0x64,0x77,0x61,0x72,0x65,0x20,0x6D, - 0x6F,0x75,0x73,0x65,0x22,0x20,0x69,0x6E,0x20,0x43,0x6F,0x6E, - 0x66,0x69,0x67,0x20,0x2D,0x3E,0x20,0x4C,0x61,0x79,0x6F,0x75, - 0x74,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x33,0x3E,0x40, + 0x65,0x2E,0x06,0x3E,0x40,0x58,0x30,0x32,0x30,0x33,0x3E,0x40, 0x43,0x30,0x30,0x31,0x51,0x3A,0x20,0x57,0x69,0x6C,0x6C,0x20, 0x79,0x6F,0x75,0x20,0x69,0x6D,0x70,0x6C,0x65,0x6D,0x65,0x6E, 0x74,0x20,0x4D,0x49,0x44,0x49,0x20,0x6F,0x75,0x74,0x20,0x66, @@ -2248,97 +2250,90 @@ const uint8_t helpData[27975] = 0x65,0x20,0x74,0x68,0x69,0x73,0x20,0x66,0x6F,0x72,0x20,0x73, 0x65,0x76,0x65,0x72,0x61,0x6C,0x20,0x72,0x65,0x61,0x73,0x6F, 0x6E,0x73,0x2C,0x20,0x74,0x68,0x6F,0x75,0x67,0x68,0x2E,0x2E, - 0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x48,0x3E,0x2D,0x20, - 0x6D,0x61,0x63,0x4F,0x53,0x20,0x2F,0x20,0x4F,0x53,0x20,0x58, - 0x3A,0x20,0x48,0x6F,0x6C,0x64,0x69,0x6E,0x67,0x20,0x64,0x6F, - 0x77,0x6E,0x20,0x61,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x62, - 0x75,0x74,0x74,0x6F,0x6E,0x20,0x77,0x6F,0x6E,0x27,0x74,0x20, - 0x74,0x72,0x61,0x70,0x20,0x74,0x68,0x65,0x20,0x6D,0x6F,0x75, - 0x73,0x65,0x20,0x63,0x75,0x72,0x73,0x6F,0x72,0x4D,0x3E,0x40, - 0x58,0x30,0x32,0x31,0x69,0x6E,0x73,0x69,0x64,0x65,0x20,0x74, - 0x68,0x65,0x20,0x77,0x69,0x6E,0x64,0x6F,0x77,0x2E,0x20,0x54, - 0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x72,0x65,0x6C,0x61,0x74, - 0x65,0x64,0x20,0x74,0x6F,0x20,0x61,0x20,0x6B,0x6C,0x75,0x64, - 0x67,0x65,0x20,0x74,0x68,0x61,0x74,0x20,0x73,0x69,0x6D,0x70, - 0x6C,0x79,0x20,0x64,0x6F,0x65,0x73,0x6E,0x27,0x74,0x20,0x77, - 0x6F,0x72,0x6B,0x4F,0x76,0x65,0x72,0x79,0x20,0x77,0x65,0x6C, - 0x6C,0x20,0x69,0x6E,0x20,0x6D,0x61,0x63,0x4F,0x53,0x2E,0x20, - 0x54,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x6D,0x6F, - 0x76,0x65,0x6D,0x65,0x6E,0x74,0x20,0x77,0x6F,0x75,0x6C,0x64, - 0x20,0x66,0x72,0x65,0x65,0x7A,0x65,0x20,0x66,0x6F,0x72,0x20, - 0x73,0x6F,0x6D,0x65,0x20,0x74,0x69,0x6D,0x65,0x20,0x61,0x66, - 0x74,0x65,0x72,0x20,0x61,0x20,0x6D,0x6F,0x75,0x73,0x65,0x15, - 0x62,0x75,0x74,0x74,0x6F,0x6E,0x20,0x77,0x61,0x73,0x20,0x68, - 0x65,0x6C,0x64,0x20,0x64,0x6F,0x77,0x6E,0x2E,0x06,0x3E,0x40, - 0x58,0x30,0x31,0x30,0x4B,0x3E,0x40,0x43,0x30,0x30,0x32,0x2D, - 0x20,0x54,0x68,0x65,0x20,0x22,0x63,0x6C,0x65,0x61,0x72,0x20, - 0x73,0x61,0x6D,0x70,0x6C,0x65,0x22,0x20,0x73,0x68,0x6F,0x72, - 0x74,0x63,0x75,0x74,0x20,0x28,0x73,0x68,0x69,0x66,0x74,0x20, - 0x2B,0x20,0x6E,0x75,0x6D,0x2D,0x70,0x61,0x64,0x20,0x44,0x65, - 0x6C,0x2F,0x27,0x2C,0x27,0x29,0x20,0x6F,0x6E,0x6C,0x79,0x20, - 0x77,0x6F,0x72,0x6B,0x73,0x20,0x69,0x66,0x37,0x3E,0x40,0x58, - 0x30,0x32,0x31,0x6E,0x75,0x6D,0x20,0x6C,0x6F,0x63,0x6B,0x20, - 0x69,0x73,0x20,0x6F,0x66,0x66,0x2E,0x20,0x54,0x68,0x65,0x72, - 0x65,0x27,0x73,0x20,0x6E,0x6F,0x20,0x77,0x61,0x79,0x20,0x49, - 0x20,0x63,0x61,0x6E,0x20,0x66,0x69,0x78,0x20,0x74,0x68,0x69, - 0x73,0x2E,0x2E,0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x0C, - 0x3E,0x40,0x43,0x30,0x30,0x31,0x56,0x69,0x64,0x65,0x6F,0x3A, - 0x06,0x3E,0x40,0x43,0x30,0x30,0x32,0x4A,0x3E,0x40,0x58,0x30, - 0x31,0x30,0x2D,0x20,0x54,0x68,0x65,0x20,0x73,0x63,0x6F,0x70, - 0x65,0x73,0x20,0x63,0x61,0x6E,0x20,0x6D,0x69,0x6C,0x64,0x6C, - 0x79,0x20,0x66,0x6C,0x69,0x63,0x6B,0x65,0x72,0x20,0x64,0x65, - 0x70,0x65,0x6E,0x64,0x69,0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74, - 0x68,0x65,0x20,0x77,0x61,0x76,0x65,0x66,0x6F,0x72,0x6D,0x20, - 0x61,0x6E,0x64,0x20,0x70,0x69,0x74,0x63,0x68,0x2E,0x4D,0x3E, - 0x40,0x58,0x30,0x32,0x31,0x54,0x68,0x69,0x73,0x20,0x69,0x73, - 0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x74,0x68,0x65, - 0x69,0x72,0x20,0x66,0x72,0x65,0x71,0x75,0x65,0x6E,0x63,0x79, - 0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x63,0x6C,0x6F,0x63, - 0x6B,0x65,0x64,0x20,0x74,0x6F,0x20,0x65,0x78,0x61,0x63,0x74, - 0x6C,0x79,0x20,0x74,0x68,0x65,0x20,0x73,0x61,0x6D,0x65,0x20, - 0x72,0x61,0x74,0x65,0x4D,0x3E,0x61,0x74,0x20,0x77,0x68,0x69, - 0x63,0x68,0x20,0x74,0x68,0x65,0x20,0x73,0x63,0x6F,0x70,0x65, - 0x73,0x20,0x61,0x72,0x65,0x20,0x72,0x65,0x6E,0x64,0x65,0x72, - 0x65,0x64,0x2E,0x20,0x49,0x74,0x27,0x73,0x20,0x63,0x6C,0x6F, - 0x73,0x65,0x2C,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x63,0x61, - 0x75,0x73,0x65,0x73,0x20,0x61,0x20,0x66,0x6C,0x69,0x63,0x6B, - 0x65,0x72,0x20,0x65,0x66,0x66,0x65,0x63,0x74,0x2E,0x01,0x3E, - 0x52,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,0x20,0x4E,0x6F,0x74, - 0x20,0x61,0x20,0x62,0x75,0x67,0x2C,0x20,0x62,0x75,0x74,0x20, - 0x69,0x66,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69, - 0x74,0x6F,0x72,0x27,0x73,0x20,0x72,0x65,0x66,0x72,0x65,0x73, - 0x68,0x20,0x72,0x61,0x74,0x65,0x20,0x69,0x73,0x20,0x6E,0x6F, - 0x74,0x20,0x73,0x65,0x74,0x20,0x74,0x6F,0x20,0x36,0x30,0x48, - 0x7A,0x20,0x28,0x6F,0x72,0x20,0x35,0x39,0x48,0x7A,0x29,0x4F, - 0x3E,0x40,0x58,0x30,0x32,0x31,0x79,0x6F,0x75,0x20,0x6D,0x61, - 0x79,0x20,0x65,0x78,0x70,0x65,0x72,0x69,0x65,0x6E,0x63,0x65, - 0x20,0x76,0x69,0x73,0x75,0x61,0x6C,0x20,0x73,0x74,0x75,0x74, - 0x74,0x65,0x72,0x69,0x6E,0x67,0x20,0x62,0x65,0x63,0x61,0x75, - 0x73,0x65,0x20,0x56,0x53,0x79,0x6E,0x63,0x20,0x77,0x69,0x6C, - 0x6C,0x20,0x6E,0x6F,0x74,0x20,0x62,0x65,0x20,0x75,0x73,0x65, - 0x64,0x20,0x74,0x68,0x65,0x6E,0x2E,0x49,0x49,0x20,0x68,0x69, - 0x67,0x68,0x6C,0x79,0x20,0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65, - 0x6E,0x64,0x20,0x72,0x75,0x6E,0x6E,0x69,0x6E,0x67,0x20,0x79, - 0x6F,0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20, - 0x61,0x74,0x20,0x36,0x30,0x48,0x7A,0x20,0x69,0x66,0x20,0x79, - 0x6F,0x75,0x27,0x72,0x65,0x20,0x61,0x20,0x68,0x61,0x72,0x64, - 0x63,0x6F,0x72,0x65,0x20,0x75,0x73,0x65,0x72,0x10,0x6F,0x66, - 0x20,0x74,0x68,0x69,0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61, - 0x6D,0x2E,0x01,0x3E,0x4C,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D, - 0x20,0x6D,0x61,0x63,0x4F,0x53,0x20,0x2F,0x20,0x4F,0x53,0x20, - 0x58,0x3A,0x20,0x49,0x66,0x20,0x22,0x48,0x61,0x72,0x64,0x77, - 0x61,0x72,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x22,0x20,0x69, - 0x73,0x20,0x65,0x6E,0x61,0x62,0x6C,0x65,0x64,0x2C,0x20,0x74, - 0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x63,0x75,0x72, - 0x73,0x6F,0x72,0x20,0x77,0x6F,0x6E,0x27,0x74,0x4C,0x3E,0x40, - 0x58,0x30,0x32,0x31,0x63,0x68,0x61,0x6E,0x67,0x65,0x20,0x77, - 0x68,0x65,0x6E,0x20,0x74,0x68,0x65,0x20,0x70,0x72,0x6F,0x67, - 0x72,0x61,0x6D,0x20,0x69,0x73,0x20,0x62,0x75,0x73,0x79,0x2E, - 0x20,0x54,0x68,0x65,0x20,0x6D,0x61,0x63,0x4F,0x53,0x20,0x22, - 0x77,0x61,0x69,0x74,0x22,0x20,0x63,0x75,0x72,0x73,0x6F,0x72, - 0x20,0x69,0x73,0x20,0x6D,0x69,0x73,0x73,0x69,0x6E,0x67,0x20, - 0x69,0x6E,0x07,0x53,0x44,0x4C,0x32,0x2E,0x2E,0x2E,0x00,0x03, - 0x45,0x4E,0x44 + 0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x47,0x3E,0x40,0x43, + 0x30,0x30,0x32,0x2D,0x20,0x6D,0x61,0x63,0x4F,0x53,0x20,0x2F, + 0x20,0x4F,0x53,0x20,0x58,0x3A,0x20,0x22,0x48,0x61,0x72,0x64, + 0x77,0x61,0x72,0x65,0x20,0x6D,0x6F,0x64,0x65,0x22,0x20,0x6D, + 0x6F,0x75,0x73,0x65,0x20,0x6C,0x6F,0x6F,0x6B,0x73,0x20,0x62, + 0x6C,0x75,0x72,0x72,0x79,0x20,0x6F,0x6E,0x20,0x72,0x65,0x74, + 0x69,0x6E,0x61,0x20,0x4D,0x61,0x63,0x73,0x06,0x3E,0x40,0x58, + 0x30,0x31,0x30,0x48,0x3E,0x2D,0x20,0x6D,0x61,0x63,0x4F,0x53, + 0x20,0x2F,0x20,0x4F,0x53,0x20,0x58,0x3A,0x20,0x48,0x6F,0x6C, + 0x64,0x69,0x6E,0x67,0x20,0x64,0x6F,0x77,0x6E,0x20,0x61,0x20, + 0x6D,0x6F,0x75,0x73,0x65,0x20,0x62,0x75,0x74,0x74,0x6F,0x6E, + 0x20,0x77,0x6F,0x6E,0x27,0x74,0x20,0x74,0x72,0x61,0x70,0x20, + 0x74,0x68,0x65,0x20,0x6D,0x6F,0x75,0x73,0x65,0x20,0x63,0x75, + 0x72,0x73,0x6F,0x72,0x4D,0x3E,0x40,0x58,0x30,0x32,0x31,0x69, + 0x6E,0x73,0x69,0x64,0x65,0x20,0x74,0x68,0x65,0x20,0x77,0x69, + 0x6E,0x64,0x6F,0x77,0x2E,0x20,0x54,0x68,0x69,0x73,0x20,0x69, + 0x73,0x20,0x72,0x65,0x6C,0x61,0x74,0x65,0x64,0x20,0x74,0x6F, + 0x20,0x61,0x20,0x6B,0x6C,0x75,0x64,0x67,0x65,0x20,0x74,0x68, + 0x61,0x74,0x20,0x73,0x69,0x6D,0x70,0x6C,0x79,0x20,0x64,0x6F, + 0x65,0x73,0x6E,0x27,0x74,0x20,0x77,0x6F,0x72,0x6B,0x4F,0x76, + 0x65,0x72,0x79,0x20,0x77,0x65,0x6C,0x6C,0x20,0x69,0x6E,0x20, + 0x6D,0x61,0x63,0x4F,0x53,0x2E,0x20,0x54,0x68,0x65,0x20,0x6D, + 0x6F,0x75,0x73,0x65,0x20,0x6D,0x6F,0x76,0x65,0x6D,0x65,0x6E, + 0x74,0x20,0x77,0x6F,0x75,0x6C,0x64,0x20,0x66,0x72,0x65,0x65, + 0x7A,0x65,0x20,0x66,0x6F,0x72,0x20,0x73,0x6F,0x6D,0x65,0x20, + 0x74,0x69,0x6D,0x65,0x20,0x61,0x66,0x74,0x65,0x72,0x20,0x61, + 0x20,0x6D,0x6F,0x75,0x73,0x65,0x15,0x62,0x75,0x74,0x74,0x6F, + 0x6E,0x20,0x77,0x61,0x73,0x20,0x68,0x65,0x6C,0x64,0x20,0x64, + 0x6F,0x77,0x6E,0x2E,0x06,0x3E,0x40,0x58,0x30,0x31,0x30,0x4B, + 0x3E,0x40,0x43,0x30,0x30,0x32,0x2D,0x20,0x54,0x68,0x65,0x20, + 0x22,0x63,0x6C,0x65,0x61,0x72,0x20,0x73,0x61,0x6D,0x70,0x6C, + 0x65,0x22,0x20,0x73,0x68,0x6F,0x72,0x74,0x63,0x75,0x74,0x20, + 0x28,0x73,0x68,0x69,0x66,0x74,0x20,0x2B,0x20,0x6E,0x75,0x6D, + 0x2D,0x70,0x61,0x64,0x20,0x44,0x65,0x6C,0x2F,0x27,0x2C,0x27, + 0x29,0x20,0x6F,0x6E,0x6C,0x79,0x20,0x77,0x6F,0x72,0x6B,0x73, + 0x20,0x69,0x66,0x37,0x3E,0x40,0x58,0x30,0x32,0x31,0x6E,0x75, + 0x6D,0x20,0x6C,0x6F,0x63,0x6B,0x20,0x69,0x73,0x20,0x6F,0x66, + 0x66,0x2E,0x20,0x54,0x68,0x65,0x72,0x65,0x27,0x73,0x20,0x6E, + 0x6F,0x20,0x77,0x61,0x79,0x20,0x49,0x20,0x63,0x61,0x6E,0x20, + 0x66,0x69,0x78,0x20,0x74,0x68,0x69,0x73,0x2E,0x2E,0x2E,0x06, + 0x3E,0x40,0x58,0x30,0x31,0x30,0x0C,0x3E,0x40,0x43,0x30,0x30, + 0x31,0x56,0x69,0x64,0x65,0x6F,0x3A,0x06,0x3E,0x40,0x43,0x30, + 0x30,0x32,0x4A,0x3E,0x40,0x58,0x30,0x31,0x30,0x2D,0x20,0x54, + 0x68,0x65,0x20,0x73,0x63,0x6F,0x70,0x65,0x73,0x20,0x63,0x61, + 0x6E,0x20,0x6D,0x69,0x6C,0x64,0x6C,0x79,0x20,0x66,0x6C,0x69, + 0x63,0x6B,0x65,0x72,0x20,0x64,0x65,0x70,0x65,0x6E,0x64,0x69, + 0x6E,0x67,0x20,0x6F,0x6E,0x20,0x74,0x68,0x65,0x20,0x77,0x61, + 0x76,0x65,0x66,0x6F,0x72,0x6D,0x20,0x61,0x6E,0x64,0x20,0x70, + 0x69,0x74,0x63,0x68,0x2E,0x4D,0x3E,0x40,0x58,0x30,0x32,0x31, + 0x54,0x68,0x69,0x73,0x20,0x69,0x73,0x20,0x62,0x65,0x63,0x61, + 0x75,0x73,0x65,0x20,0x74,0x68,0x65,0x69,0x72,0x20,0x66,0x72, + 0x65,0x71,0x75,0x65,0x6E,0x63,0x79,0x20,0x69,0x73,0x20,0x6E, + 0x6F,0x74,0x20,0x63,0x6C,0x6F,0x63,0x6B,0x65,0x64,0x20,0x74, + 0x6F,0x20,0x65,0x78,0x61,0x63,0x74,0x6C,0x79,0x20,0x74,0x68, + 0x65,0x20,0x73,0x61,0x6D,0x65,0x20,0x72,0x61,0x74,0x65,0x4D, + 0x3E,0x61,0x74,0x20,0x77,0x68,0x69,0x63,0x68,0x20,0x74,0x68, + 0x65,0x20,0x73,0x63,0x6F,0x70,0x65,0x73,0x20,0x61,0x72,0x65, + 0x20,0x72,0x65,0x6E,0x64,0x65,0x72,0x65,0x64,0x2E,0x20,0x49, + 0x74,0x27,0x73,0x20,0x63,0x6C,0x6F,0x73,0x65,0x2C,0x20,0x77, + 0x68,0x69,0x63,0x68,0x20,0x63,0x61,0x75,0x73,0x65,0x73,0x20, + 0x61,0x20,0x66,0x6C,0x69,0x63,0x6B,0x65,0x72,0x20,0x65,0x66, + 0x66,0x65,0x63,0x74,0x2E,0x01,0x3E,0x52,0x3E,0x40,0x58,0x30, + 0x31,0x30,0x2D,0x20,0x4E,0x6F,0x74,0x20,0x61,0x20,0x62,0x75, + 0x67,0x2C,0x20,0x62,0x75,0x74,0x20,0x69,0x66,0x20,0x79,0x6F, + 0x75,0x72,0x20,0x6D,0x6F,0x6E,0x69,0x74,0x6F,0x72,0x27,0x73, + 0x20,0x72,0x65,0x66,0x72,0x65,0x73,0x68,0x20,0x72,0x61,0x74, + 0x65,0x20,0x69,0x73,0x20,0x6E,0x6F,0x74,0x20,0x73,0x65,0x74, + 0x20,0x74,0x6F,0x20,0x36,0x30,0x48,0x7A,0x20,0x28,0x6F,0x72, + 0x20,0x35,0x39,0x48,0x7A,0x29,0x4F,0x3E,0x40,0x58,0x30,0x32, + 0x31,0x79,0x6F,0x75,0x20,0x6D,0x61,0x79,0x20,0x65,0x78,0x70, + 0x65,0x72,0x69,0x65,0x6E,0x63,0x65,0x20,0x76,0x69,0x73,0x75, + 0x61,0x6C,0x20,0x73,0x74,0x75,0x74,0x74,0x65,0x72,0x69,0x6E, + 0x67,0x20,0x62,0x65,0x63,0x61,0x75,0x73,0x65,0x20,0x56,0x53, + 0x79,0x6E,0x63,0x20,0x77,0x69,0x6C,0x6C,0x20,0x6E,0x6F,0x74, + 0x20,0x62,0x65,0x20,0x75,0x73,0x65,0x64,0x20,0x74,0x68,0x65, + 0x6E,0x2E,0x49,0x49,0x20,0x68,0x69,0x67,0x68,0x6C,0x79,0x20, + 0x72,0x65,0x63,0x6F,0x6D,0x6D,0x65,0x6E,0x64,0x20,0x72,0x75, + 0x6E,0x6E,0x69,0x6E,0x67,0x20,0x79,0x6F,0x75,0x72,0x20,0x6D, + 0x6F,0x6E,0x69,0x74,0x6F,0x72,0x20,0x61,0x74,0x20,0x36,0x30, + 0x48,0x7A,0x20,0x69,0x66,0x20,0x79,0x6F,0x75,0x27,0x72,0x65, + 0x20,0x61,0x20,0x68,0x61,0x72,0x64,0x63,0x6F,0x72,0x65,0x20, + 0x75,0x73,0x65,0x72,0x10,0x6F,0x66,0x20,0x74,0x68,0x69,0x73, + 0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x2E,0x00,0x03,0x45, + 0x4E,0x44 }; #endif