From 44aaf108d132e7b961db8600c16d19d6d3f37eb4 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 12:41:46 +0200
Subject: [PATCH 01/18] Externals: Integrate mGBA as a submodule
---
.gitignore | 1 +
.gitmodules | 5 +
CMakeLists.txt | 9 +
Externals/ExternalsReferenceAll.props | 3 +
Externals/licenses.md | 2 +
Externals/mGBA/CMakeLists.txt | 13 +
Externals/mGBA/make_version.c.js | 140 +++++++++
Externals/mGBA/mgba | 1 +
Externals/mGBA/mgba.vcxproj | 241 +++++++++++++++
Externals/mGBA/mgba.vcxproj.filters | 427 ++++++++++++++++++++++++++
Source/Core/Common/SCMRevGen.vcxproj | 4 -
Source/Core/Core/CMakeLists.txt | 5 +
Source/VSProps/Base.Macros.props | 2 +
Source/VSProps/Base.props | 2 +
Source/dolphin-emu.sln | 11 +
15 files changed, 862 insertions(+), 4 deletions(-)
create mode 100644 Externals/mGBA/CMakeLists.txt
create mode 100644 Externals/mGBA/make_version.c.js
create mode 160000 Externals/mGBA/mgba
create mode 100644 Externals/mGBA/mgba.vcxproj
create mode 100644 Externals/mGBA/mgba.vcxproj.filters
diff --git a/.gitignore b/.gitignore
index deef6c5b0a..cebabd2c39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@ Thumbs.db
# Ignore Finder view option files created by OS X
.DS_Store
# Ignore autogenerated source files
+Externals/mGBA/version.c
Source/Core/Common/scmrev.h
# Ignore files output by build
/[Bb]uild*/
diff --git a/.gitmodules b/.gitmodules
index 7082458311..3459dd91fb 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -3,3 +3,8 @@
url = https://github.com/dolphin-emu/ext-win-qt.git
branch = master
shallow = true
+[submodule "Externals/mGBA/mgba"]
+ path = Externals/mGBA/mgba
+ url = https://github.com/mgba-emu/mgba.git
+ branch = master
+ shallow = true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a480467b3e..d446e0b465 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,6 +45,7 @@ option(ENABLE_LLVM "Enables LLVM support, for disassembly" ON)
option(ENABLE_TESTS "Enables building the unit tests" ON)
option(ENABLE_VULKAN "Enables vulkan video backend" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence, show the current game on Discord" ON)
+option(USE_MGBA "Enables GBA controllers emulation using libmgba" ON)
# Maintainers: if you consider blanket disabling this for your users, please
# consider the following points:
@@ -826,6 +827,14 @@ if(USE_DISCORD_PRESENCE)
include_directories(Externals/discord-rpc/include)
endif()
+if(NOT ENABLE_QT)
+ set(USE_MGBA 0)
+endif()
+if(USE_MGBA)
+ message(STATUS "Using static libmgba from Externals")
+ add_subdirectory(Externals/mGBA)
+endif()
+
find_package(SYSTEMD)
if(SYSTEMD_FOUND)
message(STATUS "libsystemd found, enabling traversal server watchdog support")
diff --git a/Externals/ExternalsReferenceAll.props b/Externals/ExternalsReferenceAll.props
index 0d7de6fecd..38ec0bd827 100644
--- a/Externals/ExternalsReferenceAll.props
+++ b/Externals/ExternalsReferenceAll.props
@@ -58,6 +58,9 @@
{bdb6578b-0691-4e80-a46c-df21639fd3b8}
+
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}
+
{31643fdb-1bb8-4965-9de7-000fc88d35ae}
diff --git a/Externals/licenses.md b/Externals/licenses.md
index f7b20041b9..716b794fb0 100644
--- a/Externals/licenses.md
+++ b/Externals/licenses.md
@@ -38,6 +38,8 @@ Dolphin includes or links code of the following third-party software projects:
[University of Illinois/NCSA Open Source license](http://llvm.org/docs/DeveloperPolicy.html#license)
- [LZO](http://www.oberhumer.com/opensource/lzo/):
[GPLv2+](http://www.oberhumer.com/opensource/gpl.html)
+- [mGBA](http://mgba.io)
+ [MPL 2.0](https://github.com/mgba-emu/mgba/blob/master/LICENSE)
- [MiniUPnPc](http://miniupnp.free.fr/):
[3-clause BSD](https://github.com/miniupnp/miniupnp/blob/master/miniupnpc/LICENSE)
- [Microsoft Visual C++ Runtime Library](http://www.microsoft.com/en-us/download/details.aspx?id=40784):
diff --git a/Externals/mGBA/CMakeLists.txt b/Externals/mGBA/CMakeLists.txt
new file mode 100644
index 0000000000..4a87c7b58c
--- /dev/null
+++ b/Externals/mGBA/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(LIBMGBA_ONLY ON)
+set(USE_LZMA ON)
+add_subdirectory(mgba EXCLUDE_FROM_ALL)
+
+if(NOT MSVC)
+ target_compile_options(mgba PRIVATE -Wno-unused-parameter -Wno-unused-result -Wno-unused-variable)
+endif()
+
+if(ANDROID)
+ target_compile_definitions(mgba PRIVATE -Dfutimes=futimens)
+endif()
+
+add_library(mGBA::mgba ALIAS mgba)
diff --git a/Externals/mGBA/make_version.c.js b/Externals/mGBA/make_version.c.js
new file mode 100644
index 0000000000..d52d9e9a05
--- /dev/null
+++ b/Externals/mGBA/make_version.c.js
@@ -0,0 +1,140 @@
+var wshShell = new ActiveXObject("WScript.Shell")
+var oFS = new ActiveXObject("Scripting.FileSystemObject");
+
+wshShell.CurrentDirectory += "\\mgba";
+var outfile = "../version.c";
+var cmd_commit = " describe --always --abbrev=40 --dirty";
+var cmd_commit_short = " describe --always --dirty";
+var cmd_branch = " symbolic-ref --short HEAD";
+var cmd_rev = " rev-list HEAD --count";
+var cmd_tag = " describe --tag --exact-match";
+
+function GetGitExe()
+{
+ try
+ {
+ gitexe = wshShell.RegRead("HKCU\\Software\\GitExtensions\\gitcommand");
+ wshShell.Exec(gitexe);
+ return gitexe;
+ }
+ catch (e)
+ {}
+
+ for (var gitexe in {"git.cmd":1, "git":1, "git.bat":1})
+ {
+ try
+ {
+ wshShell.Exec(gitexe);
+ return gitexe;
+ }
+ catch (e)
+ {}
+ }
+
+ // last try - msysgit not in path (vs2015 default)
+ msyspath = "\\Git\\cmd\\git.exe";
+ gitexe = wshShell.ExpandEnvironmentStrings("%PROGRAMFILES(x86)%") + msyspath;
+ if (oFS.FileExists(gitexe)) {
+ return gitexe;
+ }
+ gitexe = wshShell.ExpandEnvironmentStrings("%PROGRAMFILES%") + msyspath;
+ if (oFS.FileExists(gitexe)) {
+ return gitexe;
+ }
+
+ WScript.Echo("Cannot find git or git.cmd, check your PATH:\n" +
+ wshShell.ExpandEnvironmentStrings("%PATH%"));
+ WScript.Quit(1);
+}
+
+function GetFirstStdOutLine(cmd)
+{
+ try
+ {
+ return wshShell.Exec(cmd).StdOut.ReadLine();
+ }
+ catch (e)
+ {
+ // catch "the system cannot find the file specified" error
+ WScript.Echo("Failed to exec " + cmd + " this should never happen");
+ WScript.Quit(1);
+ }
+}
+
+function GetFileContents(f)
+{
+ try
+ {
+ return oFS.OpenTextFile(f).ReadAll();
+ }
+ catch (e)
+ {
+ // file doesn't exist
+ return "";
+ }
+}
+
+// get version from version.cmake
+var version_cmake = GetFileContents("version.cmake");
+var version_major = version_cmake.match(/set\(LIB_VERSION_MAJOR (.*)\)/)[1];
+var version_minor = version_cmake.match(/set\(LIB_VERSION_MINOR (.*)\)/)[1];
+var version_patch = version_cmake.match(/set\(LIB_VERSION_PATCH (.*)\)/)[1];
+var version_abi = version_cmake.match(/set\(LIB_VERSION_ABI (.*)\)/)[1];
+var version_string = version_major + "." + version_minor + "." + version_patch;
+
+// get info from git
+var gitexe = GetGitExe();
+var commit = GetFirstStdOutLine(gitexe + cmd_commit);
+var commit_short = GetFirstStdOutLine(gitexe + cmd_commit_short);
+var branch = GetFirstStdOutLine(gitexe + cmd_branch);
+var rev = GetFirstStdOutLine(gitexe + cmd_rev);
+var tag = GetFirstStdOutLine(gitexe + cmd_tag);
+var binary_name = "mgba";
+var project_name = "mGBA";
+
+if (!rev)
+ rev = -1;
+
+if (tag)
+{
+ version_string = tag;
+}
+else if (branch)
+{
+ if (branch == "master")
+ version_string = rev + "-" + commit_short;
+ else
+ version_string = branch + "-" + rev + "-" + commit_short;
+
+ if (branch != version_abi)
+ version_string = version_abi + "-" + version_string;
+}
+
+if (!commit)
+ commit = "(unknown)";
+if (!commit_short)
+ commit_short = "(unknown)";
+if (!branch)
+ branch = "(unknown)";
+
+var out_contents =
+ "#include \n" +
+ "MGBA_EXPORT const char* const gitCommit = \"" + commit + "\";\n" +
+ "MGBA_EXPORT const char* const gitCommitShort = \"" + commit_short + "\";\n" +
+ "MGBA_EXPORT const char* const gitBranch = \"" + branch + "\";\n" +
+ "MGBA_EXPORT const int gitRevision = " + rev + ";\n" +
+ "MGBA_EXPORT const char* const binaryName = \"" + binary_name + "\";\n" +
+ "MGBA_EXPORT const char* const projectName = \"" + project_name + "\";\n" +
+ "MGBA_EXPORT const char* const projectVersion = \"" + version_string + "\";\n";
+
+// check if file needs updating
+if (out_contents == GetFileContents(outfile))
+{
+ WScript.Echo(project_name + ": " + outfile + " current at " + version_string);
+}
+else
+{
+ // needs updating - writeout current info
+ oFS.CreateTextFile(outfile, true).Write(out_contents);
+ WScript.Echo(project_name + ": " + outfile + " updated to " + version_string);
+}
diff --git a/Externals/mGBA/mgba b/Externals/mGBA/mgba
new file mode 160000
index 0000000000..9cccc5197e
--- /dev/null
+++ b/Externals/mGBA/mgba
@@ -0,0 +1 @@
+Subproject commit 9cccc5197ed73ba0a54f584d3121c27dc97405f5
diff --git a/Externals/mGBA/mgba.vcxproj b/Externals/mGBA/mgba.vcxproj
new file mode 100644
index 0000000000..2338faa0d8
--- /dev/null
+++ b/Externals/mGBA/mgba.vcxproj
@@ -0,0 +1,241 @@
+
+
+
+
+
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mgba\include;mgba\src;mgba\src\third-party\lzma;%(AdditionalIncludeDirectories)
+ BUILD_STATIC;M_CORE_GB;M_CORE_GBA;USE_LZMA;_7ZIP_PPMD_SUPPPORT;HAVE_STRDUP;HAVE_SETLOCALE;HAVE_CHMOD;HAVE_UMASK;%(PreprocessorDefinitions)
+
+
+ "$(CScript)" /nologo /E:JScript "make_version.c.js"
+
+
+
+
+
+ $(IntDir)/src/core/cache-set.c.obj
+
+
+ $(IntDir)/src/core/cheats.c.obj
+
+
+
+ $(IntDir)/src/core/core.c.obj
+
+
+
+ $(IntDir)/src/core/input.c.obj
+
+
+
+
+ $(IntDir)/src/core/lockstep.c.obj
+
+
+
+
+
+
+
+ $(IntDir)/src/core/serialize.c.obj
+
+
+
+
+
+
+ $(IntDir)/src/sm83/decoder.c.obj
+
+
+
+
+ $(IntDir)/src/gb/audio.c.obj
+
+
+ $(IntDir)/src/gb/cheats.c.obj
+
+
+ $(IntDir)/src/gb/core.c.obj
+
+
+
+ $(IntDir)/src/gb/input.c.obj
+
+
+ $(IntDir)/src/gb/io.c.obj
+
+
+
+ $(IntDir)/src/gb/memory.c.obj
+
+
+ $(IntDir)/src/gb/overrides.c.obj
+
+
+ $(IntDir)/src/gb/serialize.c.obj
+
+
+ $(IntDir)/src/gb/renderers/cache-set.c.obj
+
+
+
+ $(IntDir)/src/gb/sio.c.obj
+
+
+ $(IntDir)/src/gb/timer.c.obj
+
+
+ $(IntDir)/src/gb/video.c.obj
+
+
+
+
+ $(IntDir)/src/arm/decoder.c.obj
+
+
+
+
+
+ $(IntDir)/src/gba/audio.c.obj
+
+
+
+
+
+
+
+ $(IntDir)/src/gba/cheats.c.obj
+
+
+
+
+
+ $(IntDir)/src/gba/core.c.obj
+
+
+
+
+
+ $(IntDir)/src/gba/input.c.obj
+
+
+ $(IntDir)/src/gba/io.c.obj
+
+
+ $(IntDir)/src/gba/memory.c.obj
+
+
+ $(IntDir)/src/gba/overrides.c.obj
+
+
+ $(IntDir)/src/gba/renderers/cache-set.c.obj
+
+
+
+
+
+
+
+
+
+ $(IntDir)/src/gba/serialize.c.obj
+
+
+
+ $(IntDir)/src/gba/sio.c.obj
+
+
+
+
+ $(IntDir)/src/gba/timer.c.obj
+
+
+ $(IntDir)/src/gba/video.c.obj
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(IntDir)/src/platform/windows/memory.c.obj
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(IntDir)/src/gba/sio/lockstep.c.obj
+
+
+ $(IntDir)/src/gb/sio/lockstep.c.obj
+
+
+
+
+
+ $(IntDir)/src/gba/extra/proxy.c.obj
+
+
+ $(IntDir)/src/gb/extra/proxy.c.obj
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Externals/mGBA/mgba.vcxproj.filters b/Externals/mGBA/mgba.vcxproj.filters
new file mode 100644
index 0000000000..b5e7b0ed6a
--- /dev/null
+++ b/Externals/mGBA/mgba.vcxproj.filters
@@ -0,0 +1,427 @@
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Generated sources
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Windows-specific code
+
+
+ Third-party code
+
+
+ Third-party code
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Virtual files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ {57438DCC-46E8-3FBA-90F2-185F80CEBE2C}
+
+
+ {C0CFD641-7357-3B1D-B2A3-B2477AEF3147}
+
+
+ {6C07F537-79D5-3651-A634-9E523B9936B2}
+
+
+ {AFF59D0C-C624-393F-8703-2FB3784928C8}
+
+
+ {37E5D4D5-B263-3B94-8968-21228F26DF67}
+
+
+
diff --git a/Source/Core/Common/SCMRevGen.vcxproj b/Source/Core/Common/SCMRevGen.vcxproj
index f8c9829144..938df1c6f7 100644
--- a/Source/Core/Common/SCMRevGen.vcxproj
+++ b/Source/Core/Common/SCMRevGen.vcxproj
@@ -35,10 +35,6 @@
-
- %windir%\System32\cscript
- %windir%\Sysnative\cscript
-
diff --git a/Source/dolphin-emu.sln b/Source/dolphin-emu.sln
index 0f31f47801..1fdc7499b3 100644
--- a/Source/dolphin-emu.sln
+++ b/Source/dolphin-emu.sln
@@ -73,6 +73,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblzma", "..\Externals\lib
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zstd", "..\Externals\zstd\zstd.vcxproj", "{1BEA10F3-80CE-4BC4-9331-5769372CDF99}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mgba", "..\Externals\mGBA\mgba.vcxproj", "{864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -349,6 +351,14 @@ Global
{1BEA10F3-80CE-4BC4-9331-5769372CDF99}.Release|ARM64.Build.0 = Release|ARM64
{1BEA10F3-80CE-4BC4-9331-5769372CDF99}.Release|x64.ActiveCfg = Release|x64
{1BEA10F3-80CE-4BC4-9331-5769372CDF99}.Release|x64.Build.0 = Release|x64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Debug|ARM64.Build.0 = Debug|ARM64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Debug|x64.ActiveCfg = Debug|x64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Debug|x64.Build.0 = Debug|x64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Release|ARM64.ActiveCfg = Release|ARM64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Release|ARM64.Build.0 = Release|ARM64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Release|x64.ActiveCfg = Release|x64
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -379,6 +389,7 @@ Global
{1D8C51D2-FFA4-418E-B183-9F42B6A6717E} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
{055A775F-B4F5-4970-9240-F6CF7661F37B} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
{1BEA10F3-80CE-4BC4-9331-5769372CDF99} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
+ {864C4C8E-296D-3DBC-AD83-F1D5CB6E8EC6} = {87ADDFF9-5768-4DA2-A33B-2477593D6677}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {64B0A343-3B94-4522-9C24-6937FE5EFB22}
From 1b27f22cbcba62958d54daa3ec5ca3931a800e79 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 12:44:02 +0200
Subject: [PATCH 02/18] SI: Allow devices to schedule events
---
Source/Core/Core/HW/SI/SI.cpp | 40 +++++++++++++++++++++++++---
Source/Core/Core/HW/SI/SI.h | 3 +++
Source/Core/Core/HW/SI/SI_Device.cpp | 4 +++
Source/Core/Core/HW/SI/SI_Device.h | 3 +++
4 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/Source/Core/Core/HW/SI/SI.cpp b/Source/Core/Core/HW/SI/SI.cpp
index 12d0790cb4..14e586fc05 100644
--- a/Source/Core/Core/HW/SI/SI.cpp
+++ b/Source/Core/Core/HW/SI/SI.cpp
@@ -209,6 +209,7 @@ union USIEXIClockCount
static CoreTiming::EventType* s_change_device_event;
static CoreTiming::EventType* s_tranfer_pending_event;
+static std::array s_device_events;
// User-configured device type. possibly overridden by TAS/Netplay
static std::array, MAX_SI_CHANNELS> s_desired_device_types;
@@ -369,8 +370,44 @@ void DoState(PointerWrap& p)
p.Do(s_si_buffer);
}
+template
+static void DeviceEventCallback(u64 userdata, s64 cyclesLate)
+{
+ s_channel[device_number].device->OnEvent(userdata, cyclesLate);
+}
+
+static void RegisterEvents()
+{
+ s_change_device_event = CoreTiming::RegisterEvent("ChangeSIDevice", ChangeDeviceCallback);
+ s_tranfer_pending_event = CoreTiming::RegisterEvent("SITransferPending", RunSIBuffer);
+
+ constexpr std::array event_callbacks = {
+ DeviceEventCallback<0>,
+ DeviceEventCallback<1>,
+ DeviceEventCallback<2>,
+ DeviceEventCallback<3>,
+ };
+ for (int i = 0; i < MAX_SI_CHANNELS; ++i)
+ {
+ s_device_events[i] =
+ CoreTiming::RegisterEvent(fmt::format("SIEventChannel{}", i), event_callbacks[i]);
+ }
+}
+
+void ScheduleEvent(int device_number, s64 cycles_into_future, u64 userdata)
+{
+ CoreTiming::ScheduleEvent(cycles_into_future, s_device_events[device_number], userdata);
+}
+
+void RemoveEvent(int device_number)
+{
+ CoreTiming::RemoveEvent(s_device_events[device_number]);
+}
+
void Init()
{
+ RegisterEvents();
+
for (int i = 0; i < MAX_SI_CHANNELS; i++)
{
s_channel[i].out.hex = 0;
@@ -415,9 +452,6 @@ void Init()
// s_exi_clock_count.LOCK = 1;
s_si_buffer = {};
-
- s_change_device_event = CoreTiming::RegisterEvent("ChangeSIDevice", ChangeDeviceCallback);
- s_tranfer_pending_event = CoreTiming::RegisterEvent("SITransferPending", RunSIBuffer);
}
void Shutdown()
diff --git a/Source/Core/Core/HW/SI/SI.h b/Source/Core/Core/HW/SI/SI.h
index 89671fe04d..07ece208ce 100644
--- a/Source/Core/Core/HW/SI/SI.h
+++ b/Source/Core/Core/HW/SI/SI.h
@@ -30,6 +30,9 @@ void DoState(PointerWrap& p);
void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
+void ScheduleEvent(int device_number, s64 cycles_into_future, u64 userdata = 0);
+void RemoveEvent(int device_number);
+
void UpdateDevices();
void RemoveDevice(int device_number);
diff --git a/Source/Core/Core/HW/SI/SI_Device.cpp b/Source/Core/Core/HW/SI/SI_Device.cpp
index d13e64f602..5717fe10a9 100644
--- a/Source/Core/Core/HW/SI/SI_Device.cpp
+++ b/Source/Core/Core/HW/SI/SI_Device.cpp
@@ -97,6 +97,10 @@ void ISIDevice::DoState(PointerWrap& p)
{
}
+void ISIDevice::OnEvent(u64 userdata, s64 cycles_late)
+{
+}
+
// Check if a device class is inheriting from CSIDevice_GCController
// The goal of this function is to avoid special casing a long list of
// device types when there is no "real" input device, e.g. when playing
diff --git a/Source/Core/Core/HW/SI/SI_Device.h b/Source/Core/Core/HW/SI/SI_Device.h
index 9225195689..323f6c602a 100644
--- a/Source/Core/Core/HW/SI/SI_Device.h
+++ b/Source/Core/Core/HW/SI/SI_Device.h
@@ -88,6 +88,9 @@ public:
// Savestate support
virtual void DoState(PointerWrap& p);
+ // Schedulable event
+ virtual void OnEvent(u64 userdata, s64 cycles_late);
+
protected:
int m_device_number;
SIDevices m_device_type;
From 502def7f71efb78170bd0d5fed7e4cae1112120a Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 12:44:56 +0200
Subject: [PATCH 03/18] Mixer: Support GBA samples mixing
---
Source/Core/AudioCommon/Mixer.cpp | 41 +++++++++++++++++++++++++------
Source/Core/AudioCommon/Mixer.h | 18 +++++++++++---
2 files changed, 47 insertions(+), 12 deletions(-)
diff --git a/Source/Core/AudioCommon/Mixer.cpp b/Source/Core/AudioCommon/Mixer.cpp
index ae8ba47d00..e50d90b32b 100644
--- a/Source/Core/AudioCommon/Mixer.cpp
+++ b/Source/Core/AudioCommon/Mixer.cpp
@@ -47,6 +47,8 @@ void Mixer::DoState(PointerWrap& p)
m_dma_mixer.DoState(p);
m_streaming_mixer.DoState(p);
m_wiimote_speaker_mixer.DoState(p);
+ for (auto& mixer : m_gba_mixers)
+ mixer.DoState(p);
}
// Executed from sound stream thread
@@ -93,20 +95,24 @@ unsigned int Mixer::MixerFifo::Mix(short* samples, unsigned int numSamples,
s32 lvolume = m_LVolume.load();
s32 rvolume = m_RVolume.load();
+ const auto read_buffer = [this](auto index) {
+ return m_little_endian ? m_buffer[index] : Common::swap16(m_buffer[index]);
+ };
+
// TODO: consider a higher-quality resampling algorithm.
for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2)
{
u32 indexR2 = indexR + 2; // next sample
- s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); // current
- s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); // next
+ s16 l1 = read_buffer(indexR & INDEX_MASK); // current
+ s16 l2 = read_buffer(indexR2 & INDEX_MASK); // next
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16;
sampleL = (sampleL * lvolume) >> 8;
sampleL += samples[currentSample + 1];
samples[currentSample + 1] = std::clamp(sampleL, -32767, 32767);
- s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); // current
- s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); // next
+ s16 r1 = read_buffer((indexR + 1) & INDEX_MASK); // current
+ s16 r2 = read_buffer((indexR2 + 1) & INDEX_MASK); // next
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16;
sampleR = (sampleR * rvolume) >> 8;
sampleR += samples[currentSample];
@@ -122,8 +128,8 @@ unsigned int Mixer::MixerFifo::Mix(short* samples, unsigned int numSamples,
// Padding
short s[2];
- s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
- s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
+ s[0] = read_buffer((indexR - 1) & INDEX_MASK);
+ s[1] = read_buffer((indexR - 2) & INDEX_MASK);
s[0] = (s[0] * rvolume) >> 8;
s[1] = (s[1] * lvolume) >> 8;
for (; currentSample < numSamples * 2; currentSample += 2)
@@ -158,6 +164,8 @@ unsigned int Mixer::Mix(short* samples, unsigned int num_samples)
m_dma_mixer.Mix(m_scratch_buffer.data(), available_samples, false);
m_streaming_mixer.Mix(m_scratch_buffer.data(), available_samples, false);
m_wiimote_speaker_mixer.Mix(m_scratch_buffer.data(), available_samples, false);
+ for (auto& mixer : m_gba_mixers)
+ mixer.Mix(m_scratch_buffer.data(), available_samples, false);
if (!m_is_stretching)
{
@@ -172,6 +180,8 @@ unsigned int Mixer::Mix(short* samples, unsigned int num_samples)
m_dma_mixer.Mix(samples, num_samples, true);
m_streaming_mixer.Mix(samples, num_samples, true);
m_wiimote_speaker_mixer.Mix(samples, num_samples, true);
+ for (auto& mixer : m_gba_mixers)
+ mixer.Mix(samples, num_samples, true);
m_is_stretching = false;
}
@@ -258,14 +268,19 @@ void Mixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_sam
for (unsigned int i = 0; i < num_samples; ++i)
{
- samples_stereo[i * 2] = Common::swap16(samples[i]);
- samples_stereo[i * 2 + 1] = Common::swap16(samples[i]);
+ samples_stereo[i * 2] = samples[i];
+ samples_stereo[i * 2 + 1] = samples[i];
}
m_wiimote_speaker_mixer.PushSamples(samples_stereo, num_samples);
}
}
+void Mixer::PushGBASamples(int device_number, const short* samples, unsigned int num_samples)
+{
+ m_gba_mixers[device_number].PushSamples(samples, num_samples);
+}
+
void Mixer::SetDMAInputSampleRate(unsigned int rate)
{
m_dma_mixer.SetInputSampleRate(rate);
@@ -276,6 +291,11 @@ void Mixer::SetStreamInputSampleRate(unsigned int rate)
m_streaming_mixer.SetInputSampleRate(rate);
}
+void Mixer::SetGBAInputSampleRates(int device_number, unsigned int rate)
+{
+ m_gba_mixers[device_number].SetInputSampleRate(rate);
+}
+
void Mixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume)
{
m_streaming_mixer.SetVolume(lvolume, rvolume);
@@ -286,6 +306,11 @@ void Mixer::SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume)
m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume);
}
+void Mixer::SetGBAVolume(int device_number, unsigned int lvolume, unsigned int rvolume)
+{
+ m_gba_mixers[device_number].SetVolume(lvolume, rvolume);
+}
+
void Mixer::StartLogDTKAudio(const std::string& filename)
{
if (!m_log_dtk_audio)
diff --git a/Source/Core/AudioCommon/Mixer.h b/Source/Core/AudioCommon/Mixer.h
index 36630c7013..2a1a62ba4f 100644
--- a/Source/Core/AudioCommon/Mixer.h
+++ b/Source/Core/AudioCommon/Mixer.h
@@ -30,11 +30,17 @@ public:
void PushStreamingSamples(const short* samples, unsigned int num_samples);
void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples,
unsigned int sample_rate);
+ void PushGBASamples(int device_number, const short* samples, unsigned int num_samples);
+
unsigned int GetSampleRate() const { return m_sampleRate; }
+
void SetDMAInputSampleRate(unsigned int rate);
void SetStreamInputSampleRate(unsigned int rate);
+ void SetGBAInputSampleRates(int device_number, unsigned int rate);
+
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume);
+ void SetGBAVolume(int device_number, unsigned int lvolume, unsigned int rvolume);
void StartLogDTKAudio(const std::string& filename);
void StopLogDTKAudio();
@@ -57,7 +63,8 @@ private:
class MixerFifo final
{
public:
- MixerFifo(Mixer* mixer, unsigned sample_rate) : m_mixer(mixer), m_input_sample_rate(sample_rate)
+ MixerFifo(Mixer* mixer, unsigned sample_rate, bool little_endian)
+ : m_mixer(mixer), m_input_sample_rate(sample_rate), m_little_endian(little_endian)
{
}
void DoState(PointerWrap& p);
@@ -71,6 +78,7 @@ private:
private:
Mixer* m_mixer;
unsigned m_input_sample_rate;
+ bool m_little_endian;
std::array m_buffer{};
std::atomic m_indexW{0};
std::atomic m_indexR{0};
@@ -81,9 +89,11 @@ private:
u32 m_frac = 0;
};
- MixerFifo m_dma_mixer{this, 32000};
- MixerFifo m_streaming_mixer{this, 48000};
- MixerFifo m_wiimote_speaker_mixer{this, 3000};
+ MixerFifo m_dma_mixer{this, 32000, false};
+ MixerFifo m_streaming_mixer{this, 48000, false};
+ MixerFifo m_wiimote_speaker_mixer{this, 3000, true};
+ std::array m_gba_mixers{MixerFifo{this, 48000, true}, MixerFifo{this, 48000, true},
+ MixerFifo{this, 48000, true}, MixerFifo{this, 48000, true}};
unsigned int m_sampleRate;
bool m_is_stretching = false;
From 110887435c990c48548c802dff95f099d7eda921 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 12:51:36 +0200
Subject: [PATCH 04/18] Config: GBA settings
---
Source/Core/Common/CommonPaths.h | 4 ++++
Source/Core/Common/FileUtil.cpp | 4 ++++
Source/Core/Common/FileUtil.h | 3 +++
Source/Core/Core/Config/MainSettings.cpp | 12 ++++++++++++
Source/Core/Core/Config/MainSettings.h | 9 +++++++++
Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp | 2 +-
Source/Core/UICommon/UICommon.cpp | 7 ++++---
7 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h
index 4afeb698a3..e4b4a67d86 100644
--- a/Source/Core/Common/CommonPaths.h
+++ b/Source/Core/Common/CommonPaths.h
@@ -34,6 +34,7 @@
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
#define GC_USER_DIR "GC"
+#define GBA_USER_DIR "GBA"
#define WII_USER_DIR "Wii"
#define CONFIG_DIR "Config"
#define GAMESETTINGS_DIR "GameSettings"
@@ -61,6 +62,7 @@
#define RESOURCES_DIR "Resources"
#define THEMES_DIR "Themes"
#define STYLES_DIR "Styles"
+#define GBASAVES_DIR "Saves"
#define ANAGLYPH_DIR "Anaglyph"
#define PASSIVE_DIR "Passive"
#define PIPES_DIR "Pipes"
@@ -119,6 +121,8 @@
#define GC_MEMCARDB "MemoryCardB"
#define GC_MEMCARD_NETPLAY "NetPlayTemp"
+#define GBA_BIOS "gba_bios.bin"
+
#define WII_STATE "state.dat"
#define WII_SDCARD "sd.raw"
diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp
index a65f58f793..d04fdc60a1 100644
--- a/Source/Core/Common/FileUtil.cpp
+++ b/Source/Core/Common/FileUtil.cpp
@@ -983,6 +983,10 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[F_MEMORYWATCHERSOCKET_IDX] =
s_user_paths[D_MEMORYWATCHER_IDX] + MEMORYWATCHER_SOCKET;
+ s_user_paths[D_GBAUSER_IDX] = s_user_paths[D_USER_IDX] + GBA_USER_DIR DIR_SEP;
+ s_user_paths[D_GBASAVES_IDX] = s_user_paths[D_GBAUSER_IDX] + GBASAVES_DIR DIR_SEP;
+ s_user_paths[F_GBABIOS_IDX] = s_user_paths[D_GBAUSER_IDX] + GBA_BIOS;
+
// The shader cache has moved to the cache directory, so remove the old one.
// TODO: remove that someday.
File::DeleteDirRecursively(s_user_paths[D_USER_IDX] + SHADERCACHE_LEGACY_DIR DIR_SEP);
diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h
index c4419fb317..1a487bbe4e 100644
--- a/Source/Core/Common/FileUtil.h
+++ b/Source/Core/Common/FileUtil.h
@@ -59,6 +59,8 @@ enum
D_BACKUP_IDX,
D_RESOURCEPACK_IDX,
D_DYNAMICINPUT_IDX,
+ D_GBAUSER_IDX,
+ D_GBASAVES_IDX,
F_DOLPHINCONFIG_IDX,
F_GCPADCONFIG_IDX,
F_WIIPADCONFIG_IDX,
@@ -77,6 +79,7 @@ enum
F_WIISDCARD_IDX,
F_DUALSHOCKUDPCLIENTCONFIG_IDX,
F_FREELOOKCONFIG_IDX,
+ F_GBABIOS_IDX,
NUM_PATH_INDICES
};
diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp
index dd3e633991..b3fa308160 100644
--- a/Source/Core/Core/Config/MainSettings.cpp
+++ b/Source/Core/Core/Config/MainSettings.cpp
@@ -145,6 +145,18 @@ const Info MAIN_RESOURCEPACK_PATH{{System::Main, "General", "Resour
const Info MAIN_FS_PATH{{System::Main, "General", "NANDRootPath"}, ""};
const Info MAIN_SD_PATH{{System::Main, "General", "WiiSDCardPath"}, ""};
+// Main.GBA
+
+const Info MAIN_GBA_BIOS_PATH{{System::Main, "GBA", "BIOS"}, ""};
+const std::array, 4> MAIN_GBA_ROM_PATHS{
+ Info{{System::Main, "GBA", "Rom1"}, ""},
+ Info{{System::Main, "GBA", "Rom2"}, ""},
+ Info{{System::Main, "GBA", "Rom3"}, ""},
+ Info{{System::Main, "GBA", "Rom4"}, ""}};
+const Info MAIN_GBA_SAVES_PATH{{System::Main, "GBA", "SavesPath"}, ""};
+const Info MAIN_GBA_SAVES_IN_ROM_PATH{{System::Main, "GBA", "SavesInRomPath"}, false};
+const Info MAIN_GBA_THREADS{{System::Main, "GBA", "Threads"}, true};
+
// Main.Network
const Info MAIN_NETWORK_SSL_DUMP_READ{{System::Main, "Network", "SSLDumpRead"}, false};
diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h
index 981b6c663f..4fad910606 100644
--- a/Source/Core/Core/Config/MainSettings.h
+++ b/Source/Core/Core/Config/MainSettings.h
@@ -3,6 +3,7 @@
#pragma once
+#include
#include
#include "Common/Config/Config.h"
@@ -119,6 +120,14 @@ extern const Info MAIN_RESOURCEPACK_PATH;
extern const Info MAIN_FS_PATH;
extern const Info MAIN_SD_PATH;
+// Main.GBA
+
+extern const Info MAIN_GBA_BIOS_PATH;
+extern const std::array, 4> MAIN_GBA_ROM_PATHS;
+extern const Info MAIN_GBA_SAVES_PATH;
+extern const Info MAIN_GBA_SAVES_IN_ROM_PATH;
+extern const Info MAIN_GBA_THREADS;
+
// Main.Network
extern const Info MAIN_NETWORK_SSL_DUMP_READ;
diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp
index f2eb0eb930..1f8074518d 100644
--- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp
+++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp
@@ -26,7 +26,7 @@ bool IsSettingSaveable(const Config::Location& config_location)
if (config_location.system == Config::System::Main)
{
for (const std::string_view section :
- {"NetPlay", "General", "Display", "Network", "Analytics", "AndroidOverlayButtons"})
+ {"NetPlay", "General", "GBA", "Display", "Network", "Analytics", "AndroidOverlayButtons"})
{
if (config_location.section == section)
return true;
diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp
index 40815f049f..a5e7450b85 100644
--- a/Source/Core/UICommon/UICommon.cpp
+++ b/Source/Core/UICommon/UICommon.cpp
@@ -81,9 +81,10 @@ static void InitCustomPaths()
CreateLoadPath(Config::Get(Config::MAIN_LOAD_PATH));
CreateDumpPath(Config::Get(Config::MAIN_DUMP_PATH));
CreateResourcePackPath(Config::Get(Config::MAIN_RESOURCEPACK_PATH));
- const std::string sd_path = Config::Get(Config::MAIN_SD_PATH);
- if (!sd_path.empty())
- File::SetUserPath(F_WIISDCARD_IDX, sd_path);
+ File::SetUserPath(F_WIISDCARD_IDX, Config::Get(Config::MAIN_SD_PATH));
+ File::SetUserPath(F_GBABIOS_IDX, Config::Get(Config::MAIN_GBA_BIOS_PATH));
+ File::SetUserPath(D_GBASAVES_IDX, Config::Get(Config::MAIN_GBA_SAVES_PATH));
+ File::CreateFullPath(File::GetUserPath(D_GBASAVES_IDX));
}
void Init()
From ec5d557895e1fd2c9bab4e50a0d6529815b88640 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 12:53:53 +0200
Subject: [PATCH 05/18] Qt: GBA Config dialog
---
.../Core/DolphinQt/Settings/GameCubePane.cpp | 169 ++++++++++++++++++
Source/Core/DolphinQt/Settings/GameCubePane.h | 23 +++
2 files changed, 192 insertions(+)
diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp
index cc9dd14fe0..fcaa4af794 100644
--- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp
+++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include
#include
@@ -19,16 +20,19 @@
#include "Common/CommonPaths.h"
#include "Common/Config/Config.h"
#include "Common/FileUtil.h"
+#include "Common/MsgHandler.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "Core/HW/EXI/EXI.h"
#include "Core/HW/GCMemcard/GCMemcard.h"
+#include "Core/NetPlayServer.h"
#include "DolphinQt/Config/Mapping/MappingWindow.h"
#include "DolphinQt/GCMemcardManager.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h"
+#include "DolphinQt/Settings.h"
enum
{
@@ -123,8 +127,51 @@ void GameCubePane::CreateWidgets()
device_layout->addWidget(m_slot_combos[2], 2, 1);
device_layout->addWidget(m_slot_buttons[2], 2, 2);
+#ifdef HAS_LIBMGBA
+ // GBA Settings
+ auto* gba_box = new QGroupBox(tr("GBA Settings"), this);
+ auto* gba_layout = new QGridLayout(gba_box);
+ gba_box->setLayout(gba_layout);
+ int gba_row = 0;
+
+ m_gba_threads = new QCheckBox(tr("Run GBA Cores in Dedicated Threads"));
+ gba_layout->addWidget(m_gba_threads, gba_row, 0, 1, -1);
+ gba_row++;
+
+ m_gba_bios_edit = new QLineEdit();
+ m_gba_browse_bios = new QPushButton(QStringLiteral("..."));
+ gba_layout->addWidget(new QLabel(tr("BIOS:")), gba_row, 0);
+ gba_layout->addWidget(m_gba_bios_edit, gba_row, 1);
+ gba_layout->addWidget(m_gba_browse_bios, gba_row, 2);
+ gba_row++;
+
+ for (size_t i = 0; i < m_gba_rom_edits.size(); ++i)
+ {
+ m_gba_rom_edits[i] = new QLineEdit();
+ m_gba_browse_roms[i] = new QPushButton(QStringLiteral("..."));
+ gba_layout->addWidget(new QLabel(tr("Port %1 ROM:").arg(i + 1)), gba_row, 0);
+ gba_layout->addWidget(m_gba_rom_edits[i], gba_row, 1);
+ gba_layout->addWidget(m_gba_browse_roms[i], gba_row, 2);
+ gba_row++;
+ }
+
+ m_gba_save_rom_path = new QCheckBox(tr("Save in Same Directory as the ROM"));
+ gba_layout->addWidget(m_gba_save_rom_path, gba_row, 0, 1, -1);
+ gba_row++;
+
+ m_gba_saves_edit = new QLineEdit();
+ m_gba_browse_saves = new QPushButton(QStringLiteral("..."));
+ gba_layout->addWidget(new QLabel(tr("Saves:")), gba_row, 0);
+ gba_layout->addWidget(m_gba_saves_edit, gba_row, 1);
+ gba_layout->addWidget(m_gba_browse_saves, gba_row, 2);
+ gba_row++;
+#endif
+
layout->addWidget(ipl_box);
layout->addWidget(device_box);
+#ifdef HAS_LIBMGBA
+ layout->addWidget(gba_box);
+#endif
layout->addStretch();
@@ -147,6 +194,44 @@ void GameCubePane::ConnectWidgets()
&GameCubePane::SaveSettings);
connect(m_slot_buttons[i], &QPushButton::clicked, [this, i] { OnConfigPressed(i); });
}
+
+#ifdef HAS_LIBMGBA
+ // GBA Settings
+ connect(m_gba_threads, &QCheckBox::stateChanged, this, &GameCubePane::SaveSettings);
+ connect(m_gba_bios_edit, &QLineEdit::editingFinished, this, &GameCubePane::SaveSettings);
+ connect(m_gba_browse_bios, &QPushButton::clicked, this, &GameCubePane::BrowseGBABios);
+ connect(m_gba_save_rom_path, &QCheckBox::stateChanged, this, &GameCubePane::SaveRomPathChanged);
+ connect(m_gba_saves_edit, &QLineEdit::editingFinished, this, &GameCubePane::SaveSettings);
+ connect(m_gba_browse_saves, &QPushButton::clicked, this, &GameCubePane::BrowseGBASaves);
+ for (size_t i = 0; i < m_gba_browse_roms.size(); ++i)
+ {
+ connect(m_gba_rom_edits[i], &QLineEdit::editingFinished, this, &GameCubePane::SaveSettings);
+ connect(m_gba_browse_roms[i], &QPushButton::clicked, this, [this, i] { BrowseGBARom(i); });
+ }
+#endif
+
+ // Emulation State
+ connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
+ &GameCubePane::OnEmulationStateChanged);
+ OnEmulationStateChanged();
+}
+
+void GameCubePane::OnEmulationStateChanged()
+{
+#ifdef HAS_LIBMGBA
+ bool gba_enabled = !NetPlay::IsNetPlayRunning();
+ m_gba_threads->setEnabled(gba_enabled);
+ m_gba_bios_edit->setEnabled(gba_enabled);
+ m_gba_browse_bios->setEnabled(gba_enabled);
+ m_gba_save_rom_path->setEnabled(gba_enabled);
+ m_gba_saves_edit->setEnabled(gba_enabled);
+ m_gba_browse_saves->setEnabled(gba_enabled);
+ for (size_t i = 0; i < m_gba_browse_roms.size(); ++i)
+ {
+ m_gba_rom_edits[i]->setEnabled(gba_enabled);
+ m_gba_browse_roms[i]->setEnabled(gba_enabled);
+ }
+#endif
}
void GameCubePane::UpdateButton(int slot)
@@ -312,6 +397,47 @@ void GameCubePane::OnConfigPressed(int slot)
}
}
+void GameCubePane::BrowseGBABios()
+{
+ QString file = QDir::toNativeSeparators(QFileDialog::getOpenFileName(
+ this, tr("Select GBA BIOS"), QString::fromStdString(File::GetUserPath(F_GBABIOS_IDX)),
+ tr("All Files (*)")));
+ if (!file.isEmpty())
+ {
+ m_gba_bios_edit->setText(file);
+ SaveSettings();
+ }
+}
+
+void GameCubePane::BrowseGBARom(size_t index)
+{
+ QString file = QString::fromStdString(GetOpenGBARom({}));
+ if (!file.isEmpty())
+ {
+ m_gba_rom_edits[index]->setText(file);
+ SaveSettings();
+ }
+}
+
+void GameCubePane::SaveRomPathChanged()
+{
+ m_gba_saves_edit->setEnabled(!m_gba_save_rom_path->isChecked());
+ m_gba_browse_saves->setEnabled(!m_gba_save_rom_path->isChecked());
+ SaveSettings();
+}
+
+void GameCubePane::BrowseGBASaves()
+{
+ QString dir = QDir::toNativeSeparators(
+ QFileDialog::getExistingDirectory(this, tr("Select GBA Saves Path"),
+ QString::fromStdString(File::GetUserPath(D_GBASAVES_IDX))));
+ if (!dir.isEmpty())
+ {
+ m_gba_saves_edit->setText(dir);
+ SaveSettings();
+ }
+}
+
void GameCubePane::LoadSettings()
{
const SConfig& params = SConfig::GetInstance();
@@ -346,6 +472,16 @@ void GameCubePane::LoadSettings()
m_slot_combos[i]->findData(SConfig::GetInstance().m_EXIDevice[i]));
UpdateButton(i);
}
+
+#ifdef HAS_LIBMGBA
+ // GBA Settings
+ m_gba_threads->setChecked(Config::Get(Config::MAIN_GBA_THREADS));
+ m_gba_bios_edit->setText(QString::fromStdString(File::GetUserPath(F_GBABIOS_IDX)));
+ m_gba_save_rom_path->setChecked(Config::Get(Config::MAIN_GBA_SAVES_IN_ROM_PATH));
+ m_gba_saves_edit->setText(QString::fromStdString(File::GetUserPath(D_GBASAVES_IDX)));
+ for (size_t i = 0; i < m_gba_rom_edits.size(); ++i)
+ m_gba_rom_edits[i]->setText(QString::fromStdString(Config::Get(Config::MAIN_GBA_ROM_PATHS[i])));
+#endif
}
void GameCubePane::SaveSettings()
@@ -360,6 +496,7 @@ void GameCubePane::SaveSettings()
params.SelectedLanguage = m_language_combo->currentData().toInt();
Config::SetBaseOrCurrent(Config::MAIN_GC_LANGUAGE, m_language_combo->currentData().toInt());
+ // Device Settings
for (int i = 0; i < SLOT_COUNT; i++)
{
const auto dev = ExpansionInterface::TEXIDevices(m_slot_combos[i]->currentData().toInt());
@@ -389,5 +526,37 @@ void GameCubePane::SaveSettings()
break;
}
}
+
+#ifdef HAS_LIBMGBA
+ // GBA Settings
+ if (!NetPlay::IsNetPlayRunning())
+ {
+ Config::SetBaseOrCurrent(Config::MAIN_GBA_THREADS, m_gba_threads->isChecked());
+ Config::SetBaseOrCurrent(Config::MAIN_GBA_BIOS_PATH, m_gba_bios_edit->text().toStdString());
+ Config::SetBaseOrCurrent(Config::MAIN_GBA_SAVES_IN_ROM_PATH, m_gba_save_rom_path->isChecked());
+ Config::SetBaseOrCurrent(Config::MAIN_GBA_SAVES_PATH, m_gba_saves_edit->text().toStdString());
+ File::SetUserPath(F_GBABIOS_IDX, Config::Get(Config::MAIN_GBA_BIOS_PATH));
+ File::SetUserPath(D_GBASAVES_IDX, Config::Get(Config::MAIN_GBA_SAVES_PATH));
+ for (size_t i = 0; i < m_gba_rom_edits.size(); ++i)
+ {
+ Config::SetBaseOrCurrent(Config::MAIN_GBA_ROM_PATHS[i],
+ m_gba_rom_edits[i]->text().toStdString());
+ }
+ }
+#endif
+
LoadSettings();
}
+
+std::string GameCubePane::GetOpenGBARom(std::string_view title)
+{
+ QString caption = tr("Select GBA ROM");
+ if (!title.empty())
+ caption += QStringLiteral(": %1").arg(QString::fromStdString(std::string(title)));
+ return QDir::toNativeSeparators(
+ QFileDialog::getOpenFileName(
+ nullptr, caption, QString(),
+ tr("Game Boy Advance ROMs (*.gba *.gbc *.gb *.7z *.zip *.agb *.mb *.rom *.bin);;"
+ "All Files (*)")))
+ .toStdString();
+}
diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.h b/Source/Core/DolphinQt/Settings/GameCubePane.h
index 0b21a26558..ea945cfc1d 100644
--- a/Source/Core/DolphinQt/Settings/GameCubePane.h
+++ b/Source/Core/DolphinQt/Settings/GameCubePane.h
@@ -3,10 +3,15 @@
#pragma once
+#include
+#include
+#include
+
#include
class QCheckBox;
class QComboBox;
+class QLineEdit;
class QPushButton;
class GameCubePane : public QWidget
@@ -15,6 +20,8 @@ class GameCubePane : public QWidget
public:
explicit GameCubePane();
+ static std::string GetOpenGBARom(std::string_view title);
+
private:
void CreateWidgets();
void ConnectWidgets();
@@ -22,12 +29,28 @@ private:
void LoadSettings();
void SaveSettings();
+ void OnEmulationStateChanged();
+
void UpdateButton(int slot);
void OnConfigPressed(int slot);
+ void BrowseGBABios();
+ void BrowseGBARom(size_t index);
+ void SaveRomPathChanged();
+ void BrowseGBASaves();
+
QCheckBox* m_skip_main_menu;
QComboBox* m_language_combo;
QPushButton* m_slot_buttons[3];
QComboBox* m_slot_combos[3];
+
+ QCheckBox* m_gba_threads;
+ QCheckBox* m_gba_save_rom_path;
+ QPushButton* m_gba_browse_bios;
+ QLineEdit* m_gba_bios_edit;
+ std::array m_gba_browse_roms;
+ std::array m_gba_rom_edits;
+ QPushButton* m_gba_browse_saves;
+ QLineEdit* m_gba_saves_edit;
};
From 27eab609dcc0619b8b00cdb544b903550f577099 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 12:55:37 +0200
Subject: [PATCH 06/18] GCController: Make HandleMoviePadStatus static
---
Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp | 2 +-
Source/Core/Core/HW/SI/SI_DeviceGCController.cpp | 14 +++++++-------
Source/Core/Core/HW/SI/SI_DeviceGCController.h | 3 ++-
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp b/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp
index 3f60b44cf7..a3f24824fb 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceGCAdapter.cpp
@@ -38,7 +38,7 @@ GCPadStatus CSIDevice_GCAdapter::GetPadStatus()
pad_status = GCAdapter::Input(m_device_number);
}
- HandleMoviePadStatus(&pad_status);
+ HandleMoviePadStatus(m_device_number, &pad_status);
// Our GCAdapter code sets PAD_GET_ORIGIN when a new device has been connected.
// Watch for this to calibrate real controllers on connection.
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp
index 7a2b317f08..8c485f1a9f 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp
@@ -108,27 +108,27 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length)
return 0;
}
-void CSIDevice_GCController::HandleMoviePadStatus(GCPadStatus* pad_status)
+void CSIDevice_GCController::HandleMoviePadStatus(int device_number, GCPadStatus* pad_status)
{
- Movie::CallGCInputManip(pad_status, m_device_number);
+ Movie::CallGCInputManip(pad_status, device_number);
Movie::SetPolledDevice();
- if (NetPlay_GetInput(m_device_number, pad_status))
+ if (NetPlay_GetInput(device_number, pad_status))
{
}
else if (Movie::IsPlayingInput())
{
- Movie::PlayController(pad_status, m_device_number);
+ Movie::PlayController(pad_status, device_number);
Movie::InputUpdate();
}
else if (Movie::IsRecordingInput())
{
- Movie::RecordInput(pad_status, m_device_number);
+ Movie::RecordInput(pad_status, device_number);
Movie::InputUpdate();
}
else
{
- Movie::CheckPadStatus(pad_status, m_device_number);
+ Movie::CheckPadStatus(pad_status, device_number);
}
}
@@ -143,7 +143,7 @@ GCPadStatus CSIDevice_GCController::GetPadStatus()
pad_status = Pad::GetStatus(m_device_number);
}
- HandleMoviePadStatus(&pad_status);
+ HandleMoviePadStatus(m_device_number, &pad_status);
// Our GCAdapter code sets PAD_GET_ORIGIN when a new device has been connected.
// Watch for this to calibrate real controllers on connection.
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.h b/Source/Core/Core/HW/SI/SI_DeviceGCController.h
index fa336d3357..9904e855bb 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGCController.h
+++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.h
@@ -105,8 +105,9 @@ public:
// Direct rumble to the right GC Controller
static void Rumble(int pad_num, ControlState strength);
+ static void HandleMoviePadStatus(int device_number, GCPadStatus* pad_status);
+
protected:
- void HandleMoviePadStatus(GCPadStatus* pad_status);
void SetOrigin(const GCPadStatus& pad_status);
};
From d2353c79eabeec0cbcec531c94148527816a3d1a Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 12:56:07 +0200
Subject: [PATCH 07/18] MappingWidget: Support for boxes with multiple columns
---
.../Config/Mapping/MappingWidget.cpp | 48 +++++++++++++++----
.../DolphinQt/Config/Mapping/MappingWidget.h | 4 ++
2 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp
index da04403137..1206ad75ff 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.cpp
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
#include
#include
@@ -118,16 +119,7 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
}
for (auto& control : group->controls)
- {
- auto* button = new MappingButton(this, control->control_ref.get(), !indicator);
-
- button->setMinimumWidth(100);
- button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- const bool translate = control->translate == ControllerEmu::Translate;
- const QString translated_name =
- translate ? tr(control->ui_name.c_str()) : QString::fromStdString(control->ui_name);
- form_layout->addRow(translated_name, button);
- }
+ CreateControl(control.get(), form_layout, !indicator);
for (auto& setting : group->numeric_settings)
{
@@ -186,6 +178,42 @@ QGroupBox* MappingWidget::CreateGroupBox(const QString& name, ControllerEmu::Con
return group_box;
}
+QGroupBox* MappingWidget::CreateControlsBox(const QString& name, ControllerEmu::ControlGroup* group,
+ int columns)
+{
+ auto* group_box = new QGroupBox(name);
+ auto* hbox_layout = new QHBoxLayout();
+
+ group_box->setLayout(hbox_layout);
+
+ std::vector form_layouts;
+ for (int i = 0; i < columns; ++i)
+ {
+ form_layouts.push_back(new QFormLayout());
+ hbox_layout->addLayout(form_layouts[i]);
+ }
+
+ for (size_t i = 0; i < group->controls.size(); ++i)
+ {
+ CreateControl(group->controls[i].get(), form_layouts[i % columns], true);
+ }
+
+ return group_box;
+}
+
+void MappingWidget::CreateControl(const ControllerEmu::Control* control, QFormLayout* layout,
+ bool indicator)
+{
+ auto* button = new MappingButton(this, control->control_ref.get(), indicator);
+
+ button->setMinimumWidth(100);
+ button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+ const bool translate = control->translate == ControllerEmu::Translate;
+ const QString translated_name =
+ translate ? tr(control->ui_name.c_str()) : QString::fromStdString(control->ui_name);
+ layout->addRow(translated_name, button);
+}
+
ControllerEmu::EmulatedController* MappingWidget::GetController() const
{
return m_parent->GetController();
diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h
index ed092c8c5e..ad55172f4a 100644
--- a/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h
+++ b/Source/Core/DolphinQt/Config/Mapping/MappingWidget.h
@@ -16,6 +16,7 @@ class InputConfig;
class MappingButton;
class MappingNumeric;
class MappingWindow;
+class QFormLayout;
class QPushButton;
class QGroupBox;
@@ -52,6 +53,9 @@ protected:
QGroupBox* CreateGroupBox(ControllerEmu::ControlGroup* group);
QGroupBox* CreateGroupBox(const QString& name, ControllerEmu::ControlGroup* group);
+ QGroupBox* CreateControlsBox(const QString& name, ControllerEmu::ControlGroup* group,
+ int columns);
+ void CreateControl(const ControllerEmu::Control* control, QFormLayout* layout, bool indicator);
QPushButton* CreateSettingAdvancedMappingButton(ControllerEmu::NumericSettingBase& setting);
private:
From 8ee21acf3460e00d55002fb3387be4dd763b01e9 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 13:02:03 +0200
Subject: [PATCH 08/18] Pad: GBA config
---
Source/Core/Core/CMakeLists.txt | 4 +
Source/Core/Core/Core.cpp | 4 +
Source/Core/Core/FreeLookManager.cpp | 4 +-
Source/Core/Core/HW/GBAPad.cpp | 64 ++++++++++++++
Source/Core/Core/HW/GBAPad.h | 28 +++++++
Source/Core/Core/HW/GBAPadEmu.cpp | 107 ++++++++++++++++++++++++
Source/Core/Core/HW/GBAPadEmu.h | 40 +++++++++
Source/Core/Core/HW/GCKeyboard.cpp | 4 +-
Source/Core/Core/HW/GCPad.cpp | 4 +-
Source/Core/Core/HW/Wiimote.cpp | 2 +-
Source/Core/Core/HotkeyManager.cpp | 2 +-
Source/Core/DolphinLib.props | 6 +-
Source/Core/DolphinQt/MainWindow.cpp | 6 ++
Source/Core/InputCommon/InputConfig.cpp | 28 ++++---
Source/Core/InputCommon/InputConfig.h | 9 +-
15 files changed, 291 insertions(+), 21 deletions(-)
create mode 100644 Source/Core/Core/HW/GBAPad.cpp
create mode 100644 Source/Core/Core/HW/GBAPad.h
create mode 100644 Source/Core/Core/HW/GBAPadEmu.cpp
create mode 100644 Source/Core/Core/HW/GBAPadEmu.h
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 6f3ef7a623..6de7e6dbe2 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -197,6 +197,10 @@ add_library(core
HW/EXI/EXI_DeviceMic.h
HW/EXI/EXI.cpp
HW/EXI/EXI.h
+ HW/GBAPad.cpp
+ HW/GBAPad.h
+ HW/GBAPadEmu.cpp
+ HW/GBAPadEmu.h
HW/GCKeyboard.cpp
HW/GCKeyboard.h
HW/GCKeyboardEmu.cpp
diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp
index 2592a6a674..c88b16fc9d 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -48,6 +48,7 @@
#include "Core/HW/CPU.h"
#include "Core/HW/DSP.h"
#include "Core/HW/EXI/EXI.h"
+#include "Core/HW/GBAPad.h"
#include "Core/HW/GCKeyboard.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/HW.h"
@@ -460,6 +461,7 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi
{
g_controller_interface.Initialize(wsi);
Pad::Initialize();
+ Pad::InitializeGBA();
Keyboard::Initialize();
init_controllers = true;
}
@@ -467,6 +469,7 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi
{
g_controller_interface.ChangeWindow(wsi.render_window);
Pad::LoadConfig();
+ Pad::LoadGBAConfig();
Keyboard::LoadConfig();
}
@@ -517,6 +520,7 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi
Keyboard::Shutdown();
Pad::Shutdown();
+ Pad::ShutdownGBA();
g_controller_interface.Shutdown();
}};
diff --git a/Source/Core/Core/FreeLookManager.cpp b/Source/Core/Core/FreeLookManager.cpp
index 90f2667890..39d1f47278 100644
--- a/Source/Core/Core/FreeLookManager.cpp
+++ b/Source/Core/Core/FreeLookManager.cpp
@@ -309,12 +309,12 @@ void Initialize()
FreeLook::GetConfig().Refresh();
- s_config.LoadConfig(true);
+ s_config.LoadConfig(InputConfig::InputClass::GC);
}
void LoadInputConfig()
{
- s_config.LoadConfig(true);
+ s_config.LoadConfig(InputConfig::InputClass::GC);
}
bool IsInitialized()
diff --git a/Source/Core/Core/HW/GBAPad.cpp b/Source/Core/Core/HW/GBAPad.cpp
new file mode 100644
index 0000000000..7f25b56fd1
--- /dev/null
+++ b/Source/Core/Core/HW/GBAPad.cpp
@@ -0,0 +1,64 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Core/HW/GBAPad.h"
+
+#include "Core/HW/GBAPadEmu.h"
+#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
+#include "InputCommon/GCPadStatus.h"
+#include "InputCommon/InputConfig.h"
+
+namespace Pad
+{
+static InputConfig s_config("GBA", _trans("Pad"), "GBA");
+InputConfig* GetGBAConfig()
+{
+ return &s_config;
+}
+
+void ShutdownGBA()
+{
+ s_config.UnregisterHotplugCallback();
+
+ s_config.ClearControllers();
+}
+
+void InitializeGBA()
+{
+ if (s_config.ControllersNeedToBeCreated())
+ {
+ for (unsigned int i = 0; i < 4; ++i)
+ s_config.CreateController(i);
+ }
+
+ s_config.RegisterHotplugCallback();
+
+ // Load the saved controller config
+ s_config.LoadConfig(InputConfig::InputClass::GBA);
+}
+
+void LoadGBAConfig()
+{
+ s_config.LoadConfig(InputConfig::InputClass::GBA);
+}
+
+bool IsGBAInitialized()
+{
+ return !s_config.ControllersNeedToBeCreated();
+}
+
+GCPadStatus GetGBAStatus(int pad_num)
+{
+ return static_cast(s_config.GetController(pad_num))->GetInput();
+}
+
+void SetGBAReset(int pad_num, bool reset)
+{
+ static_cast(s_config.GetController(pad_num))->SetReset(reset);
+}
+
+ControllerEmu::ControlGroup* GetGBAGroup(int pad_num, GBAPadGroup group)
+{
+ return static_cast(s_config.GetController(pad_num))->GetGroup(group);
+}
+} // namespace Pad
diff --git a/Source/Core/Core/HW/GBAPad.h b/Source/Core/Core/HW/GBAPad.h
new file mode 100644
index 0000000000..76d68f26df
--- /dev/null
+++ b/Source/Core/Core/HW/GBAPad.h
@@ -0,0 +1,28 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+class InputConfig;
+enum class GBAPadGroup;
+struct GCPadStatus;
+
+namespace ControllerEmu
+{
+class ControlGroup;
+} // namespace ControllerEmu
+
+namespace Pad
+{
+void ShutdownGBA();
+void InitializeGBA();
+void LoadGBAConfig();
+bool IsGBAInitialized();
+
+InputConfig* GetGBAConfig();
+
+GCPadStatus GetGBAStatus(int pad_num);
+void SetGBAReset(int pad_num, bool reset);
+
+ControllerEmu::ControlGroup* GetGBAGroup(int pad_num, GBAPadGroup group);
+} // namespace Pad
diff --git a/Source/Core/Core/HW/GBAPadEmu.cpp b/Source/Core/Core/HW/GBAPadEmu.cpp
new file mode 100644
index 0000000000..c25763b6d8
--- /dev/null
+++ b/Source/Core/Core/HW/GBAPadEmu.cpp
@@ -0,0 +1,107 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Core/HW/GBAPadEmu.h"
+
+#include
+
+#include "InputCommon/ControllerEmu/Control/Input.h"
+#include "InputCommon/ControllerEmu/ControlGroup/Buttons.h"
+#include "InputCommon/GCPadStatus.h"
+
+static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT,
+ PAD_BUTTON_RIGHT};
+
+static const u16 button_bitmasks[] = {PAD_BUTTON_B, PAD_BUTTON_A, PAD_TRIGGER_L,
+ PAD_TRIGGER_R, PAD_TRIGGER_Z, PAD_BUTTON_START};
+
+static const char* const named_buttons[] = {"B", "A", "L", "R", _trans("SELECT"), _trans("START")};
+
+GBAPad::GBAPad(const unsigned int index) : m_reset_pending(false), m_index(index)
+{
+ // Buttons
+ groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons")));
+ for (const char* named_button : named_buttons)
+ {
+ const ControllerEmu::Translatability translate =
+ (named_button == std::string(_trans("SELECT")) ||
+ named_button == std::string(_trans("START"))) ?
+ ControllerEmu::Translate :
+ ControllerEmu::DoNotTranslate;
+ m_buttons->AddInput(translate, named_button);
+ }
+
+ // DPad
+ groups.emplace_back(m_dpad = new ControllerEmu::Buttons(_trans("D-Pad")));
+ for (const char* named_direction : named_directions)
+ {
+ m_dpad->AddInput(ControllerEmu::Translate, named_direction);
+ }
+}
+
+std::string GBAPad::GetName() const
+{
+ return fmt::format("GBA{}", m_index + 1);
+}
+
+ControllerEmu::ControlGroup* GBAPad::GetGroup(GBAPadGroup group) const
+{
+ switch (group)
+ {
+ case GBAPadGroup::Buttons:
+ return m_buttons;
+ case GBAPadGroup::DPad:
+ return m_dpad;
+ default:
+ return nullptr;
+ }
+}
+
+GCPadStatus GBAPad::GetInput()
+{
+ const auto lock = GetStateLock();
+ GCPadStatus pad = {};
+
+ // Buttons
+ m_buttons->GetState(&pad.button, button_bitmasks);
+
+ // DPad
+ m_dpad->GetState(&pad.button, dpad_bitmasks);
+
+ // Use X button as a reset signal
+ if (m_reset_pending)
+ pad.button |= PAD_BUTTON_X;
+ m_reset_pending = false;
+
+ return pad;
+}
+
+void GBAPad::SetReset(bool reset)
+{
+ const auto lock = GetStateLock();
+ m_reset_pending = reset;
+}
+
+void GBAPad::LoadDefaults(const ControllerInterface& ciface)
+{
+ EmulatedController::LoadDefaults(ciface);
+
+ // Buttons
+ m_buttons->SetControlExpression(0, "`Z`"); // B
+ m_buttons->SetControlExpression(1, "`X`"); // A
+ m_buttons->SetControlExpression(2, "`Q`"); // L
+ m_buttons->SetControlExpression(3, "`W`"); // R
+#ifdef _WIN32
+ m_buttons->SetControlExpression(4, "`BACK`"); // Select
+ m_buttons->SetControlExpression(5, "`RETURN`"); // Start
+#else
+ m_buttons->SetControlExpression(4, "`Backspace`"); // Select
+ m_buttons->SetControlExpression(5, "`Return`"); // Start
+#endif
+
+ // D-Pad
+ m_dpad->SetControlExpression(0, "`T`"); // Up
+ m_dpad->SetControlExpression(1, "`G`"); // Down
+ m_dpad->SetControlExpression(2, "`F`"); // Left
+ m_dpad->SetControlExpression(3, "`H`"); // Right
+}
diff --git a/Source/Core/Core/HW/GBAPadEmu.h b/Source/Core/Core/HW/GBAPadEmu.h
new file mode 100644
index 0000000000..8dc8f24457
--- /dev/null
+++ b/Source/Core/Core/HW/GBAPadEmu.h
@@ -0,0 +1,40 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "InputCommon/ControllerEmu/ControllerEmu.h"
+
+struct GCPadStatus;
+
+namespace ControllerEmu
+{
+class Buttons;
+} // namespace ControllerEmu
+
+enum class GBAPadGroup
+{
+ DPad,
+ Buttons
+};
+
+class GBAPad : public ControllerEmu::EmulatedController
+{
+public:
+ explicit GBAPad(unsigned int index);
+ GCPadStatus GetInput();
+ void SetReset(bool reset);
+
+ std::string GetName() const override;
+
+ ControllerEmu::ControlGroup* GetGroup(GBAPadGroup group) const;
+
+ void LoadDefaults(const ControllerInterface& ciface) override;
+
+private:
+ ControllerEmu::Buttons* m_buttons;
+ ControllerEmu::Buttons* m_dpad;
+ bool m_reset_pending;
+
+ const unsigned int m_index;
+};
diff --git a/Source/Core/Core/HW/GCKeyboard.cpp b/Source/Core/Core/HW/GCKeyboard.cpp
index de03ec32b4..f99193f4d4 100644
--- a/Source/Core/Core/HW/GCKeyboard.cpp
+++ b/Source/Core/Core/HW/GCKeyboard.cpp
@@ -41,12 +41,12 @@ void Initialize()
s_config.RegisterHotplugCallback();
// Load the saved controller config
- s_config.LoadConfig(true);
+ s_config.LoadConfig(InputConfig::InputClass::GC);
}
void LoadConfig()
{
- s_config.LoadConfig(true);
+ s_config.LoadConfig(InputConfig::InputClass::GC);
}
ControllerEmu::ControlGroup* GetGroup(int port, KeyboardGroup group)
diff --git a/Source/Core/Core/HW/GCPad.cpp b/Source/Core/Core/HW/GCPad.cpp
index a34c83638d..faf50f3c02 100644
--- a/Source/Core/Core/HW/GCPad.cpp
+++ b/Source/Core/Core/HW/GCPad.cpp
@@ -38,12 +38,12 @@ void Initialize()
s_config.RegisterHotplugCallback();
// Load the saved controller config
- s_config.LoadConfig(true);
+ s_config.LoadConfig(InputConfig::InputClass::GC);
}
void LoadConfig()
{
- s_config.LoadConfig(true);
+ s_config.LoadConfig(InputConfig::InputClass::GC);
}
bool IsInitialized()
diff --git a/Source/Core/Core/HW/Wiimote.cpp b/Source/Core/Core/HW/Wiimote.cpp
index eaed4c91eb..30d522b564 100644
--- a/Source/Core/Core/HW/Wiimote.cpp
+++ b/Source/Core/Core/HW/Wiimote.cpp
@@ -173,7 +173,7 @@ void ResetAllWiimotes()
void LoadConfig()
{
- s_config.LoadConfig(false);
+ s_config.LoadConfig(InputConfig::InputClass::Wii);
s_last_connect_request_counter.fill(0);
}
diff --git a/Source/Core/Core/HotkeyManager.cpp b/Source/Core/Core/HotkeyManager.cpp
index 77960cc381..01ddadb6be 100644
--- a/Source/Core/Core/HotkeyManager.cpp
+++ b/Source/Core/Core/HotkeyManager.cpp
@@ -285,7 +285,7 @@ void Initialize()
void LoadConfig()
{
- s_config.LoadConfig(true);
+ s_config.LoadConfig(InputConfig::InputClass::GC);
LoadLegacyConfig(s_config.GetController(0));
}
diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index 67c1a2eace..45cc52e6e3 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -1,4 +1,4 @@
-
+
@@ -264,6 +264,8 @@
+
+
@@ -844,6 +846,8 @@
+
+
diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp
index c4970c335d..317150812b 100644
--- a/Source/Core/DolphinQt/MainWindow.cpp
+++ b/Source/Core/DolphinQt/MainWindow.cpp
@@ -42,6 +42,7 @@
#include "Core/Core.h"
#include "Core/FreeLookManager.h"
#include "Core/HW/DVD/DVDInterface.h"
+#include "Core/HW/GBAPad.h"
#include "Core/HW/GCKeyboard.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/ProcessorInterface.h"
@@ -306,6 +307,7 @@ void MainWindow::InitControllers()
g_controller_interface.Initialize(GetWindowSystemInfo(windowHandle()));
Pad::Initialize();
+ Pad::InitializeGBA();
Keyboard::Initialize();
Wiimote::Initialize(Wiimote::InitializeMode::DO_NOT_WAIT_FOR_WIIMOTES);
FreeLook::Initialize();
@@ -320,6 +322,9 @@ void MainWindow::InitControllers()
Pad::LoadConfig();
Pad::GetConfig()->SaveConfig();
+ Pad::LoadGBAConfig();
+ Pad::GetGBAConfig()->SaveConfig();
+
Keyboard::LoadConfig();
Keyboard::GetConfig()->SaveConfig();
@@ -332,6 +337,7 @@ void MainWindow::ShutdownControllers()
m_hotkey_scheduler->Stop();
Pad::Shutdown();
+ Pad::ShutdownGBA();
Keyboard::Shutdown();
Wiimote::Shutdown();
HotkeyManagerEmu::Shutdown();
diff --git a/Source/Core/InputCommon/InputConfig.cpp b/Source/Core/InputCommon/InputConfig.cpp
index 07249b0ed8..bace091ca5 100644
--- a/Source/Core/InputCommon/InputConfig.cpp
+++ b/Source/Core/InputCommon/InputConfig.cpp
@@ -26,7 +26,7 @@ InputConfig::InputConfig(const std::string& ini_name, const std::string& gui_nam
InputConfig::~InputConfig() = default;
-bool InputConfig::LoadConfig(bool isGC)
+bool InputConfig::LoadConfig(InputClass type)
{
IniFile inifile;
bool useProfile[MAX_BBMOTES] = {false, false, false, false, false};
@@ -43,16 +43,22 @@ bool InputConfig::LoadConfig(bool isGC)
if (SConfig::GetInstance().GetGameID() != "00000000")
{
- std::string type;
- if (isGC)
+ std::string type_str;
+ switch (type)
{
- type = "Pad";
- path = "Profiles/GCPad/";
- }
- else
- {
- type = "Wiimote";
+ case InputClass::GBA:
+ type_str = "GBA";
+ path = "Profiles/GBA/";
+ break;
+ case InputClass::Wii:
+ type_str = "Wiimote";
path = "Profiles/Wiimote/";
+ break;
+ case InputClass::GC:
+ default:
+ type_str = "Pad";
+ path = "Profiles/GCPad/";
+ break;
}
IniFile game_ini = SConfig::GetInstance().LoadGameIni();
@@ -60,7 +66,7 @@ bool InputConfig::LoadConfig(bool isGC)
for (int i = 0; i < 4; i++)
{
- const auto profile_name = fmt::format("{}Profile{}", type, num[i]);
+ const auto profile_name = fmt::format("{}Profile{}", type_str, num[i]);
if (control_section->Exists(profile_name))
{
@@ -124,7 +130,7 @@ bool InputConfig::LoadConfig(bool isGC)
}
#if defined(ANDROID)
// Only set for wii pads
- if (!isGC && use_ir_config)
+ if (type == InputClass::Wii && use_ir_config)
{
config.Set("IR/Total Yaw", ir_values[0]);
config.Set("IR/Total Pitch", ir_values[1]);
diff --git a/Source/Core/InputCommon/InputConfig.h b/Source/Core/InputCommon/InputConfig.h
index 62eb30b4de..2c3f290576 100644
--- a/Source/Core/InputCommon/InputConfig.h
+++ b/Source/Core/InputCommon/InputConfig.h
@@ -24,7 +24,14 @@ public:
~InputConfig();
- bool LoadConfig(bool isGC);
+ enum class InputClass
+ {
+ GC,
+ Wii,
+ GBA,
+ };
+
+ bool LoadConfig(InputClass type);
void SaveConfig();
template
From fdcee30a3d191946ebbd93f6ecc5be644e493de9 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 13:02:50 +0200
Subject: [PATCH 09/18] SI: Expose Commands constants and switch to enum class
---
Source/Core/Core/HW/SI/SI_Device.h | 34 ++++++++++++++++++
Source/Core/Core/HW/SI/SI_DeviceDanceMat.cpp | 4 +--
Source/Core/Core/HW/SI/SI_DeviceGBA.cpp | 35 ++++++++-----------
Source/Core/Core/HW/SI/SI_DeviceGBA.h | 2 +-
.../Core/Core/HW/SI/SI_DeviceGCController.cpp | 27 +++++---------
.../Core/Core/HW/SI/SI_DeviceGCController.h | 29 ---------------
.../Core/HW/SI/SI_DeviceGCSteeringWheel.cpp | 8 ++---
.../Core/HW/SI/SI_DeviceGCSteeringWheel.h | 15 --------
Source/Core/Core/HW/SI/SI_DeviceKeyboard.cpp | 18 +++-------
Source/Core/Core/HW/SI/SI_DeviceKeyboard.h | 28 ---------------
10 files changed, 69 insertions(+), 131 deletions(-)
diff --git a/Source/Core/Core/HW/SI/SI_Device.h b/Source/Core/Core/HW/SI/SI_Device.h
index 323f6c602a..14bd83201c 100644
--- a/Source/Core/Core/HW/SI/SI_Device.h
+++ b/Source/Core/Core/HW/SI/SI_Device.h
@@ -41,6 +41,40 @@ enum TSIDevices : u32
SI_AM_BASEBOARD = 0x10110800 // gets ORd with dipswitch state
};
+// Commands
+enum class EBufferCommands : u8
+{
+ CMD_STATUS = 0x00,
+ CMD_READ_GBA = 0x14,
+ CMD_WRITE_GBA = 0x15,
+ CMD_DIRECT = 0x40,
+ CMD_ORIGIN = 0x41,
+ CMD_RECALIBRATE = 0x42,
+ CMD_DIRECT_KB = 0x54,
+ CMD_RESET = 0xFF
+};
+
+enum class EDirectCommands : u8
+{
+ CMD_FORCE = 0x30,
+ CMD_WRITE = 0x40,
+ CMD_POLL = 0x54
+};
+
+union UCommand
+{
+ u32 hex = 0;
+ struct
+ {
+ u32 parameter1 : 8;
+ u32 parameter2 : 8;
+ u32 command : 8;
+ u32 : 8;
+ };
+ UCommand() = default;
+ UCommand(u32 value) : hex{value} {}
+};
+
// For configuration use, since some devices can have the same SI Device ID
enum SIDevices : int
{
diff --git a/Source/Core/Core/HW/SI/SI_DeviceDanceMat.cpp b/Source/Core/Core/HW/SI/SI_DeviceDanceMat.cpp
index ae5ae477ab..f2f6545776 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceDanceMat.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceDanceMat.cpp
@@ -19,8 +19,8 @@ CSIDevice_DanceMat::CSIDevice_DanceMat(SIDevices device, int device_number)
int CSIDevice_DanceMat::RunBuffer(u8* buffer, int request_length)
{
// Read the command
- EBufferCommands command = static_cast(buffer[0]);
- if (command == CMD_RESET)
+ const auto command = static_cast(buffer[0]);
+ if (command == EBufferCommands::CMD_STATUS)
{
ISIDevice::RunBuffer(buffer, request_length);
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp b/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp
index b1d2704a47..57679bbe50 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp
@@ -34,14 +34,6 @@ int s_num_connected;
Common::Flag s_server_running;
} // namespace
-enum EJoybusCmds
-{
- CMD_RESET = 0xff,
- CMD_STATUS = 0x00,
- CMD_READ = 0x14,
- CMD_WRITE = 0x15
-};
-
constexpr auto GC_BITS_PER_SECOND = 200000;
constexpr auto GBA_BITS_PER_SECOND = 250000;
constexpr auto GC_STOP_BIT_NS = 6500;
@@ -50,7 +42,7 @@ constexpr auto SEND_MAX_SIZE = 5, RECV_MAX_SIZE = 5;
// --- GameBoy Advance "Link Cable" ---
-static int GetTransferTime(u8 cmd)
+static int GetTransferTime(EBufferCommands cmd)
{
u64 gc_bytes_transferred = 1;
u64 gba_bytes_transferred = 1;
@@ -58,18 +50,18 @@ static int GetTransferTime(u8 cmd)
switch (cmd)
{
- case CMD_RESET:
- case CMD_STATUS:
+ case EBufferCommands::CMD_RESET:
+ case EBufferCommands::CMD_STATUS:
{
gba_bytes_transferred = 3;
break;
}
- case CMD_READ:
+ case EBufferCommands::CMD_READ_GBA:
{
gba_bytes_transferred = 5;
break;
}
- case CMD_WRITE:
+ case EBufferCommands::CMD_WRITE_GBA:
{
gc_bytes_transferred = 5;
break;
@@ -249,10 +241,10 @@ void GBASockServer::Send(const u8* si_buffer)
for (size_t i = 0; i < send_data.size(); i++)
send_data[i] = si_buffer[i];
- u8 cmd = send_data[0];
+ const auto cmd = static_cast(send_data[0]);
sf::Socket::Status status;
- if (cmd == CMD_WRITE)
+ if (cmd == EBufferCommands::CMD_WRITE_GBA)
status = m_client->send(send_data.data(), send_data.size());
else
status = m_client->send(send_data.data(), 1);
@@ -334,7 +326,7 @@ int CSIDevice_GBA::RunBuffer(u8* buffer, int request_length)
return -1;
}
- m_last_cmd = buffer[0];
+ m_last_cmd = static_cast(buffer[0]);
m_timestamp_sent = CoreTiming::GetTicks();
m_next_action = NextAction::WaitTransferTime;
return 0;
@@ -355,11 +347,11 @@ int CSIDevice_GBA::RunBuffer(u8* buffer, int request_length)
u8 bytes = 1;
switch (m_last_cmd)
{
- case CMD_RESET:
- case CMD_STATUS:
+ case EBufferCommands::CMD_RESET:
+ case EBufferCommands::CMD_STATUS:
bytes = 3;
break;
- case CMD_READ:
+ case EBufferCommands::CMD_READ_GBA:
bytes = 5;
break;
default:
@@ -372,8 +364,9 @@ int CSIDevice_GBA::RunBuffer(u8* buffer, int request_length)
return -1;
#ifdef _DEBUG
const Common::Log::LOG_LEVELS log_level =
- (m_last_cmd == CMD_STATUS || m_last_cmd == CMD_RESET) ? Common::Log::LERROR :
- Common::Log::LWARNING;
+ (m_last_cmd == EBufferCommands::CMD_STATUS || m_last_cmd == EBufferCommands::CMD_RESET) ?
+ Common::Log::LERROR :
+ Common::Log::LWARNING;
GENERIC_LOG_FMT(Common::Log::SERIALINTERFACE, log_level,
"{} [< {:02x}{:02x}{:02x}{:02x}{:02x}] ({})",
m_device_number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBA.h b/Source/Core/Core/HW/SI/SI_DeviceGBA.h
index dccbf2235e..663c4c9b1f 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGBA.h
+++ b/Source/Core/Core/HW/SI/SI_DeviceGBA.h
@@ -60,7 +60,7 @@ private:
GBASockServer m_sock_server;
NextAction m_next_action = NextAction::SendCommand;
- u8 m_last_cmd;
+ EBufferCommands m_last_cmd;
u64 m_timestamp_sent = 0;
};
} // namespace SerialInterface
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp
index 8c485f1a9f..985033f97b 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.cpp
@@ -45,20 +45,20 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length)
return -1;
// Read the command
- EBufferCommands command = static_cast(buffer[0]);
+ const auto command = static_cast(buffer[0]);
// Handle it
switch (command)
{
- case CMD_RESET:
- case CMD_ID:
+ case EBufferCommands::CMD_STATUS:
+ case EBufferCommands::CMD_RESET:
{
u32 id = Common::swap32(SI_GC_CONTROLLER);
std::memcpy(buffer, &id, sizeof(id));
return sizeof(id);
}
- case CMD_DIRECT:
+ case EBufferCommands::CMD_DIRECT:
{
INFO_LOG_FMT(SERIALINTERFACE, "PAD - Direct (Request length: {})", request_length);
u32 high, low;
@@ -71,7 +71,7 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length)
return sizeof(high) + sizeof(low);
}
- case CMD_ORIGIN:
+ case EBufferCommands::CMD_ORIGIN:
{
INFO_LOG_FMT(SERIALINTERFACE, "PAD - Get Origin");
@@ -84,7 +84,7 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length)
}
// Recalibrate (FiRES: i am not 100 percent sure about this)
- case CMD_RECALIBRATE:
+ case EBufferCommands::CMD_RECALIBRATE:
{
INFO_LOG_FMT(SERIALINTERFACE, "PAD - Recalibrate");
@@ -290,13 +290,7 @@ void CSIDevice_GCController::SendCommand(u32 command, u8 poll)
{
UCommand controller_command(command);
- switch (controller_command.command)
- {
- // Costis sent it in some demos :)
- case 0x00:
- break;
-
- case CMD_WRITE:
+ if (static_cast(controller_command.command) == EDirectCommands::CMD_WRITE)
{
const u32 type = controller_command.parameter1; // 0 = stop, 1 = rumble, 2 = stop hard
@@ -317,15 +311,12 @@ void CSIDevice_GCController::SendCommand(u32 command, u8 poll)
INFO_LOG_FMT(SERIALINTERFACE, "PAD {} set to mode {}", m_device_number, m_mode);
}
}
- break;
-
- default:
+ else if (controller_command.command != 0x00)
{
+ // Costis sent 0x00 in some demos :)
ERROR_LOG_FMT(SERIALINTERFACE, "Unknown direct command ({:#x})", command);
PanicAlertFmt("SI: Unknown direct command");
}
- break;
- }
}
// Savestate support
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.h b/Source/Core/Core/HW/SI/SI_DeviceGCController.h
index 9904e855bb..6d7f4722af 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGCController.h
+++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.h
@@ -12,16 +12,6 @@ namespace SerialInterface
class CSIDevice_GCController : public ISIDevice
{
protected:
- // Commands
- enum EBufferCommands
- {
- CMD_RESET = 0x00,
- CMD_DIRECT = 0x40,
- CMD_ORIGIN = 0x41,
- CMD_RECALIBRATE = 0x42,
- CMD_ID = 0xff,
- };
-
struct SOrigin
{
u16 button;
@@ -35,25 +25,6 @@ protected:
u8 unk_5;
};
- enum EDirectCommands
- {
- CMD_WRITE = 0x40
- };
-
- union UCommand
- {
- u32 hex = 0;
- struct
- {
- u32 parameter1 : 8;
- u32 parameter2 : 8;
- u32 command : 8;
- u32 : 8;
- };
- UCommand() = default;
- UCommand(u32 value) : hex{value} {}
- };
-
enum EButtonCombo
{
COMBO_NONE = 0,
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.cpp b/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.cpp
index a801296267..212c08d52b 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.cpp
@@ -25,13 +25,13 @@ int CSIDevice_GCSteeringWheel::RunBuffer(u8* buffer, int request_length)
ISIDevice::RunBuffer(buffer, request_length);
// Read the command
- EBufferCommands command = static_cast(buffer[0]);
+ const auto command = static_cast(buffer[0]);
// Handle it
switch (command)
{
- case CMD_RESET:
- case CMD_ID:
+ case EBufferCommands::CMD_STATUS:
+ case EBufferCommands::CMD_RESET:
{
u32 id = Common::swap32(SI_GC_STEERING);
std::memcpy(buffer, &id, sizeof(id));
@@ -101,7 +101,7 @@ void CSIDevice_GCSteeringWheel::SendCommand(u32 command, u8 poll)
{
UCommand wheel_command(command);
- if (wheel_command.command == CMD_FORCE)
+ if (static_cast(wheel_command.command) == EDirectCommands::CMD_FORCE)
{
// get the correct pad number that should rumble locally when using netplay
const int pad_num = NetPlay_InGamePadToLocalPad(m_device_number);
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.h b/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.h
index 37b0cfff3a..4de13d142d 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.h
+++ b/Source/Core/Core/HW/SI/SI_DeviceGCSteeringWheel.h
@@ -17,21 +17,6 @@ public:
void SendCommand(u32 command, u8 poll) override;
private:
- // Commands
- enum EBufferCommands
- {
- CMD_RESET = 0x00,
- CMD_ORIGIN = 0x41,
- CMD_RECALIBRATE = 0x42,
- CMD_ID = 0xff,
- };
-
- enum EDirectCommands
- {
- CMD_FORCE = 0x30,
- CMD_WRITE = 0x40
- };
-
enum class ForceCommandType : u8
{
MotorOn = 0x03,
diff --git a/Source/Core/Core/HW/SI/SI_DeviceKeyboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceKeyboard.cpp
index 8c5092adc7..13f067dc59 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceKeyboard.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceKeyboard.cpp
@@ -31,15 +31,15 @@ int CSIDevice_Keyboard::RunBuffer(u8* buffer, int request_length)
// Handle it
switch (command)
{
- case CMD_RESET:
- case CMD_ID:
+ case EBufferCommands::CMD_STATUS:
+ case EBufferCommands::CMD_RESET:
{
u32 id = Common::swap32(SI_GC_KEYBOARD);
std::memcpy(buffer, &id, sizeof(id));
return sizeof(id);
}
- case CMD_DIRECT:
+ case EBufferCommands::CMD_DIRECT_KB:
{
INFO_LOG_FMT(SERIALINTERFACE, "Keyboard - Direct (Request Length: {})", request_length);
u32 high, low;
@@ -84,23 +84,15 @@ void CSIDevice_Keyboard::SendCommand(u32 command, u8 poll)
{
UCommand keyboard_command(command);
- switch (keyboard_command.command)
- {
- case 0x00:
- break;
-
- case CMD_POLL:
+ if (static_cast(keyboard_command.command) == EDirectCommands::CMD_POLL)
{
m_counter++;
m_counter &= 15;
}
- break;
- default:
+ else if (keyboard_command.command != 0x00)
{
ERROR_LOG_FMT(SERIALINTERFACE, "Unknown direct command ({:#x})", command);
}
- break;
- }
}
void CSIDevice_Keyboard::DoState(PointerWrap& p)
diff --git a/Source/Core/Core/HW/SI/SI_DeviceKeyboard.h b/Source/Core/Core/HW/SI/SI_DeviceKeyboard.h
index beb34f0960..aebc63afa0 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceKeyboard.h
+++ b/Source/Core/Core/HW/SI/SI_DeviceKeyboard.h
@@ -32,34 +32,6 @@ public:
void DoState(PointerWrap& p) override;
protected:
- // Commands
- enum EBufferCommands
- {
- CMD_RESET = 0x00,
- CMD_DIRECT = 0x54,
- CMD_ID = 0xff,
- };
-
- enum EDirectCommands
- {
- CMD_WRITE = 0x40,
- CMD_POLL = 0x54
- };
-
- union UCommand
- {
- u32 hex = 0;
- struct
- {
- u32 parameter1 : 8;
- u32 parameter2 : 8;
- u32 command : 8;
- u32 : 8;
- };
- UCommand() = default;
- UCommand(u32 value) : hex{value} {}
- };
-
// PADAnalogMode
u8 m_mode = 0;
From d849d5669509eacae4a1d9fd995af77b53159df7 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 13:03:56 +0200
Subject: [PATCH 10/18] SI/DeviceGBA: Expose GetTransferTime
---
Source/Core/Core/HW/SI/SI_Device.cpp | 44 +++++++++++++++++++++++
Source/Core/Core/HW/SI/SI_Device.h | 1 +
Source/Core/Core/HW/SI/SI_DeviceGBA.cpp | 46 ++-----------------------
3 files changed, 47 insertions(+), 44 deletions(-)
diff --git a/Source/Core/Core/HW/SI/SI_Device.cpp b/Source/Core/Core/HW/SI/SI_Device.cpp
index 5717fe10a9..401b1178a7 100644
--- a/Source/Core/Core/HW/SI/SI_Device.cpp
+++ b/Source/Core/Core/HW/SI/SI_Device.cpp
@@ -20,9 +20,15 @@
#include "Core/HW/SI/SI_DeviceGCSteeringWheel.h"
#include "Core/HW/SI/SI_DeviceKeyboard.h"
#include "Core/HW/SI/SI_DeviceNull.h"
+#include "Core/HW/SystemTimers.h"
namespace SerialInterface
{
+constexpr u64 GC_BITS_PER_SECOND = 200000;
+constexpr u64 GBA_BITS_PER_SECOND = 250000;
+constexpr u64 GC_STOP_BIT_NS = 6500;
+constexpr u64 GBA_STOP_BIT_NS = 14000;
+
std::ostream& operator<<(std::ostream& stream, SIDevices device)
{
stream << static_cast>(device);
@@ -101,6 +107,44 @@ void ISIDevice::OnEvent(u64 userdata, s64 cycles_late)
{
}
+int SIDevice_GetGBATransferTime(EBufferCommands cmd)
+{
+ u64 gc_bytes_transferred = 1;
+ u64 gba_bytes_transferred = 1;
+ u64 stop_bits_ns = GC_STOP_BIT_NS + GBA_STOP_BIT_NS;
+
+ switch (cmd)
+ {
+ case EBufferCommands::CMD_RESET:
+ case EBufferCommands::CMD_STATUS:
+ {
+ gba_bytes_transferred = 3;
+ break;
+ }
+ case EBufferCommands::CMD_READ_GBA:
+ {
+ gba_bytes_transferred = 5;
+ break;
+ }
+ case EBufferCommands::CMD_WRITE_GBA:
+ {
+ gc_bytes_transferred = 5;
+ break;
+ }
+ default:
+ {
+ gba_bytes_transferred = 0;
+ break;
+ }
+ }
+
+ u64 cycles =
+ (gba_bytes_transferred * 8 * SystemTimers::GetTicksPerSecond() / GBA_BITS_PER_SECOND) +
+ (gc_bytes_transferred * 8 * SystemTimers::GetTicksPerSecond() / GC_BITS_PER_SECOND) +
+ (stop_bits_ns * SystemTimers::GetTicksPerSecond() / 1000000000LL);
+ return static_cast(cycles);
+}
+
// Check if a device class is inheriting from CSIDevice_GCController
// The goal of this function is to avoid special casing a long list of
// device types when there is no "real" input device, e.g. when playing
diff --git a/Source/Core/Core/HW/SI/SI_Device.h b/Source/Core/Core/HW/SI/SI_Device.h
index 14bd83201c..c2ae9bef87 100644
--- a/Source/Core/Core/HW/SI/SI_Device.h
+++ b/Source/Core/Core/HW/SI/SI_Device.h
@@ -130,6 +130,7 @@ protected:
SIDevices m_device_type;
};
+int SIDevice_GetGBATransferTime(EBufferCommands cmd);
bool SIDevice_IsGCController(SIDevices type);
std::unique_ptr SIDevice_Create(SIDevices device, int port_number);
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp b/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp
index 57679bbe50..95ab599a8f 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceGBA.cpp
@@ -34,52 +34,10 @@ int s_num_connected;
Common::Flag s_server_running;
} // namespace
-constexpr auto GC_BITS_PER_SECOND = 200000;
-constexpr auto GBA_BITS_PER_SECOND = 250000;
-constexpr auto GC_STOP_BIT_NS = 6500;
-constexpr auto GBA_STOP_BIT_NS = 14000;
constexpr auto SEND_MAX_SIZE = 5, RECV_MAX_SIZE = 5;
// --- GameBoy Advance "Link Cable" ---
-static int GetTransferTime(EBufferCommands cmd)
-{
- u64 gc_bytes_transferred = 1;
- u64 gba_bytes_transferred = 1;
- u64 stop_bits_ns = GC_STOP_BIT_NS + GBA_STOP_BIT_NS;
-
- switch (cmd)
- {
- case EBufferCommands::CMD_RESET:
- case EBufferCommands::CMD_STATUS:
- {
- gba_bytes_transferred = 3;
- break;
- }
- case EBufferCommands::CMD_READ_GBA:
- {
- gba_bytes_transferred = 5;
- break;
- }
- case EBufferCommands::CMD_WRITE_GBA:
- {
- gc_bytes_transferred = 5;
- break;
- }
- default:
- {
- gba_bytes_transferred = 0;
- break;
- }
- }
-
- u64 cycles =
- (gba_bytes_transferred * 8 * SystemTimers::GetTicksPerSecond() / GBA_BITS_PER_SECOND) +
- (gc_bytes_transferred * 8 * SystemTimers::GetTicksPerSecond() / GC_BITS_PER_SECOND) +
- (stop_bits_ns * SystemTimers::GetTicksPerSecond() / 1000000000LL);
- return static_cast(cycles);
-}
-
static void GBAConnectionWaiter()
{
s_server_running.Set();
@@ -336,7 +294,7 @@ int CSIDevice_GBA::RunBuffer(u8* buffer, int request_length)
{
int elapsed_time = static_cast(CoreTiming::GetTicks() - m_timestamp_sent);
// Tell SI to ask again after TransferInterval() cycles
- if (GetTransferTime(m_last_cmd) > elapsed_time)
+ if (SIDevice_GetGBATransferTime(m_last_cmd) > elapsed_time)
return 0;
m_next_action = NextAction::ReceiveResponse;
[[fallthrough]];
@@ -383,7 +341,7 @@ int CSIDevice_GBA::RunBuffer(u8* buffer, int request_length)
int CSIDevice_GBA::TransferInterval()
{
- return GetTransferTime(m_last_cmd);
+ return SIDevice_GetGBATransferTime(m_last_cmd);
}
bool CSIDevice_GBA::GetData(u32& hi, u32& low)
From 2d744da68c31814ee3d5d3696aaa4d6bf5955a21 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 13:09:46 +0200
Subject: [PATCH 11/18] Core: Add GBA host interface
---
Source/Android/jni/MainAndroid.cpp | 5 +++++
Source/Core/Core/Host.h | 18 ++++++++++++++++++
Source/Core/DolphinNoGUI/MainNoGUI.cpp | 5 +++++
Source/Core/DolphinQt/Host.cpp | 5 +++++
Source/DSPTool/StubHost.cpp | 4 ++++
Source/UnitTests/StubHost.cpp | 4 ++++
6 files changed, 41 insertions(+)
diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp
index e1066f1584..765bab1bcb 100644
--- a/Source/Android/jni/MainAndroid.cpp
+++ b/Source/Android/jni/MainAndroid.cpp
@@ -173,6 +173,11 @@ void Host_TitleChanged()
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetOnTitleChanged());
}
+std::unique_ptr Host_CreateGBAHost(std::weak_ptr core)
+{
+ return nullptr;
+}
+
static bool MsgAlert(const char* caption, const char* text, bool yes_no, Common::MsgType style)
{
// If a panic alert happens very early in the execution of a game, we can crash here with
diff --git a/Source/Core/Core/Host.h b/Source/Core/Core/Host.h
index a66e36b360..dad850a5b2 100644
--- a/Source/Core/Core/Host.h
+++ b/Source/Core/Core/Host.h
@@ -3,9 +3,12 @@
#pragma once
+#include
#include
#include
+#include "Common/CommonTypes.h"
+
// Host - defines an interface for the emulator core to communicate back to the
// OS-specific layer
//
@@ -23,6 +26,19 @@
// The host can be just a command line app that opens a window, or a full blown debugger
// interface.
+namespace HW::GBA
+{
+class Core;
+} // namespace HW::GBA
+
+class GBAHostInterface
+{
+public:
+ virtual ~GBAHostInterface() = default;
+ virtual void GameChanged() = 0;
+ virtual void FrameEnded(const std::vector& video_buffer) = 0;
+};
+
enum class HostMessageID
{
// Begin at 10 in case there is already messages with wParam = 0, 1, 2 and so on
@@ -47,3 +63,5 @@ void Host_UpdateMainFrame();
void Host_UpdateTitle(const std::string& title);
void Host_YieldToUI();
void Host_TitleChanged();
+
+std::unique_ptr Host_CreateGBAHost(std::weak_ptr core);
diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp
index 31ddb591bb..26b0e69a22 100644
--- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp
+++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp
@@ -119,6 +119,11 @@ void Host_TitleChanged()
#endif
}
+std::unique_ptr Host_CreateGBAHost(std::weak_ptr core)
+{
+ return nullptr;
+}
+
static std::unique_ptr GetPlatform(const optparse::Values& options)
{
std::string platform_name = static_cast(options.get("platform"));
diff --git a/Source/Core/DolphinQt/Host.cpp b/Source/Core/DolphinQt/Host.cpp
index 29215f5bbe..61cbc7036d 100644
--- a/Source/Core/DolphinQt/Host.cpp
+++ b/Source/Core/DolphinQt/Host.cpp
@@ -229,3 +229,8 @@ void Host_TitleChanged()
Discord::UpdateDiscordPresence();
#endif
}
+
+std::unique_ptr Host_CreateGBAHost(std::weak_ptr core)
+{
+ return nullptr;
+}
diff --git a/Source/DSPTool/StubHost.cpp b/Source/DSPTool/StubHost.cpp
index c782978fc7..de20be198f 100644
--- a/Source/DSPTool/StubHost.cpp
+++ b/Source/DSPTool/StubHost.cpp
@@ -52,6 +52,10 @@ void Host_YieldToUI()
void Host_TitleChanged()
{
}
+std::unique_ptr Host_CreateGBAHost(std::weak_ptr core)
+{
+ return nullptr;
+}
bool Host_UIBlocksControllerState()
{
return false;
diff --git a/Source/UnitTests/StubHost.cpp b/Source/UnitTests/StubHost.cpp
index ebb0915f31..233af18f79 100644
--- a/Source/UnitTests/StubHost.cpp
+++ b/Source/UnitTests/StubHost.cpp
@@ -56,3 +56,7 @@ void Host_YieldToUI()
void Host_TitleChanged()
{
}
+std::unique_ptr Host_CreateGBAHost(std::weak_ptr core)
+{
+ return nullptr;
+}
From 9a22ff653f089057f6990ea32dfd33d96a158698 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 13:13:21 +0200
Subject: [PATCH 12/18] Core: Implement GBA Core using libmgba
---
Source/Core/Core/CMakeLists.txt | 8 +-
Source/Core/Core/HW/GBACore.cpp | 714 ++++++++++++++++++++++++++++++++
Source/Core/Core/HW/GBACore.h | 129 ++++++
Source/Core/DolphinLib.props | 2 +
4 files changed, 851 insertions(+), 2 deletions(-)
create mode 100644 Source/Core/Core/HW/GBACore.cpp
create mode 100644 Source/Core/Core/HW/GBACore.h
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 6de7e6dbe2..65eb857982 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -440,8 +440,8 @@ add_library(core
PowerPC/Interpreter/Interpreter_Tables.cpp
PowerPC/Interpreter/Interpreter.cpp
PowerPC/Interpreter/Interpreter.h
- PowerPC/JitCommon/DivUtils.cpp
- PowerPC/JitCommon/DivUtils.h
+ PowerPC/JitCommon/DivUtils.cpp
+ PowerPC/JitCommon/DivUtils.h
PowerPC/JitCommon/JitAsmCommon.cpp
PowerPC/JitCommon/JitAsmCommon.h
PowerPC/JitCommon/JitBase.cpp
@@ -621,6 +621,10 @@ if(ENABLE_VULKAN)
endif()
if(USE_MGBA)
+ target_sources(core PRIVATE
+ HW/GBACore.cpp
+ HW/GBACore.h
+ )
target_link_libraries(core PUBLIC mGBA::mgba)
target_compile_definitions(core PUBLIC -DHAS_LIBMGBA)
endif()
diff --git a/Source/Core/Core/HW/GBACore.cpp b/Source/Core/Core/HW/GBACore.cpp
new file mode 100644
index 0000000000..38ef42f1ba
--- /dev/null
+++ b/Source/Core/Core/HW/GBACore.cpp
@@ -0,0 +1,714 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "Core/HW/GBACore.h"
+
+#include
+
+#define PYCPARSE // Remove static functions from the header
+#include
+#undef PYCPARSE
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "AudioCommon/AudioCommon.h"
+#include "Common/ChunkFile.h"
+#include "Common/CommonPaths.h"
+#include "Common/CommonTypes.h"
+#include "Common/Config/Config.h"
+#include "Common/FileUtil.h"
+#include "Common/IOFile.h"
+#include "Common/MinizipUtil.h"
+#include "Common/ScopeGuard.h"
+#include "Common/Thread.h"
+#include "Core/Config/MainSettings.h"
+#include "Core/ConfigManager.h"
+#include "Core/Core.h"
+#include "Core/HW/SystemTimers.h"
+#include "Core/Host.h"
+#include "Core/NetPlayProto.h"
+
+namespace HW::GBA
+{
+namespace
+{
+mLogger s_stub_logger = {
+ [](mLogger*, int category, mLogLevel level, const char* format, va_list args) {}, nullptr};
+} // namespace
+
+constexpr auto SAMPLES = 512;
+constexpr auto SAMPLE_RATE = 48000;
+
+// libmGBA does not return the correct frequency for some GB models
+static u32 GetCoreFrequency(mCore* core)
+{
+ if (core->platform(core) != mPLATFORM_GB)
+ return static_cast(core->frequency(core));
+
+ switch (static_cast<::GB*>(core->board)->model)
+ {
+ case GB_MODEL_CGB:
+ case GB_MODEL_SCGB:
+ case GB_MODEL_AGB:
+ return CGB_SM83_FREQUENCY;
+ case GB_MODEL_SGB:
+ return SGB_SM83_FREQUENCY;
+ default:
+ return DMG_SM83_FREQUENCY;
+ }
+}
+
+static VFile* OpenROM_Archive(const char* path)
+{
+ VFile* vf{};
+ VDir* archive = VDirOpenArchive(path);
+ if (!archive)
+ return nullptr;
+ VFile* vf_archive =
+ VDirFindFirst(archive, [](VFile* vf_) { return mCoreIsCompatible(vf_) != mPLATFORM_NONE; });
+ if (vf_archive)
+ {
+ size_t size = static_cast(vf_archive->size(vf_archive));
+
+ std::vector buffer(size);
+ vf_archive->seek(vf_archive, 0, SEEK_SET);
+ vf_archive->read(vf_archive, buffer.data(), size);
+ vf_archive->close(vf_archive);
+
+ vf = VFileMemChunk(buffer.data(), size);
+ }
+ archive->close(archive);
+ return vf;
+}
+
+static VFile* OpenROM_Zip(const char* path)
+{
+ VFile* vf{};
+ unzFile zip = unzOpen(path);
+ if (!zip)
+ return nullptr;
+ do
+ {
+ unz_file_info info{};
+ if (unzGetCurrentFileInfo(zip, &info, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK ||
+ !info.uncompressed_size)
+ continue;
+
+ std::vector buffer(info.uncompressed_size);
+ if (!Common::ReadFileFromZip(zip, &buffer))
+ continue;
+
+ vf = VFileMemChunk(buffer.data(), info.uncompressed_size);
+ if (mCoreIsCompatible(vf) == mPLATFORM_GBA)
+ {
+ vf->seek(vf, 0, SEEK_SET);
+ break;
+ }
+
+ vf->close(vf);
+ vf = nullptr;
+ } while (unzGoToNextFile(zip) == UNZ_OK);
+ unzClose(zip);
+ return vf;
+}
+
+static VFile* OpenROM(const char* rom_path)
+{
+ VFile* vf{};
+
+ vf = OpenROM_Archive(rom_path);
+ if (!vf)
+ vf = OpenROM_Zip(rom_path);
+ if (!vf)
+ vf = VFileOpen(rom_path, O_RDONLY);
+ if (!vf)
+ return nullptr;
+
+ if (mCoreIsCompatible(vf) == mPLATFORM_NONE)
+ {
+ vf->close(vf);
+ return nullptr;
+ }
+ vf->seek(vf, 0, SEEK_SET);
+
+ return vf;
+}
+
+static std::array GetROMHash(VFile* rom)
+{
+ size_t size = rom->size(rom);
+ u8* buffer = static_cast(rom->map(rom, size, MAP_READ));
+
+ std::array hash;
+ mbedtls_sha1_ret(buffer, size, hash.data());
+ rom->unmap(rom, buffer, size);
+
+ return hash;
+}
+
+Core::Core(int device_number) : m_device_number(device_number)
+{
+ mLogSetDefaultLogger(&s_stub_logger);
+}
+
+Core::~Core()
+{
+ Stop();
+}
+
+bool Core::Start(u64 gc_ticks)
+{
+ if (IsStarted())
+ return false;
+
+ Common::ScopeGuard start_guard{[&] { Stop(); }};
+
+ VFile* rom{};
+ Common::ScopeGuard rom_guard{[&] {
+ if (rom)
+ rom->close(rom);
+ }};
+
+ m_rom_path = Config::Get(Config::MAIN_GBA_ROM_PATHS[m_device_number]);
+ if (!m_rom_path.empty())
+ {
+ rom = OpenROM(m_rom_path.c_str());
+ if (!rom)
+ {
+ PanicAlertFmtT("Error: GBA{0} failed to open the ROM in {1}", m_device_number + 1,
+ m_rom_path);
+ return false;
+ }
+ m_rom_hash = GetROMHash(rom);
+ }
+
+ m_core = rom ? mCoreFindVF(rom) : mCoreCreate(mPLATFORM_GBA);
+ if (!m_core)
+ {
+ PanicAlertFmtT("Error: GBA{0} failed to create core", m_device_number + 1);
+ return false;
+ }
+ m_core->init(m_core);
+
+ mCoreInitConfig(m_core, "dolphin");
+ mCoreConfigSetValue(&m_core->config, "idleOptimization", "detect");
+ mCoreConfigSetIntValue(&m_core->config, "useBios", 0);
+ mCoreConfigSetIntValue(&m_core->config, "skipBios", 0);
+
+ if (m_core->platform(m_core) == mPLATFORM_GBA &&
+ !LoadBIOS(File::GetUserPath(F_GBABIOS_IDX).c_str()))
+ {
+ return false;
+ }
+
+ if (rom)
+ {
+ if (!m_core->loadROM(m_core, rom))
+ {
+ PanicAlertFmtT("Error: GBA{0} failed to load the ROM in {1}", m_device_number + 1,
+ m_rom_path);
+ return false;
+ }
+ rom_guard.Dismiss();
+
+ std::array game_title{};
+ m_core->getGameTitle(m_core, game_title.data());
+ m_game_title = game_title.data();
+
+ m_save_path = GetSavePath(m_rom_path, m_device_number);
+ if (!m_save_path.empty() && !LoadSave(m_save_path.c_str()))
+ return false;
+ }
+
+ m_last_gc_ticks = gc_ticks;
+ m_gc_ticks_remainder = 0;
+ m_keys = 0;
+
+ SetSIODriver();
+ SetVideoBuffer();
+ SetSampleRates();
+ AddCallbacks();
+ SetAVStream();
+ SetupEvent();
+
+ m_core->reset(m_core);
+ m_started = true;
+ start_guard.Dismiss();
+ // Notify the host and handle a dimension change if that happened after reset()
+ SetVideoBuffer();
+
+ if (Config::Get(Config::MAIN_GBA_THREADS))
+ {
+ m_idle = true;
+ m_exit_loop = false;
+ m_thread = std::make_unique([this] { ThreadLoop(); });
+ }
+
+ return true;
+}
+
+void Core::Stop()
+{
+ if (m_thread)
+ {
+ Flush();
+ m_exit_loop = true;
+ {
+ std::lock_guard lock(m_queue_mutex);
+ m_command_cv.notify_one();
+ }
+ m_thread->join();
+ m_thread.reset();
+ }
+ if (m_core)
+ {
+ mCoreConfigDeinit(&m_core->config);
+ m_core->deinit(m_core);
+ m_core = nullptr;
+ }
+ m_started = false;
+ m_rom_path = {};
+ m_save_path = {};
+ m_rom_hash = {};
+ m_game_title = {};
+}
+
+void Core::Reset()
+{
+ Flush();
+ if (!IsStarted())
+ return;
+
+ m_core->reset(m_core);
+}
+
+bool Core::IsStarted() const
+{
+ return m_started;
+}
+
+void Core::SetHost(std::weak_ptr host)
+{
+ m_host = std::move(host);
+}
+
+void Core::SetForceDisconnect(bool force_disconnect)
+{
+ m_force_disconnect = force_disconnect;
+}
+
+void Core::EReaderQueueCard(std::string_view card_path)
+{
+ Flush();
+ if (!IsStarted() || m_core->platform(m_core) != mPlatform::mPLATFORM_GBA)
+ return;
+
+ File::IOFile file(std::string(card_path), "rb");
+ std::vector core_state(file.GetSize());
+ file.ReadBytes(core_state.data(), core_state.size());
+ GBACartEReaderQueueCard(static_cast<::GBA*>(m_core->board), core_state.data(), core_state.size());
+}
+
+bool Core::LoadBIOS(const char* bios_path)
+{
+ VFile* vf = VFileOpen(bios_path, O_RDONLY);
+ if (!vf)
+ {
+ PanicAlertFmtT("Error: GBA{0} failed to open the BIOS in {1}", m_device_number + 1, bios_path);
+ return false;
+ }
+
+ if (!m_core->loadBIOS(m_core, vf, 0))
+ {
+ PanicAlertFmtT("Error: GBA{0} failed to load the BIOS in {1}", m_device_number + 1, bios_path);
+ vf->close(vf);
+ return false;
+ }
+
+ return true;
+}
+
+bool Core::LoadSave(const char* save_path)
+{
+ VFile* vf = VFileOpen(save_path, O_CREAT | O_RDWR);
+ if (!vf)
+ {
+ PanicAlertFmtT("Error: GBA{0} failed to open the save in {1}", m_device_number + 1, save_path);
+ return false;
+ }
+
+ if (!m_core->loadSave(m_core, vf))
+ {
+ PanicAlertFmtT("Error: GBA{0} failed to load the save in {1}", m_device_number + 1, save_path);
+ vf->close(vf);
+ return false;
+ }
+
+ return true;
+}
+
+void Core::SetSIODriver()
+{
+ if (m_core->platform(m_core) != mPLATFORM_GBA)
+ return;
+
+ GBASIOJOYCreate(&m_sio_driver);
+ GBASIOSetDriver(&static_cast<::GBA*>(m_core->board)->sio, &m_sio_driver, SIO_JOYBUS);
+
+ m_sio_driver.core = this;
+ m_sio_driver.load = [](GBASIODriver* driver) {
+ static_cast(driver)->core->m_link_enabled = true;
+ return true;
+ };
+ m_sio_driver.unload = [](GBASIODriver* driver) {
+ static_cast(driver)->core->m_link_enabled = false;
+ return true;
+ };
+}
+
+void Core::SetVideoBuffer()
+{
+ u32 width, height;
+ m_core->desiredVideoDimensions(m_core, &width, &height);
+ m_video_buffer.resize(width * height);
+ m_core->setVideoBuffer(m_core, m_video_buffer.data(), width);
+ if (auto host = m_host.lock())
+ host->GameChanged();
+}
+
+void Core::SetSampleRates()
+{
+ m_core->setAudioBufferSize(m_core, SAMPLES);
+ blip_set_rates(m_core->getAudioChannel(m_core, 0), m_core->frequency(m_core), SAMPLE_RATE);
+ blip_set_rates(m_core->getAudioChannel(m_core, 1), m_core->frequency(m_core), SAMPLE_RATE);
+ g_sound_stream->GetMixer()->SetGBAInputSampleRates(m_device_number, SAMPLE_RATE);
+}
+
+void Core::AddCallbacks()
+{
+ mCoreCallbacks callbacks{};
+ callbacks.context = this;
+ callbacks.keysRead = [](void* context) {
+ auto core = static_cast(context);
+ core->m_core->setKeys(core->m_core, core->m_keys);
+ };
+ callbacks.videoFrameEnded = [](void* context) {
+ auto core = static_cast(context);
+ if (auto host = core->m_host.lock())
+ host->FrameEnded(core->m_video_buffer);
+ };
+ m_core->addCoreCallbacks(m_core, &callbacks);
+}
+
+void Core::SetAVStream()
+{
+ m_stream = {};
+ m_stream.core = this;
+ m_stream.videoDimensionsChanged = [](mAVStream* stream, unsigned width, unsigned height) {
+ auto core = static_cast(stream)->core;
+ core->SetVideoBuffer();
+ };
+ m_stream.postAudioBuffer = [](mAVStream* stream, blip_t* left, blip_t* right) {
+ auto core = static_cast(stream)->core;
+ std::vector buffer(SAMPLES * 2);
+ blip_read_samples(left, &buffer[0], SAMPLES, 1);
+ blip_read_samples(right, &buffer[1], SAMPLES, 1);
+ g_sound_stream->GetMixer()->PushGBASamples(core->m_device_number, &buffer[0], SAMPLES);
+ };
+ m_core->setAVStream(m_core, &m_stream);
+}
+
+void Core::SetupEvent()
+{
+ m_event.context = this;
+ m_event.name = "Dolphin Sync";
+ m_event.callback = [](mTiming* timing, void* context, u32 cycles_late) {
+ Core* core = static_cast(context);
+ if (core->m_core->platform(core->m_core) == mPLATFORM_GBA)
+ static_cast<::GBA*>(core->m_core->board)->earlyExit = true;
+ else if (core->m_core->platform(core->m_core) == mPLATFORM_GB)
+ static_cast<::GB*>(core->m_core->board)->earlyExit = true;
+ core->m_waiting_for_event = false;
+ };
+ m_event.priority = 0x80;
+}
+
+int Core::GetDeviceNumber() const
+{
+ return m_device_number;
+}
+
+void Core::GetVideoDimensions(u32* width, u32* height) const
+{
+ if (!IsStarted())
+ {
+ *width = GBA_VIDEO_HORIZONTAL_PIXELS;
+ *height = GBA_VIDEO_VERTICAL_PIXELS;
+ return;
+ }
+
+ m_core->desiredVideoDimensions(m_core, width, height);
+}
+
+std::string Core::GetGameTitle() const
+{
+ return m_game_title;
+}
+
+void Core::SendJoybusCommand(u64 gc_ticks, int transfer_time, u8* buffer, u16 keys)
+{
+ if (!IsStarted())
+ return;
+
+ Command command{};
+ command.ticks = gc_ticks;
+ command.transfer_time = transfer_time;
+ command.sync_only = buffer == nullptr;
+ if (buffer)
+ std::copy_n(buffer, command.buffer.size(), command.buffer.begin());
+ command.keys = keys;
+
+ if (m_thread)
+ {
+ std::lock_guard lock(m_queue_mutex);
+ m_command_queue.push(command);
+ m_idle = false;
+ m_command_cv.notify_one();
+ }
+ else
+ {
+ RunCommand(command);
+ }
+}
+
+std::vector Core::GetJoybusResponse()
+{
+ if (!IsStarted())
+ return {};
+
+ if (m_thread)
+ {
+ std::unique_lock lock(m_response_mutex);
+ m_response_cv.wait(lock, [&] { return m_response_ready; });
+ }
+ m_response_ready = false;
+ return m_response;
+}
+
+void Core::Flush()
+{
+ if (!IsStarted() || !m_thread)
+ return;
+ std::unique_lock lock(m_queue_mutex);
+ m_response_cv.wait(lock, [&] { return m_idle; });
+}
+
+void Core::ThreadLoop()
+{
+ Common::SetCurrentThreadName(fmt::format("GBA{}", m_device_number + 1).c_str());
+ std::unique_lock queue_lock(m_queue_mutex);
+ while (true)
+ {
+ m_command_cv.wait(queue_lock, [&] { return !m_command_queue.empty() || m_exit_loop; });
+ if (m_exit_loop)
+ break;
+ Command command{m_command_queue.front()};
+ m_command_queue.pop();
+ queue_lock.unlock();
+
+ RunCommand(command);
+
+ queue_lock.lock();
+ if (m_command_queue.empty())
+ m_idle = true;
+ m_response_cv.notify_one();
+ }
+}
+
+void Core::RunCommand(Command& command)
+{
+ m_keys = command.keys;
+ RunUntil(command.ticks);
+ if (!command.sync_only)
+ {
+ m_response.clear();
+ if (m_link_enabled && !m_force_disconnect)
+ {
+ int recvd = GBASIOJOYSendCommand(
+ &m_sio_driver, static_cast(command.buffer[0]), &command.buffer[1]);
+ std::copy(command.buffer.begin() + 1, command.buffer.begin() + 1 + recvd,
+ std::back_inserter(m_response));
+ }
+
+ if (m_thread && !m_response_ready)
+ {
+ std::lock_guard response_lock(m_response_mutex);
+ m_response_ready = true;
+ m_response_cv.notify_one();
+ }
+ else
+ {
+ m_response_ready = true;
+ }
+ }
+ if (command.transfer_time)
+ RunFor(command.transfer_time);
+}
+
+void Core::RunUntil(u64 gc_ticks)
+{
+ if (static_cast(gc_ticks - m_last_gc_ticks) <= 0)
+ return;
+
+ const u64 gc_frequency = SystemTimers::GetTicksPerSecond();
+ const u32 core_frequency = GetCoreFrequency(m_core);
+
+ mTimingSchedule(m_core->timing, &m_event,
+ static_cast((gc_ticks - m_last_gc_ticks) * core_frequency / gc_frequency));
+ m_waiting_for_event = true;
+
+ s32 begin_time = mTimingCurrentTime(m_core->timing);
+ while (m_waiting_for_event)
+ m_core->runLoop(m_core);
+ s32 end_time = mTimingCurrentTime(m_core->timing);
+
+ u64 d = (static_cast(end_time - begin_time) * gc_frequency) + m_gc_ticks_remainder;
+ m_last_gc_ticks += d / core_frequency;
+ m_gc_ticks_remainder = d % core_frequency;
+}
+
+void Core::RunFor(u64 gc_ticks)
+{
+ RunUntil(m_last_gc_ticks + gc_ticks);
+}
+
+void Core::ImportState(std::string_view state_path)
+{
+ Flush();
+ if (!IsStarted())
+ return;
+
+ std::vector core_state(m_core->stateSize(m_core));
+ File::IOFile file(std::string(state_path), "rb");
+ if (core_state.size() != file.GetSize())
+ return;
+
+ file.ReadBytes(core_state.data(), core_state.size());
+ m_core->loadState(m_core, core_state.data());
+}
+
+void Core::ExportState(std::string_view state_path)
+{
+ Flush();
+ if (!IsStarted())
+ return;
+
+ std::vector core_state(m_core->stateSize(m_core));
+ m_core->saveState(m_core, core_state.data());
+
+ File::IOFile file(std::string(state_path), "wb");
+ file.WriteBytes(core_state.data(), core_state.size());
+}
+
+void Core::DoState(PointerWrap& p)
+{
+ Flush();
+ if (!IsStarted())
+ {
+ ::Core::DisplayMessage(fmt::format("GBA{} core not started. Aborting.", m_device_number + 1),
+ 3000);
+ p.SetMode(PointerWrap::MODE_VERIFY);
+ return;
+ }
+
+ bool has_rom = !m_rom_path.empty();
+ p.Do(has_rom);
+ auto old_hash = m_rom_hash;
+ p.Do(m_rom_hash);
+ auto old_title = m_game_title;
+ p.Do(m_game_title);
+
+ if (p.GetMode() == PointerWrap::MODE_READ &&
+ (has_rom != !m_rom_path.empty() ||
+ (has_rom && (old_hash != m_rom_hash || old_title != m_game_title))))
+ {
+ ::Core::DisplayMessage(
+ fmt::format("Incompatible ROM state in GBA{}. Aborting load state.", m_device_number + 1),
+ 3000);
+ p.SetMode(PointerWrap::MODE_VERIFY);
+ return;
+ }
+
+ p.Do(m_video_buffer);
+ p.Do(m_last_gc_ticks);
+ p.Do(m_gc_ticks_remainder);
+ p.Do(m_keys);
+ p.Do(m_link_enabled);
+ p.Do(m_response_ready);
+ p.Do(m_response);
+
+ std::vector core_state;
+ core_state.resize(m_core->stateSize(m_core));
+
+ if (p.GetMode() == PointerWrap::MODE_WRITE || p.GetMode() == PointerWrap::MODE_VERIFY)
+ {
+ m_core->saveState(m_core, core_state.data());
+ }
+
+ p.Do(core_state);
+
+ if (p.GetMode() == PointerWrap::MODE_READ && m_core->stateSize(m_core) == core_state.size())
+ {
+ m_core->loadState(m_core, core_state.data());
+ if (auto host = m_host.lock())
+ host->FrameEnded(m_video_buffer);
+ }
+}
+
+bool Core::GetRomInfo(const char* rom_path, std::array& hash, std::string& title)
+{
+ VFile* rom = OpenROM(rom_path);
+ if (!rom)
+ return false;
+
+ hash = GetROMHash(rom);
+
+ mCore* core = mCoreFindVF(rom);
+ if (!core)
+ {
+ rom->close(rom);
+ return false;
+ }
+ core->init(core);
+ if (!core->loadROM(core, rom))
+ {
+ rom->close(rom);
+ return false;
+ }
+
+ std::array game_title{};
+ core->getGameTitle(core, game_title.data());
+ title = game_title.data();
+
+ core->deinit(core);
+ return true;
+}
+
+std::string Core::GetSavePath(std::string_view rom_path, int device_number)
+{
+ std::string save_path =
+ fmt::format("{}-{}.sav", rom_path.substr(0, rom_path.find_last_of('.')), device_number + 1);
+
+ if (!Config::Get(Config::MAIN_GBA_SAVES_IN_ROM_PATH))
+ {
+ save_path =
+ File::GetUserPath(D_GBASAVES_IDX) + save_path.substr(save_path.find_last_of("\\/") + 1);
+ }
+
+ return save_path;
+}
+} // namespace HW::GBA
diff --git a/Source/Core/Core/HW/GBACore.h b/Source/Core/Core/HW/GBACore.h
new file mode 100644
index 0000000000..1a9f8c4222
--- /dev/null
+++ b/Source/Core/Core/HW/GBACore.h
@@ -0,0 +1,129 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define PYCPARSE // Remove static functions from the header
+#include
+#undef PYCPARSE
+#include
+#include
+
+#include "Common/CommonTypes.h"
+
+class GBAHostInterface;
+class PointerWrap;
+
+namespace HW::GBA
+{
+class Core;
+struct SIODriver : GBASIODriver
+{
+ Core* core;
+};
+struct AVStream : mAVStream
+{
+ Core* core;
+};
+
+class Core final
+{
+public:
+ explicit Core(int device_number);
+ ~Core();
+
+ bool Start(u64 gc_ticks);
+ void Stop();
+ void Reset();
+ bool IsStarted() const;
+
+ void SetHost(std::weak_ptr host);
+ void SetForceDisconnect(bool force_disconnect);
+ void EReaderQueueCard(std::string_view card_path);
+
+ int GetDeviceNumber() const;
+ void GetVideoDimensions(u32* width, u32* height) const;
+ std::string GetGameTitle() const;
+
+ void SendJoybusCommand(u64 gc_ticks, int transfer_time, u8* buffer, u16 keys);
+ std::vector GetJoybusResponse();
+
+ void ImportState(std::string_view state_path);
+ void ExportState(std::string_view state_path);
+ void DoState(PointerWrap& p);
+
+ static bool GetRomInfo(const char* rom_path, std::array& hash, std::string& title);
+ static std::string GetSavePath(std::string_view rom_path, int device_number);
+
+private:
+ void ThreadLoop();
+ void RunUntil(u64 gc_ticks);
+ void RunFor(u64 gc_ticks);
+ void Flush();
+
+ struct Command
+ {
+ u64 ticks;
+ int transfer_time;
+ bool sync_only;
+ std::array buffer;
+ u16 keys;
+ };
+ void RunCommand(Command& command);
+
+ bool LoadBIOS(const char* bios_path);
+ bool LoadSave(const char* save_path);
+
+ void SetSIODriver();
+ void SetVideoBuffer();
+ void SetSampleRates();
+ void AddCallbacks();
+ void SetAVStream();
+ void SetupEvent();
+
+ const int m_device_number;
+
+ bool m_started = false;
+ std::string m_rom_path;
+ std::string m_save_path;
+ std::array m_rom_hash{};
+ std::string m_game_title;
+
+ mCore* m_core{};
+ mTimingEvent m_event{};
+ bool m_waiting_for_event = false;
+ SIODriver m_sio_driver{};
+ AVStream m_stream{};
+ std::vector m_video_buffer;
+
+ u64 m_last_gc_ticks = 0;
+ u64 m_gc_ticks_remainder = 0;
+ u16 m_keys = 0;
+ bool m_link_enabled = false;
+ bool m_force_disconnect = false;
+
+ std::weak_ptr m_host;
+
+ std::unique_ptr m_thread;
+ bool m_exit_loop = false;
+ bool m_idle = false;
+ std::mutex m_queue_mutex;
+ std::condition_variable m_command_cv;
+ std::queue m_command_queue;
+
+ std::mutex m_response_mutex;
+ std::condition_variable m_response_cv;
+ bool m_response_ready = false;
+ std::vector m_response;
+};
+} // namespace HW::GBA
diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index 45cc52e6e3..7835689d55 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -264,6 +264,7 @@
+
@@ -846,6 +847,7 @@
+
From d0f0b4c0e054b2177e524fba274dbefa98dfa7b7 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 13:15:37 +0200
Subject: [PATCH 13/18] SI: Implement GBAEmu device
---
Source/Core/AudioCommon/AudioCommon.cpp | 3 +-
Source/Core/Core/CMakeLists.txt | 2 +
Source/Core/Core/HW/SI/SI.cpp | 2 +-
Source/Core/Core/HW/SI/SI_Device.cpp | 12 ++
Source/Core/Core/HW/SI/SI_Device.h | 1 +
Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp | 167 +++++++++++++++++++++
Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h | 49 ++++++
Source/Core/Core/State.cpp | 2 +-
Source/Core/DolphinLib.props | 2 +
9 files changed, 237 insertions(+), 3 deletions(-)
create mode 100644 Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp
create mode 100644 Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h
diff --git a/Source/Core/AudioCommon/AudioCommon.cpp b/Source/Core/AudioCommon/AudioCommon.cpp
index cfa5383c7c..6623b5ad8b 100644
--- a/Source/Core/AudioCommon/AudioCommon.cpp
+++ b/Source/Core/AudioCommon/AudioCommon.cpp
@@ -68,7 +68,8 @@ void InitSoundStream()
void PostInitSoundStream()
{
- // This needs to be called after AudioInterface::Init where input sample rates are set
+ // This needs to be called after AudioInterface::Init and SerialInterface::Init (for GBA devices)
+ // where input sample rates are set
UpdateSoundStream();
SetSoundStreamRunning(true);
diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt
index 65eb857982..0ae84e2bdc 100644
--- a/Source/Core/Core/CMakeLists.txt
+++ b/Source/Core/Core/CMakeLists.txt
@@ -624,6 +624,8 @@ if(USE_MGBA)
target_sources(core PRIVATE
HW/GBACore.cpp
HW/GBACore.h
+ HW/SI/SI_DeviceGBAEmu.cpp
+ HW/SI/SI_DeviceGBAEmu.h
)
target_link_libraries(core PUBLIC mGBA::mgba)
target_compile_definitions(core PUBLIC -DHAS_LIBMGBA)
diff --git a/Source/Core/Core/HW/SI/SI.cpp b/Source/Core/Core/HW/SI/SI.cpp
index 14e586fc05..caaa95cbae 100644
--- a/Source/Core/Core/HW/SI/SI.cpp
+++ b/Source/Core/Core/HW/SI/SI.cpp
@@ -707,7 +707,7 @@ void UpdateDevices()
SIDevices GetDeviceType(int channel)
{
- if (channel < 0 || channel > 3)
+ if (channel < 0 || channel >= MAX_SI_CHANNELS || !s_channel[channel].device)
return SIDEVICE_NONE;
return s_channel[channel].device->GetDeviceType();
diff --git a/Source/Core/Core/HW/SI/SI_Device.cpp b/Source/Core/Core/HW/SI/SI_Device.cpp
index 401b1178a7..fa823ff8ad 100644
--- a/Source/Core/Core/HW/SI/SI_Device.cpp
+++ b/Source/Core/Core/HW/SI/SI_Device.cpp
@@ -13,8 +13,12 @@
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
+#include "Common/MsgHandler.h"
#include "Core/HW/SI/SI_DeviceDanceMat.h"
#include "Core/HW/SI/SI_DeviceGBA.h"
+#ifdef HAS_LIBMGBA
+#include "Core/HW/SI/SI_DeviceGBAEmu.h"
+#endif
#include "Core/HW/SI/SI_DeviceGCAdapter.h"
#include "Core/HW/SI/SI_DeviceGCController.h"
#include "Core/HW/SI/SI_DeviceGCSteeringWheel.h"
@@ -187,6 +191,14 @@ std::unique_ptr SIDevice_Create(const SIDevices device, const int por
case SIDEVICE_GC_GBA:
return std::make_unique(device, port_number);
+ case SIDEVICE_GC_GBA_EMULATED:
+#ifdef HAS_LIBMGBA
+ return std::make_unique(device, port_number);
+#else
+ PanicAlertT("Error: This build does not support emulated GBA controllers");
+ return std::make_unique(device, port_number);
+#endif
+
case SIDEVICE_GC_KEYBOARD:
return std::make_unique(device, port_number);
diff --git a/Source/Core/Core/HW/SI/SI_Device.h b/Source/Core/Core/HW/SI/SI_Device.h
index c2ae9bef87..d109432b90 100644
--- a/Source/Core/Core/HW/SI/SI_Device.h
+++ b/Source/Core/Core/HW/SI/SI_Device.h
@@ -93,6 +93,7 @@ enum SIDevices : int
// It's kept here so that values below will stay constant.
SIDEVICE_AM_BASEBOARD,
SIDEVICE_WIIU_ADAPTER,
+ SIDEVICE_GC_GBA_EMULATED,
// Not a valid device. Used for checking whether enum values are valid.
SIDEVICE_COUNT,
};
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp
new file mode 100644
index 0000000000..aaf971b45e
--- /dev/null
+++ b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.cpp
@@ -0,0 +1,167 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include
+
+#include "Common/ChunkFile.h"
+#include "Common/CommonTypes.h"
+#include "Common/Logging/Log.h"
+#include "Common/Swap.h"
+#include "Core/Core.h"
+#include "Core/CoreTiming.h"
+#include "Core/HW/GBACore.h"
+#include "Core/HW/GBAPad.h"
+#include "Core/HW/SI/SI.h"
+#include "Core/HW/SI/SI_DeviceGBAEmu.h"
+#include "Core/HW/SI/SI_DeviceGCController.h"
+#include "Core/HW/SystemTimers.h"
+#include "Core/Host.h"
+#include "Core/NetPlayProto.h"
+
+namespace SerialInterface
+{
+static s64 GetSyncInterval()
+{
+ return SystemTimers::GetTicksPerSecond() / 1000;
+}
+
+CSIDevice_GBAEmu::CSIDevice_GBAEmu(SIDevices device, int device_number)
+ : ISIDevice(device, device_number)
+{
+ m_core = std::make_shared(m_device_number);
+ m_core->Start(CoreTiming::GetTicks());
+ m_gbahost = Host_CreateGBAHost(m_core);
+ m_core->SetHost(m_gbahost);
+ ScheduleEvent(m_device_number, GetSyncInterval());
+}
+
+CSIDevice_GBAEmu::~CSIDevice_GBAEmu()
+{
+ RemoveEvent(m_device_number);
+ m_core->Stop();
+ m_gbahost.reset();
+ m_core.reset();
+}
+
+int CSIDevice_GBAEmu::RunBuffer(u8* buffer, int request_length)
+{
+ switch (m_next_action)
+ {
+ case NextAction::SendCommand:
+ {
+#ifdef _DEBUG
+ NOTICE_LOG_FMT(SERIALINTERFACE, "{} cmd {:02x} [> {:02x}{:02x}{:02x}{:02x}]", m_device_number,
+ buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]);
+#endif
+ m_last_cmd = static_cast(buffer[0]);
+ m_timestamp_sent = CoreTiming::GetTicks();
+ m_core->SendJoybusCommand(m_timestamp_sent, TransferInterval(), buffer, m_keys);
+
+ RemoveEvent(m_device_number);
+ ScheduleEvent(m_device_number, TransferInterval() + GetSyncInterval());
+ for (int i = 0; i < MAX_SI_CHANNELS; ++i)
+ {
+ if (i == m_device_number || SerialInterface::GetDeviceType(i) != GetDeviceType())
+ continue;
+ RemoveEvent(i);
+ ScheduleEvent(i, 0, static_cast(TransferInterval()));
+ }
+
+ m_next_action = NextAction::WaitTransferTime;
+ [[fallthrough]];
+ }
+
+ case NextAction::WaitTransferTime:
+ {
+ int elapsed_time = static_cast(CoreTiming::GetTicks() - m_timestamp_sent);
+ // Tell SI to ask again after TransferInterval() cycles
+ if (TransferInterval() > elapsed_time)
+ return 0;
+ m_next_action = NextAction::ReceiveResponse;
+ [[fallthrough]];
+ }
+
+ case NextAction::ReceiveResponse:
+ {
+ m_next_action = NextAction::SendCommand;
+
+ std::vector response = m_core->GetJoybusResponse();
+ if (response.empty())
+ return -1;
+ std::copy(response.begin(), response.end(), buffer);
+
+#ifdef _DEBUG
+ const Common::Log::LOG_LEVELS log_level =
+ (m_last_cmd == EBufferCommands::CMD_STATUS || m_last_cmd == EBufferCommands::CMD_RESET) ?
+ Common::Log::LERROR :
+ Common::Log::LWARNING;
+ GENERIC_LOG_FMT(Common::Log::SERIALINTERFACE, log_level,
+ "{} [< {:02x}{:02x}{:02x}{:02x}{:02x}] ({})",
+ m_device_number, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4],
+ response.size());
+#endif
+
+ return static_cast(response.size());
+ }
+ }
+
+ // This should never happen, but appease MSVC which thinks it might.
+ ERROR_LOG_FMT(SERIALINTERFACE, "Unknown state {}\n", m_next_action);
+ return -1;
+}
+
+int CSIDevice_GBAEmu::TransferInterval()
+{
+ return SIDevice_GetGBATransferTime(m_last_cmd);
+}
+
+bool CSIDevice_GBAEmu::GetData(u32& hi, u32& low)
+{
+ GCPadStatus pad_status{};
+ if (!NetPlay::IsNetPlayRunning())
+ pad_status = Pad::GetGBAStatus(m_device_number);
+ SerialInterface::CSIDevice_GCController::HandleMoviePadStatus(m_device_number, &pad_status);
+
+ static constexpr std::array buttons_map = {
+ PadButton::PAD_BUTTON_A, // A
+ PadButton::PAD_BUTTON_B, // B
+ PadButton::PAD_TRIGGER_Z, // Select
+ PadButton::PAD_BUTTON_START, // Start
+ PadButton::PAD_BUTTON_RIGHT, // Right
+ PadButton::PAD_BUTTON_LEFT, // Left
+ PadButton::PAD_BUTTON_UP, // Up
+ PadButton::PAD_BUTTON_DOWN, // Down
+ PadButton::PAD_TRIGGER_R, // R
+ PadButton::PAD_TRIGGER_L, // L
+ };
+
+ m_keys = 0;
+ for (size_t i = 0; i < buttons_map.size(); ++i)
+ m_keys |= static_cast(static_cast((pad_status.button & buttons_map[i]))) << i;
+
+ // Use X button as a reset signal for NetPlay/Movies
+ if (pad_status.button & PadButton::PAD_BUTTON_X)
+ m_core->Reset();
+
+ return false;
+}
+
+void CSIDevice_GBAEmu::SendCommand(u32 command, u8 poll)
+{
+}
+
+void CSIDevice_GBAEmu::DoState(PointerWrap& p)
+{
+ p.Do(m_next_action);
+ p.Do(m_last_cmd);
+ p.Do(m_timestamp_sent);
+ p.Do(m_keys);
+ m_core->DoState(p);
+}
+
+void CSIDevice_GBAEmu::OnEvent(u64 userdata, s64 cycles_late)
+{
+ m_core->SendJoybusCommand(CoreTiming::GetTicks() + userdata, 0, nullptr, m_keys);
+ ScheduleEvent(m_device_number, userdata + GetSyncInterval());
+}
+} // namespace SerialInterface
diff --git a/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h
new file mode 100644
index 0000000000..d19c43e27b
--- /dev/null
+++ b/Source/Core/Core/HW/SI/SI_DeviceGBAEmu.h
@@ -0,0 +1,49 @@
+// Copyright 2021 Dolphin Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include
+
+#include "Common/CommonTypes.h"
+#include "Core/HW/SI/SI_Device.h"
+
+namespace HW::GBA
+{
+class Core;
+} // namespace HW::GBA
+
+class GBAHostInterface;
+
+namespace SerialInterface
+{
+class CSIDevice_GBAEmu : public ISIDevice
+{
+public:
+ CSIDevice_GBAEmu(SIDevices device, int device_number);
+ ~CSIDevice_GBAEmu();
+
+ int RunBuffer(u8* buffer, int request_length) override;
+ int TransferInterval() override;
+ bool GetData(u32& hi, u32& low) override;
+ void SendCommand(u32 command, u8 poll) override;
+ void DoState(PointerWrap& p) override;
+ void OnEvent(u64 userdata, s64 cycles_late) override;
+
+private:
+ enum class NextAction
+ {
+ SendCommand,
+ WaitTransferTime,
+ ReceiveResponse
+ };
+
+ NextAction m_next_action = NextAction::SendCommand;
+ EBufferCommands m_last_cmd{};
+ u64 m_timestamp_sent = 0;
+ u16 m_keys = 0;
+
+ std::shared_ptr m_core;
+ std::shared_ptr m_gbahost;
+};
+} // namespace SerialInterface
diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp
index 5d5f6e9d33..77e31e1ac0 100644
--- a/Source/Core/Core/State.cpp
+++ b/Source/Core/Core/State.cpp
@@ -73,7 +73,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system
-constexpr u32 STATE_VERSION = 132; // Last changed in PR 9532
+constexpr u32 STATE_VERSION = 133; // Last changed in PR 9600
// Maps savestate versions to Dolphin versions.
// Versions after 42 don't need to be added to this list,
diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props
index 7835689d55..0f0dfe227b 100644
--- a/Source/Core/DolphinLib.props
+++ b/Source/Core/DolphinLib.props
@@ -287,6 +287,7 @@
+
@@ -868,6 +869,7 @@
+
From 9b80fb7deb676485fb780eadb56ea389aa4ec9d0 Mon Sep 17 00:00:00 2001
From: Bonta <40473493+Bonta0@users.noreply.github.com>
Date: Sun, 4 Jul 2021 13:17:32 +0200
Subject: [PATCH 14/18] Qt: GBA Pad config
---
Source/Core/DolphinQt/CMakeLists.txt | 2 +
.../Config/GamecubeControllersWidget.cpp | 88 +++++++++++--------
.../DolphinQt/Config/Mapping/GBAPadEmu.cpp | 45 ++++++++++
.../Core/DolphinQt/Config/Mapping/GBAPadEmu.h | 20 +++++
.../Config/Mapping/MappingWindow.cpp | 8 +-
.../DolphinQt/Config/Mapping/MappingWindow.h | 1 +
Source/Core/DolphinQt/DolphinQt.vcxproj | 2 +
7 files changed, 128 insertions(+), 38 deletions(-)
create mode 100644 Source/Core/DolphinQt/Config/Mapping/GBAPadEmu.cpp
create mode 100644 Source/Core/DolphinQt/Config/Mapping/GBAPadEmu.h
diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt
index 9ae4714317..004eb2b86f 100644
--- a/Source/Core/DolphinQt/CMakeLists.txt
+++ b/Source/Core/DolphinQt/CMakeLists.txt
@@ -124,6 +124,8 @@ add_executable(dolphin-emu
Config/Mapping/FreeLookGeneral.h
Config/Mapping/FreeLookRotation.cpp
Config/Mapping/FreeLookRotation.h
+ Config/Mapping/GBAPadEmu.cpp
+ Config/Mapping/GBAPadEmu.h
Config/Mapping/GCKeyboardEmu.cpp
Config/Mapping/GCKeyboardEmu.h
Config/Mapping/GCMicrophone.cpp
diff --git a/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp b/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp
index 9a9fdd2be0..d940f46461 100644
--- a/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp
+++ b/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp
@@ -10,8 +10,9 @@
#include
#include
-#include