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/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/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/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;
diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h
index 4afeb698a3..55ed606517 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,9 @@
#define GC_MEMCARDB "MemoryCardB"
#define GC_MEMCARD_NETPLAY "NetPlayTemp"
+#define GBA_BIOS "gba_bios.bin"
+#define GBA_SAVE_NETPLAY "NetPlayTemp"
+
#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 f8814b8c02..96d72a36d7 100644
--- a/Source/Core/Common/FileUtil.cpp
+++ b/Source/Core/Common/FileUtil.cpp
@@ -977,6 +977,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/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}