mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2024-11-14 13:27:41 -07:00
Merge remote-tracking branch 'upstream/master' into interpreter-fixes
This commit is contained in:
commit
19e0b18d15
2
.github/workflows/build-macos.yml
vendored
2
.github/workflows/build-macos.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
- name: Set up vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
vcpkgGitCommitId: 1de2026f28ead93ff1773e6e680387643e914ea1
|
||||
vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da
|
||||
- name: Build
|
||||
uses: lukka/run-cmake@v10
|
||||
with:
|
||||
|
4
.github/workflows/build-windows.yml
vendored
4
.github/workflows/build-windows.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- ci/vcpkg-update
|
||||
- ci/*
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
@ -27,7 +27,7 @@ jobs:
|
||||
- name: Set up vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
with:
|
||||
vcpkgGitCommitId: 1de2026f28ead93ff1773e6e680387643e914ea1
|
||||
vcpkgGitCommitId: 3508985146f1b1d248c67ead13f8f54be5b4f5da
|
||||
- name: Configure
|
||||
run: cmake --preset=release-mingw-x86_64
|
||||
- name: Build
|
||||
|
@ -9,7 +9,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}")
|
||||
endif()
|
||||
FetchContent_Declare(vcpkg
|
||||
GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git"
|
||||
GIT_TAG 2024.07.12
|
||||
GIT_TAG 2024.08.23
|
||||
SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg")
|
||||
FetchContent_MakeAvailable(vcpkg)
|
||||
endif()
|
||||
@ -25,6 +25,11 @@ else()
|
||||
option(USE_QT6 "Build using Qt 6 instead of 5" OFF)
|
||||
endif()
|
||||
|
||||
# Since the Linux build pulls in glib anyway, we can just use upstream libslirp
|
||||
if (UNIX AND NOT APPLE)
|
||||
option(USE_SYSTEM_LIBSLIRP "Use system libslirp instead of the bundled version" ON)
|
||||
endif()
|
||||
|
||||
if (NOT USE_QT6)
|
||||
list(APPEND VCPKG_MANIFEST_FEATURES qt5)
|
||||
set(VCPKG_MANIFEST_NO_DEFAULT_FEATURES ON)
|
||||
@ -62,6 +67,14 @@ if (USE_RECOMMENDED_TRIPLETS)
|
||||
# TODO Windows arm64 if possible
|
||||
set(_CAN_TARGET_AS_HOST ON)
|
||||
set(_WANTED_TRIPLET x64-mingw-static-release)
|
||||
elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
|
||||
# Can't really detect cross compiling here.
|
||||
set(_CAN_TARGET_AS_HOST ON)
|
||||
if (_HOST_PROCESSOR STREQUAL x86_64)
|
||||
set(_WANTED_TRIPLET x64-linux-release)
|
||||
elseif(_HOST_PROCESSOR STREQUAL "aarch64")
|
||||
set(_WANTED_TRIPLET arm64-linux-release)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Don't override it if the user set something else
|
||||
|
@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1723175592,
|
||||
"narHash": "sha256-M0xJ3FbDUc4fRZ84dPGx5VvgFsOzds77KiBMW/mMTnI=",
|
||||
"lastModified": 1725432240,
|
||||
"narHash": "sha256-+yj+xgsfZaErbfYM3T+QvEE2hU7UuE+Jf0fJCJ8uPS0=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "5e0ca22929f3342b19569b21b2f3462f053e497b",
|
||||
"rev": "ad416d066ca1222956472ab7d0555a6946746a80",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
39
flake.nix
39
flake.nix
@ -25,23 +25,23 @@
|
||||
cmake
|
||||
ninja
|
||||
pkg-config
|
||||
kdePackages.wrapQtAppsHook
|
||||
qt6.wrapQtAppsHook
|
||||
];
|
||||
|
||||
buildInputs = (with pkgs; [
|
||||
kdePackages.qtbase
|
||||
kdePackages.qtmultimedia
|
||||
extra-cmake-modules
|
||||
qt6.qtbase
|
||||
qt6.qtmultimedia
|
||||
SDL2
|
||||
zstd
|
||||
libarchive
|
||||
libGL
|
||||
libslirp
|
||||
enet
|
||||
]) ++ optionals isLinux [
|
||||
pkgs.wayland
|
||||
pkgs.kdePackages.qtwayland
|
||||
];
|
||||
]) ++ optionals (!isDarwin) (with pkgs; [
|
||||
kdePackages.extra-cmake-modules
|
||||
qt6.qtwayland
|
||||
wayland
|
||||
]);
|
||||
|
||||
cmakeFlags = [
|
||||
(cmakeBool "USE_QT6" true)
|
||||
@ -65,8 +65,27 @@
|
||||
apps.default = flake-utils.lib.mkApp {
|
||||
drv = self.packages.${system}.default;
|
||||
};
|
||||
devShells.default = pkgs.mkShell {
|
||||
inputsFrom = [ self.packages.${system}.default ];
|
||||
devShells = {
|
||||
default = pkgs.mkShell {
|
||||
inputsFrom = [ self.packages.${system}.default ];
|
||||
};
|
||||
|
||||
# Shell for building static melonDS release builds with vcpkg
|
||||
# Use mkShellNoCC to ensure Nix's gcc/clang and stdlib isn't used
|
||||
vcpkg = pkgs.mkShellNoCC {
|
||||
packages = with pkgs; [
|
||||
autoconf
|
||||
autoconf-archive
|
||||
automake
|
||||
cmake
|
||||
cups.dev # Needed by qtbase despite not enabling print support
|
||||
git
|
||||
iconv.dev
|
||||
libtool
|
||||
ninja
|
||||
pkg-config
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
117
src/GPU3D.cpp
117
src/GPU3D.cpp
@ -274,6 +274,8 @@ void GPU3D::Reset() noexcept
|
||||
memset(MatEmission, 0, sizeof(MatSpecular));
|
||||
|
||||
UseShininessTable = false;
|
||||
// Shininess table seems to be uninitialized garbage, at least on n3dsxl hw?
|
||||
// Also doesn't seem to be cleared properly unless the system is fully powered off?
|
||||
memset(ShininessTable, 0, sizeof(ShininessTable));
|
||||
|
||||
PolygonAttr = 0;
|
||||
@ -1459,67 +1461,86 @@ void GPU3D::CalculateLighting() noexcept
|
||||
TexCoords[1] = RawTexCoords[1] + (((s64)Normal[0]*TexMatrix[1] + (s64)Normal[1]*TexMatrix[5] + (s64)Normal[2]*TexMatrix[9]) >> 21);
|
||||
}
|
||||
|
||||
s32 normaltrans[3];
|
||||
normaltrans[0] = (Normal[0]*VecMatrix[0] + Normal[1]*VecMatrix[4] + Normal[2]*VecMatrix[8]) >> 12;
|
||||
normaltrans[1] = (Normal[0]*VecMatrix[1] + Normal[1]*VecMatrix[5] + Normal[2]*VecMatrix[9]) >> 12;
|
||||
normaltrans[2] = (Normal[0]*VecMatrix[2] + Normal[1]*VecMatrix[6] + Normal[2]*VecMatrix[10]) >> 12;
|
||||
|
||||
VertexColor[0] = MatEmission[0];
|
||||
VertexColor[1] = MatEmission[1];
|
||||
VertexColor[2] = MatEmission[2];
|
||||
s32 normaltrans[3]; // should be 1 bit sign 10 bits frac
|
||||
normaltrans[0] = ((Normal[0]*VecMatrix[0] + Normal[1]*VecMatrix[4] + Normal[2]*VecMatrix[8]) << 9) >> 21;
|
||||
normaltrans[1] = ((Normal[0]*VecMatrix[1] + Normal[1]*VecMatrix[5] + Normal[2]*VecMatrix[9]) << 9) >> 21;
|
||||
normaltrans[2] = ((Normal[0]*VecMatrix[2] + Normal[1]*VecMatrix[6] + Normal[2]*VecMatrix[10]) << 9) >> 21;
|
||||
|
||||
s32 c = 0;
|
||||
u32 vtxbuff[3] =
|
||||
{
|
||||
(u32)MatEmission[0] << 14,
|
||||
(u32)MatEmission[1] << 14,
|
||||
(u32)MatEmission[2] << 14
|
||||
};
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (!(CurPolygonAttr & (1<<i)))
|
||||
continue;
|
||||
|
||||
// overflow handling (for example, if the normal length is >1)
|
||||
// according to some hardware tests
|
||||
// * diffuse level is saturated to 255
|
||||
// * shininess level mirrors back to 0 and is ANDed with 0xFF, that before being squared
|
||||
// TODO: check how it behaves when the computed shininess is >=0x200
|
||||
// (credit to azusa for working out most of the details of the diff. algorithm, and essentially the entire spec. algorithm)
|
||||
|
||||
// calculate dot product
|
||||
// bottom 9 bits are discarded after multiplying and before adding
|
||||
s32 dot = ((LightDirection[i][0]*normaltrans[0]) >> 9) +
|
||||
((LightDirection[i][1]*normaltrans[1]) >> 9) +
|
||||
((LightDirection[i][2]*normaltrans[2]) >> 9);
|
||||
|
||||
s32 difflevel = (-(LightDirection[i][0]*normaltrans[0] +
|
||||
LightDirection[i][1]*normaltrans[1] +
|
||||
LightDirection[i][2]*normaltrans[2])) >> 10;
|
||||
if (difflevel < 0) difflevel = 0;
|
||||
else if (difflevel > 255) difflevel = 255;
|
||||
s32 shinelevel;
|
||||
if (dot > 0)
|
||||
{
|
||||
// -- diffuse lighting --
|
||||
|
||||
// convert dot to signed 11 bit int
|
||||
// then we truncate the result of the multiplications to an unsigned 20 bits before adding to the vtx color
|
||||
s32 diffdot = (dot << 21) >> 21;
|
||||
vtxbuff[0] += (MatDiffuse[0] * LightColor[i][0] * diffdot) & 0xFFFFF;
|
||||
vtxbuff[1] += (MatDiffuse[1] * LightColor[i][1] * diffdot) & 0xFFFFF;
|
||||
vtxbuff[2] += (MatDiffuse[2] * LightColor[i][2] * diffdot) & 0xFFFFF;
|
||||
|
||||
s32 shinelevel = -(((LightDirection[i][0]>>1)*normaltrans[0] +
|
||||
(LightDirection[i][1]>>1)*normaltrans[1] +
|
||||
((LightDirection[i][2]-0x200)>>1)*normaltrans[2]) >> 10);
|
||||
if (shinelevel < 0) shinelevel = 0;
|
||||
else if (shinelevel > 255) shinelevel = (0x100 - shinelevel) & 0xFF;
|
||||
shinelevel = ((shinelevel * shinelevel) >> 7) - 0x100; // really (2*shinelevel*shinelevel)-1
|
||||
if (shinelevel < 0) shinelevel = 0;
|
||||
// -- specular lighting --
|
||||
|
||||
// reuse the dot product from diffuse lighting
|
||||
dot += normaltrans[2];
|
||||
|
||||
// convert to s11, then square it, and truncate to 10 bits
|
||||
dot = (dot << 21) >> 21;
|
||||
dot = ((dot * dot) >> 10) & 0x3FF;
|
||||
|
||||
// multiply dot and reciprocal, the subtract '1'
|
||||
shinelevel = ((dot * SpecRecip[i]) >> 8) - (1<<9);
|
||||
|
||||
if (shinelevel < 0) shinelevel = 0;
|
||||
else
|
||||
{
|
||||
// sign extend to convert to signed 14 bit integer
|
||||
shinelevel = (shinelevel << 18) >> 18;
|
||||
if (shinelevel < 0) shinelevel = 0; // for some reason there seems to be a redundant check for <0?
|
||||
else if (shinelevel > 0x1FF) shinelevel = 0x1FF;
|
||||
}
|
||||
}
|
||||
else shinelevel = 0;
|
||||
|
||||
// convert shinelevel to use for lookup in the shininess table if enabled.
|
||||
if (UseShininessTable)
|
||||
{
|
||||
// checkme
|
||||
shinelevel >>= 1;
|
||||
shinelevel >>= 2;
|
||||
shinelevel = ShininessTable[shinelevel];
|
||||
shinelevel <<= 1;
|
||||
}
|
||||
|
||||
VertexColor[0] += ((MatSpecular[0] * LightColor[i][0] * shinelevel) >> 13);
|
||||
VertexColor[0] += ((MatDiffuse[0] * LightColor[i][0] * difflevel) >> 13);
|
||||
VertexColor[0] += ((MatAmbient[0] * LightColor[i][0]) >> 5);
|
||||
|
||||
VertexColor[1] += ((MatSpecular[1] * LightColor[i][1] * shinelevel) >> 13);
|
||||
VertexColor[1] += ((MatDiffuse[1] * LightColor[i][1] * difflevel) >> 13);
|
||||
VertexColor[1] += ((MatAmbient[1] * LightColor[i][1]) >> 5);
|
||||
|
||||
VertexColor[2] += ((MatSpecular[2] * LightColor[i][2] * shinelevel) >> 13);
|
||||
VertexColor[2] += ((MatDiffuse[2] * LightColor[i][2] * difflevel) >> 13);
|
||||
VertexColor[2] += ((MatAmbient[2] * LightColor[i][2]) >> 5);
|
||||
|
||||
if (VertexColor[0] > 31) VertexColor[0] = 31;
|
||||
if (VertexColor[1] > 31) VertexColor[1] = 31;
|
||||
if (VertexColor[2] > 31) VertexColor[2] = 31;
|
||||
// Note: ambient seems to be a plain bitshift
|
||||
vtxbuff[0] += ((MatSpecular[0] * shinelevel) + (MatAmbient[0] << 9)) * LightColor[i][0];
|
||||
vtxbuff[1] += ((MatSpecular[1] * shinelevel) + (MatAmbient[1] << 9)) * LightColor[i][1];
|
||||
vtxbuff[2] += ((MatSpecular[2] * shinelevel) + (MatAmbient[2] << 9)) * LightColor[i][2];
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
VertexColor[0] = (vtxbuff[0] >> 14 > 31) ? 31 : (vtxbuff[0] >> 14);
|
||||
VertexColor[1] = (vtxbuff[1] >> 14 > 31) ? 31 : (vtxbuff[1] >> 14);
|
||||
VertexColor[2] = (vtxbuff[2] >> 14 > 31) ? 31 : (vtxbuff[2] >> 14);
|
||||
|
||||
if (c < 1) c = 1;
|
||||
NormalPipeline = 7;
|
||||
AddCycles(c);
|
||||
@ -2012,9 +2033,15 @@ void GPU3D::ExecuteCommand() noexcept
|
||||
dir[0] = (s16)((entry.Param & 0x000003FF) << 6) >> 6;
|
||||
dir[1] = (s16)((entry.Param & 0x000FFC00) >> 4) >> 6;
|
||||
dir[2] = (s16)((entry.Param & 0x3FF00000) >> 14) >> 6;
|
||||
LightDirection[l][0] = (dir[0]*VecMatrix[0] + dir[1]*VecMatrix[4] + dir[2]*VecMatrix[8]) >> 12;
|
||||
LightDirection[l][1] = (dir[0]*VecMatrix[1] + dir[1]*VecMatrix[5] + dir[2]*VecMatrix[9]) >> 12;
|
||||
LightDirection[l][2] = (dir[0]*VecMatrix[2] + dir[1]*VecMatrix[6] + dir[2]*VecMatrix[10]) >> 12;
|
||||
// the order of operations here is very specific: discard bottom 12 bits -> negate -> then sign extend to convert to 11 bit signed int
|
||||
// except for when used to calculate the specular reciprocal; then it's: sign extend -> discard lsb -> negate.
|
||||
LightDirection[l][0] = (-((dir[0]*VecMatrix[0] + dir[1]*VecMatrix[4] + dir[2]*VecMatrix[8] ) >> 12) << 21) >> 21;
|
||||
LightDirection[l][1] = (-((dir[0]*VecMatrix[1] + dir[1]*VecMatrix[5] + dir[2]*VecMatrix[9] ) >> 12) << 21) >> 21;
|
||||
LightDirection[l][2] = (-((dir[0]*VecMatrix[2] + dir[1]*VecMatrix[6] + dir[2]*VecMatrix[10]) >> 12) << 21) >> 21;
|
||||
s32 den = -(((dir[0]*VecMatrix[2] + dir[1]*VecMatrix[6] + dir[2]*VecMatrix[10]) << 9) >> 21) + (1<<9);
|
||||
|
||||
if (den == 0) SpecRecip[l] = 0;
|
||||
else SpecRecip[l] = (1<<18) / den;
|
||||
}
|
||||
AddCycles(5);
|
||||
break;
|
||||
|
@ -286,6 +286,7 @@ public:
|
||||
s16 Normal[3] {};
|
||||
|
||||
s16 LightDirection[4][3] {};
|
||||
s32 SpecRecip[4] {};
|
||||
u8 LightColor[4][3] {};
|
||||
u8 MatDiffuse[3] {};
|
||||
u8 MatAmbient[3] {};
|
||||
|
@ -1830,7 +1830,7 @@ const ROMListEntry ROMList[] =
|
||||
{0x45564E43, 0x10000000, 0x00000005},
|
||||
{0x45564F59, 0x00800000, 0x00000001},
|
||||
{0x45565041, 0x00800000, 0x00000002},
|
||||
{0x45565042, 0x00800000, 0x00000004},
|
||||
{0x45565042, 0x00800000, 0x00000002},
|
||||
{0x45565043, 0x04000000, 0x00000002},
|
||||
{0x45565056, 0x04000000, 0x00000002},
|
||||
{0x45565059, 0x04000000, 0x00000001},
|
||||
@ -6804,4 +6804,4 @@ const ROMListEntry ROMList[] =
|
||||
|
||||
const size_t ROMListEntryCount = sizeof(ROMList) / sizeof(ROMListEntry);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../CRC32.h"
|
||||
#include "../Platform.h"
|
||||
#include "hexutil.h"
|
||||
|
||||
#include "GdbProto.h"
|
||||
#include "GdbStub.h"
|
||||
|
||||
using namespace melonDS;
|
||||
using Platform::Log;
|
||||
@ -878,6 +879,7 @@ ExecResult GdbStub::Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||
|
||||
ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||
{
|
||||
printf("must reply empty\n");
|
||||
stub->Resp(NULL, 0);
|
||||
return ExecResult::Ok;
|
||||
}
|
||||
@ -886,6 +888,7 @@ ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||
{
|
||||
if (len < 1)
|
||||
{
|
||||
printf("insufficient length");
|
||||
stub->RespStr("E01");
|
||||
return ExecResult::Ok;
|
||||
}
|
||||
@ -902,6 +905,7 @@ ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len)
|
||||
stub->RespStr("OK");
|
||||
return ExecResult::MustBreak;
|
||||
default:
|
||||
printf("invalid continue %c %s\n", cmd[0], cmd);
|
||||
stub->RespStr("E01");
|
||||
return ExecResult::Ok;
|
||||
}
|
||||
|
@ -5,12 +5,14 @@
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <poll.h>
|
||||
@ -21,8 +23,7 @@
|
||||
#include "../Platform.h"
|
||||
#include "hexutil.h"
|
||||
|
||||
#include "GdbProto.h"
|
||||
|
||||
#include "GdbStub.h"
|
||||
|
||||
using namespace melonDS;
|
||||
using Platform::Log;
|
||||
@ -42,87 +43,128 @@ namespace Gdb
|
||||
* vKill;pid
|
||||
* qRcmd? qSupported?
|
||||
*/
|
||||
u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
ssize_t Cmdlen;
|
||||
|
||||
namespace Proto
|
||||
|
||||
Gdb::ReadResult GdbStub::TryParsePacket(size_t start, size_t& packetStart, size_t& packetSize, size_t& packetContentSize)
|
||||
{
|
||||
|
||||
u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5];
|
||||
|
||||
ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/])
|
||||
{
|
||||
static ssize_t dataoff = 0;
|
||||
|
||||
ssize_t recv_total = dataoff;
|
||||
ssize_t cksumoff = -1;
|
||||
u8 sum = 0;
|
||||
|
||||
bool first = true;
|
||||
|
||||
//printf("--- dataoff=%zd\n", dataoff);
|
||||
if (dataoff != 0) {
|
||||
printf("--- got preexisting: %s\n", PacketBuf);
|
||||
|
||||
ssize_t datastart = 0;
|
||||
while (true)
|
||||
RecvBuffer[RecvBufferFilled] = '\0';
|
||||
//Log(LogLevel::Debug, "[GDB] Trying to parse packet %s %d %d\n", &RecvBuffer[0], start, RecvBufferFilled);
|
||||
size_t i = start;
|
||||
while (i < RecvBufferFilled)
|
||||
{
|
||||
char curChar = RecvBuffer[i++];
|
||||
if (curChar == '\x04') return ReadResult::Eof;
|
||||
else if (curChar == '\x03')
|
||||
{
|
||||
if (PacketBuf[datastart] == '\x04') return ReadResult::Eof;
|
||||
else if (PacketBuf[datastart] == '+' || PacketBuf[datastart] == '-')
|
||||
packetStart = i - 1;
|
||||
packetSize = packetContentSize = 1;
|
||||
return ReadResult::Break;
|
||||
}
|
||||
else if (curChar == '+' || curChar == '-') continue;
|
||||
else if (curChar == '$')
|
||||
{
|
||||
packetStart = i;
|
||||
uint8_t checksumGot = 0;
|
||||
while (i < RecvBufferFilled)
|
||||
{
|
||||
/*if (PacketBuf[datastart] == '+') SendAck(connfd);
|
||||
else SendNak(connfd);*/
|
||||
++datastart;
|
||||
continue;
|
||||
}
|
||||
else if (PacketBuf[datastart] == '$')
|
||||
{
|
||||
++datastart;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
__builtin_trap();
|
||||
return ReadResult::Wut;
|
||||
curChar = RecvBuffer[i];
|
||||
if (curChar == '#' && i + 2 < RecvBufferFilled)
|
||||
{
|
||||
u8 checksumShould = (hex2nyb(RecvBuffer[i+1]) << 4)
|
||||
| hex2nyb(RecvBuffer[i+2]);
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] found pkt, checksumGot: %02x vs %02x\n", checksumShould, checksumGot);
|
||||
|
||||
if (checksumShould != checksumGot)
|
||||
{
|
||||
return ReadResult::CksumErr;
|
||||
}
|
||||
|
||||
packetContentSize = i - packetStart;
|
||||
packetSize = packetContentSize + 3;
|
||||
return ReadResult::CmdRecvd;
|
||||
}
|
||||
else
|
||||
{
|
||||
checksumGot += curChar;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
printf("--- datastart=%zd\n", datastart);
|
||||
|
||||
for (ssize_t i = datastart; i < dataoff; ++i)
|
||||
else
|
||||
{
|
||||
if (PacketBuf[i] == '#')
|
||||
{
|
||||
cksumoff = i + 1;
|
||||
printf("--- cksumoff=%zd\n", cksumoff);
|
||||
break;
|
||||
}
|
||||
|
||||
sum += PacketBuf[i];
|
||||
}
|
||||
|
||||
if (cksumoff >= 0)
|
||||
{
|
||||
recv_total = dataoff - datastart + 1;
|
||||
dataoff = cksumoff + 2 - datastart + 1;
|
||||
cksumoff -= datastart - 1;
|
||||
|
||||
memmove(&PacketBuf[1], &PacketBuf[datastart], recv_total);
|
||||
PacketBuf[0] = '$';
|
||||
PacketBuf[recv_total] = 0;
|
||||
|
||||
printf("=== cksumoff=%zi recv_total=%zi datastart=%zi dataoff=%zi\n==> %s\n",
|
||||
cksumoff, recv_total, datastart, dataoff, PacketBuf);
|
||||
//break;
|
||||
Log(LogLevel::Error, "[GDB] Received unknown character %c (%d)\n", curChar, curChar);
|
||||
return ReadResult::Wut;
|
||||
}
|
||||
}
|
||||
|
||||
while (cksumoff < 0)
|
||||
{
|
||||
u8* pkt = &PacketBuf[dataoff];
|
||||
ssize_t n, blehoff = 0;
|
||||
return ReadResult::NoPacket;
|
||||
}
|
||||
|
||||
memset(pkt, 0, sizeof(PacketBuf) - dataoff);
|
||||
Gdb::ReadResult GdbStub::ParseAndSetupPacket()
|
||||
{
|
||||
// This complicated logic seems to be unfortunately necessary
|
||||
// to handle the case of packet resends when we answered too slowly.
|
||||
// GDB only expects a single response (as it assumes the previous packet was dropped)
|
||||
size_t i = 0;
|
||||
size_t prevPacketStart = SIZE_MAX, prevPacketSize, prevPacketContentSize;
|
||||
size_t packetStart, packetSize, packetContentSize;
|
||||
ReadResult result, prevResult;
|
||||
while (true)
|
||||
{
|
||||
ReadResult result = TryParsePacket(i, packetStart, packetSize, packetContentSize);
|
||||
if (result == ReadResult::NoPacket)
|
||||
break;
|
||||
if (result != ReadResult::CmdRecvd && result != ReadResult::Break)
|
||||
return result;
|
||||
|
||||
// looks like there is a different packet coming up
|
||||
// so we quit here
|
||||
if (prevPacketStart != SIZE_MAX &&
|
||||
(packetContentSize != prevPacketContentSize ||
|
||||
memcmp(&RecvBuffer[packetStart], &RecvBuffer[prevPacketStart], prevPacketContentSize) != 0))
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] found differing packet further back %zu %zu\n", packetContentSize, prevPacketContentSize);
|
||||
break;
|
||||
}
|
||||
|
||||
i = packetStart + packetSize;
|
||||
prevPacketStart = packetStart;
|
||||
prevPacketSize = packetSize;
|
||||
prevPacketContentSize = packetContentSize;
|
||||
prevResult = result;
|
||||
}
|
||||
|
||||
if (prevPacketStart != SIZE_MAX)
|
||||
{
|
||||
memcpy(&Cmdbuf[0], &RecvBuffer[prevPacketStart], prevPacketContentSize);
|
||||
Cmdbuf[prevPacketContentSize] = '\0';
|
||||
Cmdlen = static_cast<ssize_t>(prevPacketContentSize);
|
||||
|
||||
RecvBufferFilled -= prevPacketStart + prevPacketSize;
|
||||
if (RecvBufferFilled > 0)
|
||||
memmove(&RecvBuffer[0], &RecvBuffer[prevPacketStart + prevPacketSize], RecvBufferFilled);
|
||||
|
||||
assert(prevResult == ReadResult::CmdRecvd || prevResult == ReadResult::Break);
|
||||
return prevResult;
|
||||
}
|
||||
|
||||
assert(result == ReadResult::NoPacket);
|
||||
return ReadResult::NoPacket;
|
||||
}
|
||||
|
||||
ReadResult GdbStub::MsgRecv()
|
||||
{
|
||||
{
|
||||
ReadResult result = ParseAndSetupPacket();
|
||||
if (result != ReadResult::NoPacket)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
while (true)
|
||||
{
|
||||
int flag = 0;
|
||||
#if MOCKTEST
|
||||
static bool FIRST = false;
|
||||
@ -142,113 +184,35 @@ ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/])
|
||||
#else
|
||||
#ifndef _WIN32
|
||||
if (first) flag |= MSG_DONTWAIT;
|
||||
n = recv(connfd, pkt, sizeof(PacketBuf) - dataoff, flag);
|
||||
ssize_t receivedNum = recv(ConnFd, &RecvBuffer[RecvBufferFilled], sizeof(RecvBuffer) - RecvBufferFilled, flag);
|
||||
Log(LogLevel::Debug, "[GDB] receiving from stream %d\n", receivedNum);
|
||||
#else
|
||||
// fuck windows
|
||||
n = recv(connfd, (char*)pkt, sizeof(PacketBuf) - dataoff, flag);
|
||||
ssize_t receivedNum = recv(ConnFd, (char*)&RecvBuffer[RecvBufferFilled], sizeof(RecvBuffer) - RecvBufferFilled, flag);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (n <= 0)
|
||||
if (receivedNum <= 0)
|
||||
{
|
||||
if (first) return ReadResult::NoPacket;
|
||||
else
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", n, errno, strerror(errno));
|
||||
Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", receivedNum, errno, strerror(errno));
|
||||
return ReadResult::Eof;
|
||||
}
|
||||
}
|
||||
RecvBufferFilled += static_cast<u32>(receivedNum);
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] recv() %zd bytes: '%s' (%02x)\n", n, pkt, pkt[0]);
|
||||
first = false;
|
||||
|
||||
do
|
||||
{
|
||||
if (dataoff == 0)
|
||||
{
|
||||
if (pkt[blehoff] == '\x04') return ReadResult::Eof;
|
||||
else if (pkt[blehoff] == '\x03') return ReadResult::Break;
|
||||
else if (pkt[blehoff] != '$')
|
||||
{
|
||||
++blehoff;
|
||||
--n;
|
||||
}
|
||||
else break;
|
||||
|
||||
if (n == 0) goto next_outer;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
|
||||
if (blehoff > 0)
|
||||
{
|
||||
memmove(pkt, &pkt[blehoff], n - blehoff + 1);
|
||||
n -= blehoff - 1; // ???
|
||||
}
|
||||
|
||||
recv_total += n;
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] recv() after skipping: n=%zd, recv_total=%zd\n", n, recv_total);
|
||||
|
||||
for (ssize_t i = (dataoff == 0) ? 1 : 0; i < n; ++i)
|
||||
{
|
||||
u8 v = pkt[i];
|
||||
if (v == '#')
|
||||
{
|
||||
cksumoff = dataoff + i + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
sum += pkt[i];
|
||||
}
|
||||
|
||||
if (cksumoff < 0)
|
||||
{
|
||||
// oops, need more data
|
||||
dataoff += n;
|
||||
}
|
||||
|
||||
next_outer:;
|
||||
ReadResult result = ParseAndSetupPacket();
|
||||
if (result != ReadResult::NoPacket)
|
||||
return result;
|
||||
}
|
||||
|
||||
u8 ck = (hex2nyb(PacketBuf[cksumoff+0]) << 4)
|
||||
| hex2nyb(PacketBuf[cksumoff+1]);
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] got pkt, checksum: %02x vs %02x\n", ck, sum);
|
||||
|
||||
if (ck != sum)
|
||||
{
|
||||
//__builtin_trap();
|
||||
return ReadResult::CksumErr;
|
||||
}
|
||||
|
||||
if (cksumoff + 2 > recv_total)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] BIG MISTAKE: %zi > %zi which shouldn't happen!\n", cksumoff + 2, recv_total);
|
||||
//__builtin_trap();
|
||||
return ReadResult::Wut;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cmdlen = cksumoff - 2;
|
||||
memcpy(Cmdbuf, &PacketBuf[1], Cmdlen);
|
||||
Cmdbuf[Cmdlen] = 0;
|
||||
|
||||
if (cksumoff + 2 < recv_total) {
|
||||
// huh, we have the start of the next packet
|
||||
dataoff = recv_total - (cksumoff + 2);
|
||||
memmove(PacketBuf, &PacketBuf[cksumoff + 2], (size_t)dataoff);
|
||||
PacketBuf[dataoff] = 0;
|
||||
Log(LogLevel::Debug, "[GDB] got more: cksumoff=%zd, recvtotal=%zd, remain=%zd\n==> %s\n", cksumoff, recv_total, dataoff, PacketBuf);
|
||||
}
|
||||
else dataoff = 0;
|
||||
}
|
||||
|
||||
return ReadResult::CmdRecvd;
|
||||
}
|
||||
|
||||
int SendAck(int connfd)
|
||||
int GdbStub::SendAck()
|
||||
{
|
||||
if (NoAck) return 1;
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] send ack\n");
|
||||
u8 v = '+';
|
||||
#if MOCKTEST
|
||||
@ -257,14 +221,16 @@ int SendAck(int connfd)
|
||||
|
||||
#ifdef _WIN32
|
||||
// fuck windows
|
||||
return send(connfd, (const char*)&v, 1, 0);
|
||||
return send(ConnFd, (const char*)&v, 1, 0);
|
||||
#else
|
||||
return send(connfd, &v, 1, 0);
|
||||
return send(ConnFd, &v, 1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int SendNak(int connfd)
|
||||
int GdbStub::SendNak()
|
||||
{
|
||||
if (NoAck) return 1;
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] send nak\n");
|
||||
u8 v = '-';
|
||||
#if MOCKTEST
|
||||
@ -273,13 +239,13 @@ int SendNak(int connfd)
|
||||
|
||||
#ifdef _WIN32
|
||||
// fuck windows
|
||||
return send(connfd, (const char*)&v, 1, 0);
|
||||
return send(ConnFd, (const char*)&v, 1, 0);
|
||||
#else
|
||||
return send(connfd, &v, 1, 0);
|
||||
return send(ConnFd, &v, 1, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||
int GdbStub::WaitAckBlocking(u8* ackp, int to_ms)
|
||||
{
|
||||
#if MOCKTEST
|
||||
*ackp = '+';
|
||||
@ -289,18 +255,18 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||
#ifdef _WIN32
|
||||
fd_set infd, outfd, errfd;
|
||||
FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd);
|
||||
FD_SET(connfd, &infd);
|
||||
FD_SET(ConnFd, &infd);
|
||||
|
||||
struct timeval to;
|
||||
to.tv_sec = to_ms / 1000;
|
||||
to.tv_usec = (to_ms % 1000) * 1000;
|
||||
|
||||
int r = select(connfd+1, &infd, &outfd, &errfd, &to);
|
||||
int r = select(ConnFd+1, &infd, &outfd, &errfd, &to);
|
||||
|
||||
if (FD_ISSET(connfd, &errfd)) return -1;
|
||||
else if (FD_ISSET(connfd, &infd))
|
||||
if (FD_ISSET(ConnFd, &errfd)) return -1;
|
||||
else if (FD_ISSET(ConnFd, &infd))
|
||||
{
|
||||
r = recv(connfd, (char*)ackp, 1, 0);
|
||||
r = recv(ConnFd, (char*)ackp, 1, 0);
|
||||
if (r < 0) return r;
|
||||
return 0;
|
||||
}
|
||||
@ -309,7 +275,7 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||
#else
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = connfd;
|
||||
pfd.fd = ConnFd;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
|
||||
@ -319,14 +285,14 @@ int WaitAckBlocking(int connfd, u8* ackp, int to_ms)
|
||||
|
||||
if (pfd.revents & (POLLHUP|POLLERR)) return -69;
|
||||
|
||||
r = recv(connfd, ackp, 1, 0);
|
||||
r = recv(ConnFd, ackp, 1, 0);
|
||||
if (r < 0) return r;
|
||||
|
||||
return (r == 1) ? 0 : -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack)
|
||||
int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack)
|
||||
{
|
||||
u8 cksum = 0;
|
||||
int tries = 0;
|
||||
@ -359,22 +325,22 @@ int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2,
|
||||
ssize_t r;
|
||||
u8 ack;
|
||||
|
||||
Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", RespBuf);
|
||||
Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", &RespBuf[0]);
|
||||
#if MOCKTEST
|
||||
r = totallen+4;
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
r = send(connfd, (const char*)RespBuf, totallen+4, 0);
|
||||
r = send(ConnFd, (const char*)&RespBuf[0], totallen+4, 0);
|
||||
#else
|
||||
r = send(connfd, RespBuf, totallen+4, 0);
|
||||
r = send(ConnFd, &RespBuf[0], totallen+4, 0);
|
||||
#endif
|
||||
#endif
|
||||
if (r < 0) return r;
|
||||
|
||||
if (noack) break;
|
||||
|
||||
r = WaitAckBlocking(connfd, &ack, 2000);
|
||||
//Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack);
|
||||
r = WaitAckBlocking(&ack, 2000);
|
||||
Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack);
|
||||
if (r == 0 && ack == '+') break;
|
||||
|
||||
++tries;
|
||||
@ -386,5 +352,4 @@ int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2,
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
|
||||
#ifndef GDBPROTO_H_
|
||||
#define GDBPROTO_H_
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "GdbStub.h" /* class GdbStub */
|
||||
|
||||
|
||||
#define MOCKTEST 0
|
||||
|
||||
|
||||
namespace Gdb {
|
||||
|
||||
using namespace melonDS;
|
||||
constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128;
|
||||
|
||||
extern u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
extern ssize_t Cmdlen;
|
||||
|
||||
namespace Proto {
|
||||
|
||||
extern u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY];
|
||||
extern u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5];
|
||||
|
||||
Gdb::ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]);
|
||||
|
||||
int SendAck(int connfd);
|
||||
int SendNak(int connfd);
|
||||
|
||||
int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack);
|
||||
|
||||
int WaitAckBlocking(int connfd, u8* ackp, int to_ms);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
|
||||
#include "../Platform.h"
|
||||
#include "GdbProto.h"
|
||||
#include "GdbStub.h"
|
||||
|
||||
using namespace melonDS;
|
||||
using Platform::Log;
|
||||
@ -304,7 +304,7 @@ StubState GdbStub::Poll(bool wait)
|
||||
if (ConnFd < 0) return StubState::NoConn;
|
||||
|
||||
u8 a;
|
||||
if (Proto::WaitAckBlocking(ConnFd, &a, 1000) < 0)
|
||||
if (WaitAckBlocking(&a, 1000) < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] inital handshake: didn't receive inital ack!\n");
|
||||
close(ConnFd);
|
||||
@ -380,7 +380,7 @@ StubState GdbStub::Poll(bool wait)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ReadResult res = Proto::MsgRecv(ConnFd, Cmdbuf);
|
||||
ReadResult res = MsgRecv();
|
||||
|
||||
switch (res)
|
||||
{
|
||||
@ -422,11 +422,12 @@ ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler*
|
||||
// check if prefix matches
|
||||
if (!strncmp((const char*)cmd, handlers[i].SubStr, strlen(handlers[i].SubStr)))
|
||||
{
|
||||
if (SendAck() < 0)
|
||||
// ack should have already been sent by CmdExec
|
||||
/*if (SendAck() < 0)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] send packet ack failed!\n");
|
||||
return ExecResult::NetErr;
|
||||
}
|
||||
}*/
|
||||
return handlers[i].Handler(this, &cmd[strlen(handlers[i].SubStr)], len-strlen(handlers[i].SubStr));
|
||||
}
|
||||
}
|
||||
@ -444,7 +445,7 @@ ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler*
|
||||
|
||||
ExecResult GdbStub::CmdExec(const CmdHandler* handlers)
|
||||
{
|
||||
Log(LogLevel::Debug, "[GDB] command in: '%s'\n", Cmdbuf);
|
||||
Log(LogLevel::Debug, "[GDB] command in: '%s'\n", &Cmdbuf[0]);
|
||||
|
||||
for (size_t i = 0; handlers[i].Handler != NULL; ++i)
|
||||
{
|
||||
@ -644,24 +645,13 @@ StubState GdbStub::CheckWatchpt(u32 addr, int kind, bool enter, bool stay)
|
||||
return StubState::CheckNoHit;
|
||||
}
|
||||
|
||||
int GdbStub::SendAck()
|
||||
{
|
||||
if (NoAck) return 1;
|
||||
return Proto::SendAck(ConnFd);
|
||||
}
|
||||
int GdbStub::SendNak()
|
||||
{
|
||||
if (NoAck) return 1;
|
||||
return Proto::SendNak(ConnFd);
|
||||
}
|
||||
|
||||
int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2)
|
||||
{
|
||||
return Proto::Resp(ConnFd, data1, len1, data2, len2, NoAck);
|
||||
return Resp(data1, len1, data2, len2, NoAck);
|
||||
}
|
||||
int GdbStub::RespC(const char* data1, size_t len1, const u8* data2, size_t len2)
|
||||
{
|
||||
return Proto::Resp(ConnFd, (const u8*)data1, len1, data2, len2, NoAck);
|
||||
return Resp((const u8*)data1, len1, data2, len2, NoAck);
|
||||
}
|
||||
#if defined(__GCC__) || defined(__clang__)
|
||||
__attribute__((__format__(printf, 2/*includes implicit this*/, 3)))
|
||||
@ -670,19 +660,19 @@ int GdbStub::RespFmt(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int r = vsnprintf((char*)&Proto::RespBuf[1], sizeof(Proto::RespBuf)-5, fmt, args);
|
||||
int r = vsnprintf((char*)&RespBuf[1], sizeof(RespBuf)-5, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (r < 0) return r;
|
||||
|
||||
if ((size_t)r >= sizeof(Proto::RespBuf)-5)
|
||||
if ((size_t)r >= sizeof(RespBuf)-5)
|
||||
{
|
||||
Log(LogLevel::Error, "[GDB] truncated response in send_fmt()! (lost %zd bytes)\n",
|
||||
(ssize_t)r - (ssize_t)(sizeof(Proto::RespBuf)-5));
|
||||
r = sizeof(Proto::RespBuf)-5;
|
||||
(ssize_t)r - (ssize_t)(sizeof(RespBuf)-5));
|
||||
r = sizeof(RespBuf)-5;
|
||||
}
|
||||
|
||||
return Resp(&Proto::RespBuf[1], r);
|
||||
return Resp(&RespBuf[1], r);
|
||||
}
|
||||
|
||||
int GdbStub::RespStr(const char* str)
|
||||
|
@ -86,6 +86,8 @@ enum class ExecResult
|
||||
Continue
|
||||
};
|
||||
|
||||
constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128;
|
||||
|
||||
class GdbStub;
|
||||
|
||||
typedef ExecResult (*GdbProtoCmd)(GdbStub* stub, const u8* cmd, ssize_t len);
|
||||
@ -141,9 +143,6 @@ public:
|
||||
Gdb::ExecResult CmdExec(const CmdHandler* handlers);
|
||||
|
||||
public:
|
||||
int SendAck();
|
||||
int SendNak();
|
||||
|
||||
int Resp(const u8* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
||||
int RespC(const char* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0);
|
||||
#if defined(__GCC__) || defined(__clang__)
|
||||
@ -158,7 +157,20 @@ private:
|
||||
void Disconnect();
|
||||
StubState HandlePacket();
|
||||
|
||||
private:
|
||||
Gdb::ReadResult MsgRecv();
|
||||
|
||||
Gdb::ReadResult TryParsePacket(size_t start, size_t& packetStart, size_t& packetSize, size_t& packetContentSize);
|
||||
Gdb::ReadResult ParseAndSetupPacket();
|
||||
|
||||
void SetupCommand(size_t packetStart, size_t packetSize);
|
||||
|
||||
int SendAck();
|
||||
int SendNak();
|
||||
|
||||
int Resp(const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack);
|
||||
|
||||
int WaitAckBlocking(u8* ackp, int to_ms);
|
||||
|
||||
StubCallbacks* Cb;
|
||||
|
||||
//struct sockaddr_in server, client;
|
||||
@ -172,6 +184,13 @@ private:
|
||||
bool StatFlag;
|
||||
bool NoAck;
|
||||
|
||||
std::array<u8, GDBPROTO_BUFFER_CAPACITY> RecvBuffer;
|
||||
u32 RecvBufferFilled = 0;
|
||||
std::array<u8, GDBPROTO_BUFFER_CAPACITY> RespBuf;
|
||||
|
||||
std::array<u8, GDBPROTO_BUFFER_CAPACITY> Cmdbuf;
|
||||
ssize_t Cmdlen;
|
||||
|
||||
std::map<u32, BpWp> BpList;
|
||||
std::vector<BpWp> WpList;
|
||||
|
||||
|
@ -160,7 +160,12 @@ if (BUILD_STATIC)
|
||||
if (WIN32 AND USE_QT6)
|
||||
qt_import_plugins(melonDS INCLUDE Qt::QModernWindowsStylePlugin)
|
||||
endif()
|
||||
target_link_options(melonDS PRIVATE -static)
|
||||
if (USE_VCPKG AND UNIX AND NOT APPLE)
|
||||
pkg_check_modules(ALSA REQUIRED IMPORTED_TARGET alsa)
|
||||
target_link_libraries(melonDS PRIVATE PkgConfig::ALSA)
|
||||
else()
|
||||
target_link_options(melonDS PRIVATE -static)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
@ -55,7 +55,6 @@ DefaultList<int> DefaultInts =
|
||||
{"Screen.VSyncInterval", 1},
|
||||
{"3D.Renderer", renderer3D_Software},
|
||||
{"3D.GL.ScaleFactor", 1},
|
||||
{"MaxFPS", 1000},
|
||||
#ifdef JIT_ENABLED
|
||||
{"JIT.MaxBlockSize", 32},
|
||||
#endif
|
||||
@ -71,7 +70,7 @@ DefaultList<int> DefaultInts =
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
{"Instance*.Gdb.ARM7.Port", 3334},
|
||||
{"Instance*.Gdb.ARM9.Port", 3333},
|
||||
#endif,
|
||||
#endif
|
||||
{"LAN.HostNumPlayers", 16},
|
||||
};
|
||||
|
||||
@ -120,6 +119,13 @@ DefaultList<std::string> DefaultStrings =
|
||||
{"Instance*.Firmware.Username", "melonDS"}
|
||||
};
|
||||
|
||||
DefaultList<double> DefaultDoubles =
|
||||
{
|
||||
{"TargetFPS", 60.0},
|
||||
{"FastForwardFPS", 1000.0},
|
||||
{"SlowmoFPS", 30.0},
|
||||
};
|
||||
|
||||
LegacyEntry LegacyFile[] =
|
||||
{
|
||||
{"Key_A", 0, "Keyboard.A", true},
|
||||
@ -153,7 +159,7 @@ LegacyEntry LegacyFile[] =
|
||||
{"HKKey_Pause", 0, "Keyboard.HK_Pause", true},
|
||||
{"HKKey_Reset", 0, "Keyboard.HK_Reset", true},
|
||||
{"HKKey_FastForward", 0, "Keyboard.HK_FastForward", true},
|
||||
{"HKKey_FastForwardToggle", 0, "Keyboard.HK_FastForwardToggle", true},
|
||||
{"HKKey_FastForwardToggle", 0, "Keyboard.HK_FrameLimitToggle", true},
|
||||
{"HKKey_FullscreenToggle", 0, "Keyboard.HK_FullscreenToggle", true},
|
||||
{"HKKey_SwapScreens", 0, "Keyboard.HK_SwapScreens", true},
|
||||
{"HKKey_SwapScreenEmphasis", 0, "Keyboard.HK_SwapScreenEmphasis", true},
|
||||
@ -169,7 +175,7 @@ LegacyEntry LegacyFile[] =
|
||||
{"HKJoy_Pause", 0, "Joystick.HK_Pause", true},
|
||||
{"HKJoy_Reset", 0, "Joystick.HK_Reset", true},
|
||||
{"HKJoy_FastForward", 0, "Joystick.HK_FastForward", true},
|
||||
{"HKJoy_FastForwardToggle", 0, "Joystick.HK_FastForwardToggle", true},
|
||||
{"HKJoy_FastForwardToggle", 0, "Joystick.HK_FrameLimitToggle", true},
|
||||
{"HKJoy_FullscreenToggle", 0, "Joystick.HK_FullscreenToggle", true},
|
||||
{"HKJoy_SwapScreens", 0, "Joystick.HK_SwapScreens", true},
|
||||
{"HKJoy_SwapScreenEmphasis", 0, "Joystick.HK_SwapScreenEmphasis", true},
|
||||
@ -434,6 +440,18 @@ std::string Array::GetString(const int id)
|
||||
return tval.as_string();
|
||||
}
|
||||
|
||||
double Array::GetDouble(const int id)
|
||||
{
|
||||
while (Data.size() < id+1)
|
||||
Data.push_back(0.0);
|
||||
|
||||
toml::value& tval = Data[id];
|
||||
if (!tval.is_floating())
|
||||
tval = 0.0;
|
||||
|
||||
return tval.as_floating();
|
||||
}
|
||||
|
||||
void Array::SetInt(const int id, int val)
|
||||
{
|
||||
while (Data.size() < id+1)
|
||||
@ -470,6 +488,15 @@ void Array::SetString(const int id, const std::string& val)
|
||||
tval = val;
|
||||
}
|
||||
|
||||
void Array::SetDouble(const int id, double val)
|
||||
{
|
||||
while (Data.size() < id+1)
|
||||
Data.push_back(0.0);
|
||||
|
||||
toml::value& tval = Data[id];
|
||||
tval = val;
|
||||
}
|
||||
|
||||
|
||||
/*Table::Table()// : Data(toml::value())
|
||||
{
|
||||
@ -562,6 +589,15 @@ std::string Table::GetString(const std::string& path)
|
||||
return tval.as_string();
|
||||
}
|
||||
|
||||
double Table::GetDouble(const std::string& path)
|
||||
{
|
||||
toml::value& tval = ResolvePath(path);
|
||||
if (!tval.is_floating())
|
||||
tval = FindDefault(path, 0.0, DefaultDoubles);
|
||||
|
||||
return tval.as_floating();
|
||||
}
|
||||
|
||||
void Table::SetInt(const std::string& path, int val)
|
||||
{
|
||||
std::string rngkey = GetDefaultKey(PathPrefix+path);
|
||||
@ -593,6 +629,12 @@ void Table::SetString(const std::string& path, const std::string& val)
|
||||
tval = val;
|
||||
}
|
||||
|
||||
void Table::SetDouble(const std::string& path, double val)
|
||||
{
|
||||
toml::value& tval = ResolvePath(path);
|
||||
tval = val;
|
||||
}
|
||||
|
||||
toml::value& Table::ResolvePath(const std::string& path)
|
||||
{
|
||||
toml::value* ret = &Data;
|
||||
|
@ -61,11 +61,13 @@ public:
|
||||
int64_t GetInt64(const int id);
|
||||
bool GetBool(const int id);
|
||||
std::string GetString(const int id);
|
||||
double GetDouble(const int id);
|
||||
|
||||
void SetInt(const int id, int val);
|
||||
void SetInt64(const int id, int64_t val);
|
||||
void SetBool(const int id, bool val);
|
||||
void SetString(const int id, const std::string& val);
|
||||
void SetDouble(const int id, double val);
|
||||
|
||||
// convenience
|
||||
|
||||
@ -99,11 +101,13 @@ public:
|
||||
int64_t GetInt64(const std::string& path);
|
||||
bool GetBool(const std::string& path);
|
||||
std::string GetString(const std::string& path);
|
||||
double GetDouble(const std::string& path);
|
||||
|
||||
void SetInt(const std::string& path, int val);
|
||||
void SetInt64(const std::string& path, int64_t val);
|
||||
void SetBool(const std::string& path, bool val);
|
||||
void SetString(const std::string& path, const std::string& val);
|
||||
void SetDouble(const std::string& path, double val);
|
||||
|
||||
// convenience
|
||||
|
||||
|
@ -88,7 +88,31 @@ EmuInstance::EmuInstance(int inst) : deleting(false),
|
||||
cheatsOn = localCfg.GetBool("EnableCheats");
|
||||
|
||||
doLimitFPS = globalCfg.GetBool("LimitFPS");
|
||||
maxFPS = globalCfg.GetInt("MaxFPS");
|
||||
|
||||
double val = globalCfg.GetDouble("TargetFPS");
|
||||
if (val == 0.0)
|
||||
{
|
||||
Platform::Log(Platform::LogLevel::Error, "Target FPS in config invalid\n");
|
||||
targetFPS = 1.0 / 60.0;
|
||||
}
|
||||
else targetFPS = 1.0 / val;
|
||||
|
||||
val = globalCfg.GetDouble("FastForwardFPS");
|
||||
if (val == 0.0)
|
||||
{
|
||||
Platform::Log(Platform::LogLevel::Error, "Fast-Forward FPS in config invalid\n");
|
||||
fastForwardFPS = 1.0 / 60.0;
|
||||
}
|
||||
else fastForwardFPS = 1.0 / val;
|
||||
|
||||
val = globalCfg.GetDouble("SlowmoFPS");
|
||||
if (val == 0.0)
|
||||
{
|
||||
Platform::Log(Platform::LogLevel::Error, "Slow-Mo FPS in config invalid\n");
|
||||
slowmoFPS = 1.0 / 60.0;
|
||||
}
|
||||
else slowmoFPS = 1.0 / val;
|
||||
|
||||
doAudioSync = globalCfg.GetBool("AudioSync");
|
||||
|
||||
mpAudioMode = globalCfg.GetInt("MP.AudioMode");
|
||||
@ -1153,14 +1177,14 @@ bool EmuInstance::updateConsole(UpdateConsoleNDSArgs&& _ndsargs, UpdateConsoleGB
|
||||
#endif
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
Config::Table gdbopt = globalCfg.GetTable("Gdb");
|
||||
Config::Table gdbopt = localCfg.GetTable("Gdb");
|
||||
GDBArgs _gdbargs {
|
||||
static_cast<u16>(gdbopt.GetInt("ARM7.Port")),
|
||||
static_cast<u16>(gdbopt.GetInt("ARM9.Port")),
|
||||
gdbopt.GetBool("ARM7.BreakOnStartup"),
|
||||
gdbopt.GetBool("ARM9.BreakOnStartup"),
|
||||
};
|
||||
auto gdbargs = gdbopt.GetBool("Enable") ? std::make_optional(_gdbargs) : std::nullopt;
|
||||
auto gdbargs = gdbopt.GetBool("Enabled") ? std::make_optional(_gdbargs) : std::nullopt;
|
||||
#else
|
||||
optional<GDBArgs> gdbargs = std::nullopt;
|
||||
#endif
|
||||
|
@ -36,7 +36,7 @@ enum
|
||||
HK_Pause,
|
||||
HK_Reset,
|
||||
HK_FastForward,
|
||||
HK_FastForwardToggle,
|
||||
HK_FrameLimitToggle,
|
||||
HK_FullscreenToggle,
|
||||
HK_SwapScreens,
|
||||
HK_SwapScreenEmphasis,
|
||||
@ -46,6 +46,9 @@ enum
|
||||
HK_PowerButton,
|
||||
HK_VolumeUp,
|
||||
HK_VolumeDown,
|
||||
HK_SlowMo,
|
||||
HK_FastForwardToggle,
|
||||
HK_SlowMoToggle,
|
||||
HK_MAX
|
||||
};
|
||||
|
||||
@ -252,7 +255,12 @@ public:
|
||||
std::unique_ptr<SaveManager> firmwareSave;
|
||||
|
||||
bool doLimitFPS;
|
||||
int maxFPS;
|
||||
double curFPS;
|
||||
double targetFPS;
|
||||
double fastForwardFPS;
|
||||
double slowmoFPS;
|
||||
bool fastForwardToggled;
|
||||
bool slowmoToggled;
|
||||
bool doAudioSync;
|
||||
private:
|
||||
|
||||
|
@ -47,7 +47,7 @@ const char* EmuInstance::hotkeyNames[HK_MAX] =
|
||||
"HK_Pause",
|
||||
"HK_Reset",
|
||||
"HK_FastForward",
|
||||
"HK_FastForwardToggle",
|
||||
"HK_FrameLimitToggle",
|
||||
"HK_FullscreenToggle",
|
||||
"HK_SwapScreens",
|
||||
"HK_SwapScreenEmphasis",
|
||||
@ -56,7 +56,10 @@ const char* EmuInstance::hotkeyNames[HK_MAX] =
|
||||
"HK_FrameStep",
|
||||
"HK_PowerButton",
|
||||
"HK_VolumeUp",
|
||||
"HK_VolumeDown"
|
||||
"HK_VolumeDown",
|
||||
"HK_SlowMo",
|
||||
"HK_FastForwardToggle",
|
||||
"HK_SlowMoToggle"
|
||||
};
|
||||
|
||||
|
||||
|
@ -95,7 +95,7 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new
|
||||
#endif
|
||||
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
ui->cbGdbEnabled->setChecked(cfg.GetBool("Gdb.Enabled"));
|
||||
ui->cbGdbEnabled->setChecked(instcfg.GetBool("Gdb.Enabled"));
|
||||
ui->intGdbPortA7->setValue(instcfg.GetInt("Gdb.ARM7.Port"));
|
||||
ui->intGdbPortA9->setValue(instcfg.GetInt("Gdb.ARM9.Port"));
|
||||
ui->cbGdbBOSA7->setChecked(instcfg.GetBool("Gdb.ARM7.BreakOnStartup"));
|
||||
@ -286,7 +286,7 @@ void EmuSettingsDialog::done(int r)
|
||||
cfg.SetBool("JIT.FastMemory", ui->chkJITFastMemory->isChecked());
|
||||
#endif
|
||||
#ifdef GDBSTUB_ENABLED
|
||||
cfg.SetBool("Gdb.Enabled", ui->cbGdbEnabled->isChecked());
|
||||
instcfg.SetBool("Gdb.Enabled", ui->cbGdbEnabled->isChecked());
|
||||
instcfg.SetInt("Gdb.ARM7.Port", ui->intGdbPortA7->value());
|
||||
instcfg.SetInt("Gdb.ARM9.Port", ui->intGdbPortA9->value());
|
||||
instcfg.SetBool("Gdb.ARM7.BreakOnStartup", ui->cbGdbBOSA7->isChecked());
|
||||
|
@ -149,12 +149,17 @@ void EmuThread::run()
|
||||
|
||||
char melontitle[100];
|
||||
|
||||
bool fastforward = false;
|
||||
bool slowmo = false;
|
||||
emuInstance->fastForwardToggled = false;
|
||||
emuInstance->slowmoToggled = false;
|
||||
|
||||
while (emuStatus != emuStatus_Exit)
|
||||
{
|
||||
MPInterface::Get().Process();
|
||||
emuInstance->inputProcess();
|
||||
|
||||
if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange();
|
||||
if (emuInstance->hotkeyPressed(HK_FrameLimitToggle)) emit windowLimitFPSChange();
|
||||
|
||||
if (emuInstance->hotkeyPressed(HK_Pause)) emuTogglePause();
|
||||
if (emuInstance->hotkeyPressed(HK_Reset)) emuReset();
|
||||
@ -332,22 +337,34 @@ void EmuThread::run()
|
||||
emit windowUpdate();
|
||||
winUpdateCount = 0;
|
||||
}
|
||||
|
||||
if (emuInstance->hotkeyPressed(HK_FastForwardToggle)) emuInstance->fastForwardToggled = !emuInstance->fastForwardToggled;
|
||||
if (emuInstance->hotkeyPressed(HK_SlowMoToggle)) emuInstance->slowmoToggled = !emuInstance->slowmoToggled;
|
||||
|
||||
bool fastforward = emuInstance->hotkeyDown(HK_FastForward);
|
||||
bool enablefastforward = emuInstance->hotkeyDown(HK_FastForward) | emuInstance->fastForwardToggled;
|
||||
bool enableslowmo = emuInstance->hotkeyDown(HK_SlowMo) | emuInstance->slowmoToggled;
|
||||
|
||||
if (useOpenGL)
|
||||
{
|
||||
// when using OpenGL: when toggling fast-forward, change the vsync interval
|
||||
if (emuInstance->hotkeyPressed(HK_FastForward))
|
||||
// when using OpenGL: when toggling fast-forward or slowmo, change the vsync interval
|
||||
if ((enablefastforward || enableslowmo) && !(fastforward || slowmo))
|
||||
{
|
||||
emuInstance->setVSyncGL(false);
|
||||
}
|
||||
else if (emuInstance->hotkeyReleased(HK_FastForward))
|
||||
else if (!(enablefastforward || enableslowmo) && (fastforward || slowmo))
|
||||
{
|
||||
emuInstance->setVSyncGL(true);
|
||||
}
|
||||
}
|
||||
|
||||
fastforward = enablefastforward;
|
||||
slowmo = enableslowmo;
|
||||
|
||||
if (slowmo) emuInstance->curFPS = emuInstance->slowmoFPS;
|
||||
else if (fastforward) emuInstance->curFPS = emuInstance->fastForwardFPS;
|
||||
else if (!emuInstance->doLimitFPS) emuInstance->curFPS = 1.0 / 1000.0;
|
||||
else emuInstance->curFPS = emuInstance->targetFPS;
|
||||
|
||||
if (emuInstance->audioDSiVolumeSync && emuInstance->nds->ConsoleType == 1)
|
||||
{
|
||||
DSi* dsi = static_cast<DSi*>(emuInstance->nds);
|
||||
@ -361,23 +378,19 @@ void EmuThread::run()
|
||||
emuInstance->audioVolume = volumeLevel * (256.0 / 31.0);
|
||||
}
|
||||
|
||||
if (emuInstance->doAudioSync && !fastforward)
|
||||
if (emuInstance->doAudioSync && !(fastforward || slowmo))
|
||||
emuInstance->audioSync();
|
||||
|
||||
double frametimeStep = nlines / (60.0 * 263.0);
|
||||
|
||||
{
|
||||
bool limitfps = emuInstance->doLimitFPS && !fastforward;
|
||||
|
||||
double practicalFramelimit = limitfps ? frametimeStep : 1.0 / emuInstance->maxFPS;
|
||||
|
||||
double curtime = SDL_GetPerformanceCounter() * perfCountsSec;
|
||||
|
||||
frameLimitError += practicalFramelimit - (curtime - lastTime);
|
||||
if (frameLimitError < -practicalFramelimit)
|
||||
frameLimitError = -practicalFramelimit;
|
||||
if (frameLimitError > practicalFramelimit)
|
||||
frameLimitError = practicalFramelimit;
|
||||
frameLimitError += emuInstance->curFPS - (curtime - lastTime);
|
||||
if (frameLimitError < -emuInstance->curFPS)
|
||||
frameLimitError = -emuInstance->curFPS;
|
||||
if (frameLimitError > emuInstance->curFPS)
|
||||
frameLimitError = emuInstance->curFPS;
|
||||
|
||||
if (round(frameLimitError * 1000.0) > 0.0)
|
||||
{
|
||||
|
@ -49,6 +49,9 @@ static constexpr std::initializer_list<int> hk_general =
|
||||
HK_FrameStep,
|
||||
HK_FastForward,
|
||||
HK_FastForwardToggle,
|
||||
HK_SlowMo,
|
||||
HK_SlowMoToggle,
|
||||
HK_FrameLimitToggle,
|
||||
HK_FullscreenToggle,
|
||||
HK_Lid,
|
||||
HK_Mic,
|
||||
@ -65,6 +68,9 @@ static constexpr std::initializer_list<const char*> hk_general_labels =
|
||||
"Reset",
|
||||
"Frame step",
|
||||
"Fast forward",
|
||||
"Toggle fast forward",
|
||||
"Slow mo",
|
||||
"Toggle slow mo",
|
||||
"Toggle FPS limit",
|
||||
"Toggle fullscreen",
|
||||
"Close/open lid",
|
||||
|
@ -39,7 +39,9 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare
|
||||
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
|
||||
ui->spinMouseHideSeconds->setValue(cfg.GetInt("MouseHideSeconds"));
|
||||
ui->cbPauseLostFocus->setChecked(cfg.GetBool("PauseLostFocus"));
|
||||
ui->spinMaxFPS->setValue(cfg.GetInt("MaxFPS"));
|
||||
ui->spinTargetFPS->setValue(cfg.GetDouble("TargetFPS"));
|
||||
ui->spinFFW->setValue(cfg.GetDouble("FastForwardFPS"));
|
||||
ui->spinSlow->setValue(cfg.GetDouble("SlowmoFPS"));
|
||||
|
||||
const QList<QString> themeKeys = QStyleFactory::keys();
|
||||
const QString currentTheme = qApp->style()->objectName();
|
||||
@ -65,6 +67,41 @@ void InterfaceSettingsDialog::on_cbMouseHide_clicked()
|
||||
ui->spinMouseHideSeconds->setEnabled(ui->cbMouseHide->isChecked());
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::on_pbClean_clicked()
|
||||
{
|
||||
ui->spinTargetFPS->setValue(60.0000);
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::on_pbAccurate_clicked()
|
||||
{
|
||||
ui->spinTargetFPS->setValue(59.8261);
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::on_pb2x_clicked()
|
||||
{
|
||||
ui->spinFFW->setValue(ui->spinTargetFPS->value() * 2.0);
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::on_pb3x_clicked()
|
||||
{
|
||||
ui->spinFFW->setValue(ui->spinTargetFPS->value() * 3.0);
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::on_pbMAX_clicked()
|
||||
{
|
||||
ui->spinFFW->setValue(1000.0);
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::on_pbHalf_clicked()
|
||||
{
|
||||
ui->spinSlow->setValue(ui->spinTargetFPS->value() / 2.0);
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::on_pbQuarter_clicked()
|
||||
{
|
||||
ui->spinSlow->setValue(ui->spinTargetFPS->value() / 4.0);
|
||||
}
|
||||
|
||||
void InterfaceSettingsDialog::done(int r)
|
||||
{
|
||||
if (r == QDialog::Accepted)
|
||||
@ -74,7 +111,18 @@ void InterfaceSettingsDialog::done(int r)
|
||||
cfg.SetBool("MouseHide", ui->cbMouseHide->isChecked());
|
||||
cfg.SetInt("MouseHideSeconds", ui->spinMouseHideSeconds->value());
|
||||
cfg.SetBool("PauseLostFocus", ui->cbPauseLostFocus->isChecked());
|
||||
cfg.SetInt("MaxFPS", ui->spinMaxFPS->value());
|
||||
|
||||
double val = ui->spinTargetFPS->value();
|
||||
if (val == 0.0) cfg.SetDouble("TargetFPS", 0.0001);
|
||||
else cfg.SetDouble("TargetFPS", val);
|
||||
|
||||
val = ui->spinFFW->value();
|
||||
if (val == 0.0) cfg.SetDouble("FastForwardFPS", 0.0001);
|
||||
else cfg.SetDouble("FastForwardFPS", val);
|
||||
|
||||
val = ui->spinSlow->value();
|
||||
if (val == 0.0) cfg.SetDouble("SlowmoFPS", 0.0001);
|
||||
else cfg.SetDouble("SlowmoFPS", val);
|
||||
|
||||
QString themeName = ui->cbxUITheme->currentData().toString();
|
||||
cfg.SetQString("UITheme", themeName);
|
||||
|
@ -60,6 +60,16 @@ private slots:
|
||||
|
||||
void on_cbMouseHide_clicked();
|
||||
|
||||
void on_pbClean_clicked();
|
||||
void on_pbAccurate_clicked();
|
||||
|
||||
void on_pb2x_clicked();
|
||||
void on_pb3x_clicked();
|
||||
void on_pbMAX_clicked();
|
||||
|
||||
void on_pbHalf_clicked();
|
||||
void on_pbQuarter_clicked();
|
||||
|
||||
private:
|
||||
Ui::InterfaceSettingsDialog* ui;
|
||||
|
||||
|
@ -6,12 +6,12 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>337</width>
|
||||
<height>275</height>
|
||||
<width>389</width>
|
||||
<height>356</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -20,6 +20,9 @@
|
||||
<string>Interface settings - melonDS</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SizeConstraint::SetFixedSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
@ -95,32 +98,209 @@
|
||||
<property name="title">
|
||||
<string>Framerate </string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="0,0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Fast-forward limit</string>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<property name="horizontalSpacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>spinMaxFPS</cstring>
|
||||
<property name="verticalSpacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinMaxFPS">
|
||||
<property name="suffix">
|
||||
<string> FPS</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>60</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
</widget>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Target FPS</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Fast-Forward</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>spinFFW</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="spinTargetFPS">
|
||||
<property name="suffix">
|
||||
<string> FPS</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000100000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>59.826099999999997</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbQuarter">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>63</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1/4</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbHalf">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>62</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1/2</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="spinSlow">
|
||||
<property name="suffix">
|
||||
<string> FPS</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000100000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>29.913000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="spinFFW">
|
||||
<property name="suffix">
|
||||
<string> FPS</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000100000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>1000.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Slow-Mo</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbAccurate">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>63</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Accurate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbClean">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>62</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Clean</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pb2x">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>41</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>2x</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pb3x">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>41</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>3x</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pbMAX">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>41</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>MAX</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -128,10 +308,10 @@
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -780,6 +780,10 @@ void MainWindow::closeEvent(QCloseEvent* event)
|
||||
Config::Save();
|
||||
|
||||
emuInstance->deleteWindow(windowID, false);
|
||||
|
||||
// emuInstance may be deleted
|
||||
// prevent use after free from us
|
||||
emuInstance = nullptr;
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
|
||||
@ -970,7 +974,10 @@ void MainWindow::focusInEvent(QFocusEvent* event)
|
||||
|
||||
void MainWindow::focusOutEvent(QFocusEvent* event)
|
||||
{
|
||||
emuInstance->audioMute();
|
||||
// focusOutEvent is called through the window close event handler
|
||||
// prevent use after free
|
||||
if (emuInstance)
|
||||
emuInstance->audioMute();
|
||||
}
|
||||
|
||||
void MainWindow::onAppStateChanged(Qt::ApplicationState state)
|
||||
@ -1939,8 +1946,9 @@ void MainWindow::onOpenInterfaceSettings()
|
||||
void MainWindow::onUpdateInterfaceSettings()
|
||||
{
|
||||
pauseOnLostFocus = globalCfg.GetBool("PauseLostFocus");
|
||||
emuInstance->maxFPS = globalCfg.GetInt("MaxFPS");
|
||||
|
||||
emuInstance->targetFPS = 1.0 / globalCfg.GetDouble("TargetFPS");
|
||||
emuInstance->fastForwardFPS = 1.0 / globalCfg.GetDouble("FastForwardFPS");
|
||||
emuInstance->slowmoFPS = 1.0 / globalCfg.GetDouble("SlowmoFPS");
|
||||
panel->setMouseHide(globalCfg.GetBool("MouseHide"),
|
||||
globalCfg.GetInt("MouseHideSeconds")*1000);
|
||||
}
|
||||
|
27
vcpkg.json
27
vcpkg.json
@ -2,9 +2,22 @@
|
||||
"default-features": ["qt6"],
|
||||
"dependencies": [
|
||||
"sdl2",
|
||||
{
|
||||
"name": "sdl2",
|
||||
"platform": "linux",
|
||||
"features": [ "alsa" ]
|
||||
},
|
||||
"libarchive",
|
||||
"zstd",
|
||||
"enet"
|
||||
"enet",
|
||||
{
|
||||
"name": "ecm",
|
||||
"platform": "linux"
|
||||
},
|
||||
{
|
||||
"name": "libslirp",
|
||||
"platform": "linux"
|
||||
}
|
||||
],
|
||||
"features": {
|
||||
"qt6": {
|
||||
@ -15,6 +28,12 @@
|
||||
"default-features": false,
|
||||
"features": ["gui", "png", "thread", "widgets", "opengl", "zstd", "harfbuzz"]
|
||||
},
|
||||
{
|
||||
"name": "qtbase",
|
||||
"platform": "linux",
|
||||
"default-features": false,
|
||||
"features": ["dbus", "xcb", "xkb", "xcb-xlib", "freetype", "fontconfig"]
|
||||
},
|
||||
{
|
||||
"name": "qtbase",
|
||||
"host": true,
|
||||
@ -24,6 +43,12 @@
|
||||
"name": "qtmultimedia",
|
||||
"default-features": false
|
||||
},
|
||||
{
|
||||
"name": "qtmultimedia",
|
||||
"platform": "linux",
|
||||
"features": ["gstreamer"],
|
||||
"default-features": false
|
||||
},
|
||||
"qtsvg"
|
||||
]
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user