diff options
Diffstat (limited to 'src/libui_sdl')
-rw-r--r-- | src/libui_sdl/DlgAudioSettings.cpp | 183 | ||||
-rw-r--r-- | src/libui_sdl/DlgAudioSettings.h | 29 | ||||
-rw-r--r-- | src/libui_sdl/DlgEmuSettings.cpp | 13 | ||||
-rw-r--r-- | src/libui_sdl/DlgEmuSettings.h | 2 | ||||
-rw-r--r-- | src/libui_sdl/DlgInputConfig.cpp | 256 | ||||
-rw-r--r-- | src/libui_sdl/DlgInputConfig.h | 4 | ||||
-rw-r--r-- | src/libui_sdl/Platform.cpp | 2 | ||||
-rw-r--r-- | src/libui_sdl/libui/ui.h | 2 | ||||
-rw-r--r-- | src/libui_sdl/libui/unix/area.c | 30 | ||||
-rw-r--r-- | src/libui_sdl/libui/unix/menu.c | 17 | ||||
-rw-r--r-- | src/libui_sdl/libui/unix/stddialogs.c | 3 | ||||
-rw-r--r-- | src/libui_sdl/libui/unix/window.c | 7 | ||||
-rw-r--r-- | src/libui_sdl/libui/windows/button.cpp | 19 | ||||
-rw-r--r-- | src/libui_sdl/main.cpp | 869 |
14 files changed, 1223 insertions, 213 deletions
diff --git a/src/libui_sdl/DlgAudioSettings.cpp b/src/libui_sdl/DlgAudioSettings.cpp new file mode 100644 index 0000000..b02b474 --- /dev/null +++ b/src/libui_sdl/DlgAudioSettings.cpp @@ -0,0 +1,183 @@ +/* + Copyright 2016-2019 StapleButter + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "libui/ui.h" + +#include "../types.h" +#include "../Config.h" + +#include "DlgAudioSettings.h" + + +void MicLoadWav(char* path); + + +namespace DlgAudioSettings +{ + +bool opened; +uiWindow* win; + +uiSlider* slVolume; +uiRadioButtons* rbMicInputType; +uiEntry* txMicWavPath; + +int oldvolume; + + +int OnCloseWindow(uiWindow* window, void* blarg) +{ + opened = false; + return 1; +} + +void OnVolumeChanged(uiSlider* slider, void* blarg) +{ + Config::AudioVolume = uiSliderValue(slVolume); +} + +void OnMicWavBrowse(uiButton* btn, void* blarg) +{ + char* file = uiOpenFile(win, "WAV file (*.wav)|*.wav|Any file|*.*", NULL); + if (!file) + { + return; + } + + uiEntrySetText(txMicWavPath, file); + uiFreeText(file); +} + +void OnCancel(uiButton* btn, void* blarg) +{ + Config::AudioVolume = oldvolume; + + uiControlDestroy(uiControl(win)); + opened = false; +} + +void OnOk(uiButton* btn, void* blarg) +{ + Config::AudioVolume = uiSliderValue(slVolume); + Config::MicInputType = uiRadioButtonsSelected(rbMicInputType); + + char* wavpath = uiEntryText(txMicWavPath); + strncpy(Config::MicWavPath, wavpath, 511); + uiFreeText(wavpath); + + Config::Save(); + + if (Config::MicInputType == 3) MicLoadWav(Config::MicWavPath); + + uiControlDestroy(uiControl(win)); + opened = false; +} + +void Open() +{ + if (opened) + { + uiControlSetFocus(uiControl(win)); + return; + } + + opened = true; + win = uiNewWindow("Audio settings - melonDS", 400, 100, 0, 0); + uiWindowSetMargined(win, 1); + uiWindowOnClosing(win, OnCloseWindow, NULL); + + uiBox* top = uiNewVerticalBox(); + uiWindowSetChild(win, uiControl(top)); + uiBoxSetPadded(top, 1); + + { + uiGroup* grp = uiNewGroup("Audio output"); + uiBoxAppend(top, uiControl(grp), 0); + uiGroupSetMargined(grp, 1); + + uiBox* in_ctrl = uiNewVerticalBox(); + uiGroupSetChild(grp, uiControl(in_ctrl)); + + uiLabel* label_vol = uiNewLabel("Volume:"); + uiBoxAppend(in_ctrl, uiControl(label_vol), 0); + + slVolume = uiNewSlider(0, 256); + uiSliderOnChanged(slVolume, OnVolumeChanged, NULL); + uiBoxAppend(in_ctrl, uiControl(slVolume), 0); + } + + { + uiGroup* grp = uiNewGroup("Microphone input"); + uiBoxAppend(top, uiControl(grp), 0); + uiGroupSetMargined(grp, 1); + + uiBox* in_ctrl = uiNewVerticalBox(); + uiGroupSetChild(grp, uiControl(in_ctrl)); + + rbMicInputType = uiNewRadioButtons(); + uiRadioButtonsAppend(rbMicInputType, "None"); + uiRadioButtonsAppend(rbMicInputType, "Microphone"); + uiRadioButtonsAppend(rbMicInputType, "White noise"); + uiRadioButtonsAppend(rbMicInputType, "WAV file:"); + uiBoxAppend(in_ctrl, uiControl(rbMicInputType), 0); + + uiBox* path_box = uiNewHorizontalBox(); + uiBoxAppend(in_ctrl, uiControl(path_box), 0); + + txMicWavPath = uiNewEntry(); + uiBoxAppend(path_box, uiControl(txMicWavPath), 1); + + uiButton* path_browse = uiNewButton("..."); + uiButtonOnClicked(path_browse, OnMicWavBrowse, NULL); + uiBoxAppend(path_box, uiControl(path_browse), 0); + } + + { + uiBox* in_ctrl = uiNewHorizontalBox(); + uiBoxSetPadded(in_ctrl, 1); + uiBoxAppend(top, uiControl(in_ctrl), 0); + + uiLabel* dummy = uiNewLabel(""); + uiBoxAppend(in_ctrl, uiControl(dummy), 1); + + uiButton* btncancel = uiNewButton("Cancel"); + uiButtonOnClicked(btncancel, OnCancel, NULL); + uiBoxAppend(in_ctrl, uiControl(btncancel), 0); + + uiButton* btnok = uiNewButton("Ok"); + uiButtonOnClicked(btnok, OnOk, NULL); + uiBoxAppend(in_ctrl, uiControl(btnok), 0); + } + + if (Config::AudioVolume < 0) Config::AudioVolume = 0; + else if (Config::AudioVolume > 256) Config::AudioVolume = 256; + + oldvolume = Config::AudioVolume; + + uiSliderSetValue(slVolume, Config::AudioVolume); + uiRadioButtonsSetSelected(rbMicInputType, Config::MicInputType); + uiEntrySetText(txMicWavPath, Config::MicWavPath); + + uiControlShow(uiControl(win)); +} + +} diff --git a/src/libui_sdl/DlgAudioSettings.h b/src/libui_sdl/DlgAudioSettings.h new file mode 100644 index 0000000..2333967 --- /dev/null +++ b/src/libui_sdl/DlgAudioSettings.h @@ -0,0 +1,29 @@ +/* + Copyright 2016-2019 StapleButter + + This file is part of melonDS. + + melonDS is free software: you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) + any later version. + + melonDS is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with melonDS. If not, see http://www.gnu.org/licenses/. +*/ + +#ifndef DLGAUDIOSETTINGS_H +#define DLGAUDIOSETTINGS_H + +namespace DlgAudioSettings +{ + +void Open(); + +} + +#endif // DLGAUDIOSETTINGS_H diff --git a/src/libui_sdl/DlgEmuSettings.cpp b/src/libui_sdl/DlgEmuSettings.cpp index 8cb2ccc..42c95b8 100644 --- a/src/libui_sdl/DlgEmuSettings.cpp +++ b/src/libui_sdl/DlgEmuSettings.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -33,6 +33,7 @@ void ApplyNewSettings(); namespace DlgEmuSettings { +bool opened; uiWindow* win; uiCheckbox* cbDirectBoot; @@ -42,12 +43,14 @@ uiCheckbox* cbBindAnyAddr; int OnCloseWindow(uiWindow* window, void* blarg) { + opened = false; return 1; } void OnCancel(uiButton* btn, void* blarg) { uiControlDestroy(uiControl(win)); + opened = false; } void OnOk(uiButton* btn, void* blarg) @@ -59,12 +62,20 @@ void OnOk(uiButton* btn, void* blarg) Config::Save(); uiControlDestroy(uiControl(win)); + opened = false; ApplyNewSettings(); } void Open() { + if (opened) + { + uiControlSetFocus(uiControl(win)); + return; + } + + opened = true; win = uiNewWindow("Emu settings - melonDS", 300, 200, 0, 0); uiWindowSetMargined(win, 1); uiWindowOnClosing(win, OnCloseWindow, NULL); diff --git a/src/libui_sdl/DlgEmuSettings.h b/src/libui_sdl/DlgEmuSettings.h index f26ae14..f8da148 100644 --- a/src/libui_sdl/DlgEmuSettings.h +++ b/src/libui_sdl/DlgEmuSettings.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/libui_sdl/DlgInputConfig.cpp b/src/libui_sdl/DlgInputConfig.cpp index 5a98a91..287d7e3 100644 --- a/src/libui_sdl/DlgInputConfig.cpp +++ b/src/libui_sdl/DlgInputConfig.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -35,19 +35,33 @@ extern SDL_Joystick* Joystick; namespace DlgInputConfig { -uiWindow* win; +typedef struct +{ + int type; + uiWindow* win; + + uiAreaHandler areahandler; + uiArea* keypresscatcher; + + int numkeys; + int keymap[32]; + int joymap[32]; + + int pollid; + uiButton* pollbtn; -uiAreaHandler areahandler; -uiArea* keypresscatcher; +} InputDlgData; -int keyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 3, 2}; -char keylabels[12][8] = {"A:", "B:", "Select:", "Start:", "Right:", "Left:", "Up:", "Down:", "R:", "L:", "X:", "Y:"}; -int keymap[12]; -int joymap[12]; +int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 3, 2}; +char dskeylabels[12][8] = {"A:", "B:", "Select:", "Start:", "Right:", "Left:", "Up:", "Down:", "R:", "L:", "X:", "Y:"}; -int pollid; -uiButton* pollbtn; +int identity[32] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + +char hotkeylabels[HK_MAX][32] = {"Close/open lid:", "Microphone:"}; + +int openedmask; +InputDlgData inputdlg[2]; void JoyMappingName(int id, char* str) @@ -97,7 +111,9 @@ void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height) int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) { - if (pollid < 0) + InputDlgData* dlg = (InputDlgData*)uiControl(area)->UserData; + + if (dlg->pollid < 0) return 0; if (evt->Scancode == 0x38) // ALT @@ -105,27 +121,27 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) if (evt->Modifiers == 0x2) // ALT+key return 0; - if (pollid > 12) + if (dlg->pollid > 12) { - if (pollid < 0x100) return 0; - int id = pollid & 0xFF; + if (dlg->pollid < 0x100) return 0; + int id = dlg->pollid & 0xFF; if (id > 12) return 0; if (evt->Scancode != 0x1) // ESC { if (evt->Scancode == 0xE) // backspace - joymap[id] = -1; + dlg->joymap[id] = -1; else return 1; } char keyname[16]; - JoyMappingName(joymap[id], keyname); - uiButtonSetText(pollbtn, keyname); - uiControlEnable(uiControl(pollbtn)); + JoyMappingName(dlg->joymap[id], keyname); + uiButtonSetText(dlg->pollbtn, keyname); + uiControlEnable(uiControl(dlg->pollbtn)); - pollid = -1; + dlg->pollid = -1; - uiControlSetFocus(uiControl(pollbtn)); + uiControlSetFocus(uiControl(dlg->pollbtn)); return 1; } @@ -134,16 +150,16 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) { // set key. if (evt->Scancode != 0x1) // ESC - keymap[pollid] = evt->Scancode; + dlg->keymap[dlg->pollid] = evt->Scancode; - char* keyname = uiKeyName(keymap[pollid]); - uiButtonSetText(pollbtn, keyname); - uiControlEnable(uiControl(pollbtn)); + char* keyname = uiKeyName(dlg->keymap[dlg->pollid]); + uiButtonSetText(dlg->pollbtn, keyname); + uiControlEnable(uiControl(dlg->pollbtn)); uiFreeText(keyname); - pollid = -1; + dlg->pollid = -1; - uiControlSetFocus(uiControl(pollbtn)); + uiControlSetFocus(uiControl(dlg->pollbtn)); } return 1; @@ -151,8 +167,10 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) Uint32 JoyPoll(Uint32 interval, void* param) { - if (pollid < 0x100) return 0; - int id = pollid & 0xFF; + InputDlgData* dlg = (InputDlgData*)param; + + if (dlg->pollid < 0x100) return 0; + int id = dlg->pollid & 0xFF; if (id > 12) return 0; SDL_JoystickUpdate(); @@ -165,16 +183,16 @@ Uint32 JoyPoll(Uint32 interval, void* param) { if (SDL_JoystickGetButton(joy, i)) { - joymap[id] = i; + dlg->joymap[id] = i; char keyname[16]; - JoyMappingName(joymap[id], keyname); - uiButtonSetText(pollbtn, keyname); - uiControlEnable(uiControl(pollbtn)); + JoyMappingName(dlg->joymap[id], keyname); + uiButtonSetText(dlg->pollbtn, keyname); + uiControlEnable(uiControl(dlg->pollbtn)); - pollid = -1; + dlg->pollid = -1; - uiControlSetFocus(uiControl(pollbtn)); + uiControlSetFocus(uiControl(dlg->pollbtn)); return 0; } } @@ -187,16 +205,16 @@ Uint32 JoyPoll(Uint32 interval, void* param) else if (blackhat & 0x4) blackhat = 0x4; else blackhat = 0x8; - joymap[id] = 0x100 | blackhat; + dlg->joymap[id] = 0x100 | blackhat; char keyname[16]; - JoyMappingName(joymap[id], keyname); - uiButtonSetText(pollbtn, keyname); - uiControlEnable(uiControl(pollbtn)); + JoyMappingName(dlg->joymap[id], keyname); + uiButtonSetText(dlg->pollbtn, keyname); + uiControlEnable(uiControl(dlg->pollbtn)); - pollid = -1; + dlg->pollid = -1; - uiControlSetFocus(uiControl(pollbtn)); + uiControlSetFocus(uiControl(dlg->pollbtn)); return 0; } @@ -206,98 +224,147 @@ Uint32 JoyPoll(Uint32 interval, void* param) void OnKeyStartConfig(uiButton* btn, void* data) { - if (pollid != -1) + InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData; + + if (dlg->pollid != -1) { // TODO: handle this better? - if (pollid <= 12) - uiControlSetFocus(uiControl(keypresscatcher)); + if (dlg->pollid <= 12) + uiControlSetFocus(uiControl(dlg->keypresscatcher)); return; } int id = *(int*)data; - pollid = id; - pollbtn = btn; + dlg->pollid = id; + dlg->pollbtn = btn; uiButtonSetText(btn, "[press key]"); uiControlDisable(uiControl(btn)); - uiControlSetFocus(uiControl(keypresscatcher)); + uiControlSetFocus(uiControl(dlg->keypresscatcher)); } void OnJoyStartConfig(uiButton* btn, void* data) { - if (pollid != -1) + InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData; + + if (dlg->pollid != -1) { // TODO: handle this better? - if (pollid <= 12) - uiControlSetFocus(uiControl(keypresscatcher)); + if (dlg->pollid <= 12) + uiControlSetFocus(uiControl(dlg->keypresscatcher)); return; } int id = *(int*)data; - pollid = id | 0x100; - pollbtn = btn; + dlg->pollid = id | 0x100; + dlg->pollbtn = btn; uiButtonSetText(btn, "[press button]"); uiControlDisable(uiControl(btn)); - SDL_AddTimer(100, JoyPoll, NULL); - uiControlSetFocus(uiControl(keypresscatcher)); + SDL_AddTimer(100, JoyPoll, dlg); + uiControlSetFocus(uiControl(dlg->keypresscatcher)); } int OnCloseWindow(uiWindow* window, void* blarg) { + InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData); + openedmask &= ~(1 << dlg->type); return 1; } void OnGetFocus(uiWindow* window, void* blarg) { - if (pollid >= 0) - uiControlSetFocus(uiControl(keypresscatcher)); + InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData); + + if (dlg->pollid >= 0) + uiControlSetFocus(uiControl(dlg->keypresscatcher)); } void OnLoseFocus(uiWindow* window, void* blarg) { } -void OnCancel(uiButton* btn, void* blarg) +void OnCancel(uiButton* btn, void* data) { - uiControlDestroy(uiControl(win)); + InputDlgData* dlg = (InputDlgData*)data; + + uiControlDestroy(uiControl(dlg->win)); + openedmask &= ~(1 << dlg->type); } -void OnOk(uiButton* btn, void* blarg) +void OnOk(uiButton* btn, void* data) { - memcpy(Config::KeyMapping, keymap, sizeof(int)*12); - memcpy(Config::JoyMapping, joymap, sizeof(int)*12); + InputDlgData* dlg = (InputDlgData*)data; + + if (dlg->type == 0) + { + memcpy(Config::KeyMapping, dlg->keymap, sizeof(int)*12); + memcpy(Config::JoyMapping, dlg->joymap, sizeof(int)*12); + } + else if (dlg->type == 1) + { + memcpy(Config::HKKeyMapping, dlg->keymap, sizeof(int)*HK_MAX); + memcpy(Config::HKJoyMapping, dlg->joymap, sizeof(int)*HK_MAX); + } Config::Save(); - uiControlDestroy(uiControl(win)); + uiControlDestroy(uiControl(dlg->win)); + openedmask &= ~(1 << dlg->type); } -void Open() +void Open(int type) { - pollid = -1; + InputDlgData* dlg = &inputdlg[type]; + + int mask = 1 << type; + if (openedmask & mask) + { + uiControlSetFocus(uiControl(dlg->win)); + return; + } - memcpy(keymap, Config::KeyMapping, sizeof(int)*12); - memcpy(joymap, Config::JoyMapping, sizeof(int)*12); + openedmask |= mask; - win = uiNewWindow("Input config - melonDS", 600, 400, 0, 0); - uiWindowSetMargined(win, 1); - uiWindowOnClosing(win, OnCloseWindow, NULL); - uiWindowOnGetFocus(win, OnGetFocus, NULL); - uiWindowOnLoseFocus(win, OnLoseFocus, NULL); + dlg->type = type; + dlg->pollid = -1; - areahandler.Draw = OnAreaDraw; - areahandler.MouseEvent = OnAreaMouseEvent; - areahandler.MouseCrossed = OnAreaMouseCrossed; - areahandler.DragBroken = OnAreaDragBroken; - areahandler.KeyEvent = OnAreaKeyEvent; - areahandler.Resize = OnAreaResize; + if (type == 0) + { + dlg->numkeys = 12; + memcpy(dlg->keymap, Config::KeyMapping, sizeof(int)*12); + memcpy(dlg->joymap, Config::JoyMapping, sizeof(int)*12); + + dlg->win = uiNewWindow("Input config - melonDS", 600, 100, 0, 0); + } + else if (type == 1) + { + dlg->numkeys = HK_MAX; + memcpy(dlg->keymap, Config::HKKeyMapping, sizeof(int)*HK_MAX); + memcpy(dlg->joymap, Config::HKJoyMapping, sizeof(int)*HK_MAX); + + dlg->win = uiNewWindow("Hotkey config - melonDS", 600, 100, 0, 0); + } + + uiControl(dlg->win)->UserData = dlg; + + uiWindowSetMargined(dlg->win, 1); + uiWindowOnClosing(dlg->win, OnCloseWindow, NULL); + uiWindowOnGetFocus(dlg->win, OnGetFocus, NULL); + uiWindowOnLoseFocus(dlg->win, OnLoseFocus, NULL); + + dlg->areahandler.Draw = OnAreaDraw; + dlg->areahandler.MouseEvent = OnAreaMouseEvent; + dlg->areahandler.MouseCrossed = OnAreaMouseCrossed; + dlg->areahandler.DragBroken = OnAreaDragBroken; + dlg->areahandler.KeyEvent = OnAreaKeyEvent; + dlg->areahandler.Resize = OnAreaResize; uiBox* top = uiNewVerticalBox(); - uiWindowSetChild(win, uiControl(top)); + uiWindowSetChild(dlg->win, uiControl(top)); uiControlHide(uiControl(top)); { @@ -309,22 +376,23 @@ void Open() uiBoxAppend(in_ctrl, uiControl(g_key), 1); uiGrid* b_key = uiNewGrid(); uiGroupSetChild(g_key, uiControl(b_key)); - + const int width = 120; - for (int i = 0; i < 12; i++) + for (int i = 0; i < dlg->numkeys; i++) { - int j = keyorder[i]; + int j = (type==0) ? dskeyorder[i] : i; - uiLabel* label = uiNewLabel(keylabels[j]); + uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]); uiGridAppend(b_key, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter); uiControlSetMinSize(uiControl(label), width, 1); - char* keyname = uiKeyName(Config::KeyMapping[j]); + char* keyname = uiKeyName(dlg->keymap[j]); uiButton* btn = uiNewButton(keyname); + uiControl(btn)->UserData = dlg; uiGridAppend(b_key, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter); - uiButtonOnClicked(btn, OnKeyStartConfig, &keyorder[i]); + uiButtonOnClicked(btn, OnKeyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]); uiControlSetMinSize(uiControl(btn), width, 1); uiFreeText(keyname); @@ -335,20 +403,21 @@ void Open() uiGrid* b_joy = uiNewGrid(); uiGroupSetChild(g_joy, uiControl(b_joy)); - for (int i = 0; i < 12; i++) + for (int i = 0; i < dlg->numkeys; i++) { - int j = keyorder[i]; + int j = (type==0) ? dskeyorder[i] : i; - uiLabel* label = uiNewLabel(keylabels[j]); + uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]); uiGridAppend(b_joy, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter); uiControlSetMinSize(uiControl(label), width, 1); char keyname[16]; - JoyMappingName(Config::JoyMapping[j], keyname); + JoyMappingName(dlg->joymap[j], keyname); uiButton* btn = uiNewButton(keyname); + uiControl(btn)->UserData = dlg; uiGridAppend(b_joy, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter); - uiButtonOnClicked(btn, OnJoyStartConfig, &keyorder[i]); + uiButtonOnClicked(btn, OnJoyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]); uiControlSetMinSize(uiControl(btn), width, 1); } } @@ -364,21 +433,22 @@ void Open() uiLabel* dummy = uiNewLabel(""); uiBoxAppend(in_ctrl, uiControl(dummy), 1); - keypresscatcher = uiNewArea(&areahandler); - uiBoxAppend(in_ctrl, uiControl(keypresscatcher), 0); + dlg->keypresscatcher = uiNewArea(&dlg->areahandler); + uiControl(dlg->keypresscatcher)->UserData = dlg; + uiBoxAppend(in_ctrl, uiControl(dlg->keypresscatcher), 0); uiButton* btncancel = uiNewButton("Cancel"); - uiButtonOnClicked(btncancel, OnCancel, NULL); + uiButtonOnClicked(btncancel, OnCancel, dlg); uiBoxAppend(in_ctrl, uiControl(btncancel), 0); uiButton* btnok = uiNewButton("Ok"); - uiButtonOnClicked(btnok, OnOk, NULL); + uiButtonOnClicked(btnok, OnOk, dlg); uiBoxAppend(in_ctrl, uiControl(btnok), 0); } uiControlShow(uiControl(top)); - uiControlShow(uiControl(win)); + uiControlShow(uiControl(dlg->win)); } } diff --git a/src/libui_sdl/DlgInputConfig.h b/src/libui_sdl/DlgInputConfig.h index 33529c2..2b0b6cb 100644 --- a/src/libui_sdl/DlgInputConfig.h +++ b/src/libui_sdl/DlgInputConfig.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -22,7 +22,7 @@ namespace DlgInputConfig { -void Open(); +void Open(int type); } diff --git a/src/libui_sdl/Platform.cpp b/src/libui_sdl/Platform.cpp index 1702685..733114e 100644 --- a/src/libui_sdl/Platform.cpp +++ b/src/libui_sdl/Platform.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. diff --git a/src/libui_sdl/libui/ui.h b/src/libui_sdl/libui/ui.h index e5a866d..1af8f59 100644 --- a/src/libui_sdl/libui/ui.h +++ b/src/libui_sdl/libui/ui.h @@ -76,6 +76,8 @@ struct uiControl { void (*SetMinSize)(uiControl*, int, int); int MinWidth, MinHeight; + + void* UserData; }; // TOOD add argument names to all arguments #define uiControl(this) ((uiControl *) (this)) diff --git a/src/libui_sdl/libui/unix/area.c b/src/libui_sdl/libui/unix/area.c index ea31676..2da9bab 100644 --- a/src/libui_sdl/libui/unix/area.c +++ b/src/libui_sdl/libui/unix/area.c @@ -77,6 +77,10 @@ static void areaWidget_init(areaWidget *aw) static void areaWidget_dispose(GObject *obj) { + // remove any draw order that might still be pending + areaWidget *aw = areaWidget(obj); + while (g_idle_remove_by_data(aw->a)); + G_OBJECT_CLASS(areaWidget_parent_class)->dispose(obj); } @@ -519,16 +523,26 @@ char* uiKeyName(int scancode) { scancode = scancode_normal2unix(scancode); + char* ret; guint* keyvals; int num; - GdkKeymap* keymap = gdk_keymap_get_default(); - gdk_keymap_get_entries_for_keycode(keymap, scancode, NULL, &keyvals, &num); - - // TODO: pick smarter?? - int keyval = keyvals[0]; - - g_free(keyvals); + GdkKeymap* keymap = gdk_keymap_get_for_display(gdk_display_get_default()); + if (gdk_keymap_get_entries_for_keycode(keymap, scancode, NULL, &keyvals, &num)) + { + // TODO: pick smarter?? + int keyval = keyvals[0]; + + g_free(keyvals); + + ret = gdk_keyval_name(keyval); + } + else + { + char tmp[16]; + sprintf(tmp, "#%03X", scancode); + ret = tmp; + } - return uiUnixStrdupText(gdk_keyval_name(keyval)); + return uiUnixStrdupText(ret); } enum { diff --git a/src/libui_sdl/libui/unix/menu.c b/src/libui_sdl/libui/unix/menu.c index d6aa398..d641426 100644 --- a/src/libui_sdl/libui/unix/menu.c +++ b/src/libui_sdl/libui/unix/menu.c @@ -305,13 +305,13 @@ static void appendMenuItem(GtkMenuShell *submenu, uiMenuItem *item, uiWindow *w) { int j; uiMenu* m; - GtkWidget *submenu; + GtkWidget *c_submenu; m = item->popupchild; - submenu = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); + c_submenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), c_submenu); for (j = 0; j < m->items->len; j++) - appendMenuItem(GTK_MENU_SHELL(submenu), g_array_index(m->items, uiMenuItem *, j), w); + appendMenuItem(GTK_MENU_SHELL(c_submenu), g_array_index(m->items, uiMenuItem *, j), w); } } @@ -347,6 +347,7 @@ GtkWidget *makeMenubar(uiWindow *w) struct freeMenuItemData { GArray *items; guint i; + guint* parent_i; }; static void freeMenu(GtkWidget *widget, gpointer data); @@ -359,7 +360,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data) item = g_array_index(fmi->items, uiMenuItem *, fmi->i); if (item->popupchild != NULL) - freeMenu(widget, &item->popupchild->id); + freeMenu(widget, fmi->parent_i);//&item->popupchild->id); w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget); if (g_hash_table_remove(item->windows, widget) == FALSE) implbug("GtkMenuItem %p not in menu item's item/window map", widget); @@ -374,14 +375,16 @@ static void freeMenu(GtkWidget *widget, gpointer data) GtkMenuItem *item; GtkWidget *submenu; struct freeMenuItemData fmi; - + m = g_array_index(menus, uiMenu *, *i); item = GTK_MENU_ITEM(widget); submenu = gtk_menu_item_get_submenu(item); fmi.items = m->items; fmi.i = 0; - gtk_container_foreach(GTK_CONTAINER(submenu), freeMenuItem, &fmi); (*i)++; + fmi.parent_i = i; + gtk_container_foreach(GTK_CONTAINER(submenu), freeMenuItem, &fmi); + //(*i)++; } void freeMenubar(GtkWidget *mb) diff --git a/src/libui_sdl/libui/unix/stddialogs.c b/src/libui_sdl/libui/unix/stddialogs.c index b793d06..d2b89b9 100644 --- a/src/libui_sdl/libui/unix/stddialogs.c +++ b/src/libui_sdl/libui/unix/stddialogs.c @@ -70,6 +70,9 @@ static char *filedialog(GtkWindow *parent, GtkFileChooserAction mode, const gcha gtk_file_chooser_set_show_hidden(fc, TRUE); gtk_file_chooser_set_do_overwrite_confirmation(fc, TRUE); gtk_file_chooser_set_create_folders(fc, TRUE); + if (initpath && strlen(initpath)>0) + gtk_file_chooser_set_current_folder(fc, initpath); + response = gtk_dialog_run(GTK_DIALOG(fcd)); if (response != GTK_RESPONSE_ACCEPT) { gtk_widget_destroy(fcd); diff --git a/src/libui_sdl/libui/unix/window.c b/src/libui_sdl/libui/unix/window.c index a3dde76..04630bc 100644 --- a/src/libui_sdl/libui/unix/window.c +++ b/src/libui_sdl/libui/unix/window.c @@ -145,11 +145,16 @@ static void uiWindowShow(uiControl *c) gtk_window_resize(w->window, width, height); } +static void uiWindowSetFocus(uiControl* c) +{ + gtk_window_present(GTK_WINDOW(uiWindow(c)->widget)); +} + uiUnixControlDefaultHide(uiWindow) uiUnixControlDefaultEnabled(uiWindow) uiUnixControlDefaultEnable(uiWindow) uiUnixControlDefaultDisable(uiWindow) -uiUnixControlDefaultSetFocus(uiWindow) +//uiUnixControlDefaultSetFocus(uiWindow) uiUnixControlDefaultSetMinSize(uiWindow) // TODO? uiUnixControlDefaultSetContainer(uiWindow) diff --git a/src/libui_sdl/libui/windows/button.cpp b/src/libui_sdl/libui/windows/button.cpp index b83d6ec..aa34bfc 100644 --- a/src/libui_sdl/libui/windows/button.cpp +++ b/src/libui_sdl/libui/windows/button.cpp @@ -6,6 +6,9 @@ struct uiButton { HWND hwnd; void (*onClicked)(uiButton *, void *); void *onClickedData; + + SIZE idealSize; + int idealSizeCached; }; static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) @@ -41,6 +44,13 @@ static void uiButtonMinimumSize(uiWindowsControl *c, int *width, int *height) uiWindowsSizing sizing; int y; + if (b->idealSizeCached) + { + *width = b->idealSize.cx; + *height = b->idealSize.cy; + return; + } + // try the comctl32 version 6 way size.cx = 0; // explicitly ask for ideal size size.cy = 0; @@ -48,6 +58,9 @@ static void uiButtonMinimumSize(uiWindowsControl *c, int *width, int *height) *width = size.cx; if (*width < buttonMinWidth) *width = buttonMinWidth; *height = size.cy; + b->idealSize.cx = *width; + b->idealSize.cy = *height; + b->idealSizeCached = true; return; } @@ -60,6 +73,9 @@ static void uiButtonMinimumSize(uiWindowsControl *c, int *width, int *height) uiWindowsGetSizing(b->hwnd, &sizing); uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &y); *height = y; + b->idealSize.cx = *width; + b->idealSize.cy = *height; + b->idealSizeCached = true; } static void defaultOnClicked(uiButton *b, void *data) @@ -75,6 +91,7 @@ char *uiButtonText(uiButton *b) void uiButtonSetText(uiButton *b, const char *text) { uiWindowsSetWindowText(b->hwnd, text); + b->idealSizeCached = 0; // changing the text might necessitate a change in the button's size uiWindowsControlMinimumSizeChanged(uiWindowsControl(b)); } @@ -103,5 +120,7 @@ uiButton *uiNewButton(const char *text) uiWindowsRegisterWM_COMMANDHandler(b->hwnd, onWM_COMMAND, uiControl(b)); uiButtonOnClicked(b, defaultOnClicked, NULL); + b->idealSizeCached = 0; + return b; } diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp index 6736fbc..b30c81b 100644 --- a/src/libui_sdl/main.cpp +++ b/src/libui_sdl/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2017 StapleButter + Copyright 2016-2019 StapleButter This file is part of melonDS. @@ -19,16 +19,19 @@ #include <stdlib.h> #include <time.h> #include <stdio.h> +#include <string.h> #include <SDL2/SDL.h> #include "libui/ui.h" #include "../types.h" +#include "../melon_fopen.h" #include "../version.h" #include "../Config.h" #include "DlgEmuSettings.h" #include "DlgInputConfig.h" +#include "DlgAudioSettings.h" #include "../NDS.h" #include "../GPU.h" @@ -37,6 +40,13 @@ #include "../Platform.h" #include "../Config.h" +#include "../Savestate.h" + + +// savestate slot mapping +// 1-8: regular slots (quick access) +// '9': load/save arbitrary file +const int kSavestateNum[9] = {1, 2, 3, 4, 5, 6, 7, 8, 0}; const int kScreenRot[4] = {0, 1, 2, 3}; const int kScreenGap[6] = {0, 1, 8, 64, 90, 128}; @@ -44,13 +54,25 @@ const int kScreenLayout[3] = {0, 1, 2}; const int kScreenSizing[4] = {0, 1, 2, 3}; +char* EmuDirectory; + + uiWindow* MainWindow; uiArea* MainDrawArea; +uiMenuItem* MenuItem_SaveState; +uiMenuItem* MenuItem_LoadState; +uiMenuItem* MenuItem_UndoStateLoad; + +uiMenuItem* MenuItem_SaveStateSlot[9]; +uiMenuItem* MenuItem_LoadStateSlot[9]; + uiMenuItem* MenuItem_Pause; uiMenuItem* MenuItem_Reset; uiMenuItem* MenuItem_Stop; +uiMenuItem* MenuItem_SavestateSRAMReloc; + uiMenuItem* MenuItem_ScreenRot[4]; uiMenuItem* MenuItem_ScreenGap[6]; uiMenuItem* MenuItem_ScreenLayout[3]; @@ -62,6 +84,10 @@ volatile int EmuStatus; bool RunningSomething; char ROMPath[1024]; +char SRAMPath[1024]; +char PrevSRAMPath[1024]; // for savestate 'undo load' + +bool SavestateLoaded; bool ScreenDrawInited = false; uiDrawBitmap* ScreenBitmap = NULL; @@ -83,11 +109,121 @@ uiDrawMatrix BottomScreenTrans; bool Touching = false; u32 KeyInputMask; +bool LidCommand, LidStatus; SDL_Joystick* Joystick; +u32 MicBufferLength = 2048; +s16 MicBuffer[2048]; +u32 MicBufferReadPos, MicBufferWritePos; + +u32 MicWavLength; +s16* MicWavBuffer; + +u32 MicCommand; + void SetupScreenRects(int width, int height); +void SaveState(int slot); +void LoadState(int slot); +void UndoStateLoad(); +void GetSavestateName(int slot, char* filename, int len); + + + +bool FileExists(const char* name) +{ + FILE* f = melon_fopen(name, "rb"); + if (!f) return false; + fclose(f); + return true; +} + +bool LocalFileExists(const char* name) +{ + FILE* f = melon_fopen_local(name, "rb"); + if (!f) return false; + fclose(f); + return true; +} + + +void MicLoadWav(char* name) +{ + SDL_AudioSpec format; + memset(&format, 0, sizeof(SDL_AudioSpec)); + + if (MicWavBuffer) delete[] MicWavBuffer; + MicWavBuffer = NULL; + MicWavLength = 0; + + u8* buf; + u32 len; + if (!SDL_LoadWAV(name, &format, &buf, &len)) + return; + + const u64 dstfreq = 44100; + + if (format.format == AUDIO_S16 || format.format == AUDIO_U16) + { + int srcinc = format.channels; + len /= 2; + + MicWavLength = (len * dstfreq) / format.freq; + if (MicWavLength < 735) MicWavLength = 735; + MicWavBuffer = new s16[MicWavLength]; + + float res_incr = len / (float)MicWavLength; + float res_timer = 0; + int res_pos = 0; + + for (int i = 0; i < MicWavLength; i++) + { + u16 val = ((u16*)buf)[res_pos]; + if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000; + + MicWavBuffer[i] = val; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos += srcinc; + } + } + } + else if (format.format == AUDIO_S8 || format.format == AUDIO_U8) + { + int srcinc = format.channels; + + MicWavLength = (len * dstfreq) / format.freq; + if (MicWavLength < 735) MicWavLength = 735; + MicWavBuffer = new s16[MicWavLength]; + + float res_incr = len / (float)MicWavLength; + float res_timer = 0; + int res_pos = 0; + + for (int i = 0; i < MicWavLength; i++) + { + u16 val = buf[res_pos] << 8; + if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000; + + MicWavBuffer[i] = val; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos += srcinc; + } + } + } + else + printf("bad WAV format %08X\n", format.format); + + SDL_FreeWAV(buf); +} void UpdateWindowTitle(void* data) @@ -101,7 +237,6 @@ void AudioCallback(void* data, Uint8* stream, int len) // buffer length is 1024 samples // which is 710 samples at the original sample rate - //SPU::ReadOutput((s16*)stream, len>>2); s16 buf_in[710*2]; s16* buf_out = (s16*)stream; @@ -124,11 +259,13 @@ void AudioCallback(void* data, Uint8* stream, int len) float res_timer = 0; int res_pos = 0; + int volume = Config::AudioVolume; + for (int i = 0; i < 1024; i++) { // TODO: interp!! - buf_out[i*2 ] = buf_in[res_pos*2 ]; - buf_out[i*2+1] = buf_in[res_pos*2+1]; + buf_out[i*2 ] = (buf_in[res_pos*2 ] * volume) >> 8; + buf_out[i*2+1] = (buf_in[res_pos*2+1] * volume) >> 8; res_timer += res_incr; while (res_timer >= 1.0) @@ -139,6 +276,106 @@ void AudioCallback(void* data, Uint8* stream, int len) } } +void MicCallback(void* data, Uint8* stream, int len) +{ + if (Config::MicInputType != 1) return; + + s16* input = (s16*)stream; + len /= sizeof(s16); + + if ((MicBufferWritePos + len) > MicBufferLength) + { + u32 len1 = MicBufferLength - MicBufferWritePos; + memcpy(&MicBuffer[MicBufferWritePos], &input[0], len1*sizeof(s16)); + memcpy(&MicBuffer[0], &input[len1], (len - len1)*sizeof(s16)); + MicBufferWritePos = len - len1; + } + else + { + memcpy(&MicBuffer[MicBufferWritePos], input, len*sizeof(s16)); + MicBufferWritePos += len; + } +} + +bool JoyButtonPressed(int btnid, int njoybuttons, Uint8* joybuttons, Uint32 hat) +{ + bool pressed; + if (btnid == 0x101) // up + pressed = (hat & SDL_HAT_UP); + else if (btnid == 0x104) // down + pressed = (hat & SDL_HAT_DOWN); + else if (btnid == 0x102) // right + pressed = (hat & SDL_HAT_RIGHT); + else if (btnid == 0x108) // left + pressed = (hat & SDL_HAT_LEFT); + else + pressed = (btnid < njoybuttons) ? joybuttons[btnid] : false; + + return pressed; +} + +void FeedMicInput() +{ + int type = Config::MicInputType; + if ((type != 1 && MicCommand == 0) || + (type == 3 && MicWavBuffer == NULL)) + { + type = 0; + MicBufferReadPos = 0; + } + + switch (type) + { + case 0: // no mic + NDS::MicInputFrame(NULL, 0); + break; + + case 1: // host mic + if ((MicBufferReadPos + 735) > MicBufferLength) + { + s16 tmp[735]; + u32 len1 = MicBufferLength - MicBufferReadPos; + memcpy(&tmp[0], &MicBuffer[MicBufferReadPos], len1*sizeof(s16)); + memcpy(&tmp[len1], &MicBuffer[0], (735 - len1)*sizeof(s16)); + + NDS::MicInputFrame(tmp, 735); + MicBufferReadPos = 735 - len1; + } + else + { + NDS::MicInputFrame(&MicBuffer[MicBufferReadPos], 735); + MicBufferReadPos += 735; + } + break; + + case 2: // white noise + { + s16 tmp[735]; + for (int i = 0; i < 735; i++) tmp[i] = rand() & 0xFFFF; + NDS::MicInputFrame(tmp, 735); + } + break; + + case 3: // WAV + if ((MicBufferReadPos + 735) > MicWavLength) + { + s16 tmp[735]; + u32 len1 = MicWavLength - MicBufferReadPos; + memcpy(&tmp[0], &MicWavBuffer[MicBufferReadPos], len1*sizeof(s16)); + memcpy(&tmp[len1], &MicWavBuffer[0], (735 - len1)*sizeof(s16)); + + NDS::MicInputFrame(tmp, 735); + MicBufferReadPos = 735 - len1; + } + else + { + NDS::MicInputFrame(&MicWavBuffer[MicBufferReadPos], 735); + MicBufferReadPos += 735; + } + break; + } +} + int EmuThreadFunc(void* burp) { NDS::Init(); @@ -150,32 +387,18 @@ int EmuThreadFunc(void* burp) ScreenDrawInited = false; Touching = false; + KeyInputMask = 0xFFF; + LidCommand = false; + LidStatus = false; + MicCommand = 0; - SDL_AudioSpec whatIwant, whatIget; - memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); - whatIwant.freq = 47340; - whatIwant.format = AUDIO_S16LSB; - whatIwant.channels = 2; - whatIwant.samples = 1024; - whatIwant.callback = AudioCallback; - SDL_AudioDeviceID audio = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0); - if (!audio) - { - printf("Audio init failed: %s\n", SDL_GetError()); - } - else + Uint8* joybuttons = NULL; int njoybuttons = 0; + if (Joystick) { - SDL_PauseAudioDevice(audio, 0); + njoybuttons = SDL_JoystickNumButtons(Joystick); + if (njoybuttons) joybuttons = new Uint8[njoybuttons]; } - KeyInputMask = 0xFFF; - - // TODO: support more joysticks - if (SDL_NumJoysticks() > 0) - Joystick = SDL_JoystickOpen(0); - else - Joystick = NULL; - u32 nframes = 0; u32 starttick = SDL_GetTicks(); u32 lasttick = starttick; @@ -190,6 +413,7 @@ int EmuThreadFunc(void* burp) { EmuStatus = 1; + // poll input u32 keymask = KeyInputMask; u32 joymask = 0xFFF; @@ -201,28 +425,49 @@ int EmuThreadFunc(void* burp) Sint16 axisX = SDL_JoystickGetAxis(Joystick, 0); Sint16 axisY = SDL_JoystickGetAxis(Joystick, 1); + for (int i = 0; i < njoybuttons; i++) + joybuttons[i] = SDL_JoystickGetButton(Joystick, i); + for (int i = 0; i < 12; i++) { int btnid = Config::JoyMapping[i]; if (btnid < 0) continue; - bool pressed; - if (btnid == 0x101) // up - pressed = (hat & SDL_HAT_UP) || (axisY <= -16384); - else if (btnid == 0x104) // down - pressed = (hat & SDL_HAT_DOWN) || (axisY >= 16384); - else if (btnid == 0x102) // right - pressed = (hat & SDL_HAT_RIGHT) || (axisX >= 16384); - else if (btnid == 0x108) // left - pressed = (hat & SDL_HAT_LEFT) || (axisX <= -16384); - else - pressed = SDL_JoystickGetButton(Joystick, btnid); + bool pressed = JoyButtonPressed(btnid, njoybuttons, joybuttons, hat); + + if (i == 4) // right + pressed = pressed || (axisX >= 16384); + else if (i == 5) // left + pressed = pressed || (axisX <= -16384); + else if (i == 6) // up + pressed = pressed || (axisY <= -16384); + else if (i == 7) // down + pressed = pressed || (axisY >= 16384); if (pressed) joymask &= ~(1<<i); } + + if (JoyButtonPressed(Config::HKJoyMapping[HK_Lid], njoybuttons, joybuttons, hat)) + { + LidStatus = !LidStatus; + LidCommand = true; + } + if (JoyButtonPressed(Config::HKJoyMapping[HK_Mic], njoybuttons, joybuttons, hat)) + MicCommand |= 2; + else + MicCommand &= ~2; } NDS::SetKeyMask(keymask & joymask); + if (LidCommand) + { + NDS::SetLidClosed(LidStatus); + LidCommand = false; + } + + // microphone input + FeedMicInput(); + // emulate u32 nlines = NDS::RunFrame(); @@ -301,8 +546,6 @@ int EmuThreadFunc(void* burp) } else { - EmuStatus = 2; - // paused nframes = 0; lasttick = SDL_GetTicks(); @@ -310,16 +553,20 @@ int EmuThreadFunc(void* burp) lastmeasuretick = lasttick; fpslimitcount = 0; - uiAreaQueueRedrawAll(MainDrawArea); + if (EmuRunning == 2) + { + uiAreaQueueRedrawAll(MainDrawArea); + } + + EmuStatus = EmuRunning; + SDL_Delay(100); } } EmuStatus = 0; - if (Joystick) SDL_JoystickClose(Joystick); - - if (audio) SDL_CloseAudioDevice(audio); + if (joybuttons) delete[] joybuttons; NDS::DeInit(); @@ -438,20 +685,51 @@ int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt) if (evt->Modifiers == 0x2) // ALT+key return 0; + // d0rp + if (!RunningSomething) + return 1; + if (evt->Up) { for (int i = 0; i < 12; i++) if (evt->Scancode == Config::KeyMapping[i]) KeyInputMask |= (1<<i); + + if (evt->Scancode == Config::HKKeyMapping[HK_Mic]) + MicCommand &= ~1; } else if (!evt->Repeat) { + // F keys: 3B-44, 57-58 | SHIFT: mod. 0x4 + if (evt->Scancode >= 0x3B && evt->Scancode <= 0x42) // F1-F8, quick savestate + { + if (evt->Modifiers == 0x4) SaveState(1 + (evt->Scancode - 0x3B)); + else if (evt->Modifiers == 0x0) LoadState(1 + (evt->Scancode - 0x3B)); + } + else if (evt->Scancode == 0x43) // F9, savestate from/to file + { + if (evt->Modifiers == 0x4) SaveState(0); + else if (evt->Modifiers == 0x0) LoadState(0); + } + else if (evt->Scancode == 0x58) // F12, undo savestate + { + if (evt->Modifiers == 0x0) UndoStateLoad(); + } + for (int i = 0; i < 12; i++) if (evt->Scancode == Config::KeyMapping[i]) KeyInputMask &= ~(1<<i); - //if (evt->Scancode == 0x58) // F12 - // NDS::debug(0); + if (evt->Scancode == Config::HKKeyMapping[HK_Lid]) + { + LidStatus = !LidStatus; + LidCommand = true; + } + if (evt->Scancode == Config::HKKeyMapping[HK_Mic]) + MicCommand |= 1; + + if (evt->Scancode == 0x57) // F11 + NDS::debug(0); } return 1; @@ -710,6 +988,25 @@ void Run() EmuRunning = 1; RunningSomething = true; + uiMenuItemEnable(MenuItem_SaveState); + uiMenuItemEnable(MenuItem_LoadState); + + if (SavestateLoaded) + uiMenuItemEnable(MenuItem_UndoStateLoad); + else + uiMenuItemDisable(MenuItem_UndoStateLoad); + + for (int i = 0; i < 8; i++) + { + char ssfile[1024]; + GetSavestateName(i+1, ssfile, 1024); + if (FileExists(ssfile)) uiMenuItemEnable(MenuItem_LoadStateSlot[i]); + else uiMenuItemDisable(MenuItem_LoadStateSlot[i]); + } + + for (int i = 0; i < 9; i++) uiMenuItemEnable(MenuItem_SaveStateSlot[i]); + uiMenuItemEnable(MenuItem_LoadStateSlot[8]); + uiMenuItemEnable(MenuItem_Pause); uiMenuItemEnable(MenuItem_Reset); uiMenuItemEnable(MenuItem_Stop); @@ -723,6 +1020,12 @@ void Stop(bool internal) while (EmuStatus != 2); RunningSomething = false; + uiWindowSetTitle(MainWindow, "melonDS " MELONDS_VERSION); + + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]); + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_LoadStateSlot[i]); + uiMenuItemDisable(MenuItem_UndoStateLoad); + uiMenuItemDisable(MenuItem_Pause); uiMenuItemDisable(MenuItem_Reset); uiMenuItemDisable(MenuItem_Stop); @@ -732,16 +1035,33 @@ void Stop(bool internal) uiAreaQueueRedrawAll(MainDrawArea); } +void SetupSRAMPath() +{ + strncpy(SRAMPath, ROMPath, 1023); + SRAMPath[1023] = '\0'; + strncpy(SRAMPath + strlen(ROMPath) - 3, "sav", 3); +} + void TryLoadROM(char* file, int prevstatus) { char oldpath[1024]; + char oldsram[1024]; strncpy(oldpath, ROMPath, 1024); + strncpy(oldsram, SRAMPath, 1024); strncpy(ROMPath, file, 1023); ROMPath[1023] = '\0'; - if (NDS::LoadROM(ROMPath, Config::DirectBoot)) + SetupSRAMPath(); + + if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot)) + { + SavestateLoaded = false; + uiMenuItemDisable(MenuItem_UndoStateLoad); + + strncpy(PrevSRAMPath, SRAMPath, 1024); // safety Run(); + } else { uiMsgBoxError(MainWindow, @@ -749,18 +1069,200 @@ void TryLoadROM(char* file, int prevstatus) "Make sure the file can be accessed and isn't opened in another application."); strncpy(ROMPath, oldpath, 1024); + strncpy(SRAMPath, oldsram, 1024); EmuRunning = prevstatus; } } -int OnCloseWindow(uiWindow* window, void* blarg) +// SAVESTATE TODO +// * configurable paths. not everyone wants their ROM directory to be polluted, I guess. + +void GetSavestateName(int slot, char* filename, int len) { - if (RunningSomething) + int pos; + + if (ROMPath[0] == '\0') // running firmware, no ROM { - EmuRunning = 2; - while (EmuStatus != 2); + strcpy(filename, "firmware"); + pos = 8; + } + else + { + int l = strlen(ROMPath); + pos = l; + while (ROMPath[pos] != '.' && pos > 0) pos--; + if (pos == 0) pos = l; + + // avoid buffer overflow. shoddy + if (pos > len-5) pos = len-5; + + strncpy(&filename[0], ROMPath, pos); } + strcpy(&filename[pos], ".ml"); + filename[pos+3] = '0'+slot; + filename[pos+4] = '\0'; +} + +void LoadState(int slot) +{ + int prevstatus = EmuRunning; + EmuRunning = 2; + while (EmuStatus != 2); + + char filename[1024]; + + if (slot > 0) + { + GetSavestateName(slot, filename, 1024); + } + else + { + char* file = uiOpenFile(MainWindow, "melonDS savestate (any)|*.ml1;*.ml2;*.ml3;*.ml4;*.ml5;*.ml6;*.ml7;*.ml8;*.mln", Config::LastROMFolder); + if (!file) + { + EmuRunning = prevstatus; + return; + } + + strncpy(filename, file, 1023); + filename[1023] = '\0'; + uiFreeText(file); + } + + if (!FileExists(filename)) + { + EmuRunning = prevstatus; + return; + } + + // backup + Savestate* backup = new Savestate("timewarp.mln", true); + NDS::DoSavestate(backup); + delete backup; + + bool failed = false; + + Savestate* state = new Savestate(filename, false); + if (state->Error) + { + delete state; + + uiMsgBoxError(MainWindow, "Error", "Could not load savestate file."); + + // current state might be crapoed, so restore from sane backup + state = new Savestate("timewarp.mln", false); + failed = true; + } + + NDS::DoSavestate(state); + delete state; + + if (!failed) + { + if (Config::SavestateRelocSRAM && ROMPath[0]!='\0') + { + strncpy(PrevSRAMPath, SRAMPath, 1024); + + strncpy(SRAMPath, filename, 1019); + int len = strlen(SRAMPath); + strcpy(&SRAMPath[len], ".sav"); + SRAMPath[len+4] = '\0'; + + NDS::RelocateSave(SRAMPath, false); + } + + SavestateLoaded = true; + uiMenuItemEnable(MenuItem_UndoStateLoad); + } + + EmuRunning = prevstatus; +} + +void SaveState(int slot) +{ + int prevstatus = EmuRunning; + EmuRunning = 2; + while (EmuStatus != 2); + + char filename[1024]; + + if (slot > 0) + { + GetSavestateName(slot, filename, 1024); + } + else + { + char* file = uiSaveFile(MainWindow, "melonDS savestate (*.mln)|*.mln", Config::LastROMFolder); + if (!file) + { + EmuRunning = prevstatus; + return; + } + + strncpy(filename, file, 1023); + filename[1023] = '\0'; + uiFreeText(file); + } + + Savestate* state = new Savestate(filename, true); + if (state->Error) + { + delete state; + + uiMsgBoxError(MainWindow, "Error", "Could not save state."); + } + else + { + NDS::DoSavestate(state); + delete state; + + if (slot > 0) + uiMenuItemEnable(MenuItem_LoadStateSlot[slot-1]); + + if (Config::SavestateRelocSRAM && ROMPath[0]!='\0') + { + strncpy(SRAMPath, filename, 1019); + int len = strlen(SRAMPath); + strcpy(&SRAMPath[len], ".sav"); + SRAMPath[len+4] = '\0'; + + NDS::RelocateSave(SRAMPath, true); + } + } + + EmuRunning = prevstatus; +} + +void UndoStateLoad() +{ + if (!SavestateLoaded) return; + + int prevstatus = EmuRunning; + EmuRunning = 2; + while (EmuStatus != 2); + + // pray that this works + // what do we do if it doesn't??? + // but it should work. + Savestate* backup = new Savestate("timewarp.mln", false); + NDS::DoSavestate(backup); + delete backup; + + if (ROMPath[0]!='\0') + { + strncpy(SRAMPath, PrevSRAMPath, 1024); + NDS::RelocateSave(SRAMPath, false); + } + + EmuRunning = prevstatus; +} + + +int OnCloseWindow(uiWindow* window, void* blarg) +{ + EmuRunning = 3; + while (EmuStatus != 3); uiQuit(); return 1; @@ -795,6 +1297,9 @@ void OnLoseFocus(uiWindow* window, void* blarg) void OnCloseByMenu(uiMenuItem* item, uiWindow* window, void* blarg) { + EmuRunning = 3; + while (EmuStatus != 3); + uiControlDestroy(uiControl(window)); uiQuit(); } @@ -805,17 +1310,39 @@ void OnOpenFile(uiMenuItem* item, uiWindow* window, void* blarg) EmuRunning = 2; while (EmuStatus != 2); - char* file = uiOpenFile(window, "DS ROM (*.nds)|*.nds;*.srl|Any file|*.*", NULL); + char* file = uiOpenFile(window, "DS ROM (*.nds)|*.nds;*.srl|Any file|*.*", Config::LastROMFolder); if (!file) { EmuRunning = prevstatus; return; } + + int pos = strlen(file)-1; + while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--; + strncpy(Config::LastROMFolder, file, pos); + Config::LastROMFolder[pos] = '\0'; TryLoadROM(file, prevstatus); uiFreeText(file); } +void OnSaveState(uiMenuItem* item, uiWindow* window, void* param) +{ + int slot = *(int*)param; + SaveState(slot); +} + +void OnLoadState(uiMenuItem* item, uiWindow* window, void* param) +{ + int slot = *(int*)param; + LoadState(slot); +} + +void OnUndoStateLoad(uiMenuItem* item, uiWindow* window, void* param) +{ + UndoStateLoad(); +} + void OnRun(uiMenuItem* item, uiWindow* window, void* blarg) { if (!RunningSomething) @@ -852,10 +1379,16 @@ void OnReset(uiMenuItem* item, uiWindow* window, void* blarg) EmuRunning = 2; while (EmuStatus != 2); + SavestateLoaded = false; + uiMenuItemDisable(MenuItem_UndoStateLoad); + if (ROMPath[0] == '\0') NDS::LoadBIOS(); else - NDS::LoadROM(ROMPath, Config::DirectBoot); + { + SetupSRAMPath(); + NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot); + } Run(); } @@ -874,7 +1407,23 @@ void OnOpenEmuSettings(uiMenuItem* item, uiWindow* window, void* blarg) void OnOpenInputConfig(uiMenuItem* item, uiWindow* window, void* blarg) { - DlgInputConfig::Open(); + DlgInputConfig::Open(0); +} + +void OnOpenHotkeyConfig(uiMenuItem* item, uiWindow* window, void* blarg) +{ + DlgInputConfig::Open(1); +} + +void OnOpenAudioSettings(uiMenuItem* item, uiWindow* window, void* blarg) +{ + DlgAudioSettings::Open(); +} + + +void OnSetSavestateSRAMReloc(uiMenuItem* item, uiWindow* window, void* param) +{ + Config::SavestateRelocSRAM = uiMenuItemChecked(item) ? 1:0; } @@ -1005,15 +1554,6 @@ void ApplyNewSettings() } -bool _fileexists(char* name) -{ - FILE* f = fopen(name, "rb"); - if (!f) return false; - fclose(f); - return true; -} - - int main(int argc, char** argv) { srand(time(NULL)); @@ -1021,6 +1561,19 @@ int main(int argc, char** argv) printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); + { + int len = strlen(argv[0]); + while (len > 0) + { + if (argv[0][len] == '/') break; + if (argv[0][len] == '\\') break; + len--; + } + EmuDirectory = new char[len]; + strncpy(EmuDirectory, argv[0], len); + EmuDirectory[len] = '\0'; + } + // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); @@ -1048,7 +1601,10 @@ int main(int argc, char** argv) Config::Load(); - if (!Config::HasConfigFile("bios7.bin") || !Config::HasConfigFile("bios9.bin") || !Config::HasConfigFile("firmware.bin")) + if (Config::AudioVolume < 0) Config::AudioVolume = 0; + else if (Config::AudioVolume > 256) Config::AudioVolume = 256; + + if (!LocalFileExists("bios7.bin") || !LocalFileExists("bios9.bin") || !LocalFileExists("firmware.bin")) { uiMsgBoxError( NULL, @@ -1072,6 +1628,48 @@ int main(int argc, char** argv) menuitem = uiMenuAppendItem(menu, "Open ROM..."); uiMenuItemOnClicked(menuitem, OnOpenFile, NULL); uiMenuAppendSeparator(menu); + { + uiMenu* submenu = uiNewMenu("Save state"); + + for (int i = 0; i < 9; i++) + { + char name[32]; + if (i < 8) + sprintf(name, "%d\tShift+F%d", kSavestateNum[i], kSavestateNum[i]); + else + strcpy(name, "File...\tShift+F9"); + + uiMenuItem* ssitem = uiMenuAppendItem(submenu, name); + uiMenuItemOnClicked(ssitem, OnSaveState, (void*)&kSavestateNum[i]); + + MenuItem_SaveStateSlot[i] = ssitem; + } + + MenuItem_SaveState = uiMenuAppendSubmenu(menu, submenu); + } + { + uiMenu* submenu = uiNewMenu("Load state"); + + for (int i = 0; i < 9; i++) + { + char name[32]; + if (i < 8) + sprintf(name, "%d\tF%d", kSavestateNum[i], kSavestateNum[i]); + else + strcpy(name, "File...\tF9"); + + uiMenuItem* ssitem = uiMenuAppendItem(submenu, name); + uiMenuItemOnClicked(ssitem, OnLoadState, (void*)&kSavestateNum[i]); + + MenuItem_LoadStateSlot[i] = ssitem; + } + + MenuItem_LoadState = uiMenuAppendSubmenu(menu, submenu); + } + menuitem = uiMenuAppendItem(menu, "Undo state load\tF12"); + uiMenuItemOnClicked(menuitem, OnUndoStateLoad, NULL); + MenuItem_UndoStateLoad = menuitem; + uiMenuAppendSeparator(menu); menuitem = uiMenuAppendItem(menu, "Quit"); uiMenuItemOnClicked(menuitem, OnCloseByMenu, NULL); @@ -1090,10 +1688,25 @@ int main(int argc, char** argv) MenuItem_Stop = menuitem; menu = uiNewMenu("Config"); - menuitem = uiMenuAppendItem(menu, "Emu settings"); - uiMenuItemOnClicked(menuitem, OnOpenEmuSettings, NULL); - menuitem = uiMenuAppendItem(menu, "Input config"); - uiMenuItemOnClicked(menuitem, OnOpenInputConfig, NULL); + { + menuitem = uiMenuAppendItem(menu, "Emu settings"); + uiMenuItemOnClicked(menuitem, OnOpenEmuSettings, NULL); + menuitem = uiMenuAppendItem(menu, "Input config"); + uiMenuItemOnClicked(menuitem, OnOpenInputConfig, NULL); + menuitem = uiMenuAppendItem(menu, "Hotkey config"); + uiMenuItemOnClicked(menuitem, OnOpenHotkeyConfig, NULL); + menuitem = uiMenuAppendItem(menu, "Audio settings"); + uiMenuItemOnClicked(menuitem, OnOpenAudioSettings, NULL); + } + uiMenuAppendSeparator(menu); + { + uiMenu* submenu = uiNewMenu("Savestate settings"); + + MenuItem_SavestateSRAMReloc = uiMenuAppendCheckItem(submenu, "Separate savefiles"); + uiMenuItemOnClicked(MenuItem_SavestateSRAMReloc, OnSetSavestateSRAMReloc, NULL); + + uiMenuAppendSubmenu(menu, submenu); + } uiMenuAppendSeparator(menu); { uiMenu* submenu = uiNewMenu("Screen rotation"); @@ -1167,6 +1780,12 @@ int main(int argc, char** argv) uiWindowOnGetFocus(MainWindow, OnGetFocus, NULL); uiWindowOnLoseFocus(MainWindow, OnLoseFocus, NULL); + //uiMenuItemDisable(MenuItem_SaveState); + //uiMenuItemDisable(MenuItem_LoadState); + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_SaveStateSlot[i]); + for (int i = 0; i < 9; i++) uiMenuItemDisable(MenuItem_LoadStateSlot[i]); + uiMenuItemDisable(MenuItem_UndoStateLoad); + uiMenuItemDisable(MenuItem_Pause); uiMenuItemDisable(MenuItem_Reset); uiMenuItemDisable(MenuItem_Stop); @@ -1195,6 +1814,8 @@ int main(int argc, char** argv) SANITIZE(ScreenSizing, 0, 3); #undef SANITIZE + uiMenuItemSetChecked(MenuItem_SavestateSRAMReloc, Config::SavestateRelocSRAM?1:0); + uiMenuItemSetChecked(MenuItem_ScreenRot[ScreenRotation], 1); uiMenuItemSetChecked(MenuItem_ScreenLayout[ScreenLayout], 1); uiMenuItemSetChecked(MenuItem_ScreenSizing[ScreenSizing], 1); @@ -1207,6 +1828,53 @@ int main(int argc, char** argv) OnSetScreenRotation(MenuItem_ScreenRot[ScreenRotation], MainWindow, (void*)&kScreenRot[ScreenRotation]); + SDL_AudioSpec whatIwant, whatIget; + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = 47340; + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 2; + whatIwant.samples = 1024; + whatIwant.callback = AudioCallback; + SDL_AudioDeviceID audio = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0); + if (!audio) + { + printf("Audio init failed: %s\n", SDL_GetError()); + } + else + { + SDL_PauseAudioDevice(audio, 0); + } + + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = 44100; + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 1; + whatIwant.samples = 1024; + whatIwant.callback = MicCallback; + SDL_AudioDeviceID mic = SDL_OpenAudioDevice(NULL, 1, &whatIwant, &whatIget, 0); + if (!mic) + { + printf("Mic init failed: %s\n", SDL_GetError()); + MicBufferLength = 0; + } + else + { + SDL_PauseAudioDevice(mic, 0); + } + + memset(MicBuffer, 0, sizeof(MicBuffer)); + MicBufferReadPos = 0; + MicBufferWritePos = 0; + + MicWavBuffer = NULL; + if (Config::MicInputType == 3) MicLoadWav(Config::MicWavPath); + + // TODO: support more joysticks + if (SDL_NumJoysticks() > 0) + Joystick = SDL_JoystickOpen(0); + else + Joystick = NULL; + EmuRunning = 2; RunningSomething = false; EmuThread = SDL_CreateThread(EmuThreadFunc, "melonDS magic", NULL); @@ -1221,7 +1889,9 @@ int main(int argc, char** argv) strncpy(ROMPath, file, 1023); ROMPath[1023] = '\0'; - if (NDS::LoadROM(ROMPath, Config::DirectBoot)) + SetupSRAMPath(); + + if (NDS::LoadROM(ROMPath, SRAMPath, Config::DirectBoot)) Run(); } } @@ -1233,6 +1903,12 @@ int main(int argc, char** argv) EmuRunning = 0; SDL_WaitThread(EmuThread, NULL); + if (Joystick) SDL_JoystickClose(Joystick); + if (audio) SDL_CloseAudioDevice(audio); + if (mic) SDL_CloseAudioDevice(mic); + + if (MicWavBuffer) delete[] MicWavBuffer; + Config::ScreenRotation = ScreenRotation; Config::ScreenGap = ScreenGap; Config::ScreenLayout = ScreenLayout; @@ -1244,6 +1920,7 @@ int main(int argc, char** argv) uiUninit(); SDL_Quit(); + delete[] EmuDirectory; return 0; } @@ -1253,41 +1930,35 @@ int main(int argc, char** argv) int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdshow) { - char cmdargs[16][256]; - int arg = 1; - int j = 0; - bool inquote = false; - int len = strlen(cmdline); - for (int i = 0; i < len; i++) - { - char c = cmdline[i]; - if (c == '\0') break; - if (c == '"') inquote = !inquote; - if (!inquote && c==' ') - { - if (j > 255) j = 255; - if (arg < 16) cmdargs[arg][j] = '\0'; - arg++; - j = 0; - } - else - { - if (arg < 16 && j < 255) cmdargs[arg][j] = c; - j++; - } + int argc = 0; + wchar_t** argv_w = CommandLineToArgvW(GetCommandLineW(), &argc); + char* nullarg = ""; + + char** argv = new char*[argc]; + for (int i = 0; i < argc; i++) + { + int len = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL); + if (len < 1) return NULL; + argv[i] = new char[len]; + int res = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, argv[i], len, NULL, NULL); + if (res != len) { delete[] argv[i]; argv[i] = nullarg; } + } + + if (AttachConsole(ATTACH_PARENT_PROCESS)) + { + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + printf("\n"); } - if (j > 255) j = 255; - if (arg < 16) cmdargs[arg][j] = '\0'; - if (len > 0) arg++; - // FIXME!! - strncpy(cmdargs[0], "melonDS.exe", 256); + int ret = main(argc, argv); + + printf("\n\n>"); - char* cmdargptr[16]; - for (int i = 0; i < 16; i++) - cmdargptr[i] = &cmdargs[i][0]; + for (int i = 0; i < argc; i++) if (argv[i] != nullarg) delete[] argv[i]; + delete[] argv; - return main(arg, cmdargptr); + return ret; } #endif |