mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-21 13:20:57 -06: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:
@ -21,6 +21,7 @@ add_library(core STATIC
|
|||||||
NDS.cpp
|
NDS.cpp
|
||||||
NDSCart.cpp
|
NDSCart.cpp
|
||||||
OpenGLSupport.cpp
|
OpenGLSupport.cpp
|
||||||
|
Platform.h
|
||||||
RTC.cpp
|
RTC.cpp
|
||||||
Savestate.cpp
|
Savestate.cpp
|
||||||
SPI.cpp
|
SPI.cpp
|
||||||
|
@ -2,6 +2,8 @@ project(qt_sdl)
|
|||||||
|
|
||||||
SET(SOURCES_QT_SDL
|
SET(SOURCES_QT_SDL
|
||||||
main.cpp
|
main.cpp
|
||||||
|
Platform.cpp
|
||||||
|
PlatformConfig.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
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 <string.h>
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMainWindow>
|
#include <QMessageBox>
|
||||||
|
#include <QMenuBar>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "main.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)
|
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
|
||||||
{
|
{
|
||||||
setWindowTitle("melonDS - assfucking Qt version");
|
setWindowTitle("melonDS - assfucking Qt version");
|
||||||
|
|
||||||
// burp
|
QMenuBar* menubar = new QMenuBar();
|
||||||
QWidget *centralWidget = new QWidget(this);
|
{
|
||||||
setCentralWidget(centralWidget);
|
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()
|
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)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
@ -50,12 +401,212 @@ int main(int argc, char** argv)
|
|||||||
printf("melonDS " MELONDS_VERSION "\n");
|
printf("melonDS " MELONDS_VERSION "\n");
|
||||||
printf(MELONDS_URL "\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);
|
QApplication melon(argc, argv);
|
||||||
|
|
||||||
MainWindow win;
|
// http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
|
||||||
win.show();
|
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__
|
#ifdef __WIN32__
|
||||||
@ -71,6 +622,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
|
|||||||
char** argv = new char*[argc];
|
char** argv = new char*[argc];
|
||||||
for (int i = 0; i < argc; i++)
|
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);
|
int len = WideCharToMultiByte(CP_UTF8, 0, argv_w[i], -1, NULL, 0, NULL, NULL);
|
||||||
if (len < 1) { argv[i] = nullarg; continue; }
|
if (len < 1) { argv[i] = nullarg; continue; }
|
||||||
argv[i] = new char[len];
|
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 (res != len) { delete[] argv[i]; argv[i] = nullarg; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (argv_w) LocalFree(argv_w);
|
||||||
|
|
||||||
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
if (AttachConsole(ATTACH_PARENT_PROCESS))
|
||||||
{
|
{
|
||||||
freopen("CONOUT$", "w", stdout);
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
@ -19,8 +19,45 @@
|
|||||||
#ifndef MAIN_H
|
#ifndef MAIN_H
|
||||||
#define MAIN_H
|
#define MAIN_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QWidget>
|
||||||
#include <QMainWindow>
|
#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
|
class MainWindow : public QMainWindow
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -29,8 +66,11 @@ public:
|
|||||||
explicit MainWindow(QWidget* parent = nullptr);
|
explicit MainWindow(QWidget* parent = nullptr);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onOpenFile();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// private shit goes here
|
MainWindowPanel* panel;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAIN_H
|
#endif // MAIN_H
|
||||||
|
Reference in New Issue
Block a user