aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libui_sdl/main.cpp')
-rw-r--r--src/libui_sdl/main.cpp3061
1 files changed, 3061 insertions, 0 deletions
diff --git a/src/libui_sdl/main.cpp b/src/libui_sdl/main.cpp
new file mode 100644
index 0000000..0066668
--- /dev/null
+++ b/src/libui_sdl/main.cpp
@@ -0,0 +1,3061 @@
+/*
+ Copyright 2016-2020 Arisotura
+
+ 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 <time.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef __WIN32__
+#include <glib.h>
+#endif
+
+#include <SDL2/SDL.h>
+#include "libui/ui.h"
+
+#include "../OpenGLSupport.h"
+#include "main_shaders.h"
+
+#include "../types.h"
+#include "../version.h"
+#include "PlatformConfig.h"
+
+#include "DlgEmuSettings.h"
+#include "DlgInputConfig.h"
+#include "DlgVideoSettings.h"
+#include "DlgAudioSettings.h"
+#include "DlgWifiSettings.h"
+
+#include "../NDS.h"
+#include "../GBACart.h"
+#include "../GPU.h"
+#include "../SPU.h"
+#include "../Wifi.h"
+#include "../Platform.h"
+#include "../Config.h"
+#include "../ARMJIT.h"
+
+#include "../Savestate.h"
+
+#include "OSD.h"
+
+#ifdef MELONCAP
+#include "MelonCap.h"
+#endif // MELONCAP
+
+
+// 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 kScreenSize[4] = {1, 2, 3, 4};
+const int kScreenRot[4] = {0, 1, 2, 3};
+const int kScreenGap[6] = {0, 1, 8, 64, 90, 128};
+const int kScreenLayout[3] = {0, 1, 2};
+const int kScreenSizing[4] = {0, 1, 2, 3};
+
+
+char* EmuDirectory;
+
+
+uiWindow* MainWindow;
+uiArea* MainDrawArea;
+uiAreaHandler MainDrawAreaHandler;
+
+const u32 kGLVersions[] = {uiGLVersion(3,2), uiGLVersion(3,1), 0};
+uiGLContext* GLContext;
+
+int WindowWidth, WindowHeight;
+
+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];
+uiMenuItem* MenuItem_ScreenSizing[4];
+
+uiMenuItem* MenuItem_ScreenFilter;
+uiMenuItem* MenuItem_LimitFPS;
+uiMenuItem* MenuItem_AudioSync;
+uiMenuItem* MenuItem_ShowOSD;
+
+SDL_Thread* EmuThread;
+int EmuRunning;
+volatile int EmuStatus;
+
+bool RunningSomething;
+char ROMPath[2][1024];
+char SRAMPath[2][1024];
+char PrevSRAMPath[2][1024]; // for savestate 'undo load'
+
+bool SavestateLoaded;
+
+bool Screen_UseGL;
+
+bool ScreenDrawInited = false;
+uiDrawBitmap* ScreenBitmap[2] = {NULL,NULL};
+
+GLuint GL_ScreenShader[3];
+GLuint GL_ScreenShaderAccel[3];
+GLuint GL_ScreenShaderOSD[3];
+struct
+{
+ float uScreenSize[2];
+ u32 u3DScale;
+ u32 uFilterMode;
+
+} GL_ShaderConfig;
+GLuint GL_ShaderConfigUBO;
+GLuint GL_ScreenVertexArrayID, GL_ScreenVertexBufferID;
+float GL_ScreenVertices[2 * 3*2 * 4]; // position/texcoord
+GLuint GL_ScreenTexture;
+bool GL_ScreenSizeDirty;
+
+int GL_3DScale;
+
+bool GL_VSyncStatus;
+
+int ScreenGap = 0;
+int ScreenLayout = 0;
+int ScreenSizing = 0;
+int ScreenRotation = 0;
+
+int MainScreenPos[3];
+int AutoScreenSizing;
+
+uiRect TopScreenRect;
+uiRect BottomScreenRect;
+uiDrawMatrix TopScreenTrans;
+uiDrawMatrix BottomScreenTrans;
+
+bool Touching = false;
+
+u32 KeyInputMask, JoyInputMask;
+u32 KeyHotkeyMask, JoyHotkeyMask;
+u32 HotkeyMask, LastHotkeyMask;
+u32 HotkeyPress, HotkeyRelease;
+
+#define HotkeyDown(hk) (HotkeyMask & (1<<(hk)))
+#define HotkeyPressed(hk) (HotkeyPress & (1<<(hk)))
+#define HotkeyReleased(hk) (HotkeyRelease & (1<<(hk)))
+
+bool LidStatus;
+
+int JoystickID;
+SDL_Joystick* Joystick;
+
+int AudioFreq;
+float AudioSampleFrac;
+SDL_AudioDeviceID AudioDevice, MicDevice;
+
+SDL_cond* AudioSync;
+SDL_mutex* AudioSyncLock;
+
+u32 MicBufferLength = 2048;
+s16 MicBuffer[2048];
+u32 MicBufferReadPos, MicBufferWritePos;
+
+u32 MicWavLength;
+s16* MicWavBuffer;
+
+void SetupScreenRects(int width, int height);
+
+void TogglePause(void* blarg);
+void Reset(void* blarg);
+
+void SetupSRAMPath(int slot);
+
+void SaveState(int slot);
+void LoadState(int slot);
+void UndoStateLoad();
+void GetSavestateName(int slot, char* filename, int len);
+
+void CreateMainWindow(bool opengl);
+void DestroyMainWindow();
+void RecreateMainWindow(bool opengl);
+
+
+
+bool GLScreen_InitShader(GLuint* shader, const char* fs)
+{
+ if (!OpenGL_BuildShaderProgram(kScreenVS, fs, shader, "ScreenShader"))
+ return false;
+
+ glBindAttribLocation(shader[2], 0, "vPosition");
+ glBindAttribLocation(shader[2], 1, "vTexcoord");
+ glBindFragDataLocation(shader[2], 0, "oColor");
+
+ if (!OpenGL_LinkShaderProgram(shader))
+ return false;
+
+ GLuint uni_id;
+
+ uni_id = glGetUniformBlockIndex(shader[2], "uConfig");
+ glUniformBlockBinding(shader[2], uni_id, 16);
+
+ glUseProgram(shader[2]);
+ uni_id = glGetUniformLocation(shader[2], "ScreenTex");
+ glUniform1i(uni_id, 0);
+ uni_id = glGetUniformLocation(shader[2], "_3DTex");
+ glUniform1i(uni_id, 1);
+
+ return true;
+}
+
+bool GLScreen_InitOSDShader(GLuint* shader)
+{
+ if (!OpenGL_BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, shader, "ScreenShaderOSD"))
+ return false;
+
+ glBindAttribLocation(shader[2], 0, "vPosition");
+ glBindFragDataLocation(shader[2], 0, "oColor");
+
+ if (!OpenGL_LinkShaderProgram(shader))
+ return false;
+
+ GLuint uni_id;
+
+ uni_id = glGetUniformBlockIndex(shader[2], "uConfig");
+ glUniformBlockBinding(shader[2], uni_id, 16);
+
+ glUseProgram(shader[2]);
+ uni_id = glGetUniformLocation(shader[2], "OSDTex");
+ glUniform1i(uni_id, 0);
+
+ return true;
+}
+
+bool GLScreen_Init()
+{
+ GL_VSyncStatus = Config::ScreenVSync;
+
+ // TODO: consider using epoxy?
+ if (!OpenGL_Init())
+ return false;
+
+ const GLubyte* renderer = glGetString(GL_RENDERER); // get renderer string
+ const GLubyte* version = glGetString(GL_VERSION); // version as a string
+ printf("OpenGL: renderer: %s\n", renderer);
+ printf("OpenGL: version: %s\n", version);
+
+ if (!GLScreen_InitShader(GL_ScreenShader, kScreenFS))
+ return false;
+ if (!GLScreen_InitShader(GL_ScreenShaderAccel, kScreenFS_Accel))
+ return false;
+ if (!GLScreen_InitOSDShader(GL_ScreenShaderOSD))
+ return false;
+
+ memset(&GL_ShaderConfig, 0, sizeof(GL_ShaderConfig));
+
+ glGenBuffers(1, &GL_ShaderConfigUBO);
+ glBindBuffer(GL_UNIFORM_BUFFER, GL_ShaderConfigUBO);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(GL_ShaderConfig), &GL_ShaderConfig, GL_STATIC_DRAW);
+ glBindBufferBase(GL_UNIFORM_BUFFER, 16, GL_ShaderConfigUBO);
+
+ glGenBuffers(1, &GL_ScreenVertexBufferID);
+ glBindBuffer(GL_ARRAY_BUFFER, GL_ScreenVertexBufferID);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(GL_ScreenVertices), NULL, GL_STATIC_DRAW);
+
+ glGenVertexArrays(1, &GL_ScreenVertexArrayID);
+ glBindVertexArray(GL_ScreenVertexArrayID);
+ glEnableVertexAttribArray(0); // position
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0));
+ glEnableVertexAttribArray(1); // texcoord
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4));
+
+ glGenTextures(1, &GL_ScreenTexture);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, GL_ScreenTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL);
+
+ GL_ScreenSizeDirty = true;
+
+ return true;
+}
+
+void GLScreen_DeInit()
+{
+ glDeleteTextures(1, &GL_ScreenTexture);
+
+ glDeleteVertexArrays(1, &GL_ScreenVertexArrayID);
+ glDeleteBuffers(1, &GL_ScreenVertexBufferID);
+
+ OpenGL_DeleteShaderProgram(GL_ScreenShader);
+ OpenGL_DeleteShaderProgram(GL_ScreenShaderAccel);
+ OpenGL_DeleteShaderProgram(GL_ScreenShaderOSD);
+}
+
+void GLScreen_DrawScreen()
+{
+ bool vsync = Config::ScreenVSync && !HotkeyDown(HK_FastForward);
+ if (vsync != GL_VSyncStatus)
+ {
+ GL_VSyncStatus = vsync;
+ uiGLSetVSync(vsync);
+ }
+
+ float scale = uiGLGetFramebufferScale(GLContext);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, uiGLGetFramebuffer(GLContext));
+
+ if (GL_ScreenSizeDirty)
+ {
+ GL_ScreenSizeDirty = false;
+
+ GL_ShaderConfig.uScreenSize[0] = WindowWidth;
+ GL_ShaderConfig.uScreenSize[1] = WindowHeight;
+ GL_ShaderConfig.u3DScale = GL_3DScale;
+
+ glBindBuffer(GL_UNIFORM_BUFFER, GL_ShaderConfigUBO);
+ void* unibuf = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY);
+ if (unibuf) memcpy(unibuf, &GL_ShaderConfig, sizeof(GL_ShaderConfig));
+ glUnmapBuffer(GL_UNIFORM_BUFFER);
+
+ float scwidth, scheight;
+
+ float x0, y0, x1, y1;
+ float s0, s1, s2, s3;
+ float t0, t1, t2, t3;
+
+#define SETVERTEX(i, x, y, s, t) \
+ GL_ScreenVertices[4*(i) + 0] = x; \
+ GL_ScreenVertices[4*(i) + 1] = y; \
+ GL_ScreenVertices[4*(i) + 2] = s; \
+ GL_ScreenVertices[4*(i) + 3] = t;
+
+ x0 = TopScreenRect.X;
+ y0 = TopScreenRect.Y;
+ x1 = TopScreenRect.X + TopScreenRect.Width;
+ y1 = TopScreenRect.Y + TopScreenRect.Height;
+
+ scwidth = 256;
+ scheight = 192;
+
+ switch (ScreenRotation)
+ {
+ case 0:
+ s0 = 0; t0 = 0;
+ s1 = scwidth; t1 = 0;
+ s2 = 0; t2 = scheight;
+ s3 = scwidth; t3 = scheight;
+ break;
+
+ case 1:
+ s0 = 0; t0 = scheight;
+ s1 = 0; t1 = 0;
+ s2 = scwidth; t2 = scheight;
+ s3 = scwidth; t3 = 0;
+ break;
+
+ case 2:
+ s0 = scwidth; t0 = scheight;
+ s1 = 0; t1 = scheight;
+ s2 = scwidth; t2 = 0;
+ s3 = 0; t3 = 0;
+ break;
+
+ case 3:
+ s0 = scwidth; t0 = 0;
+ s1 = scwidth; t1 = scheight;
+ s2 = 0; t2 = 0;
+ s3 = 0; t3 = scheight;
+ break;
+ }
+
+ SETVERTEX(0, x0, y0, s0, t0);
+ SETVERTEX(1, x1, y1, s3, t3);
+ SETVERTEX(2, x1, y0, s1, t1);
+ SETVERTEX(3, x0, y0, s0, t0);
+ SETVERTEX(4, x0, y1, s2, t2);
+ SETVERTEX(5, x1, y1, s3, t3);
+
+ x0 = BottomScreenRect.X;
+ y0 = BottomScreenRect.Y;
+ x1 = BottomScreenRect.X + BottomScreenRect.Width;
+ y1 = BottomScreenRect.Y + BottomScreenRect.Height;
+
+ scwidth = 256;
+ scheight = 192;
+
+ switch (ScreenRotation)
+ {
+ case 0:
+ s0 = 0; t0 = 192;
+ s1 = scwidth; t1 = 192;
+ s2 = 0; t2 = 192+scheight;
+ s3 = scwidth; t3 = 192+scheight;
+ break;
+
+ case 1:
+ s0 = 0; t0 = 192+scheight;
+ s1 = 0; t1 = 192;
+ s2 = scwidth; t2 = 192+scheight;
+ s3 = scwidth; t3 = 192;
+ break;
+
+ case 2:
+ s0 = scwidth; t0 = 192+scheight;
+ s1 = 0; t1 = 192+scheight;
+ s2 = scwidth; t2 = 192;
+ s3 = 0; t3 = 192;
+ break;
+
+ case 3:
+ s0 = scwidth; t0 = 192;
+ s1 = scwidth; t1 = 192+scheight;
+ s2 = 0; t2 = 192;
+ s3 = 0; t3 = 192+scheight;
+ break;
+ }
+
+ SETVERTEX(6, x0, y0, s0, t0);
+ SETVERTEX(7, x1, y1, s3, t3);
+ SETVERTEX(8, x1, y0, s1, t1);
+ SETVERTEX(9, x0, y0, s0, t0);
+ SETVERTEX(10, x0, y1, s2, t2);
+ SETVERTEX(11, x1, y1, s3, t3);
+
+#undef SETVERTEX
+
+ glBindBuffer(GL_ARRAY_BUFFER, GL_ScreenVertexBufferID);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GL_ScreenVertices), GL_ScreenVertices);
+ }
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_BLEND);
+ glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+
+ glViewport(0, 0, WindowWidth*scale, WindowHeight*scale);
+
+ if (GPU3D::Renderer == 0)
+ OpenGL_UseShaderProgram(GL_ScreenShader);
+ else
+ OpenGL_UseShaderProgram(GL_ScreenShaderAccel);
+
+ glClearColor(0, 0, 0, 1);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ if (RunningSomething)
+ {
+ int frontbuf = GPU::FrontBuffer;
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, GL_ScreenTexture);
+
+ if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1])
+ {
+ if (GPU3D::Renderer == 0)
+ {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA_INTEGER,
+ GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 192, GL_RGBA_INTEGER,
+ GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]);
+ }
+ else
+ {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER,
+ GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER,
+ GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]);
+ }
+ }
+
+ glActiveTexture(GL_TEXTURE1);
+ if (GPU3D::Renderer != 0)
+ GPU3D::GLRenderer::SetupAccelFrame();
+
+ glBindBuffer(GL_ARRAY_BUFFER, GL_ScreenVertexBufferID);
+ glBindVertexArray(GL_ScreenVertexArrayID);
+ glDrawArrays(GL_TRIANGLES, 0, 4*3);
+ }
+
+ OpenGL_UseShaderProgram(GL_ScreenShaderOSD);
+ OSD::Update(true, NULL);
+
+ glFlush();
+ uiGLSwapBuffers(GLContext);
+}
+
+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 * srcinc);
+
+ 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;
+ len /= srcinc;
+
+ 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 AudioCallback(void* data, Uint8* stream, int len)
+{
+ len /= (sizeof(s16) * 2);
+
+ // resample incoming audio to match the output sample rate
+
+ float f_len_in = (len * 32823.6328125) / (float)AudioFreq;
+ f_len_in += AudioSampleFrac;
+ int len_in = (int)floor(f_len_in);
+ AudioSampleFrac = f_len_in - len_in;
+
+ s16 buf_in[1024*2];
+ s16* buf_out = (s16*)stream;
+
+ int num_in;
+ int num_out = len;
+
+ SDL_LockMutex(AudioSyncLock);
+ num_in = SPU::ReadOutput(buf_in, len_in);
+ SDL_CondSignal(AudioSync);
+ SDL_UnlockMutex(AudioSyncLock);
+
+ if (num_in < 1)
+ {
+ memset(stream, 0, len*sizeof(s16)*2);
+ return;
+ }
+
+ int margin = 6;
+ if (num_in < len_in-margin)
+ {
+ int last = num_in-1;
+ if (last < 0) last = 0;
+
+ for (int i = num_in; i < len_in-margin; i++)
+ ((u32*)buf_in)[i] = ((u32*)buf_in)[last];
+
+ num_in = len_in-margin;
+ }
+
+ float res_incr = num_in / (float)num_out;
+ float res_timer = 0;
+ int res_pos = 0;
+
+ int volume = Config::AudioVolume;
+
+ for (int i = 0; i < len; i++)
+ {
+ buf_out[i*2 ] = (buf_in[res_pos*2 ] * volume) >> 8;
+ buf_out[i*2+1] = (buf_in[res_pos*2+1] * volume) >> 8;
+
+ /*s16 s_l = buf_in[res_pos*2 ];
+ s16 s_r = buf_in[res_pos*2+1];
+
+ float a = res_timer;
+ float b = 1.0 - a;
+ s_l = (s_l * a) + (buf_in[(res_pos-1)*2 ] * b);
+ s_r = (s_r * a) + (buf_in[(res_pos-1)*2+1] * b);
+
+ buf_out[i*2 ] = (s_l * volume) >> 8;
+ buf_out[i*2+1] = (s_r * volume) >> 8;*/
+
+ res_timer += res_incr;
+ while (res_timer >= 1.0)
+ {
+ res_timer -= 1.0;
+ res_pos++;
+ }
+ }
+}
+
+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;
+ }
+}
+
+void FeedMicInput()
+{
+ int type = Config::MicInputType;
+ bool cmd = HotkeyDown(HK_Mic);
+
+ if ((type != 1 && !cmd) ||
+ (type == 1 && MicBufferLength == 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;
+ }
+}
+
+void OpenJoystick()
+{
+ if (Joystick) SDL_JoystickClose(Joystick);
+
+ int num = SDL_NumJoysticks();
+ if (num < 1)
+ {
+ Joystick = NULL;
+ return;
+ }
+
+ if (JoystickID >= num)
+ JoystickID = 0;
+
+ Joystick = SDL_JoystickOpen(JoystickID);
+}
+
+bool JoystickButtonDown(int val)
+{
+ if (val == -1) return false;
+
+ bool hasbtn = ((val & 0xFFFF) != 0xFFFF);
+
+ if (hasbtn)
+ {
+ if (val & 0x100)
+ {
+ int hatnum = (val >> 4) & 0xF;
+ int hatdir = val & 0xF;
+ Uint8 hatval = SDL_JoystickGetHat(Joystick, hatnum);
+
+ bool pressed = false;
+ if (hatdir == 0x1) pressed = (hatval & SDL_HAT_UP);
+ else if (hatdir == 0x4) pressed = (hatval & SDL_HAT_DOWN);
+ else if (hatdir == 0x2) pressed = (hatval & SDL_HAT_RIGHT);
+ else if (hatdir == 0x8) pressed = (hatval & SDL_HAT_LEFT);
+
+ if (pressed) return true;
+ }
+ else
+ {
+ int btnnum = val & 0xFFFF;
+ Uint8 btnval = SDL_JoystickGetButton(Joystick, btnnum);
+
+ if (btnval) return true;
+ }
+ }
+
+ if (val & 0x10000)
+ {
+ int axisnum = (val >> 24) & 0xF;
+ int axisdir = (val >> 20) & 0xF;
+ Sint16 axisval = SDL_JoystickGetAxis(Joystick, axisnum);
+
+ switch (axisdir)
+ {
+ case 0: // positive
+ if (axisval > 16384) return true;
+ break;
+
+ case 1: // negative
+ if (axisval < -16384) return true;
+ break;
+
+ case 2: // trigger
+ if (axisval > 0) return true;
+ break;
+ }
+ }
+
+ return false;
+}
+
+void ProcessInput()
+{
+ SDL_JoystickUpdate();
+
+ if (Joystick)
+ {
+ if (!SDL_JoystickGetAttached(Joystick))
+ {
+ SDL_JoystickClose(Joystick);
+ Joystick = NULL;
+ }
+ }
+ if (!Joystick && (SDL_NumJoysticks() > 0))
+ {
+ JoystickID = Config::JoystickID;
+ OpenJoystick();
+ }
+
+ JoyInputMask = 0xFFF;
+ for (int i = 0; i < 12; i++)
+ if (JoystickButtonDown(Config::JoyMapping[i]))
+ JoyInputMask &= ~(1<<i);
+
+ JoyHotkeyMask = 0;
+ for (int i = 0; i < HK_MAX; i++)
+ if (JoystickButtonDown(Config::HKJoyMapping[i]))
+ JoyHotkeyMask |= (1<<i);
+
+ HotkeyMask = KeyHotkeyMask | JoyHotkeyMask;
+ HotkeyPress = HotkeyMask & ~LastHotkeyMask;
+ HotkeyRelease = LastHotkeyMask & ~HotkeyMask;
+ LastHotkeyMask = HotkeyMask;
+}
+
+bool JoyButtonPressed(int btnid, int njoybuttons, Uint8* joybuttons, Uint32 hat)
+{
+ if (btnid < 0) return false;
+
+ hat &= ~(hat >> 4);
+
+ bool pressed = false;
+ 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 if (btnid < njoybuttons)
+ pressed = (joybuttons[btnid] & ~(joybuttons[btnid] >> 1)) & 0x01;
+
+ return pressed;
+}
+
+bool JoyButtonHeld(int btnid, int njoybuttons, Uint8* joybuttons, Uint32 hat)
+{
+ if (btnid < 0) return false;
+
+ bool pressed = false;
+ 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 if (btnid < njoybuttons)
+ pressed = joybuttons[btnid] & 0x01;
+
+ return pressed;
+}
+
+void UpdateWindowTitle(void* data)
+{
+ if (EmuStatus == 0) return;
+ void** dataarray = (void**)data;
+ SDL_LockMutex((SDL_mutex*)dataarray[1]);
+ uiWindowSetTitle(MainWindow, (const char*)dataarray[0]);
+ SDL_UnlockMutex((SDL_mutex*)dataarray[1]);
+}
+
+void UpdateFPSLimit(void* data)
+{
+ uiMenuItemSetChecked(MenuItem_LimitFPS, Config::LimitFPS==1);
+}
+
+int EmuThreadFunc(void* burp)
+{
+ NDS::Init();
+
+ MainScreenPos[0] = 0;
+ MainScreenPos[1] = 0;
+ MainScreenPos[2] = 0;
+ AutoScreenSizing = 0;
+
+ if (Screen_UseGL)
+ {
+ uiGLMakeContextCurrent(GLContext);
+ GPU3D::InitRenderer(true);
+ uiGLMakeContextCurrent(NULL);
+ }
+ else
+ {
+ GPU3D::InitRenderer(false);
+ }
+
+ Touching = false;
+ KeyInputMask = 0xFFF;
+ JoyInputMask = 0xFFF;
+ KeyHotkeyMask = 0;
+ JoyHotkeyMask = 0;
+ HotkeyMask = 0;
+ LastHotkeyMask = 0;
+ LidStatus = false;
+
+ u32 nframes = 0;
+ u32 starttick = SDL_GetTicks();
+ u32 lasttick = starttick;
+ u32 lastmeasuretick = lasttick;
+ u32 fpslimitcount = 0;
+ u64 perfcount = SDL_GetPerformanceCounter();
+ u64 perffreq = SDL_GetPerformanceFrequency();
+ float samplesleft = 0;
+ u32 nsamples = 0;
+
+ char melontitle[100];
+ SDL_mutex* titlemutex = SDL_CreateMutex();
+ void* titledata[2] = {melontitle, titlemutex};
+
+ while (EmuRunning != 0)
+ {
+ ProcessInput();
+
+ if (HotkeyPressed(HK_FastForwardToggle))
+ {
+ Config::LimitFPS = !Config::LimitFPS;
+ uiQueueMain(UpdateFPSLimit, NULL);
+ }
+ // TODO: similar hotkeys for video/audio sync?
+
+ if (HotkeyPressed(HK_Pause)) uiQueueMain(TogglePause, NULL);
+ if (HotkeyPressed(HK_Reset)) uiQueueMain(Reset, NULL);
+
+ if (GBACart::CartInserted && GBACart::HasSolarSensor)
+ {
+ if (HotkeyPressed(HK_SolarSensorDecrease))
+ {
+ if (GBACart_SolarSensor::LightLevel > 0) GBACart_SolarSensor::LightLevel--;
+ char msg[64];
+ sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel);
+ OSD::AddMessage(0, msg);
+ }
+ if (HotkeyPressed(HK_SolarSensorIncrease))
+ {
+ if (GBACart_SolarSensor::LightLevel < 10) GBACart_SolarSensor::LightLevel++;
+ char msg[64];
+ sprintf(msg, "Solar sensor level set to %d", GBACart_SolarSensor::LightLevel);
+ OSD::AddMessage(0, msg);
+ }
+ }
+
+ if (EmuRunning == 1)
+ {
+ EmuStatus = 1;
+
+ // process input and hotkeys
+ NDS::SetKeyMask(KeyInputMask & JoyInputMask);
+
+ if (HotkeyPressed(HK_Lid))
+ {
+ LidStatus = !LidStatus;
+ NDS::SetLidClosed(LidStatus);
+ OSD::AddMessage(0, LidStatus ? "Lid closed" : "Lid opened");
+ }
+
+ // microphone input
+ FeedMicInput();
+
+ if (Screen_UseGL)
+ {
+ uiGLBegin(GLContext);
+ uiGLMakeContextCurrent(GLContext);
+ }
+
+ // auto screen layout
+ {
+ MainScreenPos[2] = MainScreenPos[1];
+ MainScreenPos[1] = MainScreenPos[0];
+ MainScreenPos[0] = NDS::PowerControl9 >> 15;
+
+ int guess;
+ if (MainScreenPos[0] == MainScreenPos[2] &&
+ MainScreenPos[0] != MainScreenPos[1])
+ {
+ // constant flickering, likely displaying 3D on both screens
+ // TODO: when both screens are used for 2D only...???
+ guess = 0;
+ }
+ else
+ {
+ if (MainScreenPos[0] == 1)
+ guess = 1;
+ else
+ guess = 2;
+ }
+
+ if (guess != AutoScreenSizing)
+ {
+ AutoScreenSizing = guess;
+ SetupScreenRects(WindowWidth, WindowHeight);
+ }
+ }
+
+ // emulate
+ u32 nlines = NDS::RunFrame();
+
+#ifdef MELONCAP
+ MelonCap::Update();
+#endif // MELONCAP
+
+ if (EmuRunning == 0) break;
+
+ if (Screen_UseGL)
+ {
+ GLScreen_DrawScreen();
+ uiGLEnd(GLContext);
+ }
+ uiAreaQueueRedrawAll(MainDrawArea);
+
+ bool fastforward = HotkeyDown(HK_FastForward);
+
+ if (Config::AudioSync && !fastforward)
+ {
+ SDL_LockMutex(AudioSyncLock);
+ while (SPU::GetOutputSize() > 1024)
+ {
+ int ret = SDL_CondWaitTimeout(AudioSync, AudioSyncLock, 500);
+ if (ret == SDL_MUTEX_TIMEDOUT) break;
+ }
+ SDL_UnlockMutex(AudioSyncLock);
+ }
+ else
+ {
+ // ensure the audio FIFO doesn't overflow
+ //SPU::TrimOutput();
+ }
+
+ float framerate = (1000.0f * nlines) / (60.0f * 263.0f);
+
+ {
+ u32 curtick = SDL_GetTicks();
+ u32 delay = curtick - lasttick;
+
+ bool limitfps = Config::LimitFPS && !fastforward;
+ if (limitfps)
+ {
+ float wantedtickF = starttick + (framerate * (fpslimitcount+1));
+ u32 wantedtick = (u32)ceil(wantedtickF);
+ if (curtick < wantedtick) SDL_Delay(wantedtick - curtick);
+
+ lasttick = SDL_GetTicks();
+ fpslimitcount++;
+ if ((abs(wantedtickF - (float)wantedtick) < 0.001312) || (fpslimitcount > 60))
+ {
+ fpslimitcount = 0;
+ nsamples = 0;
+ starttick = lasttick;
+ }
+ }
+ else
+ {
+ if (delay < 1) SDL_Delay(1);
+ lasttick = SDL_GetTicks();
+ }
+ }
+
+ nframes++;
+ if (nframes >= 30)
+ {
+ u32 tick = SDL_GetTicks();
+ u32 diff = tick - lastmeasuretick;
+ lastmeasuretick = tick;
+
+ u32 fps;
+ if (diff < 1) fps = 77777;
+ else fps = (nframes * 1000) / diff;
+ nframes = 0;
+
+ float fpstarget;
+ if (framerate < 1) fpstarget = 999;
+ else fpstarget = 1000.0f/framerate;
+
+ SDL_LockMutex(titlemutex);
+ sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
+ SDL_UnlockMutex(titlemutex);
+ uiQueueMain(UpdateWindowTitle, titledata);
+ }
+ }
+ else
+ {
+ // paused
+ nframes = 0;
+ lasttick = SDL_GetTicks();
+ starttick = lasttick;
+ lastmeasuretick = lasttick;
+ fpslimitcount = 0;
+
+ if (EmuRunning == 2)
+ {
+ if (Screen_UseGL)
+ {
+ uiGLBegin(GLContext);
+ uiGLMakeContextCurrent(GLContext);
+ GLScreen_DrawScreen();
+ uiGLEnd(GLContext);
+ }
+ uiAreaQueueRedrawAll(MainDrawArea);
+ }
+
+ if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
+
+ EmuStatus = EmuRunning;
+
+ SDL_Delay(100);
+ }
+ }
+
+ EmuStatus = 0;
+
+ SDL_DestroyMutex(titlemutex);
+
+ if (Screen_UseGL) uiGLMakeContextCurrent(GLContext);
+
+ NDS::DeInit();
+ Platform::LAN_DeInit();
+
+ if (Screen_UseGL)
+ {
+ OSD::DeInit(true);
+ GLScreen_DeInit();
+ }
+ else
+ OSD::DeInit(false);
+
+ if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
+
+ return 44203;
+}
+
+void StopEmuThread()
+{
+ EmuRunning = 0;
+ SDL_WaitThread(EmuThread, NULL);
+}
+
+
+void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params)
+{
+ if (!ScreenDrawInited)
+ {
+ if (ScreenBitmap[0]) uiDrawFreeBitmap(ScreenBitmap[0]);
+ if (ScreenBitmap[1]) uiDrawFreeBitmap(ScreenBitmap[1]);
+
+ ScreenDrawInited = true;
+ ScreenBitmap[0] = uiDrawNewBitmap(params->Context, 256, 192, 0);
+ ScreenBitmap[1] = uiDrawNewBitmap(params->Context, 256, 192, 0);
+ }
+
+ int frontbuf = GPU::FrontBuffer;
+ if (!ScreenBitmap[0] || !ScreenBitmap[1]) return;
+ if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1]) return;
+
+ uiRect top = {0, 0, 256, 192};
+ uiRect bot = {0, 0, 256, 192};
+
+ uiDrawBitmapUpdate(ScreenBitmap[0], GPU::Framebuffer[frontbuf][0]);
+ uiDrawBitmapUpdate(ScreenBitmap[1], GPU::Framebuffer[frontbuf][1]);
+
+ uiDrawSave(params->Context);
+ uiDrawTransform(params->Context, &TopScreenTrans);
+ uiDrawBitmapDraw(params->Context, ScreenBitmap[0], &top, &TopScreenRect, Config::ScreenFilter==1);
+ uiDrawRestore(params->Context);
+
+ uiDrawSave(params->Context);
+ uiDrawTransform(params->Context, &BottomScreenTrans);
+ uiDrawBitmapDraw(params->Context, ScreenBitmap[1], &bot, &BottomScreenRect, Config::ScreenFilter==1);
+ uiDrawRestore(params->Context);
+
+ OSD::Update(false, params);
+}
+
+void OnAreaMouseEvent(uiAreaHandler* handler, uiArea* area, uiAreaMouseEvent* evt)
+{
+ int x = (int)evt->X;
+ int y = (int)evt->Y;
+
+ if (Touching && (evt->Up == 1))
+ {
+ Touching = false;
+ NDS::ReleaseKey(16+6);
+ NDS::ReleaseScreen();
+ }
+ else if (!Touching && (evt->Down == 1) &&
+ (x >= BottomScreenRect.X) && (y >= BottomScreenRect.Y) &&
+ (x < (BottomScreenRect.X+BottomScreenRect.Width)) && (y < (BottomScreenRect.Y+BottomScreenRect.Height)))
+ {
+ Touching = true;
+ NDS::PressKey(16+6);
+ }
+
+ if (Touching)
+ {
+ x -= BottomScreenRect.X;
+ y -= BottomScreenRect.Y;
+
+ if (ScreenRotation == 0 || ScreenRotation == 2)
+ {
+ if (BottomScreenRect.Width != 256)
+ x = (x * 256) / BottomScreenRect.Width;
+ if (BottomScreenRect.Height != 192)
+ y = (y * 192) / BottomScreenRect.Height;
+
+ if (ScreenRotation == 2)
+ {
+ x = 255 - x;
+ y = 191 - y;
+ }
+ }
+ else
+ {
+ if (BottomScreenRect.Width != 192)
+ x = (x * 192) / BottomScreenRect.Width;
+ if (BottomScreenRect.Height != 256)
+ y = (y * 256) / BottomScreenRect.Height;
+
+ if (ScreenRotation == 1)
+ {
+ int tmp = x;
+ x = y;
+ y = 191 - tmp;
+ }
+ else
+ {
+ int tmp = x;
+ x = 255 - y;
+ y = tmp;
+ }
+ }
+
+ // clamp
+ if (x < 0) x = 0;
+ else if (x > 255) x = 255;
+ if (y < 0) y = 0;
+ else if (y > 191) y = 191;
+
+ // TODO: take advantage of possible extra precision when possible? (scaled window for example)
+ NDS::TouchScreen(x, y);
+ }
+}
+
+void OnAreaMouseCrossed(uiAreaHandler* handler, uiArea* area, int left)
+{
+}
+
+void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area)
+{
+}
+
+bool EventMatchesKey(uiAreaKeyEvent* evt, int val, bool checkmod)
+{
+ if (val == -1) return false;
+
+ int key = val & 0xFFFF;
+ int mod = val >> 16;
+ return evt->Scancode == key && (!checkmod || evt->Modifiers == mod);
+}
+
+int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
+{
+ // TODO: release all keys if the window loses focus? or somehow global key input?
+ if (evt->Scancode == 0x38) // ALT
+ return 0;
+ if (evt->Modifiers == 0x2) // ALT+key
+ return 0;
+
+ if (evt->Up)
+ {
+ for (int i = 0; i < 12; i++)
+ if (EventMatchesKey(evt, Config::KeyMapping[i], false))
+ KeyInputMask |= (1<<i);
+
+ for (int i = 0; i < HK_MAX; i++)
+ if (EventMatchesKey(evt, Config::HKKeyMapping[i], true))
+ KeyHotkeyMask &= ~(1<<i);
+ }
+ else if (!evt->Repeat)
+ {
+ // TODO, eventually: make savestate keys configurable?
+ // 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 (EventMatchesKey(evt, Config::KeyMapping[i], false))
+ KeyInputMask &= ~(1<<i);
+
+ for (int i = 0; i < HK_MAX; i++)
+ if (EventMatchesKey(evt, Config::HKKeyMapping[i], true))
+ KeyHotkeyMask |= (1<<i);
+
+ // REMOVE ME
+ //if (evt->Scancode == 0x57) // F11
+ // NDS::debug(0);
+ }
+
+ return 1;
+}
+
+void SetupScreenRects(int width, int height)
+{
+ bool horizontal = false;
+ bool sideways = false;
+
+ if (ScreenRotation == 1 || ScreenRotation == 3)
+ sideways = true;
+
+ if (ScreenLayout == 2) horizontal = true;
+ else if (ScreenLayout == 0)
+ {
+ if (sideways)
+ horizontal = true;
+ }
+
+ int sizemode;
+ if (ScreenSizing == 3)
+ sizemode = AutoScreenSizing;
+ else
+ sizemode = ScreenSizing;
+
+ int screenW, screenH, gap;
+ if (sideways)
+ {
+ screenW = 192;
+ screenH = 256;
+ }
+ else
+ {
+ screenW = 256;
+ screenH = 192;
+ }
+
+ gap = ScreenGap;
+
+ uiRect *topscreen, *bottomscreen;
+ if (ScreenRotation == 1 || ScreenRotation == 2)
+ {
+ topscreen = &BottomScreenRect;
+ bottomscreen = &TopScreenRect;
+ }
+ else
+ {
+ topscreen = &TopScreenRect;
+ bottomscreen = &BottomScreenRect;
+ }
+
+ if (horizontal)
+ {
+ // side-by-side
+
+ int heightreq;
+ int startX = 0;
+
+ width -= gap;
+
+ if (sizemode == 0) // even
+ {
+ heightreq = (width * screenH) / (screenW*2);
+ if (heightreq > height)
+ {
+ int newwidth = (height * width) / heightreq;
+ startX = (width - newwidth) / 2;
+ heightreq = height;
+ width = newwidth;
+ }
+ }
+ else // emph. top/bottom
+ {
+ heightreq = ((width - screenW) * screenH) / screenW;
+ if (heightreq > height)
+ {
+ int newwidth = ((height * (width - screenW)) / heightreq) + screenW;
+ startX = (width - newwidth) / 2;
+ heightreq = height;
+ width = newwidth;
+ }
+ }
+
+ if (sizemode == 2)
+ {
+ topscreen->Width = screenW;
+ topscreen->Height = screenH;
+ }
+ else
+ {
+ topscreen->Width = (sizemode==0) ? (width / 2) : (width - screenW);
+ topscreen->Height = heightreq;
+ }
+ topscreen->X = startX;
+ topscreen->Y = ((height - heightreq) / 2) + (heightreq - topscreen->Height);
+
+ bottomscreen->X = topscreen->X + topscreen->Width + gap;
+
+ if (sizemode == 1)
+ {
+ bottomscreen->Width = screenW;
+ bottomscreen->Height = screenH;
+ }
+ else
+ {
+ bottomscreen->Width = width - topscreen->Width;
+ bottomscreen->Height = heightreq;
+ }
+ bottomscreen->Y = ((height - heightreq) / 2) + (heightreq - bottomscreen->Height);
+ }
+ else
+ {
+ // top then bottom
+
+ int widthreq;
+ int startY = 0;
+
+ height -= gap;
+
+ if (sizemode == 0) // even
+ {
+ widthreq = (height * screenW) / (screenH*2);
+ if (widthreq > width)
+ {
+ int newheight = (width * height) / widthreq;
+ startY = (height - newheight) / 2;
+ widthreq = width;
+ height = newheight;
+ }
+ }
+ else // emph. top/bottom
+ {
+ widthreq = ((height - screenH) * screenW) / screenH;
+ if (widthreq > width)
+ {
+ int newheight = ((width * (height - screenH)) / widthreq) + screenH;
+ startY = (height - newheight) / 2;
+ widthreq = width;
+ height = newheight;
+ }
+ }
+
+ if (sizemode == 2)
+ {
+ topscreen->Width = screenW;
+ topscreen->Height = screenH;
+ }
+ else
+ {
+ topscreen->Width = widthreq;
+ topscreen->Height = (sizemode==0) ? (height / 2) : (height - screenH);
+ }
+ topscreen->Y = startY;
+ topscreen->X = (width - topscreen->Width) / 2;
+
+ bottomscreen->Y = topscreen->Y + topscreen->Height + gap;
+
+ if (sizemode == 1)
+ {
+ bottomscreen->Width = screenW;
+ bottomscreen->Height = screenH;
+ }
+ else
+ {
+ bottomscreen->Width = widthreq;
+ bottomscreen->Height = height - topscreen->Height;
+ }
+ bottomscreen->X = (width - bottomscreen->Width) / 2;
+ }
+
+ // setup matrices for potential rotation
+
+ uiDrawMatrixSetIdentity(&TopScreenTrans);
+ uiDrawMatrixSetIdentity(&BottomScreenTrans);
+
+ switch (ScreenRotation)
+ {
+ case 1: // 90°
+ {
+ uiDrawMatrixTranslate(&TopScreenTrans, -TopScreenRect.X, -TopScreenRect.Y);
+ uiDrawMatrixRotate(&TopScreenTrans, 0, 0, M_PI/2.0f);
+ uiDrawMatrixScale(&TopScreenTrans, 0, 0,
+ TopScreenRect.Width/(double)TopScreenRect.Height,
+ TopScreenRect.Height/(double)TopScreenRect.Width);
+ uiDrawMatrixTranslate(&TopScreenTrans, TopScreenRect.X+TopScreenRect.Width, TopScreenRect.Y);
+
+ uiDrawMatrixTranslate(&BottomScreenTrans, -BottomScreenRect.X, -BottomScreenRect.Y);
+ uiDrawMatrixRotate(&BottomScreenTrans, 0, 0, M_PI/2.0f);
+ uiDrawMatrixScale(&BottomScreenTrans, 0, 0,
+ BottomScreenRect.Width/(double)BottomScreenRect.Height,
+ BottomScreenRect.Height/(double)BottomScreenRect.Width);
+ uiDrawMatrixTranslate(&BottomScreenTrans, BottomScreenRect.X+BottomScreenRect.Width, BottomScreenRect.Y);
+ }
+ break;
+
+ case 2: // 180°
+ {
+ uiDrawMatrixTranslate(&TopScreenTrans, -TopScreenRect.X, -TopScreenRect.Y);
+ uiDrawMatrixRotate(&TopScreenTrans, 0, 0, M_PI);
+ uiDrawMatrixTranslate(&TopScreenTrans, TopScreenRect.X+TopScreenRect.Width, TopScreenRect.Y+TopScreenRect.Height);
+
+ uiDrawMatrixTranslate(&BottomScreenTrans, -BottomScreenRect.X, -BottomScreenRect.Y);
+ uiDrawMatrixRotate(&BottomScreenTrans, 0, 0, M_PI);
+ uiDrawMatrixTranslate(&BottomScreenTrans, BottomScreenRect.X+BottomScreenRect.Width, BottomScreenRect.Y+BottomScreenRect.Height);
+ }
+ break;
+
+ case 3: // 270°
+ {
+ uiDrawMatrixTranslate(&TopScreenTrans, -TopScreenRect.X, -TopScreenRect.Y);
+ uiDrawMatrixRotate(&TopScreenTrans, 0, 0, -M_PI/2.0f);
+ uiDrawMatrixScale(&TopScreenTrans, 0, 0,
+ TopScreenRect.Width/(double)TopScreenRect.Height,
+ TopScreenRect.Height/(double)TopScreenRect.Width);
+ uiDrawMatrixTranslate(&TopScreenTrans, TopScreenRect.X, TopScreenRect.Y+TopScreenRect.Height);
+
+ uiDrawMatrixTranslate(&BottomScreenTrans, -BottomScreenRect.X, -BottomScreenRect.Y);
+ uiDrawMatrixRotate(&BottomScreenTrans, 0, 0, -M_PI/2.0f);
+ uiDrawMatrixScale(&BottomScreenTrans, 0, 0,
+ BottomScreenRect.Width/(double)BottomScreenRect.Height,
+ BottomScreenRect.Height/(double)BottomScreenRect.Width);
+ uiDrawMatrixTranslate(&BottomScreenTrans, BottomScreenRect.X, BottomScreenRect.Y+BottomScreenRect.Height);
+ }
+ break;
+ }
+
+ GL_ScreenSizeDirty = true;
+}
+
+void SetMinSize(int w, int h)
+{
+ int cw, ch;
+ uiWindowContentSize(MainWindow, &cw, &ch);
+
+ uiControlSetMinSize(uiControl(MainDrawArea), w, h);
+ if ((cw < w) || (ch < h))
+ {
+ if (cw < w) cw = w;
+ if (ch < h) ch = h;
+ uiWindowSetContentSize(MainWindow, cw, ch);
+ }
+}
+
+void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height)
+{
+ SetupScreenRects(width, height);
+
+ // TODO:
+ // should those be the size of the uiArea, or the size of the window client area?
+ // for now the uiArea fills the whole window anyway
+ // but... we never know, I guess
+ WindowWidth = width;
+ WindowHeight = height;
+
+ int ismax = uiWindowMaximized(MainWindow);
+ int ismin = uiWindowMinimized(MainWindow);
+
+ Config::WindowMaximized = ismax;
+ if (!ismax && !ismin)
+ {
+ Config::WindowWidth = width;
+ Config::WindowHeight = height;
+ }
+
+ OSD::WindowResized(Screen_UseGL);
+}
+
+
+void Run()
+{
+ EmuRunning = 1;
+ RunningSomething = true;
+
+ SPU::InitOutput();
+ AudioSampleFrac = 0;
+ SDL_PauseAudioDevice(AudioDevice, 0);
+ SDL_PauseAudioDevice(MicDevice, 0);
+
+ 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 (Platform::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);
+ uiMenuItemSetChecked(MenuItem_Pause, 0);
+}
+
+void TogglePause(void* blarg)
+{
+ if (!RunningSomething) return;
+
+ if (EmuRunning == 1)
+ {
+ // enable pause
+ EmuRunning = 2;
+ uiMenuItemSetChecked(MenuItem_Pause, 1);
+
+ SPU::DrainOutput();
+ SDL_PauseAudioDevice(AudioDevice, 1);
+ SDL_PauseAudioDevice(MicDevice, 1);
+
+ OSD::AddMessage(0, "Paused");
+ }
+ else
+ {
+ // disable pause
+ EmuRunning = 1;
+ uiMenuItemSetChecked(MenuItem_Pause, 0);
+
+ SPU::InitOutput();
+ AudioSampleFrac = 0;
+ SDL_PauseAudioDevice(AudioDevice, 0);
+ SDL_PauseAudioDevice(MicDevice, 0);
+
+ OSD::AddMessage(0, "Resumed");
+ }
+}
+
+void Reset(void* blarg)
+{
+ if (!RunningSomething) return;
+
+ EmuRunning = 2;
+ while (EmuStatus != 2);
+
+ SavestateLoaded = false;
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
+ if (ROMPath[0][0] == '\0')
+ NDS::LoadBIOS();
+ else
+ {
+ SetupSRAMPath(0);
+ NDS::LoadROM(ROMPath[0], SRAMPath[0], Config::DirectBoot);
+ }
+
+ if (ROMPath[1][0] != '\0')
+ {
+ SetupSRAMPath(1);
+ NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
+ }
+
+ Run();
+
+ OSD::AddMessage(0, "Reset");
+}
+
+void Stop(bool internal)
+{
+ EmuRunning = 2;
+ if (!internal) // if shutting down from the UI thread, wait till the emu thread has stopped
+ while (EmuStatus != 2);
+ RunningSomething = false;
+
+ // eject any inserted GBA cartridge
+ GBACart::Eject();
+ ROMPath[1][0] = '\0';
+
+ 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);
+ uiMenuItemSetChecked(MenuItem_Pause, 0);
+
+ uiAreaQueueRedrawAll(MainDrawArea);
+
+ SPU::DrainOutput();
+ SDL_PauseAudioDevice(AudioDevice, 1);
+ SDL_PauseAudioDevice(MicDevice, 1);
+
+ OSD::AddMessage(0xFFC040, "Shutdown");
+}
+
+void SetupSRAMPath(int slot)
+{
+ strncpy(SRAMPath[slot], ROMPath[slot], 1023);
+ SRAMPath[slot][1023] = '\0';
+ strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3);
+}
+
+void TryLoadROM(char* file, int slot, int prevstatus)
+{
+ char oldpath[1024];
+ char oldsram[1024];
+ strncpy(oldpath, ROMPath[slot], 1024);
+ strncpy(oldsram, SRAMPath[slot], 1024);
+
+ strncpy(ROMPath[slot], file, 1023);
+ ROMPath[slot][1023] = '\0';
+
+ SetupSRAMPath(0);
+ SetupSRAMPath(1);
+
+ if (slot == 0 && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], Config::DirectBoot))
+ {
+ SavestateLoaded = false;
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
+ // Reload the inserted GBA cartridge (if any)
+ if (ROMPath[1][0] != '\0') NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
+
+ strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
+ Run();
+ }
+ else if (slot == 1 && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot]))
+ {
+ SavestateLoaded = false;
+ uiMenuItemDisable(MenuItem_UndoStateLoad);
+
+ strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety
+ if (RunningSomething) Run(); // do not start just from a GBA cart
+ }
+ else
+ {
+ uiMsgBoxError(MainWindow,
+ "Failed to load the ROM",
+ "Make sure the file can be accessed and isn't opened in another application.");
+
+ strncpy(ROMPath[slot], oldpath, 1024);
+ strncpy(SRAMPath[slot], oldsram, 1024);
+ EmuRunning = prevstatus;
+ }
+}
+
+
+// SAVESTATE TODO
+// * configurable paths. not everyone wants their ROM directory to be polluted, I guess.
+
+void GetSavestateName(int slot, char* filename, int len)
+{
+ int pos;
+
+ if (ROMPath[0][0] == '\0') // running firmware, no ROM
+ {
+ strcpy(filename, "firmware");
+ pos = 8;
+ }
+ else
+ {
+ int l = strlen(ROMPath[0]);
+ pos = l;
+ while (ROMPath[0][pos] != '.' && pos > 0) pos--;
+ if (pos == 0) pos = l;
+
+ // avoid buffer overflow. shoddy
+ if (pos > len-5) pos = len-5;
+
+ strncpy(&filename[0], ROMPath[0], 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 (!Platform::FileExists(filename))
+ {
+ char msg[64];
+ if (slot > 0) sprintf(msg, "State slot %d is empty", slot);
+ else sprintf(msg, "State file does not exist");
+ OSD::AddMessage(0xFFA0A0, msg);
+
+ EmuRunning = prevstatus;
+ return;
+ }
+
+ u32 oldGBACartCRC = GBACart::CartCRC;
+
+ // 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]!='\0')
+ {
+ strncpy(PrevSRAMPath[0], SRAMPath[0], 1024);
+
+ strncpy(SRAMPath[0], filename, 1019);
+ int len = strlen(SRAMPath[0]);
+ strcpy(&SRAMPath[0][len], ".sav");
+ SRAMPath[0][len+4] = '\0';
+
+ NDS::RelocateSave(SRAMPath[0], false);
+ }
+
+ bool loadedPartialGBAROM = false;
+
+ // in case we have a GBA cart inserted, and the GBA ROM changes
+ // due to having loaded a save state, we do not want to reload
+ // the previous cartridge on reset, or commit writes to any
+ // loaded save file. therefore, their paths are "nulled".
+ if (GBACart::CartInserted && GBACart::CartCRC != oldGBACartCRC)
+ {
+ ROMPath[1][0] = '\0';
+ SRAMPath[1][0] = '\0';
+ loadedPartialGBAROM = true;
+ }
+
+ char msg[64];
+ if (slot > 0) sprintf(msg, "State loaded from slot %d%s",
+ slot, loadedPartialGBAROM ? " (GBA ROM header only)" : "");
+ else sprintf(msg, "State loaded from file%s",
+ loadedPartialGBAROM ? " (GBA ROM header only)" : "");
+ OSD::AddMessage(0, msg);
+
+ 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]!='\0')
+ {
+ strncpy(SRAMPath[0], filename, 1019);
+ int len = strlen(SRAMPath[0]);
+ strcpy(&SRAMPath[0][len], ".sav");
+ SRAMPath[0][len+4] = '\0';
+
+ NDS::RelocateSave(SRAMPath[0], true);
+ }
+ }
+
+ char msg[64];
+ if (slot > 0) sprintf(msg, "State saved to slot %d", slot);
+ else sprintf(msg, "State saved to file");
+ OSD::AddMessage(0, msg);
+
+ 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]!='\0')
+ {
+ strncpy(SRAMPath[0], PrevSRAMPath[0], 1024);
+ NDS::RelocateSave(SRAMPath[0], false);
+ }
+
+ OSD::AddMessage(0, "State load undone");
+
+ EmuRunning = prevstatus;
+}
+
+
+void CloseAllDialogs()
+{
+ DlgAudioSettings::Close();
+ DlgEmuSettings::Close();
+ DlgInputConfig::Close(0);
+ DlgInputConfig::Close(1);
+ DlgVideoSettings::Close();
+ DlgWifiSettings::Close();
+}
+
+
+int OnCloseWindow(uiWindow* window, void* blarg)
+{
+ EmuRunning = 3;
+ while (EmuStatus != 3);
+
+ CloseAllDialogs();
+ StopEmuThread();
+ uiQuit();
+ return 1;
+}
+
+void OnDropFile(uiWindow* window, char* file, void* blarg)
+{
+ char* ext = &file[strlen(file)-3];
+ int prevstatus = EmuRunning;
+
+ if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl"))
+ {
+ if (RunningSomething)
+ {
+ EmuRunning = 2;
+ while (EmuStatus != 2);
+ }
+
+ TryLoadROM(file, 0, prevstatus);
+ }
+ else if (!strcasecmp(ext, "gba"))
+ {
+ TryLoadROM(file, 1, prevstatus);
+ }
+}
+
+void OnGetFocus(uiWindow* window, void* blarg)
+{
+ uiControlSetFocus(uiControl(MainDrawArea));
+}
+
+void OnLoseFocus(uiWindow* window, void* blarg)
+{
+ // TODO: shit here?
+}
+
+void OnCloseByMenu(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ EmuRunning = 3;
+ while (EmuStatus != 3);
+
+ CloseAllDialogs();
+ StopEmuThread();
+ DestroyMainWindow();
+ uiQuit();
+}
+
+void OnOpenFile(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ int prevstatus = EmuRunning;
+ EmuRunning = 2;
+ while (EmuStatus != 2);
+
+ char* file = uiOpenFile(window, "DS ROM (*.nds)|*.nds;*.srl|GBA ROM (*.gba)|*.gba|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';
+ char* ext = &file[strlen(file)-3];
+
+ if (!strcasecmp(ext, "gba"))
+ {
+ TryLoadROM(file, 1, prevstatus);
+ }
+ else
+ {
+ TryLoadROM(file, 0, 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)
+ {
+ ROMPath[0][0] = '\0';
+ NDS::LoadBIOS();
+
+ if (ROMPath[1][0] != '\0')
+ {
+ SetupSRAMPath(1);
+ NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
+ }
+ }
+
+ Run();
+}
+
+void OnPause(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ TogglePause(NULL);
+}
+
+void OnReset(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ Reset(NULL);
+}
+
+void OnStop(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ if (!RunningSomething) return;
+
+ Stop(false);
+}
+
+void OnOpenEmuSettings(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgEmuSettings::Open();
+}
+
+void OnOpenInputConfig(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgInputConfig::Open(0);
+}
+
+void OnOpenHotkeyConfig(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgInputConfig::Open(1);
+}
+
+void OnOpenVideoSettings(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgVideoSettings::Open();
+}
+
+void OnOpenAudioSettings(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgAudioSettings::Open();
+}
+
+void OnOpenWifiSettings(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ DlgWifiSettings::Open();
+}
+
+
+void OnSetSavestateSRAMReloc(uiMenuItem* item, uiWindow* window, void* param)
+{
+ Config::SavestateRelocSRAM = uiMenuItemChecked(item) ? 1:0;
+}
+
+
+void EnsureProperMinSize()
+{
+ bool isHori = (ScreenRotation == 1 || ScreenRotation == 3);
+
+ int w0 = 256;
+ int h0 = 192;
+ int w1 = 256;
+ int h1 = 192;
+
+ if (ScreenLayout == 0) // natural
+ {
+ if (isHori)
+ SetMinSize(h0+ScreenGap+h1, std::max(w0,w1));
+ else
+ SetMinSize(std::max(w0,w1), h0+ScreenGap+h1);
+ }
+ else if (ScreenLayout == 1) // vertical
+ {
+ if (isHori)
+ SetMinSize(std::max(h0,h1), w0+ScreenGap+w1);
+ else
+ SetMinSize(std::max(w0,w1), h0+ScreenGap+h1);
+ }
+ else // horizontal
+ {
+ if (isHori)
+ SetMinSize(h0+ScreenGap+h1, std::max(w0,w1));
+ else
+ SetMinSize(w0+ScreenGap+w1, std::max(h0,h1));
+ }
+}
+
+void OnSetScreenSize(uiMenuItem* item, uiWindow* window, void* param)
+{
+ int factor = *(int*)param;
+ bool isHori = (ScreenRotation == 1 || ScreenRotation == 3);
+
+ int w = 256*factor;
+ int h = 192*factor;
+
+ // FIXME
+
+ if (ScreenLayout == 0) // natural
+ {
+ if (isHori)
+ uiWindowSetContentSize(window, (h*2)+ScreenGap, w);
+ else
+ uiWindowSetContentSize(window, w, (h*2)+ScreenGap);
+ }
+ else if (ScreenLayout == 1) // vertical
+ {
+ if (isHori)
+ uiWindowSetContentSize(window, h, (w*2)+ScreenGap);
+ else
+ uiWindowSetContentSize(window, w, (h*2)+ScreenGap);
+ }
+ else // horizontal
+ {
+ if (isHori)
+ uiWindowSetContentSize(window, (h*2)+ScreenGap, w);
+ else
+ uiWindowSetContentSize(window, (w*2)+ScreenGap, h);
+ }
+}
+
+void OnSetScreenRotation(uiMenuItem* item, uiWindow* window, void* param)
+{
+ int rot = *(int*)param;
+
+ int oldrot = ScreenRotation;
+ ScreenRotation = rot;
+
+ int w, h;
+ uiWindowContentSize(window, &w, &h);
+
+ bool isHori = (rot == 1 || rot == 3);
+ bool wasHori = (oldrot == 1 || oldrot == 3);
+
+ EnsureProperMinSize();
+
+ if (ScreenLayout == 0) // natural
+ {
+ if (isHori ^ wasHori)
+ {
+ int blarg = h;
+ h = w;
+ w = blarg;
+
+ uiWindowSetContentSize(window, w, h);
+ }
+ }
+
+ SetupScreenRects(w, h);
+
+ for (int i = 0; i < 4; i++)
+ uiMenuItemSetChecked(MenuItem_ScreenRot[i], i==ScreenRotation);
+}
+
+void OnSetScreenGap(uiMenuItem* item, uiWindow* window, void* param)
+{
+ int gap = *(int*)param;
+
+ //int oldgap = ScreenGap;
+ ScreenGap = gap;
+
+ EnsureProperMinSize();
+ SetupScreenRects(WindowWidth, WindowHeight);
+
+ for (int i = 0; i < 6; i++)
+ uiMenuItemSetChecked(MenuItem_ScreenGap[i], kScreenGap[i]==ScreenGap);
+}
+
+void OnSetScreenLayout(uiMenuItem* item, uiWindow* window, void* param)
+{
+ int layout = *(int*)param;
+ ScreenLayout = layout;
+
+ EnsureProperMinSize();
+ SetupScreenRects(WindowWidth, WindowHeight);
+
+ for (int i = 0; i < 3; i++)
+ uiMenuItemSetChecked(MenuItem_ScreenLayout[i], i==ScreenLayout);
+}
+
+void OnSetScreenSizing(uiMenuItem* item, uiWindow* window, void* param)
+{
+ int sizing = *(int*)param;
+ ScreenSizing = sizing;
+
+ SetupScreenRects(WindowWidth, WindowHeight);
+
+ for (int i = 0; i < 4; i++)
+ uiMenuItemSetChecked(MenuItem_ScreenSizing[i], i==ScreenSizing);
+}
+
+void OnSetScreenFiltering(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ int chk = uiMenuItemChecked(item);
+ if (chk != 0) Config::ScreenFilter = 1;
+ else Config::ScreenFilter = 0;
+}
+
+void OnSetLimitFPS(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ int chk = uiMenuItemChecked(item);
+ if (chk != 0) Config::LimitFPS = true;
+ else Config::LimitFPS = false;
+}
+
+void OnSetAudioSync(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ int chk = uiMenuItemChecked(item);
+ if (chk != 0) Config::AudioSync = true;
+ else Config::AudioSync = false;
+}
+
+void OnSetShowOSD(uiMenuItem* item, uiWindow* window, void* blarg)
+{
+ int chk = uiMenuItemChecked(item);
+ if (chk != 0) Config::ShowOSD = true;
+ else Config::ShowOSD = false;
+}
+
+void ApplyNewSettings(int type)
+{
+#ifdef JIT_ENABLED
+ if (type == 4)
+ {
+ Reset(NULL);
+ return;
+ }
+#endif
+
+ if (!RunningSomething)
+ {
+ if (type == 1) return;
+ }
+
+ int prevstatus = EmuRunning;
+ EmuRunning = 3;
+ while (EmuStatus != 3);
+
+ if (type == 0) // 3D renderer settings
+ {
+ if (Screen_UseGL) uiGLMakeContextCurrent(GLContext);
+ GPU3D::UpdateRendererConfig();
+ if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
+
+ GL_3DScale = Config::GL_ScaleFactor; // dorp
+ GL_ScreenSizeDirty = true;
+ }
+ else if (type == 1) // wifi settings
+ {
+ if (Wifi::MPInited)
+ {
+ Platform::MP_DeInit();
+ Platform::MP_Init();
+ }
+
+ Platform::LAN_DeInit();
+ Platform::LAN_Init();
+ }
+ else if (type == 2) // video output method
+ {
+ bool usegl = Config::ScreenUseGL || (Config::_3DRenderer != 0);
+ if (usegl != Screen_UseGL)
+ {
+ if (Screen_UseGL) uiGLMakeContextCurrent(GLContext);
+ GPU3D::DeInitRenderer();
+ OSD::DeInit(Screen_UseGL);
+ if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
+
+ Screen_UseGL = usegl;
+ RecreateMainWindow(usegl);
+
+ if (Screen_UseGL) uiGLMakeContextCurrent(GLContext);
+ GPU3D::InitRenderer(Screen_UseGL);
+ if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
+ }
+ }
+ else if (type == 3) // 3D renderer
+ {
+ if (Screen_UseGL) uiGLMakeContextCurrent(GLContext);
+ GPU3D::DeInitRenderer();
+ GPU3D::InitRenderer(Screen_UseGL);
+ if (Screen_UseGL) uiGLMakeContextCurrent(NULL);
+ }
+ EmuRunning = prevstatus;
+}
+
+
+void CreateMainWindowMenu()
+{
+ uiMenu* menu;
+ uiMenuItem* menuitem;
+
+ menu = uiNewMenu("File");
+ 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);
+
+ menu = uiNewMenu("System");
+ menuitem = uiMenuAppendItem(menu, "Run");
+ uiMenuItemOnClicked(menuitem, OnRun, NULL);
+ menuitem = uiMenuAppendCheckItem(menu, "Pause");
+ uiMenuItemOnClicked(menuitem, OnPause, NULL);
+ MenuItem_Pause = menuitem;
+ uiMenuAppendSeparator(menu);
+ menuitem = uiMenuAppendItem(menu, "Reset");
+ uiMenuItemOnClicked(menuitem, OnReset, NULL);
+ MenuItem_Reset = menuitem;
+ menuitem = uiMenuAppendItem(menu, "Stop");
+ uiMenuItemOnClicked(menuitem, OnStop, NULL);
+ 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, "Hotkey config");
+ uiMenuItemOnClicked(menuitem, OnOpenHotkeyConfig, NULL);
+ menuitem = uiMenuAppendItem(menu, "Video settings");
+ uiMenuItemOnClicked(menuitem, OnOpenVideoSettings, NULL);
+ menuitem = uiMenuAppendItem(menu, "Audio settings");
+ uiMenuItemOnClicked(menuitem, OnOpenAudioSettings, NULL);
+ menuitem = uiMenuAppendItem(menu, "Wifi settings");
+ uiMenuItemOnClicked(menuitem, OnOpenWifiSettings, 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 size");
+
+ for (int i = 0; i < 4; i++)
+ {
+ char name[32];
+ sprintf(name, "%dx", kScreenSize[i]);
+ uiMenuItem* item = uiMenuAppendItem(submenu, name);
+ uiMenuItemOnClicked(item, OnSetScreenSize, (void*)&kScreenSize[i]);
+ }
+
+ uiMenuAppendSubmenu(menu, submenu);
+ }
+ {
+ uiMenu* submenu = uiNewMenu("Screen rotation");
+
+ for (int i = 0; i < 4; i++)
+ {
+ char name[32];
+ sprintf(name, "%d", kScreenRot[i]*90);
+ MenuItem_ScreenRot[i] = uiMenuAppendCheckItem(submenu, name);
+ uiMenuItemOnClicked(MenuItem_ScreenRot[i], OnSetScreenRotation, (void*)&kScreenRot[i]);
+ }
+
+ uiMenuAppendSubmenu(menu, submenu);
+ }
+ {
+ uiMenu* submenu = uiNewMenu("Mid-screen gap");
+
+ //for (int i = 0; kScreenGap[i] != -1; i++)
+ for (int i = 0; i < 6; i++)
+ {
+ char name[32];
+ sprintf(name, "%d pixels", kScreenGap[i]);
+ MenuItem_ScreenGap[i] = uiMenuAppendCheckItem(submenu, name);
+ uiMenuItemOnClicked(MenuItem_ScreenGap[i], OnSetScreenGap, (void*)&kScreenGap[i]);
+ }
+
+ uiMenuAppendSubmenu(menu, submenu);
+ }
+ {
+ uiMenu* submenu = uiNewMenu("Screen layout");
+
+ MenuItem_ScreenLayout[0] = uiMenuAppendCheckItem(submenu, "Natural");
+ uiMenuItemOnClicked(MenuItem_ScreenLayout[0], OnSetScreenLayout, (void*)&kScreenLayout[0]);
+ MenuItem_ScreenLayout[1] = uiMenuAppendCheckItem(submenu, "Vertical");
+ uiMenuItemOnClicked(MenuItem_ScreenLayout[1], OnSetScreenLayout, (void*)&kScreenLayout[1]);
+ MenuItem_ScreenLayout[2] = uiMenuAppendCheckItem(submenu, "Horizontal");
+ uiMenuItemOnClicked(MenuItem_ScreenLayout[2], OnSetScreenLayout, (void*)&kScreenLayout[2]);
+
+ uiMenuAppendSubmenu(menu, submenu);
+ }
+ {
+ uiMenu* submenu = uiNewMenu("Screen sizing");
+
+ MenuItem_ScreenSizing[0] = uiMenuAppendCheckItem(submenu, "Even");
+ uiMenuItemOnClicked(MenuItem_ScreenSizing[0], OnSetScreenSizing, (void*)&kScreenSizing[0]);
+ MenuItem_ScreenSizing[1] = uiMenuAppendCheckItem(submenu, "Emphasize top");
+ uiMenuItemOnClicked(MenuItem_ScreenSizing[1], OnSetScreenSizing, (void*)&kScreenSizing[1]);
+ MenuItem_ScreenSizing[2] = uiMenuAppendCheckItem(submenu, "Emphasize bottom");
+ uiMenuItemOnClicked(MenuItem_ScreenSizing[2], OnSetScreenSizing, (void*)&kScreenSizing[2]);
+ MenuItem_ScreenSizing[3] = uiMenuAppendCheckItem(submenu, "Auto");
+ uiMenuItemOnClicked(MenuItem_ScreenSizing[3], OnSetScreenSizing, (void*)&kScreenSizing[3]);
+
+ uiMenuAppendSubmenu(menu, submenu);
+ }
+
+ MenuItem_ScreenFilter = uiMenuAppendCheckItem(menu, "Screen filtering");
+ uiMenuItemOnClicked(MenuItem_ScreenFilter, OnSetScreenFiltering, NULL);
+
+ MenuItem_ShowOSD = uiMenuAppendCheckItem(menu, "Show OSD");
+ uiMenuItemOnClicked(MenuItem_ShowOSD, OnSetShowOSD, NULL);
+
+ uiMenuAppendSeparator(menu);
+
+ MenuItem_LimitFPS = uiMenuAppendCheckItem(menu, "Limit framerate");
+ uiMenuItemOnClicked(MenuItem_LimitFPS, OnSetLimitFPS, NULL);
+
+ MenuItem_AudioSync = uiMenuAppendCheckItem(menu, "Audio sync");
+ uiMenuItemOnClicked(MenuItem_AudioSync, OnSetAudioSync, NULL);
+}
+
+void CreateMainWindow(bool opengl)
+{
+ MainWindow = uiNewWindow("melonDS " MELONDS_VERSION,
+ WindowWidth, WindowHeight,
+ Config::WindowMaximized, 1, 1);
+ uiWindowOnClosing(MainWindow, OnCloseWindow, NULL);
+
+ uiWindowSetDropTarget(MainWindow, 1);
+ uiWindowOnDropFile(MainWindow, OnDropFile, NULL);
+
+ uiWindowOnGetFocus(MainWindow, OnGetFocus, NULL);
+ uiWindowOnLoseFocus(MainWindow, OnLoseFocus, NULL);
+
+ ScreenDrawInited = false;
+ bool opengl_good = opengl;
+
+ if (!opengl) MainDrawArea = uiNewArea(&MainDrawAreaHandler);
+ else MainDrawArea = uiNewGLArea(&MainDrawAreaHandler, kGLVersions);
+
+ uiWindowSetChild(MainWindow, uiControl(MainDrawArea));
+ uiControlSetMinSize(uiControl(MainDrawArea), 256, 384);
+ uiAreaSetBackgroundColor(MainDrawArea, 0, 0, 0);
+
+ uiControlShow(uiControl(MainWindow));
+ uiControlSetFocus(uiControl(MainDrawArea));
+
+ if (opengl_good)
+ {
+ GLContext = uiAreaGetGLContext(MainDrawArea);
+ if (!GLContext) opengl_good = false;
+ }
+ if (opengl_good)
+ {
+ uiGLMakeContextCurrent(GLContext);
+ uiGLSetVSync(Config::ScreenVSync);
+ if (!GLScreen_Init()) opengl_good = false;
+ if (opengl_good)
+ {
+ OpenGL_UseShaderProgram(GL_ScreenShaderOSD);
+ OSD::Init(true);
+ }
+ uiGLMakeContextCurrent(NULL);
+ }
+
+ if (opengl && !opengl_good)
+ {
+ printf("OpenGL: initialization failed\n");
+ RecreateMainWindow(false);
+ Screen_UseGL = false;
+ }
+
+ if (!opengl) OSD::Init(false);
+}
+
+void DestroyMainWindow()
+{
+ uiControlDestroy(uiControl(MainWindow));
+
+ if (ScreenBitmap[0]) uiDrawFreeBitmap(ScreenBitmap[0]);
+ if (ScreenBitmap[1]) uiDrawFreeBitmap(ScreenBitmap[1]);
+
+ ScreenBitmap[0] = NULL;
+ ScreenBitmap[1] = NULL;
+}
+
+void RecreateMainWindow(bool opengl)
+{
+ int winX, winY, maxi;
+ uiWindowPosition(MainWindow, &winX, &winY);
+ maxi = uiWindowMaximized(MainWindow);
+ DestroyMainWindow();
+ CreateMainWindow(opengl);
+ uiWindowSetPosition(MainWindow, winX, winY);
+ uiWindowSetMaximized(MainWindow, maxi);
+}
+
+
+int main(int argc, char** argv)
+{
+ srand(time(NULL));
+
+ printf("melonDS " MELONDS_VERSION "\n");
+ printf(MELONDS_URL "\n");
+
+#if defined(__WIN32__) || defined(UNIX_PORTABLE)
+ if (argc > 0 && strlen(argv[0]) > 0)
+ {
+ int len = strlen(argv[0]);
+ while (len > 0)
+ {
+ if (argv[0][len] == '/') break;
+ if (argv[0][len] == '\\') break;
+ len--;
+ }
+ if (len > 0)
+ {
+ EmuDirectory = new char[len+1];
+ strncpy(EmuDirectory, argv[0], len);
+ EmuDirectory[len] = '\0';
+ }
+ else
+ {
+ EmuDirectory = new char[2];
+ strcpy(EmuDirectory, ".");
+ }
+ }
+ else
+ {
+ EmuDirectory = new char[2];
+ strcpy(EmuDirectory, ".");
+ }
+#else
+ const char* confdir = g_get_user_config_dir();
+ const char* confname = "/melonDS";
+ EmuDirectory = new char[strlen(confdir) + strlen(confname) + 1];
+ strcat(EmuDirectory, confdir);
+ strcat(EmuDirectory, confname);
+#endif
+
+ // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
+ SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
+
+ if (SDL_Init(SDL_INIT_HAPTIC) < 0)
+ {
+ printf("SDL couldn't init rumble\n");
+ }
+ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0)
+ {
+ printf("SDL shat itself :(\n");
+ return 1;
+ }
+
+ SDL_JoystickEventState(SDL_ENABLE);
+
+ uiInitOptions ui_opt;
+ memset(&ui_opt, 0, sizeof(uiInitOptions));
+ const char* ui_err = uiInit(&ui_opt);
+ if (ui_err != NULL)
+ {
+ printf("libui shat itself :( %s\n", ui_err);
+ uiFreeInitError(ui_err);
+ return 1;
+ }
+
+ Config::Load();
+
+ if (Config::AudioVolume < 0) Config::AudioVolume = 0;
+ else if (Config::AudioVolume > 256) Config::AudioVolume = 256;
+
+ if (!Platform::LocalFileExists("bios7.bin") ||
+ !Platform::LocalFileExists("bios9.bin") ||
+ !Platform::LocalFileExists("firmware.bin"))
+ {
+#if defined(__WIN32__) || defined(UNIX_PORTABLE)
+ const char* locationName = "the directory you run melonDS from";
+#else
+ char* locationName = EmuDirectory;
+#endif
+ char msgboxtext[512];
+ sprintf(msgboxtext,
+ "One or more of the following required files don't exist or couldn't be accessed:\n\n"
+ "bios7.bin -- ARM7 BIOS\n"
+ "bios9.bin -- ARM9 BIOS\n"
+ "firmware.bin -- firmware image\n\n"
+ "Dump the files from your DS and place them in %s.\n"
+ "Make sure that the files can be accessed.",
+ locationName
+ );
+
+ uiMsgBoxError(NULL, "BIOS/Firmware not found", msgboxtext);
+
+ uiUninit();
+ SDL_Quit();
+ return 0;
+ }
+ if (!Platform::LocalFileExists("firmware.bin.bak"))
+ {
+ // verify the firmware
+ //
+ // there are dumps of an old hacked firmware floating around on the internet
+ // and those are problematic
+ // the hack predates WFC, and, due to this, any game that alters the WFC
+ // access point data will brick that firmware due to it having critical
+ // data in the same area. it has the same problem on hardware.
+ //
+ // but this should help stop users from reporting that issue over and over
+ // again, when the issue is not from melonDS but from their firmware dump.
+ //
+ // I don't know about all the firmware hacks in existence, but the one I
+ // looked at has 0x180 bytes from the header repeated at 0x3FC80, but
+ // bytes 0x0C-0x14 are different.
+
+ FILE* f = Platform::OpenLocalFile("firmware.bin", "rb");
+ u8 chk1[0x180], chk2[0x180];
+
+ fseek(f, 0, SEEK_SET);
+ fread(chk1, 1, 0x180, f);
+ fseek(f, -0x380, SEEK_END);
+ fread(chk2, 1, 0x180, f);
+
+ memset(&chk1[0x0C], 0, 8);
+ memset(&chk2[0x0C], 0, 8);
+
+ fclose(f);
+
+ if (!memcmp(chk1, chk2, 0x180))
+ {
+ uiMsgBoxError(NULL,
+ "Problematic firmware dump",
+ "You are using an old hacked firmware dump.\n"
+ "Firmware boot will stop working if you run any game that alters WFC settings.\n\n"
+ "Note that the issue is not from melonDS, it would also happen on an actual DS.");
+ }
+ }
+ {
+ const char* romlist_missing = "Save memory type detection will not work correctly.\n\n"
+ "You should use the latest version of romlist.bin (provided in melonDS release packages).";
+#if !defined(UNIX_PORTABLE) && !defined(__WIN32__)
+ std::string missingstr = std::string(romlist_missing) +
+ "\n\nThe ROM list should be placed in " + g_get_user_data_dir() + "/melonds/, otherwise "
+ "melonDS will search for it in the current working directory.";
+ const char* romlist_missing_text = missingstr.c_str();
+#else
+ const char* romlist_missing_text = romlist_missing;
+#endif
+
+ FILE* f = Platform::OpenDataFile("romlist.bin");
+ if (f)
+ {
+ u32 data;
+ fread(&data, 4, 1, f);
+ fclose(f);
+
+ if ((data >> 24) == 0) // old CRC-based list
+ {
+ uiMsgBoxError(NULL, "Your version of romlist.bin is outdated.", romlist_missing_text);
+ }
+ }
+ else
+ {
+ uiMsgBoxError(NULL, "romlist.bin not found.", romlist_missing_text);
+ }
+ }
+
+ CreateMainWindowMenu();
+
+ MainDrawAreaHandler.Draw = OnAreaDraw;
+ MainDrawAreaHandler.MouseEvent = OnAreaMouseEvent;
+ MainDrawAreaHandler.MouseCrossed = OnAreaMouseCrossed;
+ MainDrawAreaHandler.DragBroken = OnAreaDragBroken;
+ MainDrawAreaHandler.KeyEvent = OnAreaKeyEvent;
+ MainDrawAreaHandler.Resize = OnAreaResize;
+
+ WindowWidth = Config::WindowWidth;
+ WindowHeight = Config::WindowHeight;
+
+ Screen_UseGL = Config::ScreenUseGL || (Config::_3DRenderer != 0);
+
+ GL_3DScale = Config::GL_ScaleFactor;
+ if (GL_3DScale < 1) GL_3DScale = 1;
+ else if (GL_3DScale > 8) GL_3DScale = 8;
+
+ CreateMainWindow(Screen_UseGL);
+
+ ScreenRotation = Config::ScreenRotation;
+ ScreenGap = Config::ScreenGap;
+ ScreenLayout = Config::ScreenLayout;
+ ScreenSizing = Config::ScreenSizing;
+
+#define SANITIZE(var, min, max) if ((var < min) || (var > max)) var = 0;
+ SANITIZE(ScreenRotation, 0, 3);
+ SANITIZE(ScreenLayout, 0, 2);
+ SANITIZE(ScreenSizing, 0, 3);
+#undef SANITIZE
+
+ 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);
+
+ uiMenuItemSetChecked(MenuItem_SavestateSRAMReloc, Config::SavestateRelocSRAM?1:0);
+
+ uiMenuItemSetChecked(MenuItem_ScreenRot[ScreenRotation], 1);
+ uiMenuItemSetChecked(MenuItem_ScreenLayout[ScreenLayout], 1);
+ uiMenuItemSetChecked(MenuItem_ScreenSizing[ScreenSizing], 1);
+
+ for (int i = 0; i < 6; i++)
+ {
+ if (ScreenGap == kScreenGap[i])
+ uiMenuItemSetChecked(MenuItem_ScreenGap[i], 1);
+ }
+
+ OnSetScreenRotation(MenuItem_ScreenRot[ScreenRotation], MainWindow, (void*)&kScreenRot[ScreenRotation]);
+
+ uiMenuItemSetChecked(MenuItem_ScreenFilter, Config::ScreenFilter==1);
+ uiMenuItemSetChecked(MenuItem_LimitFPS, Config::LimitFPS==1);
+ uiMenuItemSetChecked(MenuItem_AudioSync, Config::AudioSync==1);
+ uiMenuItemSetChecked(MenuItem_ShowOSD, Config::ShowOSD==1);
+
+#ifdef MELONCAP
+ MelonCap::Init();
+#endif // MELONCAP
+
+ AudioSync = SDL_CreateCond();
+ AudioSyncLock = SDL_CreateMutex();
+
+ AudioFreq = 48000; // TODO: make configurable?
+ SDL_AudioSpec whatIwant, whatIget;
+ memset(&whatIwant, 0, sizeof(SDL_AudioSpec));
+ whatIwant.freq = AudioFreq;
+ whatIwant.format = AUDIO_S16LSB;
+ whatIwant.channels = 2;
+ whatIwant.samples = 1024;
+ whatIwant.callback = AudioCallback;
+ AudioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
+ if (!AudioDevice)
+ {
+ printf("Audio init failed: %s\n", SDL_GetError());
+ }
+ else
+ {
+ AudioFreq = whatIget.freq;
+ printf("Audio output frequency: %d Hz\n", AudioFreq);
+ SDL_PauseAudioDevice(AudioDevice, 1);
+ }
+
+ memset(&whatIwant, 0, sizeof(SDL_AudioSpec));
+ whatIwant.freq = 44100;
+ whatIwant.format = AUDIO_S16LSB;
+ whatIwant.channels = 1;
+ whatIwant.samples = 1024;
+ whatIwant.callback = MicCallback;
+ MicDevice = SDL_OpenAudioDevice(NULL, 1, &whatIwant, &whatIget, 0);
+ if (!MicDevice)
+ {
+ printf("Mic init failed: %s\n", SDL_GetError());
+ MicBufferLength = 0;
+ }
+ else
+ {
+ SDL_PauseAudioDevice(MicDevice, 1);
+ }
+
+ memset(MicBuffer, 0, sizeof(MicBuffer));
+ MicBufferReadPos = 0;
+ MicBufferWritePos = 0;
+
+ MicWavBuffer = NULL;
+ if (Config::MicInputType == 3) MicLoadWav(Config::MicWavPath);
+
+ JoystickID = Config::JoystickID;
+ Joystick = NULL;
+ OpenJoystick();
+
+ EmuRunning = 2;
+ RunningSomething = false;
+ EmuThread = SDL_CreateThread(EmuThreadFunc, "melonDS magic", NULL);
+
+ if (argc > 1)
+ {
+ char* file = argv[1];
+ char* ext = &file[strlen(file)-3];
+
+ if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl"))
+ {
+ strncpy(ROMPath[0], file, 1023);
+ ROMPath[0][1023] = '\0';
+
+ SetupSRAMPath(0);
+
+ if (NDS::LoadROM(ROMPath[0], SRAMPath[0], Config::DirectBoot))
+ Run();
+ }
+
+ if (argc > 2)
+ {
+ file = argv[2];
+ ext = &file[strlen(file)-3];
+
+ if (!strcasecmp(ext, "gba"))
+ {
+ strncpy(ROMPath[1], file, 1023);
+ ROMPath[1][1023] = '\0';
+
+ SetupSRAMPath(1);
+
+ NDS::LoadGBAROM(ROMPath[1], SRAMPath[1]);
+ }
+ }
+ }
+
+ uiMain();
+
+ if (Joystick) SDL_JoystickClose(Joystick);
+ if (AudioDevice) SDL_CloseAudioDevice(AudioDevice);
+ if (MicDevice) SDL_CloseAudioDevice(MicDevice);
+
+ SDL_DestroyCond(AudioSync);
+ SDL_DestroyMutex(AudioSyncLock);
+
+ if (MicWavBuffer) delete[] MicWavBuffer;
+
+#ifdef MELONCAP
+ MelonCap::DeInit();
+#endif // MELONCAP
+
+ if (ScreenBitmap[0]) uiDrawFreeBitmap(ScreenBitmap[0]);
+ if (ScreenBitmap[1]) uiDrawFreeBitmap(ScreenBitmap[1]);
+
+ Config::ScreenRotation = ScreenRotation;
+ Config::ScreenGap = ScreenGap;
+ Config::ScreenLayout = ScreenLayout;
+ Config::ScreenSizing = ScreenSizing;
+
+ Config::Save();
+
+ uiUninit();
+ SDL_Quit();
+ delete[] EmuDirectory;
+ return 0;
+}
+
+#ifdef __WIN32__
+
+#include <windows.h>
+
+int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdshow)
+{
+ 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");
+ }
+
+ int ret = main(argc, argv);
+
+ printf("\n\n>");
+
+ for (int i = 0; i < argc; i++) if (argv[i] != nullarg) delete[] argv[i];
+ delete[] argv;
+
+ return ret;
+}
+
+#endif