diff options
Diffstat (limited to 'src/frontend')
-rw-r--r-- | src/frontend/qt_sdl/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/frontend/qt_sdl/Platform.cpp | 558 | ||||
-rw-r--r-- | src/frontend/qt_sdl/PlatformConfig.cpp | 151 | ||||
-rw-r--r-- | src/frontend/qt_sdl/PlatformConfig.h | 82 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.cpp | 570 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.h | 42 |
6 files changed, 1396 insertions, 9 deletions
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 1ee7629..f24464d 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -2,6 +2,8 @@ project(qt_sdl) SET(SOURCES_QT_SDL main.cpp + Platform.cpp + PlatformConfig.cpp ) if (WIN32) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp new file mode 100644 index 0000000..31b5277 --- /dev/null +++ b/src/frontend/qt_sdl/Platform.cpp @@ -0,0 +1,558 @@ +/* + 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <SDL2/SDL.h> +#include "Platform.h" +#include "PlatformConfig.h" +//#include "LAN_Socket.h" +//#include "LAN_PCap.h" +#include <string> + +#ifdef __WIN32__ + #define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK + #include <windows.h> + //#include <knownfolders.h> // FUCK THAT SHIT + extern "C" const GUID DECLSPEC_SELECTANY FOLDERID_RoamingAppData = {0x3eb685db, 0x65f9, 0x4cf6, {0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}}; + #include <shlobj.h> + #include <winsock2.h> + #include <ws2tcpip.h> + #define socket_t SOCKET + #define sockaddr_t SOCKADDR +#else + #include <glib.h> + #include <unistd.h> + #include <arpa/inet.h> + #include <netinet/in.h> + #include <sys/select.h> + #include <sys/socket.h> + #define socket_t int + #define sockaddr_t struct sockaddr + #define closesocket close +#endif + +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (socket_t)-1 +#endif + + +extern char* EmuDirectory; + +void Stop(bool internal); + + +namespace Platform +{ + + +typedef struct +{ + SDL_Thread* ID; + void (*Func)(); + +} ThreadData; + +int ThreadEntry(void* data) +{ + ThreadData* thread = (ThreadData*)data; + thread->Func(); + return 0; +} + + +socket_t MPSocket; +sockaddr_t MPSendAddr; +u8 PacketBuffer[2048]; + +#define NIFI_VER 1 + + +void StopEmu() +{ + //Stop(true); +} + + +FILE* OpenFile(const char* path, const char* mode, bool mustexist) +{ + FILE* ret; + +#ifdef __WIN32__ + + int len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); + if (len < 1) return NULL; + WCHAR* fatpath = new WCHAR[len]; + int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, fatpath, len); + if (res != len) { delete[] fatpath; return NULL; } // checkme? + + // this will be more than enough + WCHAR fatmode[4]; + fatmode[0] = mode[0]; + fatmode[1] = mode[1]; + fatmode[2] = mode[2]; + fatmode[3] = 0; + + if (mustexist) + { + ret = _wfopen(fatpath, L"rb"); + if (ret) ret = _wfreopen(fatpath, fatmode, ret); + } + else + ret = _wfopen(fatpath, fatmode); + + delete[] fatpath; + +#else + + if (mustexist) + { + ret = fopen(path, "rb"); + if (ret) ret = freopen(path, mode, ret); + } + else + ret = fopen(path, mode); + +#endif + + return ret; +} + +#if !defined(UNIX_PORTABLE) && !defined(__WIN32__) + +FILE* OpenLocalFile(const char* path, const char* mode) +{ + std::string fullpath; + if (path[0] == '/') + { + // If it's an absolute path, just open that. + fullpath = std::string(path); + } + else + { + // Check user configuration directory + std::string confpath = std::string(g_get_user_config_dir()) + "/melonDS/"; + g_mkdir_with_parents(confpath.c_str(), 0755); + fullpath = confpath + path; + } + + return OpenFile(fullpath.c_str(), mode, mode[0] != 'w'); +} + +FILE* OpenDataFile(const char* path) +{ + const char* melondir = "melonDS"; + const char* const* sys_dirs = g_get_system_data_dirs(); + const char* user_dir = g_get_user_data_dir(); + + // First check the user's data directory + char* fullpath = g_build_path("/", user_dir, melondir, path, NULL); + if (access(fullpath, R_OK) == 0) + { + FILE* f = fopen(fullpath, "r"); + g_free(fullpath); + return f; + } + free(fullpath); + + // Then check the system data directories + for (size_t i = 0; sys_dirs[i] != NULL; i++) + { + const char* dir = sys_dirs[i]; + char* fullpath = g_build_path("/", dir, melondir, path, NULL); + + if (access(fullpath, R_OK) == 0) + { + FILE* f = fopen(fullpath, "r"); + g_free(fullpath); + return f; + } + free(fullpath); + } + + FILE* f = fopen(path, "rb"); + if (f) return f; + + return NULL; +} + +#else + +FILE* OpenLocalFile(const char* path, const char* mode) +{ + bool relpath = false; + int pathlen = strlen(path); + +#ifdef __WIN32__ + if (pathlen > 3) + { + if (path[1] == ':' && path[2] == '\\') + return OpenFile(path, mode); + } +#else + if (pathlen > 1) + { + if (path[0] == '/') + return OpenFile(path, mode); + } +#endif + + if (pathlen >= 3) + { + if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path[2] == '\\')) + relpath = true; + } + + int emudirlen = strlen(EmuDirectory); + char* emudirpath; + if (emudirlen) + { + int len = emudirlen + 1 + pathlen + 1; + emudirpath = new char[len]; + strncpy(&emudirpath[0], EmuDirectory, emudirlen); + emudirpath[emudirlen] = '/'; + strncpy(&emudirpath[emudirlen+1], path, pathlen); + emudirpath[emudirlen+1+pathlen] = '\0'; + } + else + { + emudirpath = new char[pathlen+1]; + strncpy(&emudirpath[0], path, pathlen); + emudirpath[pathlen] = '\0'; + } + + // Locations are application directory, and AppData/melonDS on Windows or XDG_CONFIG_HOME/melonDS on Linux + + FILE* f; + + // First check current working directory + f = OpenFile(path, mode, true); + if (f) { delete[] emudirpath; return f; } + + // then emu directory + f = OpenFile(emudirpath, mode, true); + if (f) { delete[] emudirpath; return f; } + +#ifdef __WIN32__ + + // a path relative to AppData wouldn't make much sense + if (!relpath) + { + // Now check AppData + PWSTR appDataPath = NULL; + SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath); + if (!appDataPath) + { + delete[] emudirpath; + return NULL; + } + + // this will be more than enough + WCHAR fatperm[4]; + fatperm[0] = mode[0]; + fatperm[1] = mode[1]; + fatperm[2] = mode[2]; + fatperm[3] = 0; + + int fnlen = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); + if (fnlen < 1) { delete[] emudirpath; return NULL; } + WCHAR* wfileName = new WCHAR[fnlen]; + int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, wfileName, fnlen); + if (res != fnlen) { delete[] wfileName; delete[] emudirpath; return NULL; } // checkme? + + const WCHAR* appdir = L"\\melonDS\\"; + + int pos = wcslen(appDataPath); + void* ptr = CoTaskMemRealloc(appDataPath, (pos+wcslen(appdir)+fnlen+1)*sizeof(WCHAR)); + if (!ptr) { delete[] wfileName; delete[] emudirpath; return NULL; } // oh well + appDataPath = (PWSTR)ptr; + + wcscpy(&appDataPath[pos], appdir); pos += wcslen(appdir); + wcscpy(&appDataPath[pos], wfileName); + + f = _wfopen(appDataPath, L"rb"); + if (f) f = _wfreopen(appDataPath, fatperm, f); + CoTaskMemFree(appDataPath); + delete[] wfileName; + if (f) { delete[] emudirpath; return f; } + } + +#else + + if (!relpath) + { + // Now check XDG_CONFIG_HOME + // TODO: check for memory leak there + std::string fullpath = std::string(g_get_user_config_dir()) + "/melonDS/" + path; + f = OpenFile(fullpath.c_str(), mode, true); + if (f) { delete[] emudirpath; return f; } + } + +#endif + + if (mode[0] != 'r') + { + f = OpenFile(emudirpath, mode); + if (f) { delete[] emudirpath; return f; } + } + + delete[] emudirpath; + return NULL; +} + +FILE* OpenDataFile(const char* path) +{ + return OpenLocalFile(path, "rb"); +} + +#endif + + +void* Thread_Create(void (*func)()) +{ + ThreadData* data = new ThreadData; + data->Func = func; + data->ID = SDL_CreateThread(ThreadEntry, "melonDS core thread", data); + return data; +} + +void Thread_Free(void* thread) +{ + delete (ThreadData*)thread; +} + +void Thread_Wait(void* thread) +{ + SDL_WaitThread((SDL_Thread*)((ThreadData*)thread)->ID, NULL); +} + + +void* Semaphore_Create() +{ + return SDL_CreateSemaphore(0); +} + +void Semaphore_Free(void* sema) +{ + SDL_DestroySemaphore((SDL_sem*)sema); +} + +void Semaphore_Reset(void* sema) +{ + while (SDL_SemTryWait((SDL_sem*)sema) == 0); +} + +void Semaphore_Wait(void* sema) +{ + SDL_SemWait((SDL_sem*)sema); +} + +void Semaphore_Post(void* sema) +{ + SDL_SemPost((SDL_sem*)sema); +} + + +void* GL_GetProcAddress(const char* proc) +{ + return NULL;//uiGLGetProcAddress(proc); +} + + +bool MP_Init() +{ + int opt_true = 1; + int res; + +#ifdef __WIN32__ + WSADATA wsadata; + if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0) + { + return false; + } +#endif // __WIN32__ + + MPSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (MPSocket < 0) + { + return false; + } + + res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt_true, sizeof(int)); + if (res < 0) + { + closesocket(MPSocket); + MPSocket = INVALID_SOCKET; + return false; + } + + sockaddr_t saddr; + saddr.sa_family = AF_INET; + *(u32*)&saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK); + *(u16*)&saddr.sa_data[0] = htons(7064); + res = bind(MPSocket, &saddr, sizeof(sockaddr_t)); + if (res < 0) + { + closesocket(MPSocket); + MPSocket = INVALID_SOCKET; + return false; + } + + res = setsockopt(MPSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&opt_true, sizeof(int)); + if (res < 0) + { + closesocket(MPSocket); + MPSocket = INVALID_SOCKET; + return false; + } + + MPSendAddr.sa_family = AF_INET; + *(u32*)&MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST); + *(u16*)&MPSendAddr.sa_data[0] = htons(7064); + + return true; +} + +void MP_DeInit() +{ + if (MPSocket >= 0) + closesocket(MPSocket); + +#ifdef __WIN32__ + WSACleanup(); +#endif // __WIN32__ +} + +int MP_SendPacket(u8* data, int len) +{ + if (MPSocket < 0) + return 0; + + if (len > 2048-8) + { + printf("MP_SendPacket: error: packet too long (%d)\n", len); + return 0; + } + + *(u32*)&PacketBuffer[0] = htonl(0x4946494E); // NIFI + PacketBuffer[4] = NIFI_VER; + PacketBuffer[5] = 0; + *(u16*)&PacketBuffer[6] = htons(len); + memcpy(&PacketBuffer[8], data, len); + + int slen = sendto(MPSocket, (const char*)PacketBuffer, len+8, 0, &MPSendAddr, sizeof(sockaddr_t)); + if (slen < 8) return 0; + return slen - 8; +} + +int MP_RecvPacket(u8* data, bool block) +{ + if (MPSocket < 0) + return 0; + + fd_set fd; + struct timeval tv; + + FD_ZERO(&fd); + FD_SET(MPSocket, &fd); + tv.tv_sec = 0; + tv.tv_usec = block ? 5000 : 0; + + if (!select(MPSocket+1, &fd, 0, 0, &tv)) + { + return 0; + } + + sockaddr_t fromAddr; + socklen_t fromLen = sizeof(sockaddr_t); + int rlen = recvfrom(MPSocket, (char*)PacketBuffer, 2048, 0, &fromAddr, &fromLen); + if (rlen < 8+24) + { + return 0; + } + rlen -= 8; + + if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E) + { + return 0; + } + + if (PacketBuffer[4] != NIFI_VER) + { + return 0; + } + + if (ntohs(*(u16*)&PacketBuffer[6]) != rlen) + { + return 0; + } + + memcpy(data, &PacketBuffer[8], rlen); + return rlen; +} + + + +bool LAN_Init() +{ + /*if (Config::DirectLAN) + { + if (!LAN_PCap::Init(true)) + return false; + } + else + { + if (!LAN_Socket::Init()) + return false; + }*/ + + return true; +} + +void LAN_DeInit() +{ + // checkme. blarg + //if (Config::DirectLAN) + // LAN_PCap::DeInit(); + //else + // LAN_Socket::DeInit(); + /*LAN_PCap::DeInit(); + LAN_Socket::DeInit();*/ +} + +int LAN_SendPacket(u8* data, int len) +{ + /*if (Config::DirectLAN) + return LAN_PCap::SendPacket(data, len); + else + return LAN_Socket::SendPacket(data, len);*/ + return 0; +} + +int LAN_RecvPacket(u8* data) +{ + /*if (Config::DirectLAN) + return LAN_PCap::RecvPacket(data); + else + return LAN_Socket::RecvPacket(data);*/ + return 0; +} + + +} diff --git a/src/frontend/qt_sdl/PlatformConfig.cpp b/src/frontend/qt_sdl/PlatformConfig.cpp new file mode 100644 index 0000000..f78b195 --- /dev/null +++ b/src/frontend/qt_sdl/PlatformConfig.cpp @@ -0,0 +1,151 @@ +/* + 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "PlatformConfig.h" + +namespace Config +{ + +int KeyMapping[12]; +int JoyMapping[12]; + +int HKKeyMapping[HK_MAX]; +int HKJoyMapping[HK_MAX]; + +int JoystickID; + +int WindowWidth; +int WindowHeight; +int WindowMaximized; + +int ScreenRotation; +int ScreenGap; +int ScreenLayout; +int ScreenSizing; +int ScreenFilter; + +int ScreenUseGL; +int ScreenVSync; +int ScreenRatio; + +int LimitFPS; +int AudioSync; +int ShowOSD; + +int DirectBoot; + +int SocketBindAnyAddr; +char LANDevice[128]; +int DirectLAN; + +int SavestateRelocSRAM; + +int AudioVolume; +int MicInputType; +char MicWavPath[512]; + +char LastROMFolder[512]; + + +ConfigEntry PlatformConfigFile[] = +{ + {"Key_A", 0, &KeyMapping[0], 32, NULL, 0}, + {"Key_B", 0, &KeyMapping[1], 31, NULL, 0}, + {"Key_Select", 0, &KeyMapping[2], 57, NULL, 0}, + {"Key_Start", 0, &KeyMapping[3], 28, NULL, 0}, + {"Key_Right", 0, &KeyMapping[4], 333, NULL, 0}, + {"Key_Left", 0, &KeyMapping[5], 331, NULL, 0}, + {"Key_Up", 0, &KeyMapping[6], 328, NULL, 0}, + {"Key_Down", 0, &KeyMapping[7], 336, NULL, 0}, + {"Key_R", 0, &KeyMapping[8], 54, NULL, 0}, + {"Key_L", 0, &KeyMapping[9], 86, NULL, 0}, + {"Key_X", 0, &KeyMapping[10], 17, NULL, 0}, + {"Key_Y", 0, &KeyMapping[11], 30, NULL, 0}, + + {"Joy_A", 0, &JoyMapping[0], -1, NULL, 0}, + {"Joy_B", 0, &JoyMapping[1], -1, NULL, 0}, + {"Joy_Select", 0, &JoyMapping[2], -1, NULL, 0}, + {"Joy_Start", 0, &JoyMapping[3], -1, NULL, 0}, + {"Joy_Right", 0, &JoyMapping[4], -1, NULL, 0}, + {"Joy_Left", 0, &JoyMapping[5], -1, NULL, 0}, + {"Joy_Up", 0, &JoyMapping[6], -1, NULL, 0}, + {"Joy_Down", 0, &JoyMapping[7], -1, NULL, 0}, + {"Joy_R", 0, &JoyMapping[8], -1, NULL, 0}, + {"Joy_L", 0, &JoyMapping[9], -1, NULL, 0}, + {"Joy_X", 0, &JoyMapping[10], -1, NULL, 0}, + {"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0}, + + {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0D, NULL, 0}, + {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0}, + {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0}, + {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0}, + {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], 0x0F, NULL, 0}, + {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0}, + {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], 0x4B, NULL, 0}, + {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], 0x4D, NULL, 0}, + + {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0}, + {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0}, + {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, NULL, 0}, + {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0}, + {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0}, + {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0}, + {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, NULL, 0}, + {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, NULL, 0}, + + {"JoystickID", 0, &JoystickID, 0, NULL, 0}, + + {"WindowWidth", 0, &WindowWidth, 256, NULL, 0}, + {"WindowHeight", 0, &WindowHeight, 384, NULL, 0}, + {"WindowMax", 0, &WindowMaximized, 0, NULL, 0}, + + {"ScreenRotation", 0, &ScreenRotation, 0, NULL, 0}, + {"ScreenGap", 0, &ScreenGap, 0, NULL, 0}, + {"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0}, + {"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0}, + {"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0}, + + {"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0}, + {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0}, + {"ScreenRatio", 0, &ScreenRatio, 0, NULL, 0}, + + {"LimitFPS", 0, &LimitFPS, 0, NULL, 0}, + {"AudioSync", 0, &AudioSync, 1, NULL, 0}, + {"ShowOSD", 0, &ShowOSD, 1, NULL, 0}, + + {"DirectBoot", 0, &DirectBoot, 1, NULL, 0}, + + {"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0}, + {"LANDevice", 1, LANDevice, 0, "", 127}, + {"DirectLAN", 0, &DirectLAN, 0, NULL, 0}, + + {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 0, NULL, 0}, + + {"AudioVolume", 0, &AudioVolume, 256, NULL, 0}, + {"MicInputType", 0, &MicInputType, 1, NULL, 0}, + {"MicWavPath", 1, MicWavPath, 0, "", 511}, + + {"LastROMFolder", 1, LastROMFolder, 0, "", 511}, + + {"", -1, NULL, 0, NULL, 0} +}; + +} diff --git a/src/frontend/qt_sdl/PlatformConfig.h b/src/frontend/qt_sdl/PlatformConfig.h new file mode 100644 index 0000000..d0f765b --- /dev/null +++ b/src/frontend/qt_sdl/PlatformConfig.h @@ -0,0 +1,82 @@ +/* + 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/. +*/ + +#ifndef PLATFORMCONFIG_H +#define PLATFORMCONFIG_H + +#include "Config.h" + +enum +{ + HK_Lid = 0, + HK_Mic, + HK_Pause, + HK_Reset, + HK_FastForward, + HK_FastForwardToggle, + HK_SolarSensorDecrease, + HK_SolarSensorIncrease, + HK_MAX +}; + +namespace Config +{ + +extern int KeyMapping[12]; +extern int JoyMapping[12]; + +extern int HKKeyMapping[HK_MAX]; +extern int HKJoyMapping[HK_MAX]; + +extern int JoystickID; + +extern int WindowWidth; +extern int WindowHeight; +extern int WindowMaximized; + +extern int ScreenRotation; +extern int ScreenGap; +extern int ScreenLayout; +extern int ScreenSizing; +extern int ScreenFilter; + +extern int ScreenUseGL; +extern int ScreenVSync; +extern int ScreenRatio; + +extern int LimitFPS; +extern int AudioSync; +extern int ShowOSD; + +extern int DirectBoot; + +extern int SocketBindAnyAddr; +extern char LANDevice[128]; +extern int DirectLAN; + +extern int SavestateRelocSRAM; + +extern int AudioVolume; +extern int MicInputType; +extern char MicWavPath[512]; + +extern char LastROMFolder[512]; + +} + +#endif // PLATFORMCONFIG_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0eb84a5..9192685 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -22,20 +22,364 @@ #include <string.h> #include <QApplication> -#include <QMainWindow> +#include <QMessageBox> +#include <QMenuBar> +#include <QFileDialog> +#include <QPaintEvent> +#include <QPainter> + +#include <SDL2/SDL.h> #include "main.h" -#include "../../version.h" +#include "types.h" +#include "version.h" + +#include "NDS.h" +#include "GBACart.h" +#include "GPU.h" +#include "SPU.h" +#include "Wifi.h" +#include "Platform.h" +#include "Config.h" + +#include "Savestate.h" + + +char* EmuDirectory; + +bool RunningSomething; +char ROMPath[2][1024]; +char SRAMPath[2][1024]; +char PrevSRAMPath[2][1024]; // for savestate 'undo load' + +bool SavestateLoaded; + +MainWindow* mainWindow; +EmuThread* emuThread; + + +EmuThread::EmuThread(QObject* parent) : QThread(parent) +{ + EmuStatus = 0; + EmuRunning = 2; +} + +void EmuThread::run() +{ + 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}; +printf("emu thread start: %d\n", EmuRunning); + 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(0xFFF); + /*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); + } + + 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); + } + printf("ran iteration: status=%d run=%d\n", EmuStatus, EmuRunning); + } + + 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); +} + +void EmuThread::emuRun() +{ + EmuRunning = 1; +} + +void EmuThread::emuPause(bool refresh) +{ + int status = refresh ? 2:3; + PrevEmuStatus = EmuRunning; + EmuRunning = status;printf("emuPause %d -> %d %d\n", PrevEmuStatus, EmuRunning, EmuStatus); + while (EmuStatus != status);printf("wait done\n"); +} + +void EmuThread::emuUnpause() +{ + EmuRunning = PrevEmuStatus; +} + +void EmuThread::emuStop() +{ + EmuRunning = 0; +} + + +MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) +{ +} + +MainWindowPanel::~MainWindowPanel() +{ +} + +void MainWindowPanel::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + + //painter. +} MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { setWindowTitle("melonDS - assfucking Qt version"); - // burp - QWidget *centralWidget = new QWidget(this); - setCentralWidget(centralWidget); + QMenuBar* menubar = new QMenuBar(); + { + QMenu* menu = menubar->addMenu("File"); + QAction* act; + + act = menu->addAction("Open file..."); + connect(act, &QAction::triggered, this, &MainWindow::onOpenFile); + } + setMenuBar(menubar); + + panel = new MainWindowPanel(this); + setCentralWidget(panel); + panel->setMinimumSize(256, 384); } MainWindow::~MainWindow() @@ -43,6 +387,13 @@ MainWindow::~MainWindow() } +void MainWindow::onOpenFile() +{ + QString filename = QFileDialog::getOpenFileName(this, "Open ROM", "", "DS ROMs (*.nds *.srl);;Any file (*.*)"); + printf("fark: %p %d %s\n", filename, filename.isEmpty(), filename.toStdString().c_str()); +} + + int main(int argc, char** argv) { srand(time(NULL)); @@ -50,12 +401,212 @@ int main(int argc, char** argv) 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 + QApplication melon(argc, argv); - MainWindow win; - win.show(); + // 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) + { + QMessageBox::critical(NULL, "melonDS", "SDL shat itself :("); + return 1; + } + + SDL_JoystickEventState(SDL_ENABLE); + + Config::Load(); + + //if (Config::AudioVolume < 0) Config::AudioVolume = 0; + //else if (Config::AudioVolume > 256) Config::AudioVolume = 256; - return melon.exec(); + // TODO: those should be checked before running anything + // (as to let the user specify their own BIOS/firmware path etc) +#if 0 + 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); + } + } +#endif + + mainWindow = new MainWindow(); + mainWindow->show(); + + emuThread = new EmuThread(); + emuThread->start(); + emuThread->emuPause(true); + + 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]); + } + } + } + + int ret = melon.exec(); +printf("melon over\n"); + emuThread->emuStop();printf("STOP\n"); + emuThread->wait();printf("farked\n"); + + Config::Save(); + + SDL_Quit(); + delete[] EmuDirectory; + return ret; } #ifdef __WIN32__ @@ -71,6 +622,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho char** argv = new char*[argc]; for (int i = 0; i < argc; i++) { + if (!argv_w) { argv[i] = nullarg; continue; } int len = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL); if (len < 1) { argv[i] = nullarg; continue; } argv[i] = new char[len]; @@ -78,6 +630,8 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho if (res != len) { delete[] argv[i]; argv[i] = nullarg; } } + if (argv_w) LocalFree(argv_w); + if (AttachConsole(ATTACH_PARENT_PROCESS)) { freopen("CONOUT$", "w", stdout); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index ad795ef..92b1846 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -19,8 +19,45 @@ #ifndef MAIN_H #define MAIN_H +#include <QThread> +#include <QWidget> #include <QMainWindow> + +class EmuThread : public QThread +{ + Q_OBJECT + void run() override; + +public: + explicit EmuThread(QObject* parent = nullptr); + + // to be called from the UI thread + void emuRun(); + void emuPause(bool refresh); + void emuUnpause(); + void emuStop(); + +private: + volatile int EmuStatus; + int PrevEmuStatus; + int EmuRunning; +}; + + +class MainWindowPanel : public QWidget +{ + Q_OBJECT + +public: + explicit MainWindowPanel(QWidget* parent); + ~MainWindowPanel(); + +protected: + void paintEvent(QPaintEvent* event) override; +}; + + class MainWindow : public QMainWindow { Q_OBJECT @@ -29,8 +66,11 @@ public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow(); +private slots: + void onOpenFile(); + private: - // private shit goes here + MainWindowPanel* panel; }; #endif // MAIN_H |