mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-23 14:19:55 -06:00
* clean up code
* working-ish run/pause/reset * proper closing/cleanup * ability to run BIOS alone
This commit is contained in:
@ -328,6 +328,14 @@ void LoadROM(const char* path, bool direct)
|
|||||||
|
|
||||||
if (NDSCart::LoadROM(path, direct))
|
if (NDSCart::LoadROM(path, direct))
|
||||||
Running = true;
|
Running = true;
|
||||||
|
else
|
||||||
|
printf("Failed to load ROM %s\n", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoadBIOS()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
Running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +117,7 @@ void DeInit();
|
|||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
void LoadROM(const char* path, bool direct);
|
void LoadROM(const char* path, bool direct);
|
||||||
|
void LoadBIOS();
|
||||||
void SetupDirectBoot();
|
void SetupDirectBoot();
|
||||||
|
|
||||||
void RunFrame();
|
void RunFrame();
|
||||||
|
@ -68,6 +68,10 @@ void DeInit()
|
|||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
|
if (SRAM) delete[] SRAM;
|
||||||
|
if (Discover_Buffer) delete[] Discover_Buffer;
|
||||||
|
SRAM = NULL;
|
||||||
|
Discover_Buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadSave(char* path)
|
void LoadSave(char* path)
|
||||||
@ -625,6 +629,7 @@ void Reset()
|
|||||||
DataOutLen = 0;
|
DataOutLen = 0;
|
||||||
|
|
||||||
CartInserted = false;
|
CartInserted = false;
|
||||||
|
if (CartROM) delete[] CartROM;
|
||||||
CartROM = NULL;
|
CartROM = NULL;
|
||||||
CartROMSize = 0;
|
CartROMSize = 0;
|
||||||
CartID = 0;
|
CartID = 0;
|
||||||
@ -735,6 +740,8 @@ void ReadROM(u32 addr, u32 len, u32 offset)
|
|||||||
|
|
||||||
void ReadROM_B7(u32 addr, u32 len, u32 offset)
|
void ReadROM_B7(u32 addr, u32 len, u32 offset)
|
||||||
{
|
{
|
||||||
|
if (!CartInserted) return;
|
||||||
|
|
||||||
addr &= (CartROMSize-1);
|
addr &= (CartROMSize-1);
|
||||||
if (!CartIsHomebrew)
|
if (!CartIsHomebrew)
|
||||||
{
|
{
|
||||||
@ -858,7 +865,7 @@ void WriteROMCnt(u32 val)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x3C:
|
case 0x3C:
|
||||||
CmdEncMode = 1;
|
if (CartInserted) CmdEncMode = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xB7:
|
case 0xB7:
|
||||||
|
@ -116,7 +116,7 @@ InputConfigDialog::InputConfigDialog(wxWindow* parent)
|
|||||||
p->SetSizer(grid);
|
p->SetSizer(grid);
|
||||||
sizer->Add(p, 0, wxALL, 15);
|
sizer->Add(p, 0, wxALL, 15);
|
||||||
|
|
||||||
wxComboBox* joycombo = new wxComboBox(joyside, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN | wxCB_READONLY);
|
/*wxComboBox* joycombo = new wxComboBox(joyside, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN | wxCB_READONLY);
|
||||||
|
|
||||||
int numjoys = SDL_NumJoysticks();
|
int numjoys = SDL_NumJoysticks();
|
||||||
if (numjoys > 0)
|
if (numjoys > 0)
|
||||||
@ -133,7 +133,7 @@ InputConfigDialog::InputConfigDialog(wxWindow* parent)
|
|||||||
joycombo->Enable(false);
|
joycombo->Enable(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
sizer->Add(joycombo, 0, wxALL&(~wxTOP), 15);
|
sizer->Add(joycombo, 0, wxALL&(~wxTOP), 15);*/
|
||||||
|
|
||||||
joyside->SetSizer(sizer);
|
joyside->SetSizer(sizer);
|
||||||
}
|
}
|
||||||
@ -241,7 +241,6 @@ void InputConfigDialog::OnConfigureKey(wxMouseEvent& event)
|
|||||||
parent->pollbtn = btn;
|
parent->pollbtn = btn;
|
||||||
parent->pollbtntext = btn->GetLabel();
|
parent->pollbtntext = btn->GetLabel();
|
||||||
parent->pollid = event.GetId();
|
parent->pollid = event.GetId();
|
||||||
//parent->polltimer->Start(50);
|
|
||||||
|
|
||||||
btn->SetLabel("[press key]");
|
btn->SetLabel("[press key]");
|
||||||
btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
|
btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNHIGHLIGHT));
|
||||||
@ -270,77 +269,32 @@ void InputConfigDialog::OnPoll(wxTimerEvent& event)
|
|||||||
{
|
{
|
||||||
if (pollid < 200) return;
|
if (pollid < 200) return;
|
||||||
|
|
||||||
//keycatcher->SetFocus();
|
int id = pollid - 200;
|
||||||
//SDL_SetWindowInputFocus(sdlwin);
|
if (id >= 12) return;
|
||||||
//SDL_RaiseWindow(sdlwin);
|
|
||||||
//SDL_PumpEvents();
|
|
||||||
//keycatcher->SetFocus();
|
|
||||||
//SDL_RaiseWindow(sdlwin);
|
|
||||||
|
|
||||||
/*if (keystate[SDL_SCANCODE_ESCAPE])
|
if (keystate[SDL_SCANCODE_BACKSPACE])
|
||||||
{
|
{
|
||||||
|
joymapping[id] = -1;
|
||||||
|
|
||||||
|
char keyname[16];
|
||||||
|
JoyMappingName(joymapping[id], keyname);
|
||||||
|
pollbtn->SetLabel(keyname);
|
||||||
|
|
||||||
polltimer->Stop();
|
polltimer->Stop();
|
||||||
pollbtn->SetLabel(pollbtntext);
|
|
||||||
pollid = 0;
|
pollid = 0;
|
||||||
|
|
||||||
pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
||||||
pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||||
pollbtn->Refresh();
|
pollbtn->Refresh();
|
||||||
return;
|
return;
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if (pollid >= 200)
|
int nbuttons = SDL_JoystickNumButtons(joy);
|
||||||
|
for (int i = 0; i < nbuttons; i++)
|
||||||
{
|
{
|
||||||
int id = pollid - 200;
|
if (SDL_JoystickGetButton(joy, i))
|
||||||
if (id >= 12) return;
|
|
||||||
|
|
||||||
if (keystate[SDL_SCANCODE_BACKSPACE])
|
|
||||||
{
|
{
|
||||||
joymapping[id] = -1;
|
joymapping[id] = i;
|
||||||
|
|
||||||
char keyname[16];
|
|
||||||
JoyMappingName(joymapping[id], keyname);
|
|
||||||
pollbtn->SetLabel(keyname);
|
|
||||||
|
|
||||||
polltimer->Stop();
|
|
||||||
pollid = 0;
|
|
||||||
|
|
||||||
pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
|
||||||
pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
|
||||||
pollbtn->Refresh();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nbuttons = SDL_JoystickNumButtons(joy);
|
|
||||||
for (int i = 0; i < nbuttons; i++)
|
|
||||||
{
|
|
||||||
if (SDL_JoystickGetButton(joy, i))
|
|
||||||
{
|
|
||||||
joymapping[id] = i;
|
|
||||||
|
|
||||||
char keyname[16];
|
|
||||||
JoyMappingName(joymapping[id], keyname);
|
|
||||||
pollbtn->SetLabel(keyname);
|
|
||||||
|
|
||||||
polltimer->Stop();
|
|
||||||
pollid = 0;
|
|
||||||
|
|
||||||
pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
|
||||||
pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
|
||||||
pollbtn->Refresh();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 blackhat = SDL_JoystickGetHat(joy, 0);
|
|
||||||
if (blackhat)
|
|
||||||
{
|
|
||||||
if (blackhat & 0x1) blackhat = 0x1;
|
|
||||||
else if (blackhat & 0x2) blackhat = 0x2;
|
|
||||||
else if (blackhat & 0x4) blackhat = 0x4;
|
|
||||||
else blackhat = 0x8;
|
|
||||||
|
|
||||||
joymapping[id] = 0x100 | blackhat;
|
|
||||||
|
|
||||||
char keyname[16];
|
char keyname[16];
|
||||||
JoyMappingName(joymapping[id], keyname);
|
JoyMappingName(joymapping[id], keyname);
|
||||||
@ -355,32 +309,29 @@ void InputConfigDialog::OnPoll(wxTimerEvent& event)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*else
|
|
||||||
|
u8 blackhat = SDL_JoystickGetHat(joy, 0);
|
||||||
|
if (blackhat)
|
||||||
{
|
{
|
||||||
int id = pollid - 100;
|
if (blackhat & 0x1) blackhat = 0x1;
|
||||||
if (id >= 12) return;
|
else if (blackhat & 0x2) blackhat = 0x2;
|
||||||
|
else if (blackhat & 0x4) blackhat = 0x4;
|
||||||
|
else blackhat = 0x8;
|
||||||
|
|
||||||
for (int i = 0; i < nkeys; i++)
|
joymapping[id] = 0x100 | blackhat;
|
||||||
{
|
|
||||||
if (keystate[i])
|
|
||||||
{
|
|
||||||
keymapping[id] = i;
|
|
||||||
|
|
||||||
pollbtn->Enable(true);
|
char keyname[16];
|
||||||
|
JoyMappingName(joymapping[id], keyname);
|
||||||
|
pollbtn->SetLabel(keyname);
|
||||||
|
|
||||||
const char* keyname = SDL_GetKeyName(SDL_GetKeyFromScancode((SDL_Scancode)i));
|
polltimer->Stop();
|
||||||
pollbtn->SetLabel(keyname);
|
pollid = 0;
|
||||||
|
|
||||||
polltimer->Stop();
|
pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
||||||
pollid = 0;
|
pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
||||||
|
pollbtn->Refresh();
|
||||||
pollbtn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
|
return;
|
||||||
pollbtn->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT));
|
}
|
||||||
pollbtn->Refresh();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputConfigDialog::OnFancybuttonHover(wxMouseEvent& event)
|
void InputConfigDialog::OnFancybuttonHover(wxMouseEvent& event)
|
||||||
|
434
src/wx/main.cpp
434
src/wx/main.cpp
@ -105,10 +105,11 @@ wxBEGIN_EVENT_TABLE(MainFrame, wxFrame)
|
|||||||
EVT_MENU(ID_OPENROM, MainFrame::OnOpenROM)
|
EVT_MENU(ID_OPENROM, MainFrame::OnOpenROM)
|
||||||
EVT_MENU(ID_EXIT, MainFrame::OnCloseFromMenu)
|
EVT_MENU(ID_EXIT, MainFrame::OnCloseFromMenu)
|
||||||
|
|
||||||
EVT_MENU(ID_INPUTCONFIG, MainFrame::OnInputConfig)
|
EVT_MENU(ID_RUN, MainFrame::OnRun)
|
||||||
|
EVT_MENU(ID_PAUSE, MainFrame::OnPause)
|
||||||
|
EVT_MENU(ID_RESET, MainFrame::OnReset)
|
||||||
|
|
||||||
EVT_PAINT(MainFrame::OnPaint)
|
EVT_MENU(ID_INPUTCONFIG, MainFrame::OnInputConfig)
|
||||||
EVT_IDLE(MainFrame::OnIdle)
|
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ MainFrame::MainFrame()
|
|||||||
|
|
||||||
wxMenu* systemmenu = new wxMenu();
|
wxMenu* systemmenu = new wxMenu();
|
||||||
systemmenu->Append(ID_RUN, "Run");
|
systemmenu->Append(ID_RUN, "Run");
|
||||||
systemmenu->Append(ID_PAUSE, "Pause");
|
systemmenu->AppendCheckItem(ID_PAUSE, "Pause");
|
||||||
systemmenu->AppendSeparator();
|
systemmenu->AppendSeparator();
|
||||||
systemmenu->Append(ID_RESET, "Reset");
|
systemmenu->Append(ID_RESET, "Reset");
|
||||||
|
|
||||||
@ -136,18 +137,9 @@ MainFrame::MainFrame()
|
|||||||
|
|
||||||
SetMenuBar(melonbar);
|
SetMenuBar(melonbar);
|
||||||
|
|
||||||
SetClientSize(256, 384);
|
SetClientSize(256, 256);
|
||||||
SetMinSize(GetSize());
|
SetMinSize(GetSize());
|
||||||
|
|
||||||
emustatus = 2;
|
|
||||||
|
|
||||||
/*emustatuschangemutex = new wxMutex();
|
|
||||||
emustatuschange = new wxCondition(*emustatuschangemutex);
|
|
||||||
|
|
||||||
emustopmutex = new wxMutex();
|
|
||||||
emustop = new wxCondition(*emustopmutex);
|
|
||||||
emustopmutex->Lock();*/
|
|
||||||
|
|
||||||
emuthread = new EmuThread(this);
|
emuthread = new EmuThread(this);
|
||||||
if (emuthread->Run() != wxTHREAD_NO_ERROR)
|
if (emuthread->Run() != wxTHREAD_NO_ERROR)
|
||||||
{
|
{
|
||||||
@ -156,48 +148,33 @@ MainFrame::MainFrame()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*sdlwin = SDL_CreateWindowFrom(GetHandle());
|
|
||||||
|
|
||||||
sdlrend = SDL_CreateRenderer(sdlwin, -1, SDL_RENDERER_ACCELERATED);// | SDL_RENDERER_PRESENTVSYNC);
|
|
||||||
sdltex = SDL_CreateTexture(sdlrend, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, 256, 384);
|
|
||||||
|
|
||||||
texmutex = new wxMutex();
|
|
||||||
SDL_LockTexture(sdltex, NULL, &texpixels, &texstride);
|
|
||||||
texmutex->Unlock();*/
|
|
||||||
|
|
||||||
NDS::Init();
|
NDS::Init();
|
||||||
|
rompath = wxEmptyString;
|
||||||
|
GetMenuBar()->Enable(ID_PAUSE, false);
|
||||||
|
GetMenuBar()->Enable(ID_RESET, false);
|
||||||
|
|
||||||
Touching = false;
|
Touching = false;
|
||||||
|
|
||||||
/*SDL_GetWindowPosition(sdlwin, &WindowX, &WindowY);
|
|
||||||
SDL_GetWindowSize(sdlwin, &WindowW, &WindowH);*/
|
|
||||||
|
|
||||||
joy = NULL;
|
joy = NULL;
|
||||||
joyid = -1;
|
joyid = -1;
|
||||||
axismask = 0;
|
}
|
||||||
|
|
||||||
|
void MainFrame::CloseFromOutside()
|
||||||
|
{
|
||||||
|
emuthread = NULL;
|
||||||
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrame::OnClose(wxCloseEvent& event)
|
void MainFrame::OnClose(wxCloseEvent& event)
|
||||||
{
|
{
|
||||||
if (emustatus == 1)
|
if (emuthread)
|
||||||
{
|
{
|
||||||
emustatus = 0;
|
emuthread->EmuExit();
|
||||||
//emustop->Wait();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
emustatus = 0;
|
|
||||||
/*emustatuschangemutex->Lock();
|
|
||||||
emustatuschange->Signal();
|
|
||||||
emustatuschangemutex->Unlock();*/
|
|
||||||
}
|
|
||||||
|
|
||||||
emuthread->Wait();
|
emuthread->Wait();
|
||||||
delete emuthread;
|
delete emuthread;
|
||||||
/*delete emustatuschange;
|
emuthread = NULL;
|
||||||
delete emustatuschangemutex;
|
}
|
||||||
delete emustop;
|
|
||||||
delete emustopmutex;*/
|
|
||||||
|
|
||||||
NDS::DeInit();
|
NDS::DeInit();
|
||||||
|
|
||||||
@ -208,14 +185,6 @@ void MainFrame::OnClose(wxCloseEvent& event)
|
|||||||
joyid = -1;
|
joyid = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*SDL_UnlockTexture(sdltex);
|
|
||||||
delete texmutex;
|
|
||||||
|
|
||||||
SDL_DestroyTexture(sdltex);
|
|
||||||
SDL_DestroyRenderer(sdlrend);
|
|
||||||
|
|
||||||
SDL_DestroyWindow(sdlwin);*/
|
|
||||||
|
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
Destroy();
|
Destroy();
|
||||||
@ -232,20 +201,77 @@ void MainFrame::OnOpenROM(wxCommandEvent& event)
|
|||||||
if (opener.ShowModal() == wxID_CANCEL)
|
if (opener.ShowModal() == wxID_CANCEL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (emustatus == 1)
|
emuthread->EmuPause();
|
||||||
|
|
||||||
|
rompath = opener.GetPath();
|
||||||
|
NDS::LoadROM(rompath.mb_str(), true);
|
||||||
|
|
||||||
|
emuthread->EmuRun();
|
||||||
|
GetMenuBar()->Enable(ID_PAUSE, true);
|
||||||
|
GetMenuBar()->Check(ID_PAUSE, false);
|
||||||
|
GetMenuBar()->Enable(ID_RESET, true);
|
||||||
|
|
||||||
|
if (!joy)
|
||||||
{
|
{
|
||||||
emustatus = 2;
|
if (SDL_NumJoysticks() > 0)
|
||||||
emustop->Wait();
|
{
|
||||||
|
joy = SDL_JoystickOpen(0);
|
||||||
|
joyid = SDL_JoystickInstanceID(joy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainFrame::OnRun(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
// TODO: reduce duplicate code
|
||||||
|
|
||||||
|
if (!emuthread->EmuIsRunning())
|
||||||
|
{
|
||||||
|
NDS::LoadBIOS();
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString filename = opener.GetPath();
|
|
||||||
NDS::LoadROM(filename.mb_str(), true);
|
|
||||||
|
|
||||||
emustatus = 1;
|
|
||||||
/*emustatuschangemutex->Lock();
|
|
||||||
emustatuschange->Signal();
|
|
||||||
emustatuschangemutex->Unlock();*/
|
|
||||||
emuthread->EmuRun();
|
emuthread->EmuRun();
|
||||||
|
GetMenuBar()->Enable(ID_PAUSE, true);
|
||||||
|
GetMenuBar()->Check(ID_PAUSE, false);
|
||||||
|
GetMenuBar()->Enable(ID_RESET, true);
|
||||||
|
|
||||||
|
if (!joy)
|
||||||
|
{
|
||||||
|
if (SDL_NumJoysticks() > 0)
|
||||||
|
{
|
||||||
|
joy = SDL_JoystickOpen(0);
|
||||||
|
joyid = SDL_JoystickInstanceID(joy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainFrame::OnPause(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
if (!emuthread->EmuIsPaused())
|
||||||
|
{
|
||||||
|
emuthread->EmuPause();
|
||||||
|
GetMenuBar()->Check(ID_PAUSE, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emuthread->EmuRun();
|
||||||
|
GetMenuBar()->Check(ID_PAUSE, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainFrame::OnReset(wxCommandEvent& event)
|
||||||
|
{
|
||||||
|
emuthread->EmuPause();
|
||||||
|
|
||||||
|
if (!rompath.IsEmpty())
|
||||||
|
NDS::LoadROM(rompath.mb_str(), true);
|
||||||
|
else
|
||||||
|
NDS::LoadBIOS();
|
||||||
|
|
||||||
|
emuthread->EmuRun();
|
||||||
|
GetMenuBar()->Enable(ID_PAUSE, true);
|
||||||
|
GetMenuBar()->Check(ID_PAUSE, false);
|
||||||
|
GetMenuBar()->Enable(ID_RESET, true);
|
||||||
|
|
||||||
if (!joy)
|
if (!joy)
|
||||||
{
|
{
|
||||||
@ -276,153 +302,6 @@ void MainFrame::OnInputConfig(wxCommandEvent& event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void MainFrame::ProcessSDLEvents()
|
|
||||||
{
|
|
||||||
bool running = (emustatus == 1);
|
|
||||||
SDL_Event evt;
|
|
||||||
|
|
||||||
while (SDL_PollEvent(&evt))
|
|
||||||
{
|
|
||||||
switch (evt.type)
|
|
||||||
{
|
|
||||||
case SDL_WINDOWEVENT:
|
|
||||||
if (evt.window.event != SDL_WINDOWEVENT_EXPOSED)
|
|
||||||
{
|
|
||||||
SDL_GetWindowPosition(sdlwin, &WindowX, &WindowY);
|
|
||||||
SDL_GetWindowSize(sdlwin, &WindowW, &WindowH);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
|
||||||
if (!running) return;
|
|
||||||
if (evt.button.y >= 192 && evt.button.button == SDL_BUTTON_LEFT)
|
|
||||||
{
|
|
||||||
Touching = true;
|
|
||||||
NDS::PressKey(16+6);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_KEYDOWN:
|
|
||||||
if (!running) return;
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
if (evt.key.keysym.scancode == Config::KeyMapping[i]) NDS::PressKey(i);
|
|
||||||
if (evt.key.keysym.scancode == Config::KeyMapping[10]) NDS::PressKey(16);
|
|
||||||
if (evt.key.keysym.scancode == Config::KeyMapping[11]) NDS::PressKey(17);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_KEYUP:
|
|
||||||
if (!running) return;
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
if (evt.key.keysym.scancode == Config::KeyMapping[i]) NDS::ReleaseKey(i);
|
|
||||||
if (evt.key.keysym.scancode == Config::KeyMapping[10]) NDS::ReleaseKey(16);
|
|
||||||
if (evt.key.keysym.scancode == Config::KeyMapping[11]) NDS::ReleaseKey(17);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYBUTTONDOWN:
|
|
||||||
if (!running) return;
|
|
||||||
if (evt.jbutton.which != joyid) return;
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
if (evt.jbutton.button == Config::JoyMapping[i]) NDS::PressKey(i);
|
|
||||||
if (evt.jbutton.button == Config::JoyMapping[10]) NDS::PressKey(16);
|
|
||||||
if (evt.jbutton.button == Config::JoyMapping[11]) NDS::PressKey(17);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYBUTTONUP:
|
|
||||||
if (!running) return;
|
|
||||||
if (evt.jbutton.which != joyid) return;
|
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
if (evt.jbutton.button == Config::JoyMapping[i]) NDS::ReleaseKey(i);
|
|
||||||
if (evt.jbutton.button == Config::JoyMapping[10]) NDS::ReleaseKey(16);
|
|
||||||
if (evt.jbutton.button == Config::JoyMapping[11]) NDS::ReleaseKey(17);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYHATMOTION:
|
|
||||||
if (!running) return;
|
|
||||||
if (evt.jhat.which != joyid) return;
|
|
||||||
if (evt.jhat.hat != 0) return;
|
|
||||||
for (int i = 0; i < 12; i++)
|
|
||||||
{
|
|
||||||
int j = (i >= 10) ? (i+6) : i;
|
|
||||||
if (Config::JoyMapping[i] == 0x101)
|
|
||||||
(evt.jhat.value & SDL_HAT_UP) ? NDS::PressKey(j) : NDS::ReleaseKey(j);
|
|
||||||
else if (Config::JoyMapping[i] == 0x102)
|
|
||||||
(evt.jhat.value & SDL_HAT_RIGHT) ? NDS::PressKey(j) : NDS::ReleaseKey(j);
|
|
||||||
else if (Config::JoyMapping[i] == 0x104)
|
|
||||||
(evt.jhat.value & SDL_HAT_DOWN) ? NDS::PressKey(j) : NDS::ReleaseKey(j);
|
|
||||||
else if (Config::JoyMapping[i] == 0x108)
|
|
||||||
(evt.jhat.value & SDL_HAT_LEFT) ? NDS::PressKey(j) : NDS::ReleaseKey(j);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SDL_JOYAXISMOTION:
|
|
||||||
if (!running) return;
|
|
||||||
if (evt.jaxis.which != joyid) return;
|
|
||||||
if (evt.jaxis.axis == 0)
|
|
||||||
{
|
|
||||||
if (evt.jaxis.value >= 16384) { NDS::PressKey(4); axismask |= 0x1; }
|
|
||||||
else if (axismask & 0x1) { NDS::ReleaseKey(4); axismask &= ~0x1; }
|
|
||||||
if (evt.jaxis.value <= -16384) { NDS::PressKey(5); axismask |= 0x2; }
|
|
||||||
else if (axismask & 0x2) { NDS::ReleaseKey(5); axismask &= ~0x2; }
|
|
||||||
}
|
|
||||||
else if (evt.jaxis.axis == 1)
|
|
||||||
{
|
|
||||||
if (evt.jaxis.value >= 16384) { NDS::PressKey(7); axismask |= 0x4; }
|
|
||||||
else if (axismask & 0x4) { NDS::ReleaseKey(7); axismask &= ~0x4; }
|
|
||||||
if (evt.jaxis.value <= -16384) { NDS::PressKey(6); axismask |= 0x8; }
|
|
||||||
else if (axismask & 0x8) { NDS::ReleaseKey(6); axismask &= ~0x8; }
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Touching)
|
|
||||||
{
|
|
||||||
int mx, my;
|
|
||||||
u32 btn = SDL_GetGlobalMouseState(&mx, &my);
|
|
||||||
if (!(btn & SDL_BUTTON(SDL_BUTTON_LEFT)))
|
|
||||||
{
|
|
||||||
Touching = false;
|
|
||||||
NDS::ReleaseKey(16+6);
|
|
||||||
NDS::ReleaseScreen();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mx -= WindowX;
|
|
||||||
my -= (WindowY + 192);
|
|
||||||
|
|
||||||
if (mx < 0) mx = 0;
|
|
||||||
else if (mx > 255) mx = 255;
|
|
||||||
|
|
||||||
if (my < 0) my = 0;
|
|
||||||
else if (my > 191) my = 191;
|
|
||||||
|
|
||||||
NDS::TouchScreen(mx, my);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
void MainFrame::OnPaint(wxPaintEvent& event)
|
|
||||||
{
|
|
||||||
wxPaintDC dc(this);
|
|
||||||
|
|
||||||
/*texmutex->Lock();
|
|
||||||
SDL_UnlockTexture(sdltex);
|
|
||||||
|
|
||||||
//SDL_RenderClear(sdlrend);
|
|
||||||
SDL_RenderCopy(sdlrend, sdltex, NULL, NULL);
|
|
||||||
SDL_RenderPresent(sdlrend);
|
|
||||||
|
|
||||||
SDL_LockTexture(sdltex, NULL, &texpixels, &texstride);
|
|
||||||
texmutex->Unlock();
|
|
||||||
|
|
||||||
ProcessSDLEvents();*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainFrame::OnIdle(wxIdleEvent& event)
|
|
||||||
{
|
|
||||||
//ProcessSDLEvents();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EmuThread::EmuThread(MainFrame* parent)
|
EmuThread::EmuThread(MainFrame* parent)
|
||||||
: wxThread(wxTHREAD_JOINABLE)
|
: wxThread(wxTHREAD_JOINABLE)
|
||||||
@ -436,8 +315,8 @@ EmuThread::~EmuThread()
|
|||||||
|
|
||||||
wxThread::ExitCode EmuThread::Entry()
|
wxThread::ExitCode EmuThread::Entry()
|
||||||
{
|
{
|
||||||
//parent->emustatuschangemutex->Lock();
|
emustatus = 3;
|
||||||
emustatus = 2;
|
emupaused = false;
|
||||||
|
|
||||||
sdlwin = SDL_CreateWindow("melonDS " MELONDS_VERSION,
|
sdlwin = SDL_CreateWindow("melonDS " MELONDS_VERSION,
|
||||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
@ -447,9 +326,12 @@ wxThread::ExitCode EmuThread::Entry()
|
|||||||
sdlrend = SDL_CreateRenderer(sdlwin, -1, SDL_RENDERER_ACCELERATED);// | SDL_RENDERER_PRESENTVSYNC);
|
sdlrend = SDL_CreateRenderer(sdlwin, -1, SDL_RENDERER_ACCELERATED);// | SDL_RENDERER_PRESENTVSYNC);
|
||||||
sdltex = SDL_CreateTexture(sdlrend, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, 256, 384);
|
sdltex = SDL_CreateTexture(sdlrend, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, 256, 384);
|
||||||
|
|
||||||
joyid = -1;
|
|
||||||
axismask = 0;
|
axismask = 0;
|
||||||
|
|
||||||
|
u32 nframes = 0;
|
||||||
|
u32 lasttick = SDL_GetTicks();
|
||||||
|
u32 fpslimitcount = 0;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
if (emustatus == 0) break;
|
if (emustatus == 0) break;
|
||||||
@ -458,7 +340,7 @@ wxThread::ExitCode EmuThread::Entry()
|
|||||||
|
|
||||||
if (emustatus == 1)
|
if (emustatus == 1)
|
||||||
{
|
{
|
||||||
//
|
u32 starttick = SDL_GetTicks();
|
||||||
|
|
||||||
NDS::RunFrame();
|
NDS::RunFrame();
|
||||||
|
|
||||||
@ -481,81 +363,51 @@ wxThread::ExitCode EmuThread::Entry()
|
|||||||
//SDL_RenderClear(sdlrend);
|
//SDL_RenderClear(sdlrend);
|
||||||
SDL_RenderCopy(sdlrend, sdltex, NULL, NULL);
|
SDL_RenderCopy(sdlrend, sdltex, NULL, NULL);
|
||||||
SDL_RenderPresent(sdlrend);
|
SDL_RenderPresent(sdlrend);
|
||||||
|
|
||||||
|
fpslimitcount++;
|
||||||
|
if (fpslimitcount >= 3) fpslimitcount = 0;
|
||||||
|
u32 frametime = (fpslimitcount == 0) ? 16 : 17;
|
||||||
|
|
||||||
|
u32 endtick = SDL_GetTicks();
|
||||||
|
u32 diff = endtick - starttick;
|
||||||
|
if (diff < frametime)
|
||||||
|
Sleep(frametime - diff);
|
||||||
|
|
||||||
|
nframes++;
|
||||||
|
if (nframes >= 30)
|
||||||
|
{
|
||||||
|
u32 tick = SDL_GetTicks();
|
||||||
|
u32 diff = tick - lasttick;
|
||||||
|
lasttick = tick;
|
||||||
|
|
||||||
|
u32 fps = (nframes * 1000) / diff;
|
||||||
|
nframes = 0;
|
||||||
|
|
||||||
|
char melontitle[100];
|
||||||
|
sprintf(melontitle, "%d FPS - melonDS " MELONDS_VERSION, fps);
|
||||||
|
SDL_SetWindowTitle(sdlwin, melontitle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Sleep(17);
|
nframes = 0;
|
||||||
|
lasttick = SDL_GetTicks();
|
||||||
|
fpslimitcount = 0;
|
||||||
|
|
||||||
|
emupaused = true;
|
||||||
|
Sleep(50);
|
||||||
|
|
||||||
|
if (emustatus == 2)
|
||||||
|
{
|
||||||
|
char* melontitle = "Paused - melonDS " MELONDS_VERSION;
|
||||||
|
SDL_SetWindowTitle(sdlwin, melontitle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*for (;;)
|
SDL_DestroyTexture(sdltex);
|
||||||
{
|
SDL_DestroyRenderer(sdlrend);
|
||||||
parent->emustatuschange->Wait();
|
SDL_DestroyWindow(sdlwin);
|
||||||
|
|
||||||
if (parent->emustatus == 1)
|
|
||||||
{
|
|
||||||
u32 nframes = 0;
|
|
||||||
u32 lasttick = SDL_GetTicks();
|
|
||||||
u32 fpslimitcount = 0;
|
|
||||||
|
|
||||||
while (parent->emustatus == 1)
|
|
||||||
{
|
|
||||||
u32 starttick = SDL_GetTicks();
|
|
||||||
|
|
||||||
NDS::RunFrame();
|
|
||||||
|
|
||||||
parent->texmutex->Lock();
|
|
||||||
|
|
||||||
if (parent->texstride == 256*4)
|
|
||||||
{
|
|
||||||
memcpy(parent->texpixels, GPU::Framebuffer, 256*384*4);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int dsty = 0;
|
|
||||||
for (int y = 0; y < 256*384; y+=256)
|
|
||||||
{
|
|
||||||
memcpy(&((u8*)parent->texpixels)[dsty], &GPU::Framebuffer[y], 256*4);
|
|
||||||
dsty += parent->texstride;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parent->texmutex->Unlock();
|
|
||||||
parent->Refresh();
|
|
||||||
|
|
||||||
fpslimitcount++;
|
|
||||||
if (fpslimitcount >= 3) fpslimitcount = 0;
|
|
||||||
u32 frametime = (fpslimitcount == 0) ? 16 : 17;
|
|
||||||
|
|
||||||
u32 endtick = SDL_GetTicks();
|
|
||||||
u32 diff = endtick - starttick;
|
|
||||||
if (diff < frametime)
|
|
||||||
Sleep(frametime - diff);
|
|
||||||
|
|
||||||
nframes++;
|
|
||||||
if (nframes >= 30)
|
|
||||||
{
|
|
||||||
u32 tick = SDL_GetTicks();
|
|
||||||
u32 diff = tick - lasttick;
|
|
||||||
lasttick = tick;
|
|
||||||
|
|
||||||
u32 fps = (nframes * 1000) / diff;
|
|
||||||
nframes = 0;
|
|
||||||
|
|
||||||
char melontitle[100];
|
|
||||||
sprintf(melontitle, "%d FPS - melonDS " MELONDS_VERSION, fps);
|
|
||||||
parent->SetTitle(melontitle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parent->emustopmutex->Lock();
|
|
||||||
parent->emustop->Signal();
|
|
||||||
parent->emustopmutex->Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent->emustatus == 0)
|
|
||||||
break;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return (wxThread::ExitCode)0;
|
return (wxThread::ExitCode)0;
|
||||||
}
|
}
|
||||||
@ -570,6 +422,14 @@ void EmuThread::ProcessEvents()
|
|||||||
switch (evt.type)
|
switch (evt.type)
|
||||||
{
|
{
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
|
if (evt.window.event == SDL_WINDOWEVENT_CLOSE)
|
||||||
|
{
|
||||||
|
wxThread* thread = parent->emuthread;
|
||||||
|
parent->CloseFromOutside();
|
||||||
|
EmuExit();
|
||||||
|
delete thread;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (evt.window.event != SDL_WINDOWEVENT_EXPOSED)
|
if (evt.window.event != SDL_WINDOWEVENT_EXPOSED)
|
||||||
{
|
{
|
||||||
SDL_GetWindowPosition(sdlwin, &WindowX, &WindowY);
|
SDL_GetWindowPosition(sdlwin, &WindowX, &WindowY);
|
||||||
@ -604,7 +464,7 @@ void EmuThread::ProcessEvents()
|
|||||||
|
|
||||||
case SDL_JOYBUTTONDOWN:
|
case SDL_JOYBUTTONDOWN:
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
if (evt.jbutton.which != joyid) return;
|
if (evt.jbutton.which != parent->joyid) return;
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
if (evt.jbutton.button == Config::JoyMapping[i]) NDS::PressKey(i);
|
if (evt.jbutton.button == Config::JoyMapping[i]) NDS::PressKey(i);
|
||||||
if (evt.jbutton.button == Config::JoyMapping[10]) NDS::PressKey(16);
|
if (evt.jbutton.button == Config::JoyMapping[10]) NDS::PressKey(16);
|
||||||
@ -613,7 +473,7 @@ void EmuThread::ProcessEvents()
|
|||||||
|
|
||||||
case SDL_JOYBUTTONUP:
|
case SDL_JOYBUTTONUP:
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
if (evt.jbutton.which != joyid) return;
|
if (evt.jbutton.which != parent->joyid) return;
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
if (evt.jbutton.button == Config::JoyMapping[i]) NDS::ReleaseKey(i);
|
if (evt.jbutton.button == Config::JoyMapping[i]) NDS::ReleaseKey(i);
|
||||||
if (evt.jbutton.button == Config::JoyMapping[10]) NDS::ReleaseKey(16);
|
if (evt.jbutton.button == Config::JoyMapping[10]) NDS::ReleaseKey(16);
|
||||||
@ -622,7 +482,7 @@ void EmuThread::ProcessEvents()
|
|||||||
|
|
||||||
case SDL_JOYHATMOTION:
|
case SDL_JOYHATMOTION:
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
if (evt.jhat.which != joyid) return;
|
if (evt.jhat.which != parent->joyid) return;
|
||||||
if (evt.jhat.hat != 0) return;
|
if (evt.jhat.hat != 0) return;
|
||||||
for (int i = 0; i < 12; i++)
|
for (int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
@ -640,7 +500,7 @@ void EmuThread::ProcessEvents()
|
|||||||
|
|
||||||
case SDL_JOYAXISMOTION:
|
case SDL_JOYAXISMOTION:
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
if (evt.jaxis.which != joyid) return;
|
if (evt.jaxis.which != parent->joyid) return;
|
||||||
if (evt.jaxis.axis == 0)
|
if (evt.jaxis.axis == 0)
|
||||||
{
|
{
|
||||||
if (evt.jaxis.value >= 16384) { NDS::PressKey(4); axismask |= 0x1; }
|
if (evt.jaxis.value >= 16384) { NDS::PressKey(4); axismask |= 0x1; }
|
||||||
@ -659,7 +519,7 @@ void EmuThread::ProcessEvents()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Touching)
|
if (Touching && running)
|
||||||
{
|
{
|
||||||
int mx, my;
|
int mx, my;
|
||||||
u32 btn = SDL_GetGlobalMouseState(&mx, &my);
|
u32 btn = SDL_GetGlobalMouseState(&mx, &my);
|
||||||
|
@ -51,24 +51,14 @@ class MainFrame : public wxFrame
|
|||||||
public:
|
public:
|
||||||
MainFrame();
|
MainFrame();
|
||||||
|
|
||||||
SDL_Window* sdlwin;
|
|
||||||
SDL_Renderer* sdlrend;
|
|
||||||
SDL_Texture* sdltex;
|
|
||||||
|
|
||||||
SDL_Joystick* joy;
|
SDL_Joystick* joy;
|
||||||
SDL_JoystickID joyid;
|
SDL_JoystickID joyid;
|
||||||
u8 axismask;
|
|
||||||
|
|
||||||
wxMutex* texmutex;
|
|
||||||
void* texpixels;
|
|
||||||
int texstride;
|
|
||||||
|
|
||||||
int emustatus;
|
|
||||||
EmuThread* emuthread;
|
EmuThread* emuthread;
|
||||||
wxMutex* emustatuschangemutex;
|
|
||||||
wxCondition* emustatuschange;
|
wxString rompath;
|
||||||
wxMutex* emustopmutex;
|
|
||||||
wxCondition* emustop;
|
void CloseFromOutside();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
@ -77,12 +67,13 @@ private:
|
|||||||
void OnCloseFromMenu(wxCommandEvent& event);
|
void OnCloseFromMenu(wxCommandEvent& event);
|
||||||
void OnOpenROM(wxCommandEvent& event);
|
void OnOpenROM(wxCommandEvent& event);
|
||||||
|
|
||||||
|
void OnRun(wxCommandEvent& event);
|
||||||
|
void OnPause(wxCommandEvent& event);
|
||||||
|
void OnReset(wxCommandEvent& event);
|
||||||
|
|
||||||
void OnInputConfig(wxCommandEvent& event);
|
void OnInputConfig(wxCommandEvent& event);
|
||||||
|
|
||||||
void ProcessSDLEvents();
|
void ProcessSDLEvents();
|
||||||
|
|
||||||
void OnPaint(wxPaintEvent& event);
|
|
||||||
void OnIdle(wxIdleEvent& event);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class EmuThread : public wxThread
|
class EmuThread : public wxThread
|
||||||
@ -91,10 +82,13 @@ public:
|
|||||||
EmuThread(MainFrame* parent);
|
EmuThread(MainFrame* parent);
|
||||||
~EmuThread();
|
~EmuThread();
|
||||||
|
|
||||||
void EmuRun() { emustatus = 1; }
|
void EmuRun() { emustatus = 1; emupaused = false; SDL_RaiseWindow(sdlwin); }
|
||||||
void EmuPause() { emustatus = 2; }
|
void EmuPause() { emustatus = 2; while (!emupaused); }
|
||||||
void EmuExit() { emustatus = 0; }
|
void EmuExit() { emustatus = 0; }
|
||||||
|
|
||||||
|
bool EmuIsRunning() { return (emustatus == 1) || (emustatus == 2); }
|
||||||
|
bool EmuIsPaused() { return (emustatus == 2) && emupaused; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ExitCode Entry();
|
virtual ExitCode Entry();
|
||||||
void ProcessEvents();
|
void ProcessEvents();
|
||||||
@ -108,10 +102,10 @@ protected:
|
|||||||
void* texpixels;
|
void* texpixels;
|
||||||
int texstride;
|
int texstride;
|
||||||
|
|
||||||
int joyid;
|
|
||||||
u32 axismask;
|
u32 axismask;
|
||||||
|
|
||||||
int emustatus;
|
int emustatus;
|
||||||
|
volatile bool emupaused;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // WX_MAIN_H
|
#endif // WX_MAIN_H
|
||||||
|
Reference in New Issue
Block a user