mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2024-11-14 21:37:42 -07:00
get some of the shit going, I guess
atleast the emuthread is going and we have its control system down and other fun shit, too
This commit is contained in:
parent
439ca1b2b5
commit
690f9f3874
@ -21,6 +21,7 @@ add_library(core STATIC
|
||||
NDS.cpp
|
||||
NDSCart.cpp
|
||||
OpenGLSupport.cpp
|
||||
Platform.h
|
||||
RTC.cpp
|
||||
Savestate.cpp
|
||||
SPI.cpp
|
||||
|
@ -2,6 +2,8 @@ project(qt_sdl)
|
||||
|
||||
SET(SOURCES_QT_SDL
|
||||
main.cpp
|
||||
Platform.cpp
|
||||
PlatformConfig.cpp
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
|
558
src/frontend/qt_sdl/Platform.cpp
Normal file
558
src/frontend/qt_sdl/Platform.cpp
Normal file
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
151
src/frontend/qt_sdl/PlatformConfig.cpp
Normal file
151
src/frontend/qt_sdl/PlatformConfig.cpp
Normal file
@ -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}
|
||||
};
|
||||
|
||||
}
|
82
src/frontend/qt_sdl/PlatformConfig.h
Normal file
82
src/frontend/qt_sdl/PlatformConfig.h
Normal file
@ -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
|
@ -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");
|
||||
|
||||
return melon.exec();
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user