Merge latest master changes

This commit is contained in:
Rodolfo Bogado 2016-08-07 21:17:50 -03:00
parent 1410e6e83b
commit fea36cb56f
111 changed files with 2453 additions and 1302 deletions

View file

@ -24,12 +24,38 @@ This guide is for developers who wish to contribute to the Dolphin codebase. It
Following this guide and formatting your code as detailed will likely get your pull request merged much faster than if you don't (assuming the written code has no mistakes in itself).
This project uses clang-format (stable branch) to check for common style issues. In case of conflicts between this guide and clang-format rules, the latter should be followed instead of this guide.
### Checking and fixing formatting issues
In most cases, clang-format can and **should** be used to automatically reformat code and solve most formatting issues.
- To run clang-format on all staged files:
```
git diff --cached --name-only | egrep '[.](cpp|h|mm)$' | xargs clang-format -i
```
- Formatting issues can be checked for before committing with a lint script that is included with the codebase. To enable it as a pre-commit hook (assuming you are in the repository root):
```
ln -s ../../Tools/lint.sh .git/hooks/pre-commit
```
- Alternatively, a custom git filter driver can be used to automatically and transparently reformat any changes:
```
git config filter.clang_format.smudge 'cat'
git config filter.clang_format.clean 'clang-format %f'
echo '/Source/Core/**/*.cpp filter=clang_format' >> .git/info/attributes
echo '/Source/Core/**/*.h filter=clang_format' >> .git/info/attributes
echo '/Source/Core/**/*.mm filter=clang_format' >> .git/info/attributes
```
## Styling and formatting
### General
- Try to limit lines of code to a maximum of 100 characters.
- Note that this does not mean you should try and use all 100 characters every time you have the chance. Typically with well formatted code, you normally shouldn't hit a line count of anything over 80 or 90 characters.
- The indentation style we use is tabs for initial indentation and then, if vertical alignment is needed, spaces are to be used.
- The indentation style we use is 1 tab per level.
- The opening brace for namespaces, classes, functions, enums, structs, unions, conditionals, and loops go on the next line.
- With array initializer lists and lambda expressions it is OK to keep the brace on the same line.
- References and pointers have the ampersand or asterisk against the type name, not the variable name. Example: `int* var`, not `int *var`.
@ -39,10 +65,10 @@ Following this guide and formatting your code as detailed will likely get your p
```c++
if (condition)
return 0;
return 0;
while (var != 0)
var--;
var--;
```
- No:
@ -73,30 +99,30 @@ Following this guide and formatting your code as detailed will likely get your p
```c++
if (condition)
{
// code
// code
}
else
{
// code
// code
}
```
- Acceptable:
```c++
if (condition)
// code line
// code line
else
// code line
// code line
```
- No:
```c++
if (condition)
{
// code
// code
}
else
// code line
// code line
```
@ -111,18 +137,18 @@ Following this guide and formatting your code as detailed will likely get your p
class ExampleClass : public SomeParent
{
public:
ExampleClass(int x, int y);
ExampleClass(int x, int y);
int GetX() const;
int GetY() const;
int GetX() const;
int GetY() const;
protected:
virtual void SomeProtectedFunction() = 0;
static float s_some_variable;
virtual void SomeProtectedFunction() = 0;
static float s_some_variable;
private:
int m_x;
int m_y;
int m_x;
int m_y;
};
```
@ -143,9 +169,10 @@ private:
- If a header is not necessary in a certain source file, remove them.
- If you find duplicate includes of a certain header, remove it.
- When declaring includes in a source file, make sure they follow the given pattern:
- The header for the source file
- Standard library headers
- System-specific headers (these should also likely be in an `#ifdef` block unless the source file itself is system-specific).
- Dolphin source file headers
- Other Dolphin source file headers
- Each of the above header sections should also be in alphabetical order
- Project source file headers should be included in a way that is relative to the `[Dolphin Root]/Source/Core` directory.
- This project uses `#pragma once` as header guards.
@ -172,10 +199,10 @@ private:
template<class T>
inline void Clamp(T& val, const T& min, const T& max)
{
if (val < min)
val = min;
else if (val > max)
val = max;
if (val < min)
val = min;
else if (val > max)
val = max;
}
```
@ -187,10 +214,10 @@ private:
template<class T>
inline void Clamp(T* val, const T& min, const T& max)
{
if (*val < min)
*val = min;
else if (*val > max)
*val = max;
if (*val < min)
*val = min;
else if (*val > max)
*val = max;
}
```
@ -202,7 +229,7 @@ private:
class ClassName : ParentClass
{
public:
void Update() final;
void Update() final;
};
```
@ -212,7 +239,7 @@ private:
class ClassName : ParentClass
{
public:
void Update() override;
void Update() override;
};
```
@ -222,10 +249,10 @@ private:
```c++
class ClassName final : ParentClass
{
// Class definitions
// Class definitions
};
```
## Java
The Android project is currently written in Java. If you are using Android Studio to contribute, you can import the project's code style from `code-style-java.jar`, located in `[Dolphin Root]/Source/Android`. Please organize imports before committing.
The Android project is currently written in Java. If you are using Android Studio to contribute, you can import the project's code style from `code-style-java.jar`, located in `[Dolphin Root]/Source/Android`. Please organize imports before committing.

View file

@ -177,7 +177,6 @@ C21BFA20 00000003
04261B1C 60000000
04261B30 60000000
041A55EC 4E800020
C208D600 00000009
8A830678 3DC08000
61CE45D4 1EB40008
@ -188,7 +187,6 @@ C208D600 00000009
62523DA4 1EB40E90
7E52AA14 92720000
3880FFFF 00000000
C208D6A4 00000009
8A830678 3DC08000
61CE45D4 1EB40008
@ -199,7 +197,6 @@ C208D6A4 00000009
62523DA4 1EB40E90
7E52AA14 92720000
EC010024 00000000
04040BBC 60000000
20C61DB8 16200D20
06C61DB8 00000014
@ -208,8 +205,6 @@ EC010024 00000000
1A210300 00000000
020045D0 000F0000
E2000001 00000000
#Stage Strike
C2259C40 0000001F
39600000 3D408045
614AC388 38600000
@ -242,8 +237,6 @@ C2259C40 0000001F
4182000C 38630001
4BFFFF4C 800DB604
28000000 00000000
#Neutral Spawn Points
C216E2DC 00000017
3C608000 60633300
3C80804D 88846CAE
@ -299,9 +292,7 @@ C216E468 00000003
0000001c 00030102
00010302 ffffffff
00010203 00010203
0416B480 60000000
C21A4160 00000008
39C00000 3DE08046
61EFB108 820F0000
@ -316,16 +307,11 @@ C21A4160 00000008
04452F5C 42000000
04452F60 40200000
04452F64 47000000
0406AE90 38000000
040300A4 38000000
0422D638 38000006
043FA25B 01000000
#Leaving debug menu goes to CSS
041b0a14 38600002
#Toggle frozen stages
224d6c94 00000000
04c8ee24 00000000
04c8ea04 99b3b3ff
@ -382,8 +368,6 @@ C2259C48 0000000A
6A940001 9A930000
3A80001E 9A8DB60E
886DB60E 00000000
#Increase input timing accuracy
C21A4DA0 00000003
901C0000 3D808001
618C95FC 7D8903A6
@ -392,8 +376,8 @@ C21A4DA0 00000003
20020004 00000000
04019860 4bfffd9d
$Netplay Safe Kill Music [JMC47]
024D3886 00000000
$Netplay Safe Kill Music [Dan Salvato, Summate]
040249a4 38600001
$Unlock All Characters and Stages [Datel]
*Also Unlocks All Star Mode, Sound Test, and Vs. Mode Additions
@ -422,6 +406,17 @@ $Boot to Character Select Screen [Dan Salvato, Achilles]
$Debug Menu Replaces Tournament Mode [Magus, donny2112]
0422D638 38000006
$Increase Input Timing Accuracy [Dan Salvato]
*Reorganizes input polls to remove drift that causes occasional input lag.
*Also doubles the controller's poll rate.
C21A4DA0 00000003
901C0000 3D808001
618C95FC 7D8903A6
4E800421 00000000
08402cc4 004e0400
20020004 00000000
04019860 4bfffd9d
$Disable Name Tag Reset After Closing Character Port [Ato]
04261B1C 60000000
04261B30 60000000

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 234 B

View file

@ -105,14 +105,17 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
public void onViewCreated(View view, Bundle savedInstanceState)
{
Button doneButton = (Button) view.findViewById(R.id.done_control_config);
doneButton.setOnClickListener(new View.OnClickListener()
if (doneButton != null)
{
@Override
public void onClick(View v)
doneButton.setOnClickListener(new View.OnClickListener()
{
stopConfiguringControls();
}
});
@Override
public void onClick(View v)
{
stopConfiguringControls();
}
});
}
}
@Override

View file

@ -2,10 +2,9 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "AudioCommon/AlsaSoundStream.h"
#include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/AudioCommon.h"
#include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/AlsaSoundStream.h"
#include "AudioCommon/CoreAudioSoundStream.h"
#include "AudioCommon/DSoundStream.h"
#include "AudioCommon/Mixer.h"
@ -13,18 +12,17 @@
#include "AudioCommon/OpenALStream.h"
#include "AudioCommon/OpenSLESStream.h"
#include "AudioCommon/PulseAudioStream.h"
#include "AudioCommon/XAudio2_7Stream.h"
#include "AudioCommon/XAudio2Stream.h"
#include "AudioCommon/XAudio2_7Stream.h"
#include "Common/Common.h"
#include "Common/FileUtil.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Core/ConfigManager.h"
#include "Core/Movie.h"
// This shouldn't be a global, at least not here.
SoundStream* g_sound_stream = nullptr;
std::unique_ptr<SoundStream> g_sound_stream;
static bool s_audio_dump_start = false;
@ -33,82 +31,78 @@ namespace AudioCommon
static const int AUDIO_VOLUME_MIN = 0;
static const int AUDIO_VOLUME_MAX = 100;
SoundStream* InitSoundStream(void *hWnd)
void InitSoundStream(void* hWnd)
{
std::string backend = SConfig::GetInstance().sBackend;
if (backend == BACKEND_OPENAL && OpenALStream::isValid())
g_sound_stream = new OpenALStream();
else if (backend == BACKEND_NULLSOUND && NullSound::isValid())
g_sound_stream = new NullSound();
std::string backend = SConfig::GetInstance().sBackend;
if (backend == BACKEND_OPENAL && OpenALStream::isValid())
g_sound_stream = std::make_unique<OpenALStream>();
else if (backend == BACKEND_NULLSOUND && NullSound::isValid())
g_sound_stream = std::make_unique<NullSound>();
else if (backend == BACKEND_DIRECTSOUND && DSound::isValid())
g_sound_stream = new DSound(hWnd);
else if (backend == BACKEND_XAUDIO2)
{
if (XAudio2::isValid())
g_sound_stream = new XAudio2();
else if (XAudio2_7::isValid())
g_sound_stream = new XAudio2_7();
}
else if (backend == BACKEND_AOSOUND && AOSound::isValid())
g_sound_stream = new AOSound();
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
g_sound_stream = new AlsaSound();
else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid())
g_sound_stream = new CoreAudioSound();
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
g_sound_stream = new PulseAudio();
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
g_sound_stream = new OpenSLESStream();
g_sound_stream = std::make_unique<DSound>(hWnd);
else if (backend == BACKEND_XAUDIO2)
{
if (XAudio2::isValid())
g_sound_stream = std::make_unique<XAudio2>();
else if (XAudio2_7::isValid())
g_sound_stream = std::make_unique<XAudio2_7>();
}
else if (backend == BACKEND_AOSOUND && AOSound::isValid())
g_sound_stream = std::make_unique<AOSound>();
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
g_sound_stream = std::make_unique<AlsaSound>();
else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid())
g_sound_stream = std::make_unique<CoreAudioSound>();
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
g_sound_stream = std::make_unique<PulseAudio>();
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
g_sound_stream = std::make_unique<OpenSLESStream>();
if (!g_sound_stream && NullSound::isValid())
{
WARN_LOG(DSPHLE, "Could not initialize backend %s, using %s instead.",
backend.c_str(), BACKEND_NULLSOUND);
g_sound_stream = new NullSound();
}
if (!g_sound_stream && NullSound::isValid())
{
WARN_LOG(AUDIO, "Could not initialize backend %s, using %s instead.", backend.c_str(),
BACKEND_NULLSOUND);
g_sound_stream = std::make_unique<NullSound>();
}
if (g_sound_stream)
{
UpdateSoundStream();
if (g_sound_stream->Start())
{
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
UpdateSoundStream();
return g_sound_stream;
}
PanicAlertT("Could not initialize backend %s.", backend.c_str());
}
if (!g_sound_stream->Start())
{
ERROR_LOG(AUDIO, "Could not start backend %s, using %s instead", backend.c_str(),
BACKEND_NULLSOUND);
PanicAlertT("Sound backend %s is not valid.", backend.c_str());
g_sound_stream = std::make_unique<NullSound>();
g_sound_stream->Start();
}
delete g_sound_stream;
g_sound_stream = nullptr;
return nullptr;
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
}
void ShutdownSoundStream()
{
INFO_LOG(DSPHLE, "Shutting down sound stream");
INFO_LOG(AUDIO, "Shutting down sound stream");
if (g_sound_stream)
{
g_sound_stream->Stop();
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
delete g_sound_stream;
g_sound_stream = nullptr;
}
if (g_sound_stream)
{
g_sound_stream->Stop();
INFO_LOG(DSPHLE, "Done shutting down sound stream");
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
g_sound_stream.reset();
}
INFO_LOG(AUDIO, "Done shutting down sound stream");
}
std::vector<std::string> GetSoundBackends()
{
std::vector<std::string> backends;
std::vector<std::string> backends;
if (NullSound::isValid())
backends.push_back(BACKEND_NULLSOUND);
if (NullSound::isValid())
backends.push_back(BACKEND_NULLSOUND);
if (DSound::isValid())
backends.push_back(BACKEND_DIRECTSOUND);
if (XAudio2_7::isValid()
@ -116,118 +110,99 @@ std::vector<std::string> GetSoundBackends()
|| XAudio2::isValid()
#endif
)
backends.push_back(BACKEND_XAUDIO2);
if (AOSound::isValid())
backends.push_back(BACKEND_AOSOUND);
if (AlsaSound::isValid())
backends.push_back(BACKEND_ALSA);
if (CoreAudioSound::isValid())
backends.push_back(BACKEND_COREAUDIO);
if (PulseAudio::isValid())
backends.push_back(BACKEND_PULSEAUDIO);
if (OpenALStream::isValid())
backends.push_back(BACKEND_OPENAL);
if (OpenSLESStream::isValid())
backends.push_back(BACKEND_OPENSLES);
return backends;
}
void PauseAndLock(bool doLock, bool unpauseOnUnlock)
{
if (g_sound_stream)
{
// audio typically doesn't maintain its own "paused" state
// (that's already handled by the CPU and whatever else being paused)
// so it should be good enough to only lock/unlock here.
CMixer* pMixer = g_sound_stream->GetMixer();
if (pMixer)
{
std::mutex& csMixing = pMixer->MixerCritical();
if (doLock)
csMixing.lock();
else
csMixing.unlock();
}
}
backends.push_back(BACKEND_XAUDIO2);
if (AOSound::isValid())
backends.push_back(BACKEND_AOSOUND);
if (AlsaSound::isValid())
backends.push_back(BACKEND_ALSA);
if (CoreAudioSound::isValid())
backends.push_back(BACKEND_COREAUDIO);
if (PulseAudio::isValid())
backends.push_back(BACKEND_PULSEAUDIO);
if (OpenALStream::isValid())
backends.push_back(BACKEND_OPENAL);
if (OpenSLESStream::isValid())
backends.push_back(BACKEND_OPENSLES);
return backends;
}
void UpdateSoundStream()
{
if (g_sound_stream)
{
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
g_sound_stream->SetVolume(volume);
}
if (g_sound_stream)
{
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
g_sound_stream->SetVolume(volume);
}
}
void ClearAudioBuffer(bool mute)
{
if (g_sound_stream)
g_sound_stream->Clear(mute);
if (g_sound_stream)
g_sound_stream->Clear(mute);
}
void SendAIBuffer(short *samples, unsigned int num_samples)
void SendAIBuffer(const short* samples, unsigned int num_samples)
{
if (!g_sound_stream)
return;
if (!g_sound_stream)
return;
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
CMixer* pMixer = g_sound_stream->GetMixer();
CMixer* pMixer = g_sound_stream->GetMixer();
if (pMixer && samples)
{
pMixer->PushSamples(samples, num_samples);
}
if (pMixer && samples)
{
pMixer->PushSamples(samples, num_samples);
}
g_sound_stream->Update();
g_sound_stream->Update();
}
void StartAudioDump()
{
std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
File::CreateFullPath(audio_file_name_dtk);
File::CreateFullPath(audio_file_name_dsp);
g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk);
g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp);
s_audio_dump_start = true;
std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
File::CreateFullPath(audio_file_name_dtk);
File::CreateFullPath(audio_file_name_dsp);
g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk);
g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp);
s_audio_dump_start = true;
}
void StopAudioDump()
{
g_sound_stream->GetMixer()->StopLogDTKAudio();
g_sound_stream->GetMixer()->StopLogDSPAudio();
s_audio_dump_start = false;
g_sound_stream->GetMixer()->StopLogDTKAudio();
g_sound_stream->GetMixer()->StopLogDSPAudio();
s_audio_dump_start = false;
}
void IncreaseVolume(unsigned short offset)
{
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume += offset;
if (currentVolume > AUDIO_VOLUME_MAX)
currentVolume = AUDIO_VOLUME_MAX;
UpdateSoundStream();
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume += offset;
if (currentVolume > AUDIO_VOLUME_MAX)
currentVolume = AUDIO_VOLUME_MAX;
UpdateSoundStream();
}
void DecreaseVolume(unsigned short offset)
{
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume -= offset;
if (currentVolume < AUDIO_VOLUME_MIN)
currentVolume = AUDIO_VOLUME_MIN;
UpdateSoundStream();
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume -= offset;
if (currentVolume < AUDIO_VOLUME_MIN)
currentVolume = AUDIO_VOLUME_MIN;
UpdateSoundStream();
}
void ToggleMuteVolume()
{
bool& isMuted = SConfig::GetInstance().m_IsMuted;
isMuted = !isMuted;
UpdateSoundStream();
bool& isMuted = SConfig::GetInstance().m_IsMuted;
isMuted = !isMuted;
UpdateSoundStream();
}
}

View file

@ -4,23 +4,23 @@
#pragma once
#include <memory>
#include "AudioCommon/SoundStream.h"
#include "Common/CommonTypes.h"
class CMixer;
extern SoundStream *g_sound_stream;
extern std::unique_ptr<SoundStream> g_sound_stream;
namespace AudioCommon
{
SoundStream* InitSoundStream(void *hWnd);
void InitSoundStream(void* hwnd);
void ShutdownSoundStream();
std::vector<std::string> GetSoundBackends();
void PauseAndLock(bool doLock, bool unpauseOnUnlock = true);
void UpdateSoundStream();
void ClearAudioBuffer(bool mute);
void SendAIBuffer(short* samples, unsigned int num_samples);
void SendAIBuffer(const short* samples, unsigned int num_samples);
void StartAudioDump();
void StopAudioDump();
void IncreaseVolume(unsigned short offset);

View file

@ -11,13 +11,13 @@
#include <utility>
#include <vector>
#include <curl/curl.h>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/FifoQueue.h"
#include "Common/Flag.h"
typedef void CURL;
// Utilities for analytics reporting in Dolphin. This reporting is designed to
// provide anonymous data about how well Dolphin performs in the wild. It also
// allows developers to declare trace points in Dolphin's source code and get
@ -41,15 +41,12 @@ typedef void CURL;
namespace Common
{
// Generic interface for an analytics reporting backends. The main
// implementation used in Dolphin can be found in Core/Analytics.h.
class AnalyticsReportingBackend
{
public:
virtual ~AnalyticsReportingBackend()
{}
virtual ~AnalyticsReportingBackend() {}
// Called from the AnalyticsReporter backend thread.
virtual void Send(std::string report) = 0;
};
@ -61,11 +58,7 @@ public:
AnalyticsReportBuilder();
~AnalyticsReportBuilder() = default;
AnalyticsReportBuilder(const AnalyticsReportBuilder& other)
{
*this = other;
}
AnalyticsReportBuilder(const AnalyticsReportBuilder& other) { *this = other; }
AnalyticsReportBuilder(AnalyticsReportBuilder&& other)
{
std::lock_guard<std::mutex> lk(other.m_lock);
@ -147,26 +140,14 @@ public:
// Gets the base report builder which is closed for each subsequent report
// being sent. DO NOT use this builder to send a report. Only use it to add
// new fields that should be globally available.
AnalyticsReportBuilder& BaseBuilder()
{
return m_base_builder;
}
AnalyticsReportBuilder& BaseBuilder() { return m_base_builder; }
// Gets a cloned builder that can be used to send a report.
AnalyticsReportBuilder Builder() const
{
return m_base_builder;
}
AnalyticsReportBuilder Builder() const { return m_base_builder; }
// Enqueues a report for sending. Consumes the report builder.
void Send(AnalyticsReportBuilder&& report);
// For convenience.
void Send(AnalyticsReportBuilder& report)
{
Send(std::move(report));
}
void Send(AnalyticsReportBuilder& report) { Send(std::move(report)); }
protected:
void ThreadProc();

View file

@ -256,12 +256,10 @@ elseif(APPLE)
elseif(UNIX)
set(SRCS ${SRCS} HW/BBA-TAP/TAP_Unix.cpp)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND BLUEZ_FOUND)
set(SRCS ${SRCS} HW/WiimoteReal/IONix.cpp)
set(SRCS ${SRCS} HW/WiimoteReal/IOLinux.cpp)
set(LIBS ${LIBS} bluetooth)
elseif(ANDROID)
set(SRCS ${SRCS} HW/WiimoteReal/IOAndroid.cpp)
else()
set(SRCS ${SRCS} HW/WiimoteReal/IODummy.cpp)
endif()
endif()

View file

@ -134,6 +134,7 @@ void SConfig::SaveGeneralSettings(IniFile& ini)
general->Set("DumpPath", m_DumpPath);
CreateDumpPath(m_DumpPath);
general->Set("WirelessMac", m_WirelessMac);
general->Set("WiiSDCardPath", m_strWiiSDCardPath);
#ifdef USE_GDBSTUB
#ifndef _WIN32
@ -409,6 +410,8 @@ void SConfig::LoadGeneralSettings(IniFile& ini)
general->Get("DumpPath", &m_DumpPath);
CreateDumpPath(m_DumpPath);
general->Get("WirelessMac", &m_WirelessMac);
general->Get("WiiSDCardPath", &m_strWiiSDCardPath, File::GetUserPath(F_WIISDCARD_IDX));
File::SetUserPath(F_WIISDCARD_IDX, m_strWiiSDCardPath);
}
void SConfig::LoadInterfaceSettings(IniFile& ini)
@ -657,6 +660,7 @@ void SConfig::LoadDefaults()
bHalfAudioRate = false;
bSyncGPU = false;
bFastDiscSpeed = false;
m_strWiiSDCardPath = File::GetUserPath(F_WIISDCARD_IDX);
bEnableMemcardSdWriting = true;
SelectedLanguage = 0;
bOverrideGCLanguage = false;

View file

@ -183,6 +183,7 @@ struct SConfig : NonCopyable
std::string m_strApploader;
std::string m_strUniqueID;
std::string m_strName;
std::string m_strWiiSDCardPath;
u16 m_revision;
std::string m_perfDir;

View file

@ -912,11 +912,11 @@ void UpdateTitle()
if (Movie::IsPlayingInput())
SFPS = StringFromFormat("Input: %u/%u - VI: %u - FPS: %.0f - VPS: %.0f - %.0f%%",
(u32)Movie::g_currentInputCount, (u32)Movie::g_totalInputCount,
(u32)Movie::g_currentFrame, FPS, VPS, Speed);
(u32)Movie::GetCurrentInputCount(), (u32)Movie::GetTotalInputCount(),
(u32)Movie::GetCurrentFrame(), FPS, VPS, Speed);
else if (Movie::IsRecordingInput())
SFPS = StringFromFormat("Input: %u - VI: %u - FPS: %.0f - VPS: %.0f - %.0f%%",
(u32)Movie::g_currentInputCount, (u32)Movie::g_currentFrame, FPS, VPS,
(u32)Movie::GetCurrentInputCount(), (u32)Movie::GetCurrentFrame(), FPS, VPS,
Speed);
else
{

View file

@ -295,6 +295,26 @@ void ClearPendingEvents()
}
}
static Event* AddEventToQueueEx(Event* before_evt, Event* ne)
{
Event* prev = nullptr;
Event** pNext = !before_evt || ne->time < before_evt->time ? &first : &before_evt->next;
for (;;)
{
Event*& next = *pNext;
if (!next || ne->time < next->time)
{
ne->next = next;
next = ne;
prev = next;
break;
}
prev = next;
pNext = &prev->next;
}
return prev;
}
static void AddEventToQueue(Event* ne)
{
Event* prev = nullptr;
@ -406,6 +426,7 @@ void ForceExceptionCheck(s64 cycles)
void MoveEvents()
{
Event* before_evt = nullptr;
BaseEvent sevt;
while (tsQueue.Pop(sevt))
{
@ -413,7 +434,7 @@ void MoveEvents()
evt->time = sevt.time;
evt->userdata = sevt.userdata;
evt->type = sevt.type;
AddEventToQueue(evt);
before_evt = AddEventToQueueEx(before_evt, evt);
}
}

View file

@ -145,7 +145,7 @@ std::unique_ptr<CPUCoreBase> FifoPlayer::GetCPUCore()
return std::make_unique<CPUCore>(this);
}
u32 FifoPlayer::GetFrameObjectCount()
u32 FifoPlayer::GetFrameObjectCount() const
{
if (m_CurrentFrame < m_FrameInfo.size())
{
@ -413,7 +413,7 @@ void FifoPlayer::LoadMemory()
SetupFifo();
u32* regs = m_File->GetBPMem();
const u32* regs = m_File->GetBPMem();
for (int i = 0; i < FifoDataFile::BP_MEM_SIZE; ++i)
{
if (ShouldLoadBP(i))
@ -494,7 +494,7 @@ void FifoPlayer::LoadXFReg(u16 reg, u32 value)
GPFifo::Write32(value);
}
void FifoPlayer::LoadXFMem16(u16 address, u32* data)
void FifoPlayer::LoadXFMem16(u16 address, const u32* data)
{
// Loads 16 * 4 bytes in xf memory starting at address
GPFifo::Write8(0x10); // load XF reg

View file

@ -65,8 +65,8 @@ public:
// PowerPC state.
std::unique_ptr<CPUCoreBase> GetCPUCore();
FifoDataFile* GetFile() { return m_File.get(); }
u32 GetFrameObjectCount();
FifoDataFile* GetFile() const { return m_File.get(); }
u32 GetFrameObjectCount() const;
u32 GetCurrentFrameNum() const { return m_CurrentFrame; }
const AnalyzedFrameInfo& GetAnalyzedFrameInfo(u32 frame) const { return m_FrameInfo[frame]; }
// Frame range
@ -119,7 +119,7 @@ private:
void LoadBPReg(u8 reg, u32 value);
void LoadCPReg(u8 reg, u32 value);
void LoadXFReg(u16 reg, u32 value);
void LoadXFMem16(u16 address, u32* data);
void LoadXFMem16(u16 address, const u32* data);
bool ShouldLoadBP(u8 address);

View file

@ -13,7 +13,7 @@
using namespace FifoAnalyzer;
void FifoRecordAnalyzer::Initialize(u32* cpMem)
void FifoRecordAnalyzer::Initialize(const u32* cpMem)
{
s_DrawingObject = false;

View file

@ -9,7 +9,7 @@
namespace FifoRecordAnalyzer
{
// Must call this before analyzing Fifo commands with FifoAnalyzer::AnalyzeCommand()
void Initialize(u32* cpMem);
void Initialize(const u32* cpMem);
void ProcessLoadIndexedXf(u32 val, int array);
void WriteVertexArray(int arrayIndex, const u8* vertexData, int vertexSize, int numVertices);

View file

@ -59,7 +59,7 @@ void FifoRecorder::StopRecording()
m_RequestedRecordingEnd = true;
}
void FifoRecorder::WriteGPCommand(u8* data, u32 size)
void FifoRecorder::WriteGPCommand(const u8* data, u32 size)
{
if (!m_SkipNextData)
{
@ -181,7 +181,8 @@ void FifoRecorder::EndFrame(u32 fifoStart, u32 fifoEnd)
}
}
void FifoRecorder::SetVideoMemory(u32* bpMem, u32* cpMem, u32* xfMem, u32* xfRegs, u32 xfRegsSize)
void FifoRecorder::SetVideoMemory(const u32* bpMem, const u32* cpMem, const u32* xfMem,
const u32* xfRegs, u32 xfRegsSize)
{
std::lock_guard<std::recursive_mutex> lk(sMutex);

View file

@ -19,11 +19,11 @@ public:
void StartRecording(s32 numFrames, CallbackFunc finishedCb);
void StopRecording();
FifoDataFile* GetRecordedFile() { return m_File; }
FifoDataFile* GetRecordedFile() const { return m_File; }
// Called from video thread
// Must write one full GP command at a time
void WriteGPCommand(u8* data, u32 size);
void WriteGPCommand(const u8* data, u32 size);
// Track memory that has been used and write it to the fifolog if it has changed.
// If memory is updated by the video backend (dynamicUpdate == true) take special care to make
@ -36,7 +36,8 @@ public:
// This function must be called before writing GP commands
// bpMem must point to the actual bp mem array used by the plugin because it will be read as fifo
// data is recorded
void SetVideoMemory(u32* bpMem, u32* cpMem, u32* xfMem, u32* xfRegs, u32 xfRegsSize);
void SetVideoMemory(const u32* bpMem, const u32* cpMem, const u32* xfMem, const u32* xfRegs,
u32 xfRegsSize);
// Checked once per frame prior to callng EndFrame()
bool IsRecording() const { return m_IsRecording; }

View file

@ -2,6 +2,7 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
@ -12,7 +13,6 @@
namespace Gecko
{
void LoadCodes(const IniFile& globalIni, const IniFile& localIni, std::vector<GeckoCode>& gcodes)
{
const IniFile* inis[2] = { &globalIni, &localIni };
@ -24,16 +24,16 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, std::vector<Ge
GeckoCode gcode;
lines.erase(std::remove_if(lines.begin(), lines.end(),
[](const auto& line) { return line.empty() || line[0] == '#'; }),
lines.end());
for (auto& line : lines)
{
if (line.empty())
continue;
std::istringstream ss(line);
switch ((line)[0])
{
// enabled or disabled code
case '+':
ss.seekg(1);
@ -67,7 +67,6 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, std::vector<Ge
}
break;
}
}
// add the last code
@ -97,7 +96,8 @@ void LoadCodes(const IniFile& globalIni, const IniFile& localIni, std::vector<Ge
}
// used by the SaveGeckoCodes function
static void SaveGeckoCode(std::vector<std::string>& lines, std::vector<std::string>& enabledLines, const GeckoCode& gcode)
static void SaveGeckoCode(std::vector<std::string>& lines, std::vector<std::string>& enabledLines,
const GeckoCode& gcode)
{
if (gcode.enabled)
enabledLines.push_back("$" + gcode.name);
@ -124,10 +124,10 @@ static void SaveGeckoCode(std::vector<std::string>& lines, std::vector<std::stri
// save all the code lines
for (const GeckoCode::Code& code : gcode.codes)
{
//ss << std::hex << codes_iter->address << ' ' << codes_iter->data;
//lines.push_back(StringFromFormat("%08X %08X", codes_iter->address, codes_iter->data));
// ss << std::hex << codes_iter->address << ' ' << codes_iter->data;
// lines.push_back(StringFromFormat("%08X %08X", codes_iter->address, codes_iter->data));
lines.push_back(code.original_line);
//ss.clear();
// ss.clear();
}
// save the notes
@ -148,5 +148,4 @@ void SaveCodes(IniFile& inifile, const std::vector<GeckoCode>& gcodes)
inifile.SetLines("Gecko", lines);
inifile.SetLines("Gecko_Enabled", enabledLines);
}
}

View file

@ -30,7 +30,7 @@ enum
};
// UDSPControl
#define DSP_CONTROL_MASK 0x0C07
constexpr u16 DSP_CONTROL_MASK = 0x0C07;
union UDSPControl {
u16 Hex;
struct

View file

@ -359,7 +359,7 @@ static void DTKStreamingCallback(u64 userdata, s64 cyclesLate)
}
if (bStreaming || !bTimeStretching)
g_sound_stream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed);
g_sound_stream->GetMixer()->PushStreamingSamples(tempPCM, samples_processed);
int ticks_to_dtk = int(SystemTimers::GetTicksPerSecond() * u64(samples_processed) / 48000);
CoreTiming::ScheduleEvent(ticks_to_dtk - cyclesLate, s_dtk);
@ -495,7 +495,6 @@ void ChangeDiscAsCPU(const std::string& newFileName)
CoreTiming::ScheduleEvent(500000000, s_insert_disc, (u64)_FileName);
if (Movie::IsRecordingInput())
{
Movie::g_bDiscChange = true;
std::string fileName = newFileName;
auto sizeofpath = fileName.find_last_of("/\\") + 1;
if (fileName.substr(sizeofpath).length() > 40)
@ -504,7 +503,7 @@ void ChangeDiscAsCPU(const std::string& newFileName)
"The filename of the disc image must not be longer than 40 characters.",
newFileName.c_str());
}
Movie::g_discChange = fileName.substr(sizeofpath);
Movie::SignalDiscChange(fileName.substr(sizeofpath));
}
}

View file

@ -36,6 +36,7 @@ void Initialize(void* const hwnd)
}
g_controller_interface.Initialize(hwnd);
g_controller_interface.RegisterHotplugCallback(LoadConfig);
// Load the saved controller config
s_config.LoadConfig(true);
@ -46,12 +47,8 @@ void LoadConfig()
s_config.LoadConfig(true);
}
void GetStatus(u8 port, KeyboardStatus* keyboard_status)
KeyboardStatus GetStatus(u8 port)
{
memset(keyboard_status, 0, sizeof(*keyboard_status));
keyboard_status->err = PAD_ERR_NONE;
// Get input
static_cast<GCKeyboard*>(s_config.GetController(port))->GetInput(keyboard_status);
return static_cast<GCKeyboard*>(s_config.GetController(port))->GetInput();
}
}

View file

@ -17,5 +17,5 @@ void LoadConfig();
InputConfig* GetConfig();
void GetStatus(u8 port, KeyboardStatus* keyboard_status);
KeyboardStatus GetStatus(u8 port);
}

View file

@ -84,14 +84,20 @@ std::string GCKeyboard::GetName() const
return std::string("GCKeyboard") + char('1' + m_index);
}
void GCKeyboard::GetInput(KeyboardStatus* const kb)
KeyboardStatus GCKeyboard::GetInput() const
{
m_keys0x->GetState(&kb->key0x, keys0_bitmasks);
m_keys1x->GetState(&kb->key1x, keys1_bitmasks);
m_keys2x->GetState(&kb->key2x, keys2_bitmasks);
m_keys3x->GetState(&kb->key3x, keys3_bitmasks);
m_keys4x->GetState(&kb->key4x, keys4_bitmasks);
m_keys5x->GetState(&kb->key5x, keys5_bitmasks);
auto lock = ControllerEmu::GetStateLock();
KeyboardStatus kb = {};
m_keys0x->GetState(&kb.key0x, keys0_bitmasks);
m_keys1x->GetState(&kb.key1x, keys1_bitmasks);
m_keys2x->GetState(&kb.key2x, keys2_bitmasks);
m_keys3x->GetState(&kb.key3x, keys3_bitmasks);
m_keys4x->GetState(&kb.key4x, keys4_bitmasks);
m_keys5x->GetState(&kb.key5x, keys5_bitmasks);
return kb;
}
void GCKeyboard::LoadDefaults(const ControllerInterface& ciface)

View file

@ -14,7 +14,7 @@ class GCKeyboard : public ControllerEmu
{
public:
GCKeyboard(const unsigned int index);
void GetInput(KeyboardStatus* const pad);
KeyboardStatus GetInput() const;
std::string GetName() const override;
void LoadDefaults(const ControllerInterface& ciface) override;

View file

@ -35,6 +35,7 @@ void Initialize(void* const hwnd)
}
g_controller_interface.Initialize(hwnd);
g_controller_interface.RegisterHotplugCallback(LoadConfig);
// Load the saved controller config
s_config.LoadConfig(true);
@ -45,13 +46,9 @@ void LoadConfig()
s_config.LoadConfig(true);
}
void GetStatus(u8 pad_num, GCPadStatus* pad_status)
GCPadStatus GetStatus(u8 pad_num)
{
memset(pad_status, 0, sizeof(*pad_status));
pad_status->err = PAD_ERR_NONE;
// Get input
static_cast<GCPad*>(s_config.GetController(pad_num))->GetInput(pad_status);
return static_cast<GCPad*>(s_config.GetController(pad_num))->GetInput();
}
void Rumble(const u8 pad_num, const ControlState strength)

View file

@ -18,7 +18,7 @@ void LoadConfig();
InputConfig* GetConfig();
void GetStatus(u8 pad_num, GCPadStatus* pad_status);
GCPadStatus GetStatus(u8 pad_num);
void Rumble(u8 pad_num, ControlState strength);
bool GetMicButton(u8 pad_num);

View file

@ -79,43 +79,49 @@ std::string GCPad::GetName() const
return std::string("GCPad") + char('1' + m_index);
}
void GCPad::GetInput(GCPadStatus* const pad)
GCPadStatus GCPad::GetInput() const
{
auto lock = ControllerEmu::GetStateLock();
ControlState x, y, triggers[2];
GCPadStatus pad = {};
// buttons
m_buttons->GetState(&pad->button, button_bitmasks);
m_buttons->GetState(&pad.button, button_bitmasks);
// set analog A/B analog to full or w/e, prolly not needed
if (pad->button & PAD_BUTTON_A)
pad->analogA = 0xFF;
if (pad->button & PAD_BUTTON_B)
pad->analogB = 0xFF;
if (pad.button & PAD_BUTTON_A)
pad.analogA = 0xFF;
if (pad.button & PAD_BUTTON_B)
pad.analogB = 0xFF;
// dpad
m_dpad->GetState(&pad->button, dpad_bitmasks);
m_dpad->GetState(&pad.button, dpad_bitmasks);
// sticks
m_main_stick->GetState(&x, &y);
pad->stickX =
pad.stickX =
static_cast<u8>(GCPadStatus::MAIN_STICK_CENTER_X + (x * GCPadStatus::MAIN_STICK_RADIUS));
pad->stickY =
pad.stickY =
static_cast<u8>(GCPadStatus::MAIN_STICK_CENTER_Y + (y * GCPadStatus::MAIN_STICK_RADIUS));
m_c_stick->GetState(&x, &y);
pad->substickX =
pad.substickX =
static_cast<u8>(GCPadStatus::C_STICK_CENTER_X + (x * GCPadStatus::C_STICK_RADIUS));
pad->substickY =
pad.substickY =
static_cast<u8>(GCPadStatus::C_STICK_CENTER_Y + (y * GCPadStatus::C_STICK_RADIUS));
// triggers
m_triggers->GetState(&pad->button, trigger_bitmasks, triggers);
pad->triggerLeft = static_cast<u8>(triggers[0] * 0xFF);
pad->triggerRight = static_cast<u8>(triggers[1] * 0xFF);
m_triggers->GetState(&pad.button, trigger_bitmasks, triggers);
pad.triggerLeft = static_cast<u8>(triggers[0] * 0xFF);
pad.triggerRight = static_cast<u8>(triggers[1] * 0xFF);
return pad;
}
void GCPad::SetOutput(const ControlState strength)
{
auto lock = ControllerEmu::GetStateLock();
m_rumble->controls[0]->control_ref->State(strength);
}
@ -190,5 +196,6 @@ void GCPad::LoadDefaults(const ControllerInterface& ciface)
bool GCPad::GetMicButton() const
{
auto lock = ControllerEmu::GetStateLock();
return (0.0f != m_buttons->controls.back()->control_ref->State());
}

View file

@ -12,7 +12,7 @@ class GCPad : public ControllerEmu
{
public:
GCPad(const unsigned int index);
void GetInput(GCPadStatus* const pad);
GCPadStatus GetInput() const;
void SetOutput(const ControlState strength);
bool GetMicButton() const;

View file

@ -132,16 +132,14 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* _pBuffer, int _iLength)
case 0x10:
{
DEBUG_LOG(AMBASEBOARDDEBUG, "GC-AM: Command 10, %02x (READ STATUS&SWITCHES)", ptr(1));
GCPadStatus PadStatus;
memset(&PadStatus, 0, sizeof(PadStatus));
Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus);
GCPadStatus pad_status = Pad::GetStatus(m_iDeviceNumber);
res[resp++] = 0x10;
res[resp++] = 0x2;
int d10_0 = 0xdf;
if (PadStatus.triggerLeft)
if (pad_status.triggerLeft)
d10_0 &= ~0x80;
if (PadStatus.triggerRight)
if (pad_status.triggerRight)
d10_0 &= ~0x40;
res[resp++] = d10_0;
@ -299,32 +297,31 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* _pBuffer, int _iLength)
msg.AddData(0); // tilt
for (i = 0; i < nr_players; ++i)
{
GCPadStatus PadStatus;
Pad::GetStatus(i, &PadStatus);
GCPadStatus pad_status = Pad::GetStatus(i);
unsigned char player_data[2] = { 0, 0 };
if (PadStatus.button & PAD_BUTTON_START)
if (pad_status.button & PAD_BUTTON_START)
player_data[0] |= 0x80;
if (PadStatus.button & PAD_BUTTON_UP)
if (pad_status.button & PAD_BUTTON_UP)
player_data[0] |= 0x20;
if (PadStatus.button & PAD_BUTTON_DOWN)
if (pad_status.button & PAD_BUTTON_DOWN)
player_data[0] |= 0x10;
if (PadStatus.button & PAD_BUTTON_LEFT)
if (pad_status.button & PAD_BUTTON_LEFT)
player_data[0] |= 0x08;
if (PadStatus.button & PAD_BUTTON_RIGHT)
if (pad_status.button & PAD_BUTTON_RIGHT)
player_data[0] |= 0x04;
if (PadStatus.button & PAD_BUTTON_A)
if (pad_status.button & PAD_BUTTON_A)
player_data[0] |= 0x02;
if (PadStatus.button & PAD_BUTTON_B)
if (pad_status.button & PAD_BUTTON_B)
player_data[0] |= 0x01;
if (PadStatus.button & PAD_BUTTON_X)
if (pad_status.button & PAD_BUTTON_X)
player_data[1] |= 0x80;
if (PadStatus.button & PAD_BUTTON_Y)
if (pad_status.button & PAD_BUTTON_Y)
player_data[1] |= 0x40;
if (PadStatus.button & PAD_TRIGGER_L)
if (pad_status.button & PAD_TRIGGER_L)
player_data[1] |= 0x20;
if (PadStatus.button & PAD_TRIGGER_R)
if (pad_status.button & PAD_TRIGGER_R)
player_data[1] |= 0x10;
for (j = 0; j < bytes_per_player; ++j)
@ -336,12 +333,11 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* _pBuffer, int _iLength)
{
int slots = *jvs_io++;
msg.AddData(1);
GCPadStatus PadStatus;
Pad::GetStatus(0, &PadStatus);
GCPadStatus pad_status = Pad::GetStatus(0);
while (slots--)
{
msg.AddData(0);
msg.AddData((PadStatus.button & PAD_BUTTON_START) ? 1 : 0);
msg.AddData((pad_status.button & PAD_BUTTON_START) ? 1 : 0);
}
break;
}

View file

@ -234,7 +234,7 @@ void GBASockServer::ClockSync()
}
}
void GBASockServer::Send(u8* si_buffer)
void GBASockServer::Send(const u8* si_buffer)
{
if (!client)
if (!GetAvailableSock(client))

View file

@ -26,7 +26,7 @@ public:
void ClockSync();
void Send(u8* si_buffer);
void Send(const u8* si_buffer);
int Receive(u8* si_buffer);
private:

View file

@ -25,19 +25,18 @@ CSIDevice_GCAdapter::CSIDevice_GCAdapter(SIDevices device, int _iDeviceNumber)
GCPadStatus CSIDevice_GCAdapter::GetPadStatus()
{
GCPadStatus PadStatus;
memset(&PadStatus, 0, sizeof(PadStatus));
GCPadStatus pad_status = {};
// For netplay, the local controllers are polled in GetNetPads(), and
// the remote controllers receive their status there as well
if (!NetPlay::IsNetPlayRunning())
{
GCAdapter::Input(ISIDevice::m_iDeviceNumber, &PadStatus);
pad_status = GCAdapter::Input(m_iDeviceNumber);
}
HandleMoviePadStatus(&PadStatus);
HandleMoviePadStatus(&pad_status);
return PadStatus;
return pad_status;
}
int CSIDevice_GCAdapter::RunBuffer(u8* buffer, int length)

View file

@ -140,18 +140,17 @@ void CSIDevice_GCController::HandleMoviePadStatus(GCPadStatus* PadStatus)
GCPadStatus CSIDevice_GCController::GetPadStatus()
{
GCPadStatus PadStatus;
memset(&PadStatus, 0, sizeof(PadStatus));
GCPadStatus pad_status = {};
// For netplay, the local controllers are polled in GetNetPads(), and
// the remote controllers receive their status there as well
if (!NetPlay::IsNetPlayRunning())
{
Pad::GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus);
pad_status = Pad::GetStatus(m_iDeviceNumber);
}
HandleMoviePadStatus(&PadStatus);
return PadStatus;
HandleMoviePadStatus(&pad_status);
return pad_status;
}
// GetData

View file

@ -56,9 +56,7 @@ int CSIDevice_Keyboard::RunBuffer(u8* _pBuffer, int _iLength)
KeyboardStatus CSIDevice_Keyboard::GetKeyboardStatus() const
{
KeyboardStatus KeyStatus = {};
Keyboard::GetStatus(ISIDevice::m_iDeviceNumber, &KeyStatus);
return KeyStatus;
return Keyboard::GetStatus(m_iDeviceNumber);
}
bool CSIDevice_Keyboard::GetData(u32& _Hi, u32& _Low)

View file

@ -37,6 +37,7 @@ void Initialize(void* const hwnd, InitializeMode init_mode)
}
g_controller_interface.Initialize(hwnd);
g_controller_interface.RegisterHotplugCallback(LoadConfig);
s_config.LoadConfig(false);

View file

@ -623,8 +623,11 @@ void Wiimote::Update()
return;
// returns true if a report was sent
if (Step())
return;
{
auto lock = ControllerEmu::GetStateLock();
if (Step())
return;
}
u8 data[MAX_PAYLOAD];
memset(data, 0, sizeof(data));
@ -646,6 +649,8 @@ void Wiimote::Update()
data[0] = 0xA1;
data[1] = m_reporting_mode;
auto lock = ControllerEmu::GetStateLock();
// core buttons
if (rptf.core)
GetButtonData(data + rptf.core);
@ -876,6 +881,7 @@ void Wiimote::ConnectOnInput()
}
u16 buttons = 0;
auto lock = ControllerEmu::GetStateLock();
m_buttons->GetState(&buttons, button_bitmasks);
m_dpad->GetState(&buttons, dpad_bitmasks);

View file

@ -12,7 +12,7 @@
#include "Common/Thread.h"
#include "Common/Timer.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/HW/WiimoteReal/IOAndroid.h"
// Global java_vm class
extern JavaVM* g_java_vm;
@ -22,45 +22,8 @@ namespace WiimoteReal
// Java classes
static jclass s_adapter_class;
class WiimoteAndroid final : public Wiimote
{
public:
WiimoteAndroid(int index);
~WiimoteAndroid() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() {}
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
int m_mayflash_index;
bool is_connected = true;
JNIEnv* m_env;
jmethodID m_input_func;
jmethodID m_output_func;
jbyteArray m_java_wiimote_payload;
};
WiimoteScanner::WiimoteScanner()
{
}
WiimoteScanner::~WiimoteScanner()
{
}
void WiimoteScanner::Update()
{
}
void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
void WiimoteScannerAndroid::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
Wiimote*& found_board)
{
found_wiimotes.clear();
found_board = nullptr;
@ -87,11 +50,6 @@ void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote
g_java_vm->DetachCurrentThread();
}
bool WiimoteScanner::IsReady() const
{
return true;
}
WiimoteAndroid::WiimoteAndroid(int index) : Wiimote(), m_mayflash_index(index)
{
}

View file

@ -0,0 +1,57 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#ifdef ANDROID
#include <jni.h>
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
class WiimoteAndroid final : public Wiimote
{
public:
WiimoteAndroid(int index);
~WiimoteAndroid() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override{};
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
int m_mayflash_index;
bool is_connected = true;
JNIEnv* m_env;
jmethodID m_input_func;
jmethodID m_output_func;
jbyteArray m_java_wiimote_payload;
};
class WiimoteScannerAndroid final : public WiimoteScannerBackend
{
public:
WiimoteScannerAndroid() = default;
~WiimoteScannerAndroid() override = default;
bool IsReady() const override { return true; }
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override {}
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerAndroid = WiimoteScannerDummy;
}
#endif

View file

@ -0,0 +1,20 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
class WiimoteScannerDummy final : public WiimoteScannerBackend
{
public:
WiimoteScannerDummy() = default;
~WiimoteScannerDummy() override = default;
bool IsReady() const override { return false; }
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override {}
void Update() override {}
};
}

View file

@ -0,0 +1,260 @@
// Copyright 2010 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>
#include <unistd.h>
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
#include "Core/HW/WiimoteReal/IOLinux.h"
namespace WiimoteReal
{
// This is used to store the Bluetooth address of connected Wiimotes,
// so we can ignore Wiimotes that are already connected.
static std::vector<std::string> s_known_addrs;
static bool IsNewWiimote(const std::string& addr)
{
return std::find(s_known_addrs.begin(), s_known_addrs.end(), addr) == s_known_addrs.end();
}
WiimoteScannerLinux::WiimoteScannerLinux() : m_device_id(-1), m_device_sock(-1)
{
// Get the id of the first Bluetooth device.
m_device_id = hci_get_route(nullptr);
if (m_device_id < 0)
{
NOTICE_LOG(WIIMOTE, "Bluetooth not found.");
return;
}
// Create a socket to the device
m_device_sock = hci_open_dev(m_device_id);
if (m_device_sock < 0)
{
ERROR_LOG(WIIMOTE, "Unable to open Bluetooth.");
return;
}
}
WiimoteScannerLinux::~WiimoteScannerLinux()
{
if (IsReady())
close(m_device_sock);
}
bool WiimoteScannerLinux::IsReady() const
{
return m_device_sock > 0;
}
void WiimoteScannerLinux::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
{
// supposedly 1.28 seconds
int const wait_len = 1;
int const max_infos = 255;
inquiry_info scan_infos[max_infos] = {};
auto* scan_infos_ptr = scan_infos;
found_board = nullptr;
// Use Limited Dedicated Inquiry Access Code (LIAC) to query, since third-party Wiimotes
// cannot be discovered without it.
const u8 lap[3] = {0x00, 0x8b, 0x9e};
// Scan for Bluetooth devices
int const found_devices =
hci_inquiry(m_device_id, wait_len, max_infos, lap, &scan_infos_ptr, IREQ_CACHE_FLUSH);
if (found_devices < 0)
{
ERROR_LOG(WIIMOTE, "Error searching for Bluetooth devices.");
return;
}
DEBUG_LOG(WIIMOTE, "Found %i Bluetooth device(s).", found_devices);
// Display discovered devices
for (int i = 0; i < found_devices; ++i)
{
ERROR_LOG(WIIMOTE, "found a device...");
// BT names are a maximum of 248 bytes apparently
char name[255] = {};
if (hci_read_remote_name(m_device_sock, &scan_infos[i].bdaddr, sizeof(name), name, 1000) < 0)
{
ERROR_LOG(WIIMOTE, "name request failed");
continue;
}
ERROR_LOG(WIIMOTE, "device name %s", name);
if (!IsValidBluetoothName(name))
continue;
char bdaddr_str[18] = {};
ba2str(&scan_infos[i].bdaddr, bdaddr_str);
if (!IsNewWiimote(bdaddr_str))
continue;
// Found a new device
s_known_addrs.push_back(bdaddr_str);
Wiimote* wm = new WiimoteLinux(scan_infos[i].bdaddr);
if (IsBalanceBoardName(name))
{
found_board = wm;
NOTICE_LOG(WIIMOTE, "Found balance board (%s).", bdaddr_str);
}
else
{
found_wiimotes.push_back(wm);
NOTICE_LOG(WIIMOTE, "Found Wiimote (%s).", bdaddr_str);
}
}
}
WiimoteLinux::WiimoteLinux(bdaddr_t bdaddr) : Wiimote(), m_bdaddr(bdaddr)
{
m_really_disconnect = true;
m_cmd_sock = -1;
m_int_sock = -1;
int fds[2];
if (pipe(fds))
{
ERROR_LOG(WIIMOTE, "pipe failed");
abort();
}
m_wakeup_pipe_w = fds[1];
m_wakeup_pipe_r = fds[0];
}
WiimoteLinux::~WiimoteLinux()
{
Shutdown();
close(m_wakeup_pipe_w);
close(m_wakeup_pipe_r);
}
// Connect to a Wiimote with a known address.
bool WiimoteLinux::ConnectInternal()
{
sockaddr_l2 addr = {};
addr.l2_family = AF_BLUETOOTH;
addr.l2_bdaddr = m_bdaddr;
addr.l2_cid = 0;
// Output channel
addr.l2_psm = htobs(WM_OUTPUT_CHANNEL);
if ((m_cmd_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
connect(m_cmd_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
{
WARN_LOG(WIIMOTE, "Unable to open output socket to Wiimote: %s", strerror(errno));
close(m_cmd_sock);
m_cmd_sock = -1;
return false;
}
// Input channel
addr.l2_psm = htobs(WM_INPUT_CHANNEL);
if ((m_int_sock = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) == -1 ||
connect(m_int_sock, (sockaddr*)&addr, sizeof(addr)) < 0)
{
WARN_LOG(WIIMOTE, "Unable to open input socket from Wiimote: %s", strerror(errno));
close(m_int_sock);
close(m_cmd_sock);
m_int_sock = m_cmd_sock = -1;
return false;
}
return true;
}
void WiimoteLinux::DisconnectInternal()
{
close(m_cmd_sock);
close(m_int_sock);
m_cmd_sock = -1;
m_int_sock = -1;
char bdaddr_str[18] = {};
ba2str(&m_bdaddr, bdaddr_str);
s_known_addrs.erase(std::remove(s_known_addrs.begin(), s_known_addrs.end(), bdaddr_str),
s_known_addrs.end());
}
bool WiimoteLinux::IsConnected() const
{
return m_cmd_sock != -1; // && int_sock != -1;
}
void WiimoteLinux::IOWakeup()
{
char c = 0;
if (write(m_wakeup_pipe_w, &c, 1) != 1)
{
ERROR_LOG(WIIMOTE, "Unable to write to wakeup pipe.");
}
}
// positive = read packet
// negative = didn't read packet
// zero = error
int WiimoteLinux::IORead(u8* buf)
{
// Block select for 1/2000th of a second
fd_set fds;
FD_ZERO(&fds);
FD_SET(m_int_sock, &fds);
FD_SET(m_wakeup_pipe_r, &fds);
if (select(m_int_sock + 1, &fds, nullptr, nullptr, nullptr) == -1)
{
ERROR_LOG(WIIMOTE, "Unable to select Wiimote %i input socket.", m_index + 1);
return -1;
}
if (FD_ISSET(m_wakeup_pipe_r, &fds))
{
char c;
if (read(m_wakeup_pipe_r, &c, 1) != 1)
{
ERROR_LOG(WIIMOTE, "Unable to read from wakeup pipe.");
}
return -1;
}
if (!FD_ISSET(m_int_sock, &fds))
return -1;
// Read the pending message into the buffer
int r = read(m_int_sock, buf, MAX_PAYLOAD);
if (r == -1)
{
// Error reading data
ERROR_LOG(WIIMOTE, "Receiving data from Wiimote %i.", m_index + 1);
if (errno == ENOTCONN)
{
// This can happen if the Bluetooth dongle is disconnected
ERROR_LOG(WIIMOTE, "Bluetooth appears to be disconnected. "
"Wiimote %i will be disconnected.",
m_index + 1);
}
r = 0;
}
return r;
}
int WiimoteLinux::IOWrite(u8 const* buf, size_t len)
{
return write(m_int_sock, buf, (int)len);
}
}; // WiimoteReal

View file

@ -0,0 +1,57 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#if defined(__linux__) && HAVE_BLUEZ
#include <bluetooth/bluetooth.h>
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
class WiimoteLinux final : public Wiimote
{
public:
WiimoteLinux(bdaddr_t bdaddr);
~WiimoteLinux() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
bdaddr_t m_bdaddr; // Bluetooth address
int m_cmd_sock; // Command socket
int m_int_sock; // Interrupt socket
int m_wakeup_pipe_w;
int m_wakeup_pipe_r;
};
class WiimoteScannerLinux final : public WiimoteScannerBackend
{
public:
WiimoteScannerLinux();
~WiimoteScannerLinux() override;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override{}; // not needed on Linux
private:
int m_device_id;
int m_device_sock;
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerLinux = WiimoteScannerDummy;
}
#endif

View file

@ -27,8 +27,7 @@
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "Common/Thread.h"
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/HW/WiimoteReal/IOWin.h"
//#define AUTHENTICATE_WIIMOTES
#define SHARE_WRITE_WIIMOTES
@ -192,28 +191,6 @@ inline void init_lib()
namespace WiimoteReal
{
class WiimoteWindows final : public Wiimote
{
public:
WiimoteWindows(const std::basic_string<TCHAR>& path, WinWriteMethod initial_write_method);
~WiimoteWindows() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
std::basic_string<TCHAR> m_devicepath; // Unique Wiimote reference
HANDLE m_dev_handle; // HID handle
OVERLAPPED m_hid_overlap_read; // Overlap handles
OVERLAPPED m_hid_overlap_write;
WinWriteMethod m_write_method; // Type of Write Method to use
};
int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, enum WinWriteMethod& stack,
const u8* buf, size_t len, DWORD* written);
int IORead(HANDLE& dev_handle, OVERLAPPED& hid_overlap_read, u8* buf, int index);
@ -225,12 +202,12 @@ bool AttachWiimote(HANDLE hRadio, const BLUETOOTH_RADIO_INFO&, BLUETOOTH_DEVICE_
void RemoveWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
bool ForgetWiimote(BLUETOOTH_DEVICE_INFO_STRUCT&);
WiimoteScanner::WiimoteScanner()
WiimoteScannerWindows::WiimoteScannerWindows()
{
init_lib();
}
WiimoteScanner::~WiimoteScanner()
WiimoteScannerWindows::~WiimoteScannerWindows()
{
// TODO: what do we want here?
#if 0
@ -241,7 +218,7 @@ WiimoteScanner::~WiimoteScanner()
#endif
}
void WiimoteScanner::Update()
void WiimoteScannerWindows::Update()
{
if (!s_loaded_ok)
return;
@ -371,7 +348,8 @@ static WinWriteMethod GetInitialWriteMethod(bool IsUsingToshibaStack)
// Does not replace already found Wiimotes even if they are disconnected.
// wm is an array of max_wiimotes Wiimotes
// Returns the total number of found and connected Wiimotes.
void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
void WiimoteScannerWindows::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
Wiimote*& found_board)
{
if (!s_loaded_ok)
return;
@ -480,8 +458,9 @@ int CheckDeviceType_Read(HANDLE& dev_handle, u8* buf, int attempts)
// Wiimote.
// Because nothing on Windows should be easy.
// (We can't seem to easily identify the Bluetooth device an HID device belongs to...)
void WiimoteScanner::CheckDeviceType(std::basic_string<TCHAR>& devicepath,
WinWriteMethod& write_method, bool& real_wiimote, bool& is_bb)
void WiimoteScannerWindows::CheckDeviceType(std::basic_string<TCHAR>& devicepath,
WinWriteMethod& write_method, bool& real_wiimote,
bool& is_bb)
{
real_wiimote = false;
is_bb = false;
@ -606,7 +585,7 @@ void WiimoteScanner::CheckDeviceType(std::basic_string<TCHAR>& devicepath,
CloseHandle(dev_handle);
}
bool WiimoteScanner::IsReady() const
bool WiimoteScannerWindows::IsReady() const
{
if (!s_loaded_ok)
{

View file

@ -0,0 +1,66 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#ifdef _WIN32
#include <windows.h>
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
// Different methods to send data Wiimote on Windows depending on OS and Bluetooth Stack
enum WinWriteMethod
{
WWM_WRITE_FILE_LARGEST_REPORT_SIZE,
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE,
WWM_SET_OUTPUT_REPORT
};
class WiimoteWindows final : public Wiimote
{
public:
WiimoteWindows(const std::basic_string<TCHAR>& path, WinWriteMethod initial_write_method);
~WiimoteWindows() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
std::basic_string<TCHAR> m_devicepath; // Unique Wiimote reference
HANDLE m_dev_handle; // HID handle
OVERLAPPED m_hid_overlap_read; // Overlap handles
OVERLAPPED m_hid_overlap_write;
WinWriteMethod m_write_method; // Type of Write Method to use
};
class WiimoteScannerWindows final : public WiimoteScannerBackend
{
public:
WiimoteScannerWindows();
~WiimoteScannerWindows() override;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override;
private:
void CheckDeviceType(std::basic_string<TCHAR>& devicepath, WinWriteMethod& write_method,
bool& real_wiimote, bool& is_bb);
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerWindows = WiimoteScannerDummy;
}
#endif

View file

@ -0,0 +1,106 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#ifdef __APPLE__
// Work around an Apple bug: for some reason, IOBluetooth.h errors on
// inclusion in Mavericks, but only in Objective-C++ C++11 mode. I filed
// this as <rdar://15312520>; in the meantime...
#import <Foundation/Foundation.h>
#undef NS_ENUM_AVAILABLE
#define NS_ENUM_AVAILABLE(...)
// end hack
#import <IOBluetooth/IOBluetooth.h>
#include <IOKit/hid/IOHIDManager.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include "Core/HW/WiimoteReal/WiimoteReal.h"
namespace WiimoteReal
{
class WiimoteDarwin final : public Wiimote
{
public:
WiimoteDarwin(IOBluetoothDevice* device);
~WiimoteDarwin() override;
// These are not protected/private because ConnectBT needs them.
void DisconnectInternal() override;
IOBluetoothDevice* m_btd;
unsigned char* m_input;
int m_inputlen;
protected:
bool ConnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
void EnablePowerAssertionInternal() override;
void DisablePowerAssertionInternal() override;
private:
IOBluetoothL2CAPChannel* m_ichan;
IOBluetoothL2CAPChannel* m_cchan;
bool m_connected;
CFRunLoopRef m_wiimote_thread_run_loop;
IOPMAssertionID m_pm_assertion;
};
class WiimoteDarwinHid final : public Wiimote
{
public:
WiimoteDarwinHid(IOHIDDeviceRef device);
~WiimoteDarwinHid() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
static void ReportCallback(void* context, IOReturn result, void* sender, IOHIDReportType type,
u32 reportID, u8* report, CFIndex reportLength);
static void RemoveCallback(void* context, IOReturn result, void* sender);
void QueueBufferReport(int length);
IOHIDDeviceRef m_device;
bool m_connected;
std::atomic<bool> m_interrupted;
Report m_report_buffer;
Common::FifoQueue<Report> m_buffered_reports;
};
class WiimoteScannerDarwin final : public WiimoteScannerBackend
{
public:
WiimoteScannerDarwin() = default;
~WiimoteScannerDarwin() override = default;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override{}; // not needed
};
class WiimoteScannerDarwinHID final : public WiimoteScannerBackend
{
public:
WiimoteScannerDarwinHID() = default;
~WiimoteScannerDarwinHID() override = default;
bool IsReady() const override;
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) override;
void Update() override{}; // not needed
};
}
#else
#include "Core/HW/WiimoteReal/IODummy.h"
namespace WiimoteReal
{
using WiimoteScannerDarwin = WiimoteScannerDummy;
using WiimoteScannerDarwinHID = WiimoteScannerDummy;
}
#endif

View file

@ -1,9 +1,9 @@
#define BLUETOOTH_VERSION_USE_CURRENT
#include "Core/HW/WiimoteReal/IOdarwin.h"
#include "Common/Common.h"
#include "Common/Logging/Log.h"
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
@interface SearchBT : NSObject
{
@ -20,188 +20,123 @@
namespace WiimoteReal
{
class WiimoteDarwin final : public Wiimote
{
public:
WiimoteDarwin(IOBluetoothDevice* device);
~WiimoteDarwin() override;
// These are not protected/private because ConnectBT needs them.
void DisconnectInternal() override;
IOBluetoothDevice* m_btd;
unsigned char* m_input;
int m_inputlen;
protected:
bool ConnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
void EnablePowerAssertionInternal() override;
void DisablePowerAssertionInternal() override;
private:
IOBluetoothL2CAPChannel* m_ichan;
IOBluetoothL2CAPChannel* m_cchan;
bool m_connected;
CFRunLoopRef m_wiimote_thread_run_loop;
IOPMAssertionID m_pm_assertion;
};
class WiimoteDarwinHid final : public Wiimote
{
public:
WiimoteDarwinHid(IOHIDDeviceRef device);
~WiimoteDarwinHid() override;
protected:
bool ConnectInternal() override;
void DisconnectInternal() override;
bool IsConnected() const override;
void IOWakeup() override;
int IORead(u8* buf) override;
int IOWrite(u8 const* buf, size_t len) override;
private:
static void ReportCallback(void* context, IOReturn result, void* sender, IOHIDReportType type,
u32 reportID, u8* report, CFIndex reportLength);
static void RemoveCallback(void* context, IOReturn result, void* sender);
void QueueBufferReport(int length);
IOHIDDeviceRef m_device;
bool m_connected;
std::atomic<bool> m_interrupted;
Report m_report_buffer;
Common::FifoQueue<Report> m_buffered_reports;
};
WiimoteScanner::WiimoteScanner()
{
}
WiimoteScanner::~WiimoteScanner()
{
}
void WiimoteScanner::Update()
{
}
void WiimoteScanner::FindWiimotes(std::vector<Wiimote*>& found_wiimotes, Wiimote*& found_board)
void WiimoteScannerDarwin::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
Wiimote*& found_board)
{
// TODO: find the device in the constructor and save it for later
IOBluetoothHostController* bth;
IOBluetoothDeviceInquiry* bti;
IOHIDManagerRef hid;
SearchBT* sbt;
NSEnumerator* en;
found_board = nullptr;
bool btFailed = false;
bool hidFailed = false;
bth = [[IOBluetoothHostController alloc] init];
btFailed = [bth addressAsString] == nil;
bool btFailed = [bth addressAsString] == nil;
if (btFailed)
WARN_LOG(WIIMOTE, "No Bluetooth host controller");
hid = IOHIDManagerCreate(NULL, kIOHIDOptionsTypeNone);
hidFailed = CFGetTypeID(hid) != IOHIDManagerGetTypeID();
if (hidFailed)
WARN_LOG(WIIMOTE, "No HID manager");
if (hidFailed && btFailed)
{
CFRelease(hid);
WARN_LOG(WIIMOTE, "No Bluetooth host controller");
[bth release];
return;
}
if (!btFailed)
SearchBT* sbt = [[SearchBT alloc] init];
sbt->maxDevices = 32;
bti = [[IOBluetoothDeviceInquiry alloc] init];
[bti setDelegate:sbt];
[bti setInquiryLength:2];
if ([bti start] != kIOReturnSuccess)
{
sbt = [[SearchBT alloc] init];
sbt->maxDevices = 32;
bti = [[IOBluetoothDeviceInquiry alloc] init];
[bti setDelegate:sbt];
[bti setInquiryLength:2];
ERROR_LOG(WIIMOTE, "Unable to do Bluetooth discovery");
[bth release];
[sbt release];
btFailed = true;
}
if ([bti start] != kIOReturnSuccess)
do
{
CFRunLoopRun();
} while (!sbt->done);
int found_devices = [[bti foundDevices] count];
if (found_devices)
NOTICE_LOG(WIIMOTE, "Found %i Bluetooth devices", found_devices);
NSEnumerator* en = [[bti foundDevices] objectEnumerator];
for (int i = 0; i < found_devices; i++)
{
IOBluetoothDevice* dev = [en nextObject];
if (!IsValidBluetoothName([[dev name] UTF8String]))
continue;
Wiimote* wm = new WiimoteDarwin([dev retain]);
if (IsBalanceBoardName([[dev name] UTF8String]))
{
ERROR_LOG(WIIMOTE, "Unable to do Bluetooth discovery");
[bth release];
[sbt release];
btFailed = true;
found_board = wm;
}
do
else
{
CFRunLoopRun();
} while (!sbt->done);
found_wiimotes.push_back(wm);
}
}
int found_devices = [[bti foundDevices] count];
[bth release];
[bti release];
[sbt release];
}
bool WiimoteScannerDarwin::IsReady() const
{
// TODO: only return true when a BT device is present
return true;
}
void WiimoteScannerDarwinHID::FindWiimotes(std::vector<Wiimote*>& found_wiimotes,
Wiimote*& found_board)
{
found_board = nullptr;
IOHIDManagerRef hid = IOHIDManagerCreate(NULL, kIOHIDOptionsTypeNone);
bool hidFailed = CFGetTypeID(hid) != IOHIDManagerGetTypeID();
if (hidFailed)
{
CFRelease(hid);
WARN_LOG(WIIMOTE, "No HID manager");
return;
}
NSArray* criteria = @[
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0306 },
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0330 },
];
IOHIDManagerSetDeviceMatchingMultiple(hid, (CFArrayRef)criteria);
if (IOHIDManagerOpen(hid, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
WARN_LOG(WIIMOTE, "Failed to open HID Manager");
CFSetRef devices = IOHIDManagerCopyDevices(hid);
if (devices)
{
int found_devices = CFSetGetCount(devices);
if (found_devices)
NOTICE_LOG(WIIMOTE, "Found %i Bluetooth devices", found_devices);
en = [[bti foundDevices] objectEnumerator];
for (int i = 0; i < found_devices; i++)
{
IOBluetoothDevice* dev = [en nextObject];
if (!IsValidBluetoothName([[dev name] UTF8String]))
continue;
NOTICE_LOG(WIIMOTE, "Found %i HID devices", found_devices);
Wiimote* wm = new WiimoteDarwin([dev retain]);
if (IsBalanceBoardName([[dev name] UTF8String]))
{
found_board = wm;
}
else
IOHIDDeviceRef values[found_devices];
CFSetGetValues(devices, reinterpret_cast<const void**>(&values));
for (int i = 0; i < found_devices; i++)
{
Wiimote* wm = new WiimoteDarwinHid(values[i]);
found_wiimotes.push_back(wm);
}
}
[bth release];
[bti release];
[sbt release];
}
if (!hidFailed)
{
NSArray* criteria = @[
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0306 },
@{ @kIOHIDVendorIDKey : @0x057e,
@kIOHIDProductIDKey : @0x0330 },
];
IOHIDManagerSetDeviceMatchingMultiple(hid, (CFArrayRef)criteria);
if (IOHIDManagerOpen(hid, kIOHIDOptionsTypeNone) != kIOReturnSuccess)
WARN_LOG(WIIMOTE, "Failed to open HID Manager");
CFSetRef devices = IOHIDManagerCopyDevices(hid);
if (devices)
{
int found_devices = CFSetGetCount(devices);
if (found_devices)
{
NOTICE_LOG(WIIMOTE, "Found %i HID devices", found_devices);
IOHIDDeviceRef values[found_devices];
CFSetGetValues(devices, reinterpret_cast<const void**>(&values));
for (int i = 0; i < found_devices; i++)
{
Wiimote* wm = new WiimoteDarwinHid(values[i]);
found_wiimotes.push_back(wm);
}
}
}
CFRelease(hid);
}
CFRelease(hid);
}
bool WiimoteScanner::IsReady() const
bool WiimoteScannerDarwinHID::IsReady() const
{
// TODO: only return true when a BT device is present
// TODO: only return true when !hidFailed
return true;
}

View file

@ -6,6 +6,8 @@
#include <cstdlib>
#include <queue>
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/IniFile.h"
@ -15,7 +17,10 @@
#include "Core/Core.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteEmu/WiimoteHid.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/HW/WiimoteReal/IOAndroid.h"
#include "Core/HW/WiimoteReal/IOLinux.h"
#include "Core/HW/WiimoteReal/IOWin.h"
#include "Core/HW/WiimoteReal/IOdarwin.h"
#include "Core/Host.h"
#include "InputCommon/InputConfig.h"
@ -452,6 +457,17 @@ void WiimoteScanner::SetScanMode(WiimoteScanMode scan_mode)
m_scan_mode_changed_event.Set();
}
bool WiimoteScanner::IsReady() const
{
return std::any_of(m_scanner_backends.begin(), m_scanner_backends.end(),
[](const auto& backend) { return backend->IsReady(); });
}
void WiimoteScanner::AddScannerBackend(std::unique_ptr<WiimoteScannerBackend> backend)
{
m_scanner_backends.emplace_back(std::move(backend));
}
static void CheckForDisconnectedWiimotes()
{
std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
@ -475,21 +491,24 @@ void WiimoteScanner::ThreadFunc()
if (m_scan_mode.load() == WiimoteScanMode::DO_NOT_SCAN)
continue;
if (CalculateWantedWiimotes() != 0 || CalculateWantedBB() != 0)
for (const auto& backend : m_scanner_backends)
{
std::vector<Wiimote*> found_wiimotes;
Wiimote* found_board = nullptr;
FindWiimotes(found_wiimotes, found_board);
if (CalculateWantedWiimotes() != 0 || CalculateWantedBB() != 0)
{
std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
std::for_each(found_wiimotes.begin(), found_wiimotes.end(), TryToConnectWiimote);
if (found_board)
TryToConnectBalanceBoard(found_board);
std::vector<Wiimote*> found_wiimotes;
Wiimote* found_board = nullptr;
backend->FindWiimotes(found_wiimotes, found_board);
{
std::lock_guard<std::mutex> lk(g_wiimotes_mutex);
std::for_each(found_wiimotes.begin(), found_wiimotes.end(), TryToConnectWiimote);
if (found_board)
TryToConnectBalanceBoard(found_board);
}
}
else
{
backend->Update(); // Does stuff needed to detect disconnects on Windows
}
}
else
{
Update(); // Does stuff needed to detect disconnects on Windows
}
if (m_scan_mode.load() == WiimoteScanMode::SCAN_ONCE)
@ -615,7 +634,14 @@ void LoadSettings()
void Initialize(::Wiimote::InitializeMode init_mode)
{
if (!g_real_wiimotes_initialized)
{
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerLinux>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerAndroid>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerWindows>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerDarwin>());
g_wiimote_scanner.AddScannerBackend(std::make_unique<WiimoteScannerDarwinHID>());
g_wiimote_scanner.StartThread();
}
if (SConfig::GetInstance().m_WiimoteContinuousScanning)
g_wiimote_scanner.SetScanMode(WiimoteScanMode::CONTINUOUSLY_SCAN);

View file

@ -114,6 +114,16 @@ private:
Common::FifoQueue<Report> m_write_reports;
};
class WiimoteScannerBackend
{
public:
virtual ~WiimoteScannerBackend() = default;
virtual bool IsReady() const = 0;
virtual void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&) = 0;
// function called when not looking for more Wiimotes
virtual void Update() = 0;
};
enum class WiimoteScanMode
{
DO_NOT_SCAN,
@ -124,36 +134,23 @@ enum class WiimoteScanMode
class WiimoteScanner
{
public:
WiimoteScanner();
~WiimoteScanner();
bool IsReady() const;
WiimoteScanner() = default;
void StartThread();
void StopThread();
void SetScanMode(WiimoteScanMode scan_mode);
void FindWiimotes(std::vector<Wiimote*>&, Wiimote*&);
// function called when not looking for more Wiimotes
void Update();
void AddScannerBackend(std::unique_ptr<WiimoteScannerBackend> backend);
bool IsReady() const;
private:
void ThreadFunc();
std::thread m_scan_thread;
Common::Flag m_scan_thread_running;
Common::Event m_scan_mode_changed_event;
Common::Flag m_scan_thread_running;
std::atomic<WiimoteScanMode> m_scan_mode{ WiimoteScanMode::DO_NOT_SCAN };
#if defined(_WIN32)
void CheckDeviceType(std::basic_string<TCHAR>& devicepath, WinWriteMethod& write_method,
bool& real_wiimote, bool& is_bb);
#elif defined(__linux__) && HAVE_BLUEZ
int device_id;
int device_sock;
#endif
std::vector<std::unique_ptr<WiimoteScannerBackend>> m_scanner_backends;
};
extern std::mutex g_wiimotes_mutex;

View file

@ -4,23 +4,6 @@
#pragma once
#ifdef _WIN32
#include <windows.h>
#elif defined(__APPLE__)
// Work around an Apple bug: for some reason, IOBluetooth.h errors on
// inclusion in Mavericks, but only in Objective-C++ C++11 mode. I filed
// this as <rdar://15312520>; in the meantime...
#import <Foundation/Foundation.h>
#undef NS_ENUM_AVAILABLE
#define NS_ENUM_AVAILABLE(...)
// end hack
#import <IOBluetooth/IOBluetooth.h>
#include <IOKit/hid/IOHIDManager.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#elif defined(__linux__) && HAVE_BLUEZ
#include <bluetooth/bluetooth.h>
#endif
// Wiimote internal codes
// Communication channels
@ -51,13 +34,3 @@
// It's 23. NOT 32!
#define MAX_PAYLOAD 23
#define WIIMOTE_DEFAULT_TIMEOUT 1000
#ifdef _WIN32
// Different methods to send data Wiimote on Windows depending on OS and Bluetooth Stack
enum WinWriteMethod
{
WWM_WRITE_FILE_LARGEST_REPORT_SIZE,
WWM_WRITE_FILE_ACTUAL_REPORT_SIZE,
WWM_SET_OUTPUT_REPORT
};
#endif

View file

@ -12,124 +12,124 @@
const std::string hotkey_labels[] =
{
_trans("Open"),
_trans("Change Disc"),
_trans("Refresh List"),
_trans("Open"),
_trans("Change Disc"),
_trans("Refresh List"),
_trans("Toggle Pause"),
_trans("Stop"),
_trans("Reset"),
_trans("Frame Advance"),
_trans("Frame Advance Decrease Speed"),
_trans("Frame Advance Increase Speed"),
_trans("Frame Advance Reset Speed"),
_trans("Toggle Pause"),
_trans("Stop"),
_trans("Reset"),
_trans("Frame Advance"),
_trans("Frame Advance Decrease Speed"),
_trans("Frame Advance Increase Speed"),
_trans("Frame Advance Reset Speed"),
_trans("Start Recording"),
_trans("Play Recording"),
_trans("Export Recording"),
_trans("Read-only mode"),
_trans("Start Recording"),
_trans("Play Recording"),
_trans("Export Recording"),
_trans("Read-only mode"),
_trans("Toggle Fullscreen"),
_trans("Take Screenshot"),
_trans("Exit"),
_trans("Toggle Fullscreen"),
_trans("Take Screenshot"),
_trans("Exit"),
_trans("Connect Wiimote 1"),
_trans("Connect Wiimote 2"),
_trans("Connect Wiimote 3"),
_trans("Connect Wiimote 4"),
_trans("Connect Balance Board"),
_trans("Connect Wiimote 1"),
_trans("Connect Wiimote 2"),
_trans("Connect Wiimote 3"),
_trans("Connect Wiimote 4"),
_trans("Connect Balance Board"),
_trans("Volume Down"),
_trans("Volume Up"),
_trans("Volume Toggle Mute"),
_trans("Volume Down"),
_trans("Volume Up"),
_trans("Volume Toggle Mute"),
_trans("Increase IR"),
_trans("Decrease IR"),
_trans("Increase IR"),
_trans("Decrease IR"),
_trans("Toggle Crop"),
_trans("Toggle Aspect Ratio"),
_trans("Toggle EFB Copies"),
_trans("Toggle Fog"),
_trans("Disable Emulation Speed Limit"),
_trans("Decrease Emulation Speed"),
_trans("Increase Emulation Speed"),
_trans("Toggle Crop"),
_trans("Toggle Aspect Ratio"),
_trans("Toggle EFB Copies"),
_trans("Toggle Fog"),
_trans("Disable Emulation Speed Limit"),
_trans("Decrease Emulation Speed"),
_trans("Increase Emulation Speed"),
_trans("Freelook Decrease Speed"),
_trans("Freelook Increase Speed"),
_trans("Freelook Reset Speed"),
_trans("Freelook Move Up"),
_trans("Freelook Move Down"),
_trans("Freelook Move Left"),
_trans("Freelook Move Right"),
_trans("Freelook Zoom In"),
_trans("Freelook Zoom Out"),
_trans("Freelook Reset"),
_trans("Freelook Decrease Speed"),
_trans("Freelook Increase Speed"),
_trans("Freelook Reset Speed"),
_trans("Freelook Move Up"),
_trans("Freelook Move Down"),
_trans("Freelook Move Left"),
_trans("Freelook Move Right"),
_trans("Freelook Zoom In"),
_trans("Freelook Zoom Out"),
_trans("Freelook Reset"),
_trans("Toggle 3D Side-by-side"),
_trans("Toggle 3D Top-bottom"),
_trans("Toggle 3D Anaglyph"),
_trans("Toggle 3D Vision"),
_trans("Toggle 3D Side-by-side"),
_trans("Toggle 3D Top-bottom"),
_trans("Toggle 3D Anaglyph"),
_trans("Toggle 3D Vision"),
_trans("Decrease Depth"),
_trans("Increase Depth"),
_trans("Decrease Convergence"),
_trans("Increase Convergence"),
_trans("Decrease Depth"),
_trans("Increase Depth"),
_trans("Decrease Convergence"),
_trans("Increase Convergence"),
_trans("Load State Slot 1"),
_trans("Load State Slot 2"),
_trans("Load State Slot 3"),
_trans("Load State Slot 4"),
_trans("Load State Slot 5"),
_trans("Load State Slot 6"),
_trans("Load State Slot 7"),
_trans("Load State Slot 8"),
_trans("Load State Slot 9"),
_trans("Load State Slot 10"),
_trans("Load State Slot 1"),
_trans("Load State Slot 2"),
_trans("Load State Slot 3"),
_trans("Load State Slot 4"),
_trans("Load State Slot 5"),
_trans("Load State Slot 6"),
_trans("Load State Slot 7"),
_trans("Load State Slot 8"),
_trans("Load State Slot 9"),
_trans("Load State Slot 10"),
_trans("Save State Slot 1"),
_trans("Save State Slot 2"),
_trans("Save State Slot 3"),
_trans("Save State Slot 4"),
_trans("Save State Slot 5"),
_trans("Save State Slot 6"),
_trans("Save State Slot 7"),
_trans("Save State Slot 8"),
_trans("Save State Slot 9"),
_trans("Save State Slot 10"),
_trans("Save State Slot 1"),
_trans("Save State Slot 2"),
_trans("Save State Slot 3"),
_trans("Save State Slot 4"),
_trans("Save State Slot 5"),
_trans("Save State Slot 6"),
_trans("Save State Slot 7"),
_trans("Save State Slot 8"),
_trans("Save State Slot 9"),
_trans("Save State Slot 10"),
_trans("Select State Slot 1"),
_trans("Select State Slot 2"),
_trans("Select State Slot 3"),
_trans("Select State Slot 4"),
_trans("Select State Slot 5"),
_trans("Select State Slot 6"),
_trans("Select State Slot 7"),
_trans("Select State Slot 8"),
_trans("Select State Slot 9"),
_trans("Select State Slot 10"),
_trans("Select State Slot 1"),
_trans("Select State Slot 2"),
_trans("Select State Slot 3"),
_trans("Select State Slot 4"),
_trans("Select State Slot 5"),
_trans("Select State Slot 6"),
_trans("Select State Slot 7"),
_trans("Select State Slot 8"),
_trans("Select State Slot 9"),
_trans("Select State Slot 10"),
_trans("Save to selected slot"),
_trans("Load from selected slot"),
_trans("Save to selected slot"),
_trans("Load from selected slot"),
_trans("Load State Last 1"),
_trans("Load State Last 2"),
_trans("Load State Last 3"),
_trans("Load State Last 4"),
_trans("Load State Last 5"),
_trans("Load State Last 6"),
_trans("Load State Last 7"),
_trans("Load State Last 8"),
_trans("Load State Last 9"),
_trans("Load State Last 10"),
_trans("Load State Last 1"),
_trans("Load State Last 2"),
_trans("Load State Last 3"),
_trans("Load State Last 4"),
_trans("Load State Last 5"),
_trans("Load State Last 6"),
_trans("Load State Last 7"),
_trans("Load State Last 8"),
_trans("Load State Last 9"),
_trans("Load State Last 10"),
_trans("Save Oldest State"),
_trans("Undo Load State"),
_trans("Undo Save State"),
_trans("Save State"),
_trans("Load State"),
_trans("Reload Post-Processing Shaders"),
_trans("Switch Hires Textures"),
_trans("Switch Material Textures"),
_trans("Save Oldest State"),
_trans("Undo Load State"),
_trans("Undo Save State"),
_trans("Save State"),
_trans("Load State"),
_trans("Reload Post-Processing Shaders"),
_trans("Switch Hires Textures"),
_trans("Switch Material Textures"),
};
static_assert(NUM_HOTKEYS == sizeof(hotkey_labels) / sizeof(hotkey_labels[0]),
"Wrong count of hotkey_labels");
@ -190,6 +190,7 @@ void Initialize(void* const hwnd)
s_config.CreateController<HotkeyManager>();
g_controller_interface.Initialize(hwnd);
g_controller_interface.RegisterHotplugCallback(LoadConfig);
// load the saved controller config
s_config.LoadConfig(true);
@ -243,6 +244,7 @@ std::string HotkeyManager::GetName() const
void HotkeyManager::GetInput(HotkeyStatus* const kb)
{
auto lock = ControllerEmu::GetStateLock();
for (int set = 0; set < (NUM_HOTKEYS + 31) / 32; set++)
{
std::vector<u32> bitmasks;

View file

@ -1147,15 +1147,15 @@ u32 CWII_IPC_HLE_Device_es::ES_DIVerify(const std::vector<u8>& tmd)
File::CreateFullPath(tmd_path);
File::CreateFullPath(Common::GetTitleDataPath(tmd_title_id, Common::FROM_SESSION_ROOT));
Movie::g_titleID = tmd_title_id;
Movie::SetTitleId(tmd_title_id);
std::string save_path = Common::GetTitleDataPath(tmd_title_id, Common::FROM_SESSION_ROOT);
if (Movie::IsRecordingInput())
{
// TODO: Check for the actual save data
if (File::Exists(save_path + "banner.bin"))
Movie::g_bClearSave = false;
Movie::SetClearSave(false);
else
Movie::g_bClearSave = true;
Movie::SetClearSave(true);
}
// TODO: Force the game to save to another location, instead of moving the user's save.

View file

@ -61,10 +61,10 @@ static DTMHeader tmpHeader;
static u8* tmpInput = nullptr;
static size_t tmpInputAllocated = 0;
static u64 s_currentByte = 0, s_totalBytes = 0;
u64 g_currentFrame = 0, g_totalFrames = 0; // VI
u64 g_currentLagCount = 0;
static u64 s_currentFrame = 0, s_totalFrames = 0; // VI
static u64 s_currentLagCount = 0;
static u64 s_totalLagCount = 0; // just stats
u64 g_currentInputCount = 0, g_totalInputCount = 0; // just stats
static u64 s_currentInputCount = 0, s_totalInputCount = 0; // just stats
static u64 s_totalTickCount = 0, s_tickCountAtLastInput = 0; // just stats
static u64 s_recordingStartTime; // seconds since 1970 that recording started
static bool s_bSaveConfig = false, s_bSkipIdle = false, s_bDualCore = false;
@ -73,12 +73,12 @@ static bool s_bDSPHLE = false, s_bFastDiscSpeed = false;
static bool s_bSyncGPU = false, s_bNetPlay = false;
static std::string s_videoBackend = "unknown";
static int s_iCPUCore = 1;
bool g_bClearSave = false;
bool g_bDiscChange = false;
bool g_bReset = false;
static bool s_bClearSave = false;
static bool s_bDiscChange = false;
static bool s_bReset = false;
static std::string s_author = "";
std::string g_discChange = "";
u64 g_titleID = 0;
static std::string s_discChange = "";
static u64 s_titleID = 0;
static u8 s_MD5[16];
static u8 s_bongos, s_memcards;
static u8 s_revision[20];
@ -196,14 +196,14 @@ void FrameUpdate()
{
// TODO[comex]: This runs on the GPU thread, yet it messes with the CPU
// state directly. That's super sketchy.
g_currentFrame++;
s_currentFrame++;
if (!s_bPolled)
g_currentLagCount++;
s_currentLagCount++;
if (IsRecordingInput())
{
g_totalFrames = g_currentFrame;
s_totalLagCount = g_currentLagCount;
s_totalFrames = s_currentFrame;
s_totalLagCount = s_currentLagCount;
}
if (s_bFrameStep)
{
@ -260,19 +260,19 @@ void Init()
s_bRecordingFromSaveState = false;
s_rerecords = 0;
s_currentByte = 0;
g_currentFrame = 0;
g_currentLagCount = 0;
g_currentInputCount = 0;
s_currentFrame = 0;
s_currentLagCount = 0;
s_currentInputCount = 0;
}
}
// NOTE: CPU Thread
void InputUpdate()
{
g_currentInputCount++;
s_currentInputCount++;
if (IsRecordingInput())
{
g_totalInputCount = g_currentInputCount;
s_totalInputCount = s_currentInputCount;
s_totalTickCount += CoreTiming::GetTicks() - s_tickCountAtLastInput;
s_tickCountAtLastInput = CoreTiming::GetTicks();
}
@ -352,12 +352,12 @@ bool IsRecordingInputFromSaveState()
bool IsJustStartingRecordingInputFromSaveState()
{
return IsRecordingInputFromSaveState() && g_currentFrame == 0;
return IsRecordingInputFromSaveState() && s_currentFrame == 0;
}
bool IsJustStartingPlayingInputFromSaveState()
{
return IsRecordingInputFromSaveState() && g_currentFrame == 1 && IsPlayingInput();
return IsRecordingInputFromSaveState() && s_currentFrame == 1 && IsPlayingInput();
}
bool IsPlayingInput()
@ -380,6 +380,57 @@ u64 GetRecordingStartTime()
return s_recordingStartTime;
}
u64 GetCurrentFrame()
{
return s_currentFrame;
}
u64 GetTotalFrames()
{
return s_totalFrames;
}
u64 GetCurrentInputCount()
{
return s_currentInputCount;
}
u64 GetTotalInputCount()
{
return s_totalInputCount;
}
u64 GetCurrentLagCount()
{
return s_currentLagCount;
}
u64 GetTotalLagCount()
{
return s_totalLagCount;
}
void SetClearSave(bool enabled)
{
s_bClearSave = enabled;
}
void SignalDiscChange(const std::string& new_disc_filename)
{
s_discChange = new_disc_filename;
s_bDiscChange = true;
}
void SetReset(bool reset)
{
s_bReset = reset;
}
void SetTitleId(u64 title_id)
{
s_titleID = title_id;
}
bool IsUsingPad(int controller)
{
return ((s_numPads & (1 << controller)) != 0);
@ -441,7 +492,7 @@ u8 GetLanguage()
bool IsStartingFromClearSave()
{
return g_bClearSave;
return s_bClearSave;
}
bool IsUsingMemcard(int memcard)
@ -520,9 +571,9 @@ bool BeginRecordingInput(int controllers)
bool was_unpaused = Core::PauseAndLock(true);
s_numPads = controllers;
g_currentFrame = g_totalFrames = 0;
g_currentLagCount = s_totalLagCount = 0;
g_currentInputCount = g_totalInputCount = 0;
s_currentFrame = s_totalFrames = 0;
s_currentLagCount = s_totalLagCount = 0;
s_currentInputCount = s_totalInputCount = 0;
s_totalTickCount = s_tickCountAtLastInput = 0;
s_bongos = 0;
s_memcards = 0;
@ -560,11 +611,11 @@ bool BeginRecordingInput(int controllers)
// TODO: find a way to GetTitleDataPath() from Movie::Init()
if (SConfig::GetInstance().bWii)
{
if (File::Exists(Common::GetTitleDataPath(g_titleID, Common::FROM_SESSION_ROOT) +
if (File::Exists(Common::GetTitleDataPath(s_titleID, Common::FROM_SESSION_ROOT) +
"banner.bin"))
Movie::g_bClearSave = false;
Movie::s_bClearSave = false;
else
Movie::g_bClearSave = true;
Movie::s_bClearSave = true;
}
std::thread md5thread(GetMD5);
md5thread.detach();
@ -832,10 +883,10 @@ void CheckPadStatus(GCPadStatus* PadStatus, int controllerID)
s_padState.CStickX = PadStatus->substickX;
s_padState.CStickY = PadStatus->substickY;
s_padState.disc = g_bDiscChange;
g_bDiscChange = false;
s_padState.reset = g_bReset;
g_bReset = false;
s_padState.disc = s_bDiscChange;
s_bDiscChange = false;
s_padState.reset = s_bReset;
s_bReset = false;
SetInputDisplayString(s_padState, controllerID);
}
@ -895,7 +946,7 @@ void ReadHeader()
s_bDSPHLE = tmpHeader.bDSPHLE;
s_bFastDiscSpeed = tmpHeader.bFastDiscSpeed;
s_iCPUCore = tmpHeader.CPUCore;
g_bClearSave = tmpHeader.bClearSave;
s_bClearSave = tmpHeader.bClearSave;
s_memcards = tmpHeader.memcards;
s_bongos = tmpHeader.bongos;
s_bSyncGPU = tmpHeader.bSyncGPU;
@ -909,7 +960,7 @@ void ReadHeader()
}
s_videoBackend = (char*)tmpHeader.videoBackend;
g_discChange = (char*)tmpHeader.discChange;
s_discChange = (char*)tmpHeader.discChange;
s_author = (char*)tmpHeader.author;
memcpy(s_MD5, tmpHeader.md5, 16);
s_DSPiromHash = tmpHeader.DSPiromHash;
@ -940,13 +991,13 @@ bool PlayInput(const std::string& filename)
}
ReadHeader();
g_totalFrames = tmpHeader.frameCount;
s_totalFrames = tmpHeader.frameCount;
s_totalLagCount = tmpHeader.lagCount;
g_totalInputCount = tmpHeader.inputCount;
s_totalInputCount = tmpHeader.inputCount;
s_totalTickCount = tmpHeader.tickCount;
g_currentFrame = 0;
g_currentLagCount = 0;
g_currentInputCount = 0;
s_currentFrame = 0;
s_currentLagCount = 0;
s_currentInputCount = 0;
s_playMode = MODE_PLAYING;
@ -978,13 +1029,13 @@ void DoState(PointerWrap& p)
{
// many of these could be useful to save even when no movie is active,
// and the data is tiny, so let's just save it regardless of movie state.
p.Do(g_currentFrame);
p.Do(s_currentFrame);
p.Do(s_currentByte);
p.Do(g_currentLagCount);
p.Do(g_currentInputCount);
p.Do(s_currentLagCount);
p.Do(s_currentInputCount);
p.Do(s_bPolled);
p.Do(s_tickCountAtLastInput);
// other variables (such as s_totalBytes and g_totalFrames) are set in LoadInput
// other variables (such as s_totalBytes and s_totalFrames) are set in LoadInput
}
// NOTE: Host Thread
@ -1028,15 +1079,15 @@ void LoadInput(const std::string& filename)
PanicAlertT("Warning: You loaded a save whose movie ends before the current frame in the save "
"(byte %u < %u) (frame %u < %u). You should load another save before continuing.",
(u32)totalSavedBytes + 256, (u32)s_currentByte + 256, (u32)tmpHeader.frameCount,
(u32)g_currentFrame);
(u32)s_currentFrame);
afterEnd = true;
}
if (!s_bReadOnly || tmpInput == nullptr)
{
g_totalFrames = tmpHeader.frameCount;
s_totalFrames = tmpHeader.frameCount;
s_totalLagCount = tmpHeader.lagCount;
g_totalInputCount = tmpHeader.inputCount;
s_totalInputCount = tmpHeader.inputCount;
s_totalTickCount = s_tickCountAtLastInput = tmpHeader.tickCount;
EnsureTmpInputSize((size_t)totalSavedBytes);
@ -1054,8 +1105,8 @@ void LoadInput(const std::string& filename)
PanicAlertT("Warning: You loaded a save that's after the end of the current movie. (byte %u "
"> %u) (input %u > %u). You should load another save before continuing, or load "
"this state with read-only mode off.",
(u32)s_currentByte + 256, (u32)s_totalBytes + 256, (u32)g_currentInputCount,
(u32)g_totalInputCount);
(u32)s_currentByte + 256, (u32)s_totalBytes + 256, (u32)s_currentInputCount,
(u32)s_totalInputCount);
}
else if (s_currentByte > 0 && s_totalBytes > 0)
{
@ -1104,7 +1155,7 @@ void LoadInput(const std::string& filename)
"On frame %td, the savestate's movie presses:\n"
"Start=%d, A=%d, B=%d, X=%d, Y=%d, Z=%d, DUp=%d, DDown=%d, DLeft=%d, DRight=%d, "
"L=%d, R=%d, LT=%d, RT=%d, AnalogX=%d, AnalogY=%d, CX=%d, CY=%d",
frame, (int)g_totalFrames, (int)tmpHeader.frameCount, frame, (int)curPadState.Start,
frame, (int)s_totalFrames, (int)tmpHeader.frameCount, frame, (int)curPadState.Start,
(int)curPadState.A, (int)curPadState.B, (int)curPadState.X, (int)curPadState.Y,
(int)curPadState.Z, (int)curPadState.DPadUp, (int)curPadState.DPadDown,
(int)curPadState.DPadLeft, (int)curPadState.DPadRight, (int)curPadState.L,
@ -1237,7 +1288,7 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
std::string path;
for (const std::string& iso_folder : SConfig::GetInstance().m_ISOFolder)
{
path = iso_folder + '/' + g_discChange;
path = iso_folder + '/' + s_discChange;
if (File::Exists(path))
{
found = true;
@ -1251,7 +1302,7 @@ void PlayController(GCPadStatus* PadStatus, int controllerID)
else
{
CPU::Break();
PanicAlertT("Change the disc to %s", g_discChange.c_str());
PanicAlertT("Change the disc to %s", s_discChange.c_str());
}
}
@ -1305,7 +1356,7 @@ bool PlayWiimote(int wiimote, u8* data, const WiimoteEmu::ReportFeatures& rptf,
memcpy(data, &(tmpInput[s_currentByte]), size);
s_currentByte += size;
g_currentInputCount++;
s_currentInputCount++;
CheckInputEnd();
return true;
@ -1332,7 +1383,7 @@ void EndPlayInput(bool cont)
s_bRecordingFromSaveState = false;
// we don't clear these things because otherwise we can't resume playback if we load a movie
// state later
// g_totalFrames = s_totalBytes = 0;
// s_totalFrames = s_totalBytes = 0;
// delete tmpInput;
// tmpInput = nullptr;
@ -1361,9 +1412,9 @@ void SaveRecording(const std::string& filename)
header.numControllers = s_numPads & (SConfig::GetInstance().bWii ? 0xFF : 0x0F);
header.bFromSaveState = s_bRecordingFromSaveState;
header.frameCount = g_totalFrames;
header.frameCount = s_totalFrames;
header.lagCount = s_totalLagCount;
header.inputCount = g_totalInputCount;
header.inputCount = s_totalInputCount;
header.numRerecords = s_rerecords;
header.recordingStartTime = s_recordingStartTime;
@ -1384,10 +1435,10 @@ void SaveRecording(const std::string& filename)
header.bUseXFB = g_ActiveConfig.bUseXFB;
header.bUseRealXFB = g_ActiveConfig.bUseRealXFB;
header.memcards = s_memcards;
header.bClearSave = g_bClearSave;
header.bClearSave = s_bClearSave;
header.bSyncGPU = s_bSyncGPU;
header.bNetPlay = s_bNetPlay;
strncpy((char*)header.discChange, g_discChange.c_str(), ArraySize(header.discChange));
strncpy((char*)header.discChange, s_discChange.c_str(), ArraySize(header.discChange));
strncpy((char*)header.author, s_author.c_str(), ArraySize(header.author));
memcpy(header.md5, s_MD5, 16);
header.bongos = s_bongos;
@ -1466,7 +1517,7 @@ void GetSettings()
s_bNetPlay = NetPlay::IsNetPlayRunning();
s_language = SConfig::GetInstance().m_SYSCONF->GetData<u8>("IPL.LNG");
if (!SConfig::GetInstance().bWii)
g_bClearSave = !File::Exists(SConfig::GetInstance().m_strMemoryCardA);
s_bClearSave = !File::Exists(SConfig::GetInstance().m_strMemoryCardA);
s_memcards |= (SConfig::GetInstance().m_EXIDevice[0] == EXIDEVICE_MEMORYCARD) << 0;
s_memcards |= (SConfig::GetInstance().m_EXIDevice[1] == EXIDEVICE_MEMORYCARD) << 1;
@ -1543,7 +1594,7 @@ void GetMD5()
// NOTE: EmuThread
void Shutdown()
{
g_currentInputCount = g_totalInputCount = g_totalFrames = s_totalBytes = s_tickCountAtLastInput =
s_currentInputCount = s_totalInputCount = s_totalFrames = s_totalBytes = s_tickCountAtLastInput =
0;
delete[] tmpInput;
tmpInput = nullptr;

View file

@ -48,15 +48,6 @@ struct ControllerState
static_assert(sizeof(ControllerState) == 8, "ControllerState should be 8 bytes");
#pragma pack(pop)
// Global declarations
extern bool g_bDiscChange, g_bClearSave, g_bReset;
extern u64 g_titleID;
extern u64 g_currentFrame, g_totalFrames;
extern u64 g_currentLagCount;
extern u64 g_currentInputCount, g_totalInputCount;
extern std::string g_discChange;
#pragma pack(push, 1)
struct DTMHeader
{
@ -130,6 +121,18 @@ bool IsMovieActive();
bool IsReadOnly();
u64 GetRecordingStartTime();
u64 GetCurrentFrame();
u64 GetTotalFrames();
u64 GetCurrentInputCount();
u64 GetTotalInputCount();
u64 GetCurrentLagCount();
u64 GetTotalLagCount();
void SetClearSave(bool enabled);
void SignalDiscChange(const std::string& new_disc_filename);
void SetReset(bool reset);
void SetTitleId(u64 title_id);
bool IsConfigSaved();
bool IsDualCore();
bool IsProgressive();

View file

@ -24,8 +24,6 @@
#include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/IPC_HLE/WII_IPC_HLE_Device_usb.h"
#include "Core/Movie.h"
#include "Core/Movie.h"
#include "Core/NetPlayClient.h"
#include "InputCommon/GCAdapter.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoConfig.h"
@ -326,6 +324,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
// Trusting server for good map value (>=0 && <4)
// add to pad buffer
m_pad_buffer.at(map).Push(pad);
m_gc_pad_event.Set();
}
break;
@ -344,6 +343,7 @@ unsigned int NetPlayClient::OnData(sf::Packet& packet)
// Trusting server for good map value (>=0 && <4)
// add to Wiimote buffer
m_wiimote_buffer.at(map).Push(nw);
m_wii_pad_event.Set();
}
break;
@ -971,11 +971,11 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status)
switch (SConfig::GetInstance().m_SIDevice[local_pad])
{
case SIDEVICE_WIIU_ADAPTER:
GCAdapter::Input(local_pad, pad_status);
*pad_status = GCAdapter::Input(local_pad);
break;
case SIDEVICE_GC_CONTROLLER:
default:
Pad::GetStatus(local_pad, pad_status);
*pad_status = Pad::GetStatus(local_pad);
break;
}
@ -996,15 +996,18 @@ bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status)
// Now, we either use the data pushed earlier, or wait for the
// other clients to send it to us
while (!m_pad_buffer[pad_nb].Pop(*pad_status))
while (m_pad_buffer[pad_nb].Size() == 0)
{
if (!m_is_running.load())
{
return false;
}
// TODO: use a condition instead of sleeping
Common::SleepCurrentThread(1);
m_gc_pad_event.Wait();
}
m_pad_buffer[pad_nb].Pop(*pad_status);
if (Movie::IsRecordingInput())
{
Movie::RecordInput(pad_status, pad_nb);
@ -1042,14 +1045,19 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size)
} // unlock players
while (!m_wiimote_buffer[_number].Pop(nw))
while (m_wiimote_buffer[_number].Size() == 0)
{
// wait for receiving thread to push some data
Common::SleepCurrentThread(1);
if (!m_is_running.load())
{
return false;
}
// wait for receiving thread to push some data
m_wii_pad_event.Wait();
}
m_wiimote_buffer[_number].Pop(nw);
// If the reporting mode has changed, we just need to pop through the buffer,
// until we reach a good input
if (nw.size() != size)
@ -1057,12 +1065,19 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size)
u32 tries = 0;
while (nw.size() != size)
{
while (!m_wiimote_buffer[_number].Pop(nw))
while (m_wiimote_buffer[_number].Size() == 0)
{
Common::SleepCurrentThread(1);
if (!m_is_running.load())
{
return false;
}
// wait for receiving thread to push some data
m_wii_pad_event.Wait();
}
m_wiimote_buffer[_number].Pop(nw);
++tries;
if (tries > m_target_buffer_size * 200 / 120)
break;
@ -1083,13 +1098,12 @@ bool NetPlayClient::WiimoteUpdate(int _number, u8* data, const u8 size)
// called from ---GUI--- thread and ---NETPLAY--- thread (client side)
bool NetPlayClient::StopGame()
{
if (!m_is_running.load())
{
PanicAlertT("Game isn't running!");
return false;
}
m_is_running.store(false);
// stop waiting for input
m_gc_pad_event.Set();
m_wii_pad_event.Set();
NetPlay_Disable();
// stop game
@ -1104,6 +1118,12 @@ void NetPlayClient::Stop()
if (!m_is_running.load())
return;
m_is_running.store(false);
// stop waiting for input
m_gc_pad_event.Set();
m_wii_pad_event.Set();
// Tell the server to stop if we have a pad mapped in game.
if (LocalPlayerHasControllerMapped())
SendStopGamePacket();

View file

@ -14,6 +14,7 @@
#include <thread>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/FifoQueue.h"
#include "Common/TraversalClient.h"
#include "Core/NetPlayProto.h"
@ -175,6 +176,8 @@ private:
TraversalClient* m_traversal_client = nullptr;
std::thread m_MD5_thread;
bool m_should_compute_MD5 = false;
Common::Event m_gc_pad_event;
Common::Event m_wii_pad_event;
u32 m_timebase_frame = 0;
};

View file

@ -20,6 +20,7 @@ void CachedInterpreter::Init()
jo.enableBlocklink = false;
JitBaseBlockCache::Init();
UpdateMemoryOptions();
code_block.m_stats = &js.st;
code_block.m_gpa = &js.gpa;
@ -41,34 +42,29 @@ void CachedInterpreter::Run()
void CachedInterpreter::SingleStep()
{
int block = GetBlockNumberFromStartAddress(PC);
if (block >= 0)
const u8* normalEntry = jit->GetBlockCache()->Dispatch();
const Instruction* code = reinterpret_cast<const Instruction*>(normalEntry);
while (true)
{
Instruction* code = (Instruction*)GetCompiledCodeFromBlock(block);
while (true)
switch (code->type)
{
switch (code->type)
{
case Instruction::INSTRUCTION_ABORT:
case Instruction::INSTRUCTION_ABORT:
return;
case Instruction::INSTRUCTION_TYPE_COMMON:
code->common_callback(UGeckoInstruction(code->data));
code++;
break;
case Instruction::INSTRUCTION_TYPE_CONDITIONAL:
bool ret = code->conditional_callback(code->data);
code++;
if (ret)
return;
case Instruction::INSTRUCTION_TYPE_COMMON:
code->common_callback(UGeckoInstruction(code->data));
code++;
break;
case Instruction::INSTRUCTION_TYPE_CONDITIONAL:
bool ret = code->conditional_callback(code->data);
code++;
if (ret)
return;
break;
}
break;
}
}
Jit(PC);
}
static void EndBlock(UGeckoInstruction data)
@ -87,14 +83,30 @@ static void WritePC(UGeckoInstruction data)
NPC = data.hex + 4;
}
static void WriteBrokenBlockNPC(UGeckoInstruction data)
{
NPC = data.hex;
}
static bool CheckFPU(u32 data)
{
UReg_MSR& msr = (UReg_MSR&)MSR;
if (!msr.FP)
{
PC = NPC = data;
PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE;
PowerPC::CheckExceptions();
PowerPC::ppcState.downcount -= data;
return true;
}
return false;
}
static bool CheckDSI(u32 data)
{
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
PowerPC::CheckExceptions();
PowerPC::ppcState.downcount -= data;
return true;
}
return false;
@ -161,22 +173,29 @@ void CachedInterpreter::Jit(u32 address)
if (!ops[i].skip)
{
if ((ops[i].opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound)
bool check_fpu = (ops[i].opinfo->flags & FL_USE_FPU) && !js.firstFPInstructionFound;
bool endblock = (ops[i].opinfo->flags & FL_ENDBLOCK) != 0;
bool memcheck = (ops[i].opinfo->flags & FL_LOADSTORE) && jo.memcheck;
if (check_fpu)
{
m_code.emplace_back(CheckFPU, ops[i].address);
m_code.emplace_back(WritePC, ops[i].address);
m_code.emplace_back(CheckFPU, js.downcountAmount);
js.firstFPInstructionFound = true;
}
if (ops[i].opinfo->flags & FL_ENDBLOCK)
if (endblock || memcheck)
m_code.emplace_back(WritePC, ops[i].address);
m_code.emplace_back(GetInterpreterOp(ops[i].inst), ops[i].inst);
if (ops[i].opinfo->flags & FL_ENDBLOCK)
if (memcheck)
m_code.emplace_back(CheckDSI, js.downcountAmount);
if (endblock)
m_code.emplace_back(EndBlock, js.downcountAmount);
}
}
if (code_block.m_broken)
{
m_code.emplace_back(WritePC, nextPC);
m_code.emplace_back(WriteBrokenBlockNPC, nextPC);
m_code.emplace_back(EndBlock, js.downcountAmount);
}
m_code.emplace_back();
@ -191,12 +210,5 @@ void CachedInterpreter::ClearCache()
{
m_code.clear();
JitBaseBlockCache::Clear();
}
void CachedInterpreter::WriteDestroyBlock(const u8* location, u32 address)
{
}
void CachedInterpreter::WriteLinkBlock(u8* location, const JitBlock& block)
{
UpdateMemoryOptions();
}

View file

@ -28,11 +28,8 @@ public:
JitBaseBlockCache* GetBlockCache() override { return this; }
const char* GetName() override { return "Cached Interpreter"; }
void WriteLinkBlock(u8* location, const JitBlock& block) override;
void WriteDestroyBlock(const u8* location, u32 address) override;
const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; };
void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) override {}
const CommonAsmRoutinesBase* GetAsmRoutines() override { return nullptr; }
private:
struct Instruction
{

View file

@ -396,29 +396,12 @@ void Jit64::JustWriteExit(u32 destination, bool bl, u32 after)
linkData.exitAddress = destination;
linkData.linkStatus = false;
// Link opportunity!
int block;
if (jo.enableBlocklink && (block = blocks.GetBlockNumberFromStartAddress(destination)) >= 0)
{
// It exists! Joy of joy!
JitBlock* jb = blocks.GetBlock(block);
const u8* addr = jb->checkedEntry;
linkData.exitPtrs = GetWritableCodePtr();
if (bl)
CALL(addr);
else
JMP(addr, true);
linkData.linkStatus = true;
}
MOV(32, PPCSTATE(pc), Imm32(destination));
linkData.exitPtrs = GetWritableCodePtr();
if (bl)
CALL(asm_routines.dispatcher);
else
{
MOV(32, PPCSTATE(pc), Imm32(destination));
linkData.exitPtrs = GetWritableCodePtr();
if (bl)
CALL(asm_routines.dispatcher);
else
JMP(asm_routines.dispatcher, true);
}
JMP(asm_routines.dispatcher, true);
b->linkData.push_back(linkData);

View file

@ -64,7 +64,7 @@ void Jit64AsmRoutineManager::Generate()
ABI_PopRegistersAndAdjustStack(1 << RSCRATCH2, 0);
#endif
ResetStack();
ResetStack(*this);
SUB(32, PPCSTATE(downcount), R(RSCRATCH2));
@ -102,31 +102,15 @@ void Jit64AsmRoutineManager::Generate()
MOV(64, R(RMEM), Imm64((u64)Memory::logical_base));
SetJumpTarget(membaseend);
// The following is an translation of JitBaseBlockCache::Dispatch into assembly.
// Fast block number lookup.
MOV(32, R(RSCRATCH), PPCSTATE(pc));
// TODO: We need to handle code which executes the same PC with
// different values of MSR.IR. It probably makes sense to handle
// MSR.DR here too, to allow IsOptimizableRAMAddress-based
// optimizations safe, because IR and DR are usually set/cleared together.
// TODO: Branching based on the 20 most significant bits of instruction
// addresses without translating them is wrong.
u64 icache = (u64)jit->GetBlockCache()->iCache.data();
u64 icacheVmem = (u64)jit->GetBlockCache()->iCacheVMEM.data();
u64 icacheEx = (u64)jit->GetBlockCache()->iCacheEx.data();
u32 mask = 0;
FixupBranch no_mem;
FixupBranch exit_mem;
FixupBranch exit_vmem;
if (SConfig::GetInstance().bWii)
mask = JIT_ICACHE_EXRAM_BIT;
mask |= JIT_ICACHE_VMEM_BIT;
TEST(32, R(RSCRATCH), Imm32(mask));
no_mem = J_CC(CC_NZ);
AND(32, R(RSCRATCH), Imm32(JIT_ICACHE_MASK));
u64 icache = reinterpret_cast<u64>(jit->GetBlockCache()->GetICache());
AND(32, R(RSCRATCH), Imm32(JitBaseBlockCache::iCache_Mask << 2));
if (icache <= INT_MAX)
{
MOV(32, R(RSCRATCH), MDisp(RSCRATCH, (s32)icache));
MOV(32, R(RSCRATCH), MDisp(RSCRATCH, static_cast<s32>(icache)));
}
else
{
@ -134,73 +118,46 @@ void Jit64AsmRoutineManager::Generate()
MOV(32, R(RSCRATCH), MRegSum(RSCRATCH2, RSCRATCH));
}
exit_mem = J();
SetJumpTarget(no_mem);
TEST(32, R(RSCRATCH), Imm32(JIT_ICACHE_VMEM_BIT));
FixupBranch no_vmem = J_CC(CC_Z);
AND(32, R(RSCRATCH), Imm32(JIT_ICACHE_MASK));
if (icacheVmem <= INT_MAX)
// Check whether the block we found matches the current state.
u64 blocks = reinterpret_cast<u64>(jit->GetBlockCache()->GetBlocks());
IMUL(32, RSCRATCH, R(RSCRATCH), Imm32(sizeof(JitBlock)));
if (blocks <= INT_MAX)
{
MOV(32, R(RSCRATCH), MDisp(RSCRATCH, (s32)icacheVmem));
ADD(64, R(RSCRATCH), Imm32(static_cast<s32>(blocks)));
}
else
{
MOV(64, R(RSCRATCH2), Imm64(icacheVmem));
MOV(32, R(RSCRATCH), MRegSum(RSCRATCH2, RSCRATCH));
}
if (SConfig::GetInstance().bWii)
exit_vmem = J();
SetJumpTarget(no_vmem);
if (SConfig::GetInstance().bWii)
{
TEST(32, R(RSCRATCH), Imm32(JIT_ICACHE_EXRAM_BIT));
FixupBranch no_exram = J_CC(CC_Z);
AND(32, R(RSCRATCH), Imm32(JIT_ICACHEEX_MASK));
if (icacheEx <= INT_MAX)
{
MOV(32, R(RSCRATCH), MDisp(RSCRATCH, (s32)icacheEx));
}
else
{
MOV(64, R(RSCRATCH2), Imm64(icacheEx));
MOV(32, R(RSCRATCH), MRegSum(RSCRATCH2, RSCRATCH));
}
SetJumpTarget(no_exram);
}
SetJumpTarget(exit_mem);
if (SConfig::GetInstance().bWii)
SetJumpTarget(exit_vmem);
TEST(32, R(RSCRATCH), R(RSCRATCH));
FixupBranch notfound = J_CC(CC_L);
// grab from list and jump to it
u64 codePointers = (u64)jit->GetBlockCache()->GetCodePointers();
if (codePointers <= INT_MAX)
{
JMPptr(MScaled(RSCRATCH, SCALE_8, (s32)codePointers));
}
else
{
MOV(64, R(RSCRATCH2), Imm64(codePointers));
JMPptr(MComplex(RSCRATCH2, RSCRATCH, SCALE_8, 0));
MOV(64, R(RSCRATCH2), Imm64(blocks));
ADD(64, R(RSCRATCH), R(RSCRATCH2));
}
// Check both block.effectiveAddress and block.msrBits.
MOV(32, R(RSCRATCH2), PPCSTATE(msr));
AND(32, R(RSCRATCH2), Imm32(JitBlock::JIT_CACHE_MSR_MASK));
SHL(64, R(RSCRATCH2), Imm8(32));
MOV(32, R(RSCRATCH_EXTRA), PPCSTATE(pc));
OR(64, R(RSCRATCH2), R(RSCRATCH_EXTRA));
CMP(64, R(RSCRATCH2), MDisp(RSCRATCH, static_cast<s32>(offsetof(JitBlock, effectiveAddress))));
FixupBranch notfound = J_CC(CC_NE);
// Success; branch to the block we found.
JMPptr(MDisp(RSCRATCH, static_cast<s32>(offsetof(JitBlock, normalEntry))));
SetJumpTarget(notfound);
// Failure; call into the block cache to update the state, then try again.
// (We need to loop because Jit() might not actually generate a block
// if we hit an ISI.)
// We reset the stack because Jit might clear the code cache.
// Also if we are in the middle of disabling BLR optimization on windows
// we need to reset the stack before _resetstkoflw() is called in Jit
// otherwise we will generate a second stack overflow exception during DoJit()
ResetStack();
ResetStack(*this);
// Ok, no block, let's jit
// Ok, no block, let's call the slow dispatcher
ABI_PushRegistersAndAdjustStack({}, 0);
ABI_CallFunctionA(32, (void*)&Jit, PPCSTATE(pc));
ABI_CallFunction(reinterpret_cast<void*>(&JitBase::Dispatch));
ABI_PopRegistersAndAdjustStack({}, 0);
JMP(dispatcherNoCheck, true); // no point in special casing this
// JMPptr(R(ABI_RETURN));
JMP(dispatcherNoCheck, true);
SetJumpTarget(bail);
doTiming = GetCodePtr();
@ -217,7 +174,7 @@ void Jit64AsmRoutineManager::Generate()
// Landing pad for drec space
if (SConfig::GetInstance().bEnableDebugging)
SetJumpTarget(dbg_exit);
ResetStack();
ResetStack(*this);
if (m_stack_top)
{
ADD(64, R(RSP), Imm8(0x18));
@ -232,12 +189,12 @@ void Jit64AsmRoutineManager::Generate()
GenerateCommon();
}
void Jit64AsmRoutineManager::ResetStack()
void Jit64AsmRoutineManager::ResetStack(X64CodeBlock& emitter)
{
if (m_stack_top)
MOV(64, R(RSP), Imm64((u64)m_stack_top - 0x20));
emitter.MOV(64, R(RSP), Imm64((u64)m_stack_top - 0x20));
else
MOV(64, R(RSP), M(&s_saved_rsp));
emitter.MOV(64, R(RSP), M(&s_saved_rsp));
}
void Jit64AsmRoutineManager::GenerateCommon()

View file

@ -25,7 +25,6 @@ class Jit64AsmRoutineManager : public CommonAsmRoutines
{
private:
void Generate();
void ResetStack();
void GenerateCommon();
u8* m_stack_top;
@ -41,4 +40,5 @@ public:
}
void Shutdown() { FreeCodeSpace(); }
void ResetStack(X64CodeBlock& emitter);
};

View file

@ -310,6 +310,7 @@ void Jit64::dcbx(UGeckoInstruction inst)
XOR(32, R(ABI_PARAM3), R(ABI_PARAM3));
ABI_CallFunction((void*)JitInterface::InvalidateICache);
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
asm_routines.ResetStack(*this);
c = J(true);
SwitchToNearCode();
SetJumpTarget(c);

View file

@ -391,6 +391,10 @@ void Jit64::mtmsr(UGeckoInstruction inst)
gpr.Flush();
fpr.Flush();
// Our jit cache also stores some MSR bits, as they have changed, we either
// have to validate them in the BLR/RET check, or just flush the stack here.
asm_routines.ResetStack(*this);
// If some exceptions are pending and EE are now enabled, force checking
// external exceptions when going out of mtmsr in order to execute delayed
// interrupts as soon as possible.

View file

@ -373,19 +373,9 @@ void JitIL::WriteExit(u32 destination)
linkData.exitPtrs = GetWritableCodePtr();
linkData.linkStatus = false;
// Link opportunity!
int block;
if (jo.enableBlocklink && (block = blocks.GetBlockNumberFromStartAddress(destination)) >= 0)
{
// It exists! Joy of joy!
JMP(blocks.GetBlock(block)->checkedEntry, true);
linkData.linkStatus = true;
}
else
{
MOV(32, PPCSTATE(pc), Imm32(destination));
JMP(asm_routines.dispatcher, true);
}
MOV(32, PPCSTATE(pc), Imm32(destination));
JMP(asm_routines.dispatcher, true);
b->linkData.push_back(linkData);
}

View file

@ -196,7 +196,6 @@ void JitArm64::WriteExit(u32 destination)
linkData.linkStatus = false;
b->linkData.push_back(linkData);
// the code generated in JitArm64BlockCache::WriteDestroyBlock must fit in this block
MOVI2R(DISPATCHER_PC, destination);
B(dispatcher);
}

View file

@ -7,32 +7,39 @@
#include "Core/PowerPC/JitArm64/JitArm64Cache.h"
#include "Core/PowerPC/JitInterface.h"
void JitArm64BlockCache::WriteLinkBlock(u8* location, const JitBlock& block)
void JitArm64BlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest)
{
u8* location = source.exitPtrs;
ARM64XEmitter emit(location);
// Are we able to jump directly to the normal entry?
s64 distance = ((s64)block.normalEntry - (s64)location) >> 2;
if (distance >= -0x40000 && distance <= 0x3FFFF)
if (dest)
{
emit.B(CC_LE, block.normalEntry);
// Are we able to jump directly to the normal entry?
s64 distance = ((s64)dest->normalEntry - (s64)location) >> 2;
if (distance >= -0x40000 && distance <= 0x3FFFF)
{
emit.B(CC_LE, dest->normalEntry);
}
// We can't write DISPATCHER_PC here, as blink linking is only for 8bytes.
// So we'll hit two jumps when calling Advance.
emit.B(block.checkedEntry);
// Use the checked entry if either downcount is smaller zero,
// or if we're not able to inline the downcount check here.
emit.B(dest->checkedEntry);
}
else
{
emit.B(block.checkedEntry);
emit.MOVI2R(DISPATCHER_PC, source.exitAddress);
emit.B(jit->GetAsmRoutines()->dispatcher);
}
emit.FlushIcache();
}
void JitArm64BlockCache::WriteDestroyBlock(const u8* location, u32 address)
void JitArm64BlockCache::WriteDestroyBlock(const JitBlock& block)
{
// must fit within the code generated in JitArm64::WriteExit
ARM64XEmitter emit((u8*)location);
emit.MOVI2R(DISPATCHER_PC, address);
emit.B(jit->GetAsmRoutines()->dispatcher);
// Only clear the entry points as we might still be within this block.
ARM64XEmitter emit((u8*)block.checkedEntry);
while (emit.GetWritableCodePtr() <= block.normalEntry)
emit.BRK(0x123);
emit.FlushIcache();
}

View file

@ -11,6 +11,6 @@ typedef void (*CompiledCode)();
class JitArm64BlockCache : public JitBaseBlockCache
{
private:
void WriteLinkBlock(u8* location, const JitBlock& block);
void WriteDestroyBlock(const u8* location, u32 address);
void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) override;
void WriteDestroyBlock(const JitBlock& block) override;
};

View file

@ -791,11 +791,6 @@ void JitArm64::dcbz(UGeckoInstruction inst)
int a = inst.RA, b = inst.RB;
u32 mem_mask = Memory::ADDR_MASK_HW_ACCESS;
// The following masks the region used by the GC/Wii virtual memory lib
mem_mask |= Memory::ADDR_MASK_MEM1;
gpr.Lock(W0);
ARM64Reg addr_reg = W0;

View file

@ -48,54 +48,54 @@ void JitArm64::GenerateAsm()
dispatcherNoCheck = GetCodePtr();
FixupBranch exram, vmem, not_exram, not_vmem;
ARM64Reg pc_masked = W25;
ARM64Reg cache_base = X27;
bool assembly_dispatcher = true;
// VMEM
not_vmem = TBZ(DISPATCHER_PC, IntLog2(JIT_ICACHE_VMEM_BIT));
ANDI2R(pc_masked, DISPATCHER_PC, JIT_ICACHE_MASK);
MOVI2R(cache_base, (u64)jit->GetBlockCache()->iCacheVMEM.data());
vmem = B();
SetJumpTarget(not_vmem);
if (SConfig::GetInstance().bWii)
if (assembly_dispatcher)
{
// Wii EX-RAM
not_exram = TBZ(DISPATCHER_PC, IntLog2(JIT_ICACHE_EXRAM_BIT));
ANDI2R(pc_masked, DISPATCHER_PC, JIT_ICACHEEX_MASK);
MOVI2R(cache_base, (u64)jit->GetBlockCache()->iCacheEx.data());
exram = B();
SetJumpTarget(not_exram);
// iCache[(address >> 2) & iCache_Mask];
ARM64Reg pc_masked = W25;
ARM64Reg cache_base = X27;
ARM64Reg block_num = W27;
ANDI2R(pc_masked, DISPATCHER_PC, JitBaseBlockCache::iCache_Mask << 2);
MOVP2R(cache_base, jit->GetBlockCache()->GetICache());
LDR(block_num, cache_base, EncodeRegTo64(pc_masked));
// blocks[block_num]
ARM64Reg block = X30;
ARM64Reg jit_block_size = W24;
MOVI2R(jit_block_size, sizeof(JitBlock));
MUL(block_num, block_num, jit_block_size);
MOVP2R(block, jit->GetBlockCache()->GetBlocks());
ADD(block, block, EncodeRegTo64(block_num));
// b.effectiveAddress != addr || b.msrBits != msr
ARM64Reg pc_and_msr = W25;
ARM64Reg pc_and_msr2 = W24;
LDR(INDEX_UNSIGNED, pc_and_msr, block, offsetof(JitBlock, effectiveAddress));
CMP(pc_and_msr, DISPATCHER_PC);
FixupBranch pc_missmatch = B(CC_NEQ);
LDR(INDEX_UNSIGNED, pc_and_msr2, PPC_REG, PPCSTATE_OFF(msr));
ANDI2R(pc_and_msr2, pc_and_msr2, JitBlock::JIT_CACHE_MSR_MASK);
LDR(INDEX_UNSIGNED, pc_and_msr, block, offsetof(JitBlock, msrBits));
CMP(pc_and_msr, pc_and_msr2);
FixupBranch msr_missmatch = B(CC_NEQ);
// return blocks[block_num].normalEntry;
LDR(INDEX_UNSIGNED, block, block, offsetof(JitBlock, normalEntry));
BR(block);
SetJumpTarget(pc_missmatch);
SetJumpTarget(msr_missmatch);
}
// Common memory
ANDI2R(pc_masked, DISPATCHER_PC, JIT_ICACHE_MASK);
MOVI2R(cache_base, (u64)jit->GetBlockCache()->iCache.data());
SetJumpTarget(vmem);
if (SConfig::GetInstance().bWii)
SetJumpTarget(exram);
LDR(W27, cache_base, EncodeRegTo64(pc_masked));
FixupBranch JitBlock = TBNZ(W27, 7); // Test the 7th bit
// Success, it is our Jitblock.
MOVI2R(X30, (u64)jit->GetBlockCache()->GetCodePointers());
UBFM(X27, X27, 61, 60); // Same as X27 << 3
LDR(X30, X30, X27); // Load the block address in to R14
BR(X30);
// No need to jump anywhere after here, the block will go back to dispatcher start
SetJumpTarget(JitBlock);
// Call C version of Dispatch().
// FIXME: Implement this in inline assembly.
STR(INDEX_UNSIGNED, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(pc));
MOVI2R(X30, (u64) & ::Jit);
MOVP2R(X30, reinterpret_cast<void*>(&JitBase::Dispatch));
BLR(X30);
LDR(INDEX_UNSIGNED, DISPATCHER_PC, PPC_REG, PPCSTATE_OFF(pc));
B(dispatcherNoCheck);
// Jump to next block.
BR(X0);
SetJumpTarget(bail);
doTiming = GetCodePtr();

View file

@ -55,6 +55,10 @@
#define JITDISABLE(setting) \
FALLBACK_IF(SConfig::GetInstance().bJITOff || SConfig::GetInstance().setting)
class JitBase;
extern JitBase* jit;
class JitBase : public CPUCoreBase
{
protected:
@ -125,6 +129,7 @@ public:
JitOptions jo;
JitState js;
static const u8* Dispatch() { return jit->GetBlockCache()->Dispatch(); };
virtual JitBaseBlockCache* GetBlockCache() = 0;
virtual void Jit(u32 em_address) = 0;
@ -147,8 +152,6 @@ public:
bool HandleFault(uintptr_t access_address, SContext* ctx) override;
};
extern JitBase* jit;
void Jit(u32 em_address);
// Merged routines that should be moved somewhere better

View file

@ -34,26 +34,15 @@ bool JitBaseBlockCache::IsFull() const
void JitBaseBlockCache::Init()
{
if (m_initialized)
{
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
return;
}
JitRegister::Init(SConfig::GetInstance().m_perfDir);
iCache.fill(JIT_ICACHE_INVALID_BYTE);
iCacheEx.fill(JIT_ICACHE_INVALID_BYTE);
iCacheVMEM.fill(JIT_ICACHE_INVALID_BYTE);
iCache.fill(0);
Clear();
m_initialized = true;
}
void JitBaseBlockCache::Shutdown()
{
num_blocks = 0;
m_initialized = false;
num_blocks = 1;
JitRegister::Shutdown();
}
@ -70,7 +59,7 @@ void JitBaseBlockCache::Clear()
#endif
jit->js.fifoWriteAddresses.clear();
jit->js.pairedQuantizeAddresses.clear();
for (int i = 0; i < num_blocks; i++)
for (int i = 1; i < num_blocks; i++)
{
DestroyBlock(i, false);
}
@ -79,8 +68,9 @@ void JitBaseBlockCache::Clear()
valid_block.ClearAll();
num_blocks = 0;
blockCodePointers.fill(nullptr);
num_blocks = 1;
blocks[0].msrBits = 0xFFFFFFFF;
blocks[0].invalid = true;
}
void JitBaseBlockCache::Reset()
@ -103,7 +93,9 @@ int JitBaseBlockCache::AllocateBlock(u32 em_address)
{
JitBlock& b = blocks[num_blocks];
b.invalid = false;
b.originalAddress = em_address;
b.effectiveAddress = em_address;
b.physicalAddress = PowerPC::JitCache_TranslateAddress(em_address).address;
b.msrBits = MSR & JitBlock::JIT_CACHE_MSR_MASK;
b.linkData.clear();
num_blocks++; // commit the current block
return num_blocks - 1;
@ -111,13 +103,23 @@ int JitBaseBlockCache::AllocateBlock(u32 em_address)
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8* code_ptr)
{
blockCodePointers[block_num] = code_ptr;
JitBlock& b = blocks[block_num];
if (start_block_map.count(b.physicalAddress))
{
// We already have a block at this address; invalidate the old block.
// This should be very rare. This will only happen if the same block
// is called both with DR/IR enabled or disabled.
WARN_LOG(DYNA_REC, "Invalidating compiled block at same address %08x", b.physicalAddress);
int old_block_num = start_block_map[b.physicalAddress];
const JitBlock& old_b = blocks[old_block_num];
block_map.erase(
std::make_pair(old_b.physicalAddress + 4 * old_b.originalSize - 1, old_b.physicalAddress));
DestroyBlock(old_block_num, true);
}
start_block_map[b.physicalAddress] = block_num;
FastLookupEntryForAddress(b.effectiveAddress) = block_num;
std::memcpy(GetICachePtr(b.originalAddress), &block_num, sizeof(u32));
// Convert the logical address to a physical address for the block map
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
u32 pAddr = b.physicalAddress;
for (u32 block = pAddr / 32; block <= (pAddr + (b.originalSize - 1) * 4) / 32; ++block)
valid_block.Set(block);
@ -132,49 +134,64 @@ void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8*
}
LinkBlock(block_num);
LinkBlockExits(block_num);
}
JitRegister::Register(blockCodePointers[block_num], b.codeSize, "JIT_PPC_%08x",
b.originalAddress);
JitRegister::Register(b.checkedEntry, b.codeSize, "JIT_PPC_%08x", b.physicalAddress);
}
const u8** JitBaseBlockCache::GetCodePointers()
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr, u32 msr)
{
return blockCodePointers.data();
}
u32 translated_addr = addr;
if (UReg_MSR(msr).IR)
{
auto translated = PowerPC::JitCache_TranslateAddress(addr);
if (!translated.valid)
{
return -1;
}
translated_addr = translated.address;
}
u8* JitBaseBlockCache::GetICachePtr(u32 addr)
{
if (addr & JIT_ICACHE_VMEM_BIT)
return &jit->GetBlockCache()->iCacheVMEM[addr & JIT_ICACHE_MASK];
if (addr & JIT_ICACHE_EXRAM_BIT)
return &jit->GetBlockCache()->iCacheEx[addr & JIT_ICACHEEX_MASK];
return &jit->GetBlockCache()->iCache[addr & JIT_ICACHE_MASK];
}
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
{
u32 inst;
std::memcpy(&inst, GetICachePtr(addr), sizeof(u32));
if (inst & 0xfc000000) // definitely not a JIT block
auto map_result = start_block_map.find(translated_addr);
if (map_result == start_block_map.end())
return -1;
if ((int)inst >= num_blocks)
int block_num = map_result->second;
const JitBlock& b = blocks[block_num];
if (b.invalid)
return -1;
if (blocks[inst].originalAddress != addr)
if (b.effectiveAddress != addr)
return -1;
return inst;
if (b.msrBits != (msr & JitBlock::JIT_CACHE_MSR_MASK))
return -1;
return block_num;
}
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
void JitBaseBlockCache::MoveBlockIntoFastCache(u32 addr, u32 msr)
{
return (CompiledCode)blockCodePointers[block_num];
int block_num = GetBlockNumberFromStartAddress(addr, msr);
if (block_num < 0)
{
Jit(addr);
}
else
{
FastLookupEntryForAddress(addr) = block_num;
LinkBlock(block_num);
}
}
const u8* JitBaseBlockCache::Dispatch()
{
int block_num = FastLookupEntryForAddress(PC);
while (blocks[block_num].effectiveAddress != PC ||
blocks[block_num].msrBits != (MSR & JitBlock::JIT_CACHE_MSR_MASK))
{
MoveBlockIntoFastCache(PC, MSR & JitBlock::JIT_CACHE_MSR_MASK);
block_num = FastLookupEntryForAddress(PC);
}
return blocks[block_num].normalEntry;
}
// Block linker
@ -195,11 +212,14 @@ void JitBaseBlockCache::LinkBlockExits(int i)
{
if (!e.linkStatus)
{
int destinationBlock = GetBlockNumberFromStartAddress(e.exitAddress);
int destinationBlock = GetBlockNumberFromStartAddress(e.exitAddress, b.msrBits);
if (destinationBlock != -1)
{
WriteLinkBlock(e.exitPtrs, blocks[destinationBlock]);
e.linkStatus = true;
if (!blocks[destinationBlock].invalid)
{
WriteLinkBlock(e, &blocks[destinationBlock]);
e.linkStatus = true;
}
}
}
}
@ -208,39 +228,37 @@ void JitBaseBlockCache::LinkBlockExits(int i)
void JitBaseBlockCache::LinkBlock(int i)
{
LinkBlockExits(i);
JitBlock& b = blocks[i];
// equal_range(b) returns pair<iterator,iterator> representing the range
// of element with key b
auto ppp = links_to.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
const JitBlock& b = blocks[i];
auto ppp = links_to.equal_range(b.effectiveAddress);
for (auto iter = ppp.first; iter != ppp.second; ++iter)
{
// PanicAlert("Linking block %i to block %i", iter->second, i);
LinkBlockExits(iter->second);
const JitBlock& b2 = blocks[iter->second];
if (b.msrBits == b2.msrBits)
LinkBlockExits(iter->second);
}
}
void JitBaseBlockCache::UnlinkBlock(int i)
{
JitBlock& b = blocks[i];
auto ppp = links_to.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
auto ppp = links_to.equal_range(b.effectiveAddress);
for (auto iter = ppp.first; iter != ppp.second; ++iter)
{
JitBlock& sourceBlock = blocks[iter->second];
if (sourceBlock.msrBits != b.msrBits)
continue;
for (auto& e : sourceBlock.linkData)
{
if (e.exitAddress == b.originalAddress)
if (e.exitAddress == b.effectiveAddress)
{
WriteLinkBlock(e, nullptr);
e.linkStatus = false;
}
}
}
links_to.erase(b.originalAddress);
}
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
@ -258,20 +276,31 @@ void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
return;
}
b.invalid = true;
std::memcpy(GetICachePtr(b.originalAddress), &JIT_ICACHE_INVALID_WORD, sizeof(u32));
start_block_map.erase(b.physicalAddress);
FastLookupEntryForAddress(b.effectiveAddress) = 0;
UnlinkBlock(block_num);
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// Spurious entrances from previously linked blocks can only come through checkedEntry
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
// Delete linking adresses
auto it = links_to.equal_range(b.effectiveAddress);
while (it.first != it.second)
{
if (it.first->second == block_num)
it.first = links_to.erase(it.first);
else
it.first++;
}
// Raise an signal if we are going to call this block again
WriteDestroyBlock(b);
}
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool forced)
{
// Convert the logical address to a physical address for the block map
u32 pAddr = address & 0x1FFFFFFF;
auto translated = PowerPC::JitCache_TranslateAddress(address);
if (!translated.valid)
return;
u32 pAddr = translated.address;
// Optimize the common case of length == 32 which is used by Interpreter::dcb*
bool destroy_block = true;
@ -288,20 +317,11 @@ void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool for
// address
if (destroy_block)
{
std::map<std::pair<u32, u32>, u32>::iterator it1 = block_map.lower_bound(
std::make_pair(pAddr, 0)),
it2 = it1;
while (it2 != block_map.end() && it2->first.second < pAddr + length)
auto it = block_map.lower_bound(std::make_pair(pAddr, 0));
while (it != block_map.end() && it->first.second < pAddr + length)
{
JitBlock& b = blocks[it2->second];
std::memcpy(GetICachePtr(b.originalAddress), &JIT_ICACHE_INVALID_WORD, sizeof(u32));
DestroyBlock(it2->second, true);
++it2;
}
if (it1 != it2)
{
block_map.erase(it1, it2);
DestroyBlock(it->second, true);
it = block_map.erase(it);
}
// If the code was actually modified, we need to clear the relevant entries from the
@ -319,9 +339,10 @@ void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length, bool for
}
}
void JitBlockCache::WriteLinkBlock(u8* location, const JitBlock& block)
void JitBlockCache::WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest)
{
const u8* address = block.checkedEntry;
u8* location = source.exitPtrs;
const u8* address = dest ? dest->checkedEntry : jit->GetAsmRoutines()->dispatcher;
XEmitter emit(location);
if (*location == 0xE8)
{
@ -340,9 +361,11 @@ void JitBlockCache::WriteLinkBlock(u8* location, const JitBlock& block)
}
}
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
void JitBlockCache::WriteDestroyBlock(const JitBlock& block)
{
XEmitter emit((u8*)location);
emit.MOV(32, PPCSTATE(pc), Imm32(address));
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
// Only clear the entry points as we might still be within this block.
XEmitter emit((u8*)block.checkedEntry);
emit.INT3();
XEmitter emit2((u8*)block.normalEntry);
emit2.INT3();
}

View file

@ -12,32 +12,55 @@
#include "Common/CommonTypes.h"
static const u32 JIT_ICACHE_SIZE = 0x2000000;
static const u32 JIT_ICACHE_MASK = 0x1ffffff;
static const u32 JIT_ICACHEEX_SIZE = 0x4000000;
static const u32 JIT_ICACHEEX_MASK = 0x3ffffff;
static const u32 JIT_ICACHE_EXRAM_BIT = 0x10000000;
static const u32 JIT_ICACHE_VMEM_BIT = 0x20000000;
// This corresponds to opcode 5 which is invalid in PowerPC
static const u32 JIT_ICACHE_INVALID_BYTE = 0x80;
static const u32 JIT_ICACHE_INVALID_WORD = 0x80808080;
// A JitBlock is block of compiled code which corresponds to the PowerPC
// code at a given address.
//
// The notion of the address of a block is a bit complicated because of the
// way address translation works, but basically it's the combination of an
// effective address, the address translation bits in MSR, and the physical
// address.
struct JitBlock
{
enum
{
// Mask for the MSR bits which determine whether a compiled block
// is valid (MSR.IR and MSR.DR, the address translation bits).
JIT_CACHE_MSR_MASK = 0x30,
};
// A special entry point for block linking; usually used to check the
// downcount.
const u8* checkedEntry;
// The normal entry point for the block, returned by Dispatch().
const u8* normalEntry;
u32 originalAddress;
// The effective address (PC) for the beginning of the block.
u32 effectiveAddress;
// The MSR bits expected for this block to be valid; see JIT_CACHE_MSR_MASK.
u32 msrBits;
// The physical address of the code represented by this block.
// Various maps in the cache are indexed by this (start_block_map,
// block_map, and valid_block in particular). This is useful because of
// of the way the instruction cache works on PowerPC.
u32 physicalAddress;
// The number of bytes of JIT'ed code contained in this block. Mostly
// useful for logging.
u32 codeSize;
// The number of PPC instructions represented by this block. Mostly
// useful for logging.
u32 originalSize;
int runCount; // for profiling.
// Whether this struct refers to a valid block. This is mostly useful as
// a debugging aid.
// FIXME: Change current users of invalid bit to assertions?
bool invalid;
// Information about exits to a known address from this block.
// This is used to implement block linking.
struct LinkData
{
u8* exitPtrs; // to be able to rewrite the exit jum
u8* exitPtrs; // to be able to rewrite the exit jump
u32 exitAddress;
bool linkStatus; // is it already linked?
};
@ -59,7 +82,12 @@ class ValidBlockBitSet final
public:
enum
{
VALID_BLOCK_MASK_SIZE = 0x20000000 / 32,
// ValidBlockBitSet covers the whole 32-bit address-space in 32-byte
// chunks.
// FIXME: Maybe we can get away with less? There isn't any actual
// RAM in most of this space.
VALID_BLOCK_MASK_SIZE = (1ULL << 32) / 32,
// The number of elements in the allocated array. Each u32 contains 32 bits.
VALID_BLOCK_ALLOC_ELEMENTS = VALID_BLOCK_MASK_SIZE / 32
};
// Directly accessed by Jit64.
@ -79,33 +107,53 @@ public:
class JitBaseBlockCache
{
enum
{
MAX_NUM_BLOCKS = 65536 * 2,
};
public:
static constexpr int MAX_NUM_BLOCKS = 65536 * 2;
static constexpr u32 iCache_Num_Elements = 0x10000;
static constexpr u32 iCache_Mask = iCache_Num_Elements - 1;
std::array<const u8*, MAX_NUM_BLOCKS> blockCodePointers;
std::array<JitBlock, MAX_NUM_BLOCKS> blocks;
private:
// We store the metadata of all blocks in a linear way within this array.
// Note: blocks[0] must not be used as it is referenced as invalid block in iCache.
std::array<JitBlock, MAX_NUM_BLOCKS> blocks; // number -> JitBlock
int num_blocks;
std::multimap<u32, int> links_to;
// links_to hold all exit points of all valid blocks in a reverse way.
// It is used to query all blocks which links to an address.
std::multimap<u32, int> links_to; // destination_PC -> number
// Map indexed by the physical memory location.
// It is used to invalidate blocks based on memory location.
std::map<std::pair<u32, u32>, u32> block_map; // (end_addr, start_addr) -> number
// Map indexed by the physical address of the entry point.
// This is used to query the block based on the current PC in a slow way.
// TODO: This is redundant with block_map, and both should be a multimap.
std::map<u32, u32> start_block_map; // start_addr -> number
// This bitsets shows which cachelines overlap with any blocks.
// It is used to provide a fast way to query if no icache invalidation is needed.
ValidBlockBitSet valid_block;
bool m_initialized;
// This array is indexed with the masked PC and likely holds the correct block id.
// This is used as a fast cache of start_block_map used in the assembly dispatcher.
std::array<int, iCache_Num_Elements> iCache; // start_addr & mask -> number
void LinkBlockExits(int i);
void LinkBlock(int i);
void UnlinkBlock(int i);
u8* GetICachePtr(u32 addr);
void DestroyBlock(int block_num, bool invalidate);
// Virtual for overloaded
virtual void WriteLinkBlock(u8* location, const JitBlock& block) = 0;
virtual void WriteDestroyBlock(const u8* location, u32 address) = 0;
void MoveBlockIntoFastCache(u32 em_address, u32 msr);
// Fast but risky block lookup based on iCache.
int& FastLookupEntryForAddress(u32 address) { return iCache[(address >> 2) & iCache_Mask]; }
// Virtual for overloaded
virtual void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) = 0;
virtual void WriteDestroyBlock(const JitBlock& block) {}
public:
JitBaseBlockCache() : num_blocks(0), m_initialized(false) {}
JitBaseBlockCache() : num_blocks(1) {}
virtual ~JitBaseBlockCache() {}
int AllocateBlock(u32 em_address);
void FinalizeBlock(int block_num, bool block_link, const u8* code_ptr);
@ -119,18 +167,20 @@ public:
// Code Cache
JitBlock* GetBlock(int block_num);
JitBlock* GetBlocks() { return blocks.data(); }
int* GetICache() { return iCache.data(); }
int GetNumBlocks() const;
const u8** GetCodePointers();
std::array<u8, JIT_ICACHE_SIZE> iCache;
std::array<u8, JIT_ICACHEEX_SIZE> iCacheEx;
std::array<u8, JIT_ICACHE_SIZE> iCacheVMEM;
// Fast way to get a block. Only works on the first ppc instruction of a block.
int GetBlockNumberFromStartAddress(u32 em_address);
// Look for the block in the slow but accurate way.
// This function shall be used if FastLookupEntryForAddress() failed.
int GetBlockNumberFromStartAddress(u32 em_address, u32 msr);
CompiledCode GetCompiledCodeFromBlock(int block_num);
// Get the normal entry for the block associated with the current program
// counter. This will JIT code if necessary. (This is the reference
// implementation; high-performance JITs will want to use a custom
// assembly version.)
const u8* Dispatch();
// DOES NOT WORK CORRECTLY WITH INLINING
void InvalidateICache(u32 address, const u32 length, bool forced);
u32* GetBlockBitSet() const { return valid_block.m_valid_block.get(); }
@ -140,6 +190,6 @@ public:
class JitBlockCache : public JitBaseBlockCache
{
private:
void WriteLinkBlock(u8* location, const JitBlock& block) override;
void WriteDestroyBlock(const u8* location, u32 address) override;
void WriteLinkBlock(const JitBlock::LinkData& source, const JitBlock* dest) override;
void WriteDestroyBlock(const JitBlock& block) override;
};

View file

@ -150,7 +150,7 @@ void GetProfileResults(ProfileStats* prof_stats)
u64 timecost = block->ticCounter;
// Todo: tweak.
if (block->runCount >= 1)
prof_stats->block_stats.emplace_back(i, block->originalAddress, cost, timecost,
prof_stats->block_stats.emplace_back(i, block->effectiveAddress, cost, timecost,
block->runCount, block->codeSize);
prof_stats->cost_sum += cost;
prof_stats->timecost_sum += timecost;
@ -169,12 +169,12 @@ int GetHostCode(u32* address, const u8** code, u32* code_size)
return 1;
}
int block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address);
int block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address, MSR);
if (block_num < 0)
{
for (int i = 0; i < 500; i++)
{
block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address - 4 * i);
block_num = jit->GetBlockCache()->GetBlockNumberFromStartAddress(*address - 4 * i, MSR);
if (block_num >= 0)
break;
}
@ -182,8 +182,8 @@ int GetHostCode(u32* address, const u8** code, u32* code_size)
if (block_num >= 0)
{
JitBlock* block = jit->GetBlockCache()->GetBlock(block_num);
if (!(block->originalAddress <= *address &&
block->originalSize + block->originalAddress >= *address))
if (!(block->effectiveAddress <= *address &&
block->originalSize + block->effectiveAddress >= *address))
block_num = -1;
}
@ -199,7 +199,7 @@ int GetHostCode(u32* address, const u8** code, u32* code_size)
*code = block->checkedEntry;
*code_size = block->codeSize;
*address = block->originalAddress;
*address = block->effectiveAddress;
return 0;
}

View file

@ -76,7 +76,19 @@ enum XCheckTLBFlag
FLAG_READ,
FLAG_WRITE,
FLAG_OPCODE,
FLAG_OPCODE_NO_EXCEPTION
};
static bool IsOpcodeFlag(XCheckTLBFlag flag)
{
return flag == FLAG_OPCODE || flag == FLAG_OPCODE_NO_EXCEPTION;
}
static bool IsNoExceptionFlag(XCheckTLBFlag flag)
{
return flag == FLAG_NO_EXCEPTION || flag == FLAG_OPCODE_NO_EXCEPTION;
}
template <const XCheckTLBFlag flag>
static u32 TranslateAddress(const u32 address);
@ -836,6 +848,43 @@ bool IsOptimizableGatherPipeWrite(u32 address)
return address == 0xCC008000;
}
TranslateResult JitCache_TranslateAddress(u32 address)
{
if (!UReg_MSR(MSR).IR)
return TranslateResult{ true, true, address };
bool from_bat = true;
int segment = address >> 28;
if (SConfig::GetInstance().bMMU && (address & Memory::ADDR_MASK_MEM1))
{
u32 tlb_addr = TranslateAddress<FLAG_OPCODE>(address);
if (tlb_addr == 0)
{
return TranslateResult{ false, false, 0 };
}
else
{
address = tlb_addr;
from_bat = false;
}
}
else
{
if ((segment == 0x8 || segment == 0x0) && (address & 0x0FFFFFFF) < Memory::REALRAM_SIZE)
address = address & 0x3FFFFFFF;
else if (Memory::m_pEXRAM && segment == 0x9 && (address & 0x0FFFFFFF) < Memory::EXRAM_SIZE)
address = address & 0x3FFFFFFF;
else if (Memory::bFakeVMEM && (segment == 0x7 || segment == 0x4))
address = 0x7E000000 | (address & Memory::FAKEVMEM_MASK);
else
return TranslateResult{ false, false, 0 };
}
return TranslateResult{ true, from_bat, address };
}
// *********************************************************************************
// Warning: Test Area
//
@ -952,6 +1001,7 @@ static void GenerateISIException(u32 _EffectiveAddress)
NPC = _EffectiveAddress;
PowerPC::ppcState.Exceptions |= EXCEPTION_ISI;
WARN_LOG(POWERPC, "ISI exception at 0x%08x", PC);
}
void SDRUpdated()
@ -990,7 +1040,7 @@ static __forceinline TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag fl
u32* paddr)
{
u32 tag = vpa >> HW_PAGE_INDEX_SHIFT;
PowerPC::tlb_entry* tlbe = &PowerPC::ppcState.tlb[flag == FLAG_OPCODE][tag & HW_PAGE_INDEX_MASK];
PowerPC::tlb_entry* tlbe = &PowerPC::ppcState.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
if (tlbe->tag[0] == tag)
{
// Check if C bit requires updating
@ -1006,7 +1056,7 @@ static __forceinline TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag fl
}
}
if (flag != FLAG_NO_EXCEPTION)
if (!IsNoExceptionFlag(flag))
tlbe->recent = 0;
*paddr = tlbe->paddr[0] | (vpa & 0xfff);
@ -1028,7 +1078,7 @@ static __forceinline TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag fl
}
}
if (flag != FLAG_NO_EXCEPTION)
if (!IsNoExceptionFlag(flag))
tlbe->recent = 1;
*paddr = tlbe->paddr[1] | (vpa & 0xfff);
@ -1040,11 +1090,11 @@ static __forceinline TLBLookupResult LookupTLBPageAddress(const XCheckTLBFlag fl
static __forceinline void UpdateTLBEntry(const XCheckTLBFlag flag, UPTE2 PTE2, const u32 address)
{
if (flag == FLAG_NO_EXCEPTION)
if (IsNoExceptionFlag(flag))
return;
int tag = address >> HW_PAGE_INDEX_SHIFT;
PowerPC::tlb_entry* tlbe = &PowerPC::ppcState.tlb[flag == FLAG_OPCODE][tag & HW_PAGE_INDEX_MASK];
PowerPC::tlb_entry* tlbe = &PowerPC::ppcState.tlb[IsOpcodeFlag(flag)][tag & HW_PAGE_INDEX_MASK];
int index = tlbe->recent == 0 && tlbe->tag[0] != TLB_TAG_INVALID;
tlbe->recent = index;
tlbe->paddr[index] = PTE2.RPN << HW_PAGE_INDEX_SHIFT;
@ -1110,6 +1160,7 @@ static __forceinline u32 TranslatePageAddress(const u32 address, const XCheckTLB
switch (flag)
{
case FLAG_NO_EXCEPTION:
case FLAG_OPCODE_NO_EXCEPTION:
break;
case FLAG_READ:
PTE2.R = 1;
@ -1123,7 +1174,7 @@ static __forceinline u32 TranslatePageAddress(const u32 address, const XCheckTLB
break;
}
if (flag != FLAG_NO_EXCEPTION)
if (!IsNoExceptionFlag(flag))
*(u32*)&Memory::physical_base[pteg_addr + 4] = bswap(PTE2.Hex);
// We already updated the TLB entry if this was caused by a C bit.

View file

@ -272,6 +272,13 @@ bool IsOptimizableRAMAddress(const u32 address);
u32 IsOptimizableMMIOAccess(u32 address, u32 accessSize);
bool IsOptimizableGatherPipeWrite(u32 address);
struct TranslateResult
{
bool valid;
bool from_bat;
u32 address;
};
TranslateResult JitCache_TranslateAddress(u32 address);
} // namespace
enum CRBits

View file

@ -58,6 +58,9 @@ void PathConfigPane::InitializeGUI()
m_dump_path_dirpicker =
new wxDirPickerCtrl(this, wxID_ANY, wxEmptyString, _("Choose a dump directory:"),
wxDefaultPosition, wxDefaultSize, wxDIRP_USE_TEXTCTRL | wxDIRP_SMALL);
m_wii_sdcard_filepicker = new wxFilePickerCtrl(
this, wxID_ANY, wxEmptyString, _("Choose an SD Card file:"), wxFileSelectorDefaultWildcardStr,
wxDefaultPosition, wxDefaultSize, wxDIRP_USE_TEXTCTRL | wxDIRP_SMALL);
m_iso_paths_listbox->Bind(wxEVT_LISTBOX, &PathConfigPane::OnISOPathSelectionChanged, this);
m_recursive_iso_paths_checkbox->Bind(wxEVT_CHECKBOX,
@ -71,6 +74,8 @@ void PathConfigPane::InitializeGUI()
&PathConfigPane::OnApploaderPathChanged, this);
m_nand_root_dirpicker->Bind(wxEVT_DIRPICKER_CHANGED, &PathConfigPane::OnNANDRootChanged, this);
m_dump_path_dirpicker->Bind(wxEVT_DIRPICKER_CHANGED, &PathConfigPane::OnDumpPathChanged, this);
m_wii_sdcard_filepicker->Bind(wxEVT_FILEPICKER_CHANGED, &PathConfigPane::OnSdCardPathChanged,
this);
wxBoxSizer* const iso_button_sizer = new wxBoxSizer(wxHORIZONTAL);
iso_button_sizer->Add(m_recursive_iso_paths_checkbox, 0, wxALL | wxALIGN_CENTER);
@ -101,6 +106,10 @@ void PathConfigPane::InitializeGUI()
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("Dump Path:")), wxGBPosition(4, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5);
picker_sizer->Add(m_dump_path_dirpicker, wxGBPosition(4, 1), wxDefaultSpan, wxEXPAND | wxALL, 5);
picker_sizer->Add(new wxStaticText(this, wxID_ANY, _("SD Card Path:")), wxGBPosition(5, 0),
wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxALL, 5);
picker_sizer->Add(m_wii_sdcard_filepicker, wxGBPosition(5, 1), wxDefaultSpan, wxEXPAND | wxALL,
5);
picker_sizer->AddGrowableCol(1);
// Populate the Paths page
@ -121,6 +130,7 @@ void PathConfigPane::LoadGUIValues()
m_apploader_path_filepicker->SetPath(StrToWxStr(startup_params.m_strApploader));
m_nand_root_dirpicker->SetPath(StrToWxStr(SConfig::GetInstance().m_NANDPath));
m_dump_path_dirpicker->SetPath(StrToWxStr(SConfig::GetInstance().m_DumpPath));
m_wii_sdcard_filepicker->SetPath(StrToWxStr(SConfig::GetInstance().m_strWiiSDCardPath));
// Update selected ISO paths
for (const std::string& folder : SConfig::GetInstance().m_ISOFolder)
@ -194,6 +204,13 @@ void PathConfigPane::OnApploaderPathChanged(wxCommandEvent& event)
SConfig::GetInstance().m_strApploader = WxStrToStr(m_apploader_path_filepicker->GetPath());
}
void PathConfigPane::OnSdCardPathChanged(wxCommandEvent& event)
{
std::string sd_card_path = WxStrToStr(m_wii_sdcard_filepicker->GetPath());
SConfig::GetInstance().m_strWiiSDCardPath = sd_card_path;
File::SetUserPath(F_WIISDCARD_IDX, sd_card_path);
}
void PathConfigPane::OnNANDRootChanged(wxCommandEvent& event)
{
std::string nand_path = SConfig::GetInstance().m_NANDPath =

View file

@ -31,6 +31,7 @@ private:
void OnApploaderPathChanged(wxCommandEvent&);
void OnNANDRootChanged(wxCommandEvent&);
void OnDumpPathChanged(wxCommandEvent&);
void OnSdCardPathChanged(wxCommandEvent&);
void SaveISOPathChanges();
@ -44,4 +45,5 @@ private:
wxFilePickerCtrl* m_default_iso_filepicker;
wxFilePickerCtrl* m_apploader_path_filepicker;
wxDirPickerCtrl* m_dump_path_dirpicker;
wxFilePickerCtrl* m_wii_sdcard_filepicker;
};

View file

@ -67,8 +67,6 @@
#include "VideoCommon/VertexShaderManager.h"
#include "VideoCommon/VideoConfig.h"
int g_saveSlot = 1;
#if defined(HAVE_X11) && HAVE_X11
// X11Utils nastiness that's only used here
namespace X11Utils
@ -1529,11 +1527,11 @@ void CFrame::ParseHotkeys()
}
if (IsHotkey(HK_SAVE_STATE_SLOT_SELECTED))
{
State::Save(g_saveSlot);
State::Save(m_saveSlot);
}
if (IsHotkey(HK_LOAD_STATE_SLOT_SELECTED))
{
State::Load(g_saveSlot);
State::Load(m_saveSlot);
}
if (IsHotkey(HK_TOGGLE_STEREO_SBS))

View file

@ -158,6 +158,7 @@ private:
bool m_bGameLoading;
bool m_bClosing;
bool m_confirmStop;
int m_saveSlot = 1;
std::vector<std::string> drives;
@ -347,4 +348,3 @@ void OnStoppedCallback();
void GCTASManipFunction(GCPadStatus* PadStatus, int controllerID);
void WiiTASManipFunction(u8* data, WiimoteEmu::ReportFeatures rptf, int controllerID, int ext,
const wiimote_key key);
extern int g_saveSlot;

View file

@ -273,7 +273,7 @@ wxMenuBar* CFrame::CreateMenu()
static const wxString menu_text[] =
{
_("&Registers"), _("&Watch"), _("&Breakpoints"),
_("&Memory"), _("&JIT"), _("&Sound"),
_("&Memory"), _("&JIT"), _("&Sound"),
_("&Video")
};
@ -1135,10 +1135,17 @@ void CFrame::DoStop()
// Pause the state during confirmation and restore it afterwards
Core::EState state = Core::GetState();
// Do not pause if netplay is running as CPU thread might be blocked
// waiting on inputs
bool should_pause = !NetPlayDialog::GetNetPlayClient();
// If exclusive fullscreen is not enabled then we can pause the emulation
// before we've exited fullscreen. If not then we need to exit fullscreen first.
if (!RendererIsFullscreen() || !g_Config.ExclusiveFullscreenEnabled() ||
SConfig::GetInstance().bRenderToMain)
should_pause =
should_pause && (!RendererIsFullscreen() || !g_Config.ExclusiveFullscreenEnabled() ||
SConfig::GetInstance().bRenderToMain);
if (should_pause)
{
Core::SetState(Core::CORE_PAUSE);
}
@ -1152,7 +1159,9 @@ void CFrame::DoStop()
HotkeyManagerEmu::Enable(true);
if (Ret != wxID_YES)
{
Core::SetState(state);
if (should_pause)
Core::SetState(state);
m_confirmStop = false;
return;
}
@ -1288,7 +1297,7 @@ void CFrame::OnStop(wxCommandEvent& WXUNUSED(event))
void CFrame::OnReset(wxCommandEvent& WXUNUSED(event))
{
if (Movie::IsRecordingInput())
Movie::g_bReset = true;
Movie::SetReset(true);
ProcessorInterface::ResetButton_Tap();
}
@ -1667,9 +1676,9 @@ void CFrame::OnFrameSkip(wxCommandEvent& event)
void CFrame::OnSelectSlot(wxCommandEvent& event)
{
g_saveSlot = event.GetId() - IDM_SELECT_SLOT_1 + 1;
Core::DisplayMessage(StringFromFormat("Selected slot %d - %s", g_saveSlot,
State::GetInfoStringOfSlot(g_saveSlot).c_str()),
m_saveSlot = event.GetId() - IDM_SELECT_SLOT_1 + 1;
Core::DisplayMessage(StringFromFormat("Selected slot %d - %s", m_saveSlot,
State::GetInfoStringOfSlot(m_saveSlot).c_str()),
2500);
}
@ -1677,7 +1686,7 @@ void CFrame::OnLoadCurrentSlot(wxCommandEvent& event)
{
if (Core::IsRunningAndStarted())
{
State::Load(g_saveSlot);
State::Load(m_saveSlot);
}
}
@ -1685,7 +1694,7 @@ void CFrame::OnSaveCurrentSlot(wxCommandEvent& event)
{
if (Core::IsRunningAndStarted())
{
State::Save(g_saveSlot);
State::Save(m_saveSlot);
}
}

View file

@ -315,11 +315,13 @@ bool ControlDialog::Validate()
{
control_reference->expression = WxStrToStr(textctrl->GetValue());
auto lock = ControllerEmu::GetStateLock();
g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device);
UpdateGUI();
return (control_reference->parse_error == EXPRESSION_PARSE_SUCCESS);
return (control_reference->parse_error == EXPRESSION_PARSE_SUCCESS ||
control_reference->parse_error == EXPRESSION_PARSE_NO_DEVICE);
}
void GamepadPage::SetDevice(wxCommandEvent&)
@ -351,6 +353,7 @@ void ControlDialog::ClearControl(wxCommandEvent&)
{
control_reference->expression.clear();
auto lock = ControllerEmu::GetStateLock();
g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device);
UpdateGUI();
@ -408,6 +411,7 @@ void ControlDialog::SetSelectedControl(wxCommandEvent&)
textctrl->WriteText(expr);
control_reference->expression = textctrl->GetValue();
auto lock = ControllerEmu::GetStateLock();
g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device);
UpdateGUI();
@ -442,6 +446,7 @@ void ControlDialog::AppendControl(wxCommandEvent& event)
textctrl->WriteText(expr);
control_reference->expression = textctrl->GetValue();
auto lock = ControllerEmu::GetStateLock();
g_controller_interface.UpdateReference(control_reference, m_parent->controller->default_device);
UpdateGUI();
@ -556,6 +561,7 @@ bool GamepadPage::DetectButton(ControlButton* button)
wxString expr;
GetExpressionForControl(expr, control_name);
button->control_reference->expression = expr;
auto lock = ControllerEmu::GetStateLock();
g_controller_interface.UpdateReference(button->control_reference, controller->default_device);
success = true;
}

View file

@ -4,7 +4,6 @@
#include <cstring>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include <wx/bitmap.h>
@ -125,6 +124,7 @@ static void DrawButton(unsigned int* const bitmasks, unsigned int buttons, unsig
}
else
{
auto lock = ControllerEmu::GetStateLock();
unsigned char amt = 255 - g->control_group->controls[(row * 8) + n]->control_ref->State() * 128;
dc.SetBrush(wxBrush(wxColour(amt, amt, amt)));
}
@ -232,17 +232,15 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
}
// raw dot
{
ControlState xx, yy;
xx = g->control_group->controls[3]->control_ref->State();
xx -= g->control_group->controls[2]->control_ref->State();
yy = g->control_group->controls[1]->control_ref->State();
yy -= g->control_group->controls[0]->control_ref->State();
ControlState xx, yy;
xx = g->control_group->controls[3]->control_ref->State();
xx -= g->control_group->controls[2]->control_ref->State();
yy = g->control_group->controls[1]->control_ref->State();
yy -= g->control_group->controls[0]->control_ref->State();
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxGREY_BRUSH);
DrawCoordinate(dc, xx, yy);
}
dc.SetPen(*wxGREY_PEN);
dc.SetBrush(*wxGREY_BRUSH);
DrawCoordinate(dc, xx, yy);
// adjusted dot
if (x != 0 || y != 0)
@ -403,6 +401,7 @@ static void DrawControlGroupBox(wxDC& dc, ControlGroupBox* g)
for (unsigned int n = 0; n < trigger_count; ++n)
{
dc.SetBrush(*wxRED_BRUSH);
ControlState trig_d = g->control_group->controls[n]->control_ref->State();
ControlState trig_a =
@ -465,6 +464,7 @@ void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event))
GamepadPage* const current_page =
(GamepadPage*)m_pad_notebook->GetPage(m_pad_notebook->GetSelection());
auto lock = ControllerEmu::GetStateLock();
for (ControlGroupBox* g : current_page->control_groups)
{
// if this control group has a bitmap

View file

@ -324,6 +324,24 @@ void NetPlaySetupFrame::MakeNetPlayDiag(int port, const std::string& game, bool
trav, centralServer, (u16)centralPort);
if (netplay_client->IsConnected())
{
int winPosX, winPosY, winWidth, winHeight;
// Remember the window size and position for NetWindow
netplay_section.Get("NetWindowPosX", &winPosX, -1);
netplay_section.Get("NetWindowPosY", &winPosY, -1);
netplay_section.Get("NetWindowWidth", &winWidth, 768);
netplay_section.Get("NetWindowHeight", &winHeight, 768 - 128);
if (winPosX == -1 || winPosY == -1)
{
npd->SetSize(768, 768 - 128);
npd->Center();
}
else
{
npd->SetSize(winPosX, winPosY, winWidth, winHeight);
}
npd->Show();
Destroy();
}

View file

@ -243,13 +243,22 @@ NetPlayDialog::NetPlayDialog(wxWindow* const parent, const CGameListCtrl* const
panel->SetSizerAndFit(main_szr);
main_szr->SetSizeHints(this);
SetSize(768, 768 - 128);
Center();
}
NetPlayDialog::~NetPlayDialog()
{
IniFile inifile;
const std::string dolphin_ini = File::GetUserPath(F_DOLPHINCONFIG_IDX);
inifile.Load(dolphin_ini);
IniFile::Section& netplay_config = *inifile.GetOrCreateSection("NetPlay");
netplay_config.Set("NetWindowPosX", GetPosition().x);
netplay_config.Set("NetWindowPosY", GetPosition().y);
netplay_config.Set("NetWindowWidth", GetSize().GetWidth());
netplay_config.Set("NetWindowHeight", GetSize().GetHeight());
inifile.Save(dolphin_ini);
if (netplay_client)
{
delete netplay_client;

View file

@ -39,7 +39,7 @@ enum
enum
{
INITIAL_PAD_BUFFER_SIZE = 20
INITIAL_PAD_BUFFER_SIZE = 5
};
enum class ChatMessageType
@ -112,7 +112,7 @@ private:
void OnPlayerSelect(wxCommandEvent& event);
void GetNetSettings(NetSettings& settings);
std::string FindCurrentGame();
std::string FindGame(const std::string& game);
std::string FindGame(const std::string& game) override;
void AddChatMessage(ChatMessageType type, const std::string& msg);
void OnCopyIP(wxCommandEvent&);

View file

@ -1106,11 +1106,11 @@ void TASInputDlg::SetTurbo(wxMouseEvent& event)
// NOTE: Host / CPU Thread
void TASInputDlg::ButtonTurbo()
{
static u64 frame = Movie::g_currentFrame;
static u64 frame = Movie::GetCurrentFrame();
if (frame != Movie::g_currentFrame)
if (frame != Movie::GetCurrentFrame())
{
frame = Movie::g_currentFrame;
frame = Movie::GetCurrentFrame();
for (Button* const button : m_buttons)
{
if (button != nullptr && button->turbo_on)

View file

@ -6,8 +6,19 @@
#include <memory>
#include "Common/Common.h"
// This should be called before calling GetState() or State() on a control reference
// to prevent a race condition.
// This is a recursive mutex because UpdateReferences is recursive.
static std::recursive_mutex s_get_state_mutex;
std::unique_lock<std::recursive_mutex> ControllerEmu::GetStateLock()
{
std::unique_lock<std::recursive_mutex> lock(s_get_state_mutex);
return lock;
}
void ControllerEmu::UpdateReferences(ControllerInterface& devi)
{
auto lock = ControllerEmu::GetStateLock();
for (auto& ctrlGroup : groups)
{
for (auto& control : ctrlGroup->controls)

View file

@ -7,6 +7,7 @@
#include <algorithm>
#include <cmath>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
@ -444,6 +445,12 @@ public:
void UpdateReferences(ControllerInterface& devi);
// This returns a lock that should be held before calling State() on any control
// references and GetState(), by extension. This prevents a race condition
// which happens while handling a hotplug event because a control reference's State()
// could be called before we have finished updating the reference.
static std::unique_lock<std::recursive_mutex> GetStateLock();
std::vector<std::unique_ptr<ControlGroup>> groups;
ciface::Core::DeviceQualifier default_device;

View file

@ -106,17 +106,6 @@ void ControllerInterface::Shutdown()
if (!m_is_init)
return;
std::lock_guard<std::mutex> lk(m_devices_mutex);
for (const auto& d : m_devices)
{
// Set outputs to ZERO before destroying device
for (ciface::Core::Device::Output* o : d->Outputs())
o->SetState(0);
}
m_devices.clear();
#ifdef CIFACE_USE_XINPUT
ciface::XInput::DeInit();
#endif
@ -136,6 +125,20 @@ void ControllerInterface::Shutdown()
#ifdef CIFACE_USE_ANDROID
// nothing needed
#endif
#ifdef CIFACE_USE_EVDEV
ciface::evdev::Shutdown();
#endif
std::lock_guard<std::mutex> lk(m_devices_mutex);
for (const auto& d : m_devices)
{
// Set outputs to ZERO before destroying device
for (ciface::Core::Device::Output* o : d->Outputs())
o->SetState(0);
}
m_devices.clear();
m_is_init = false;
}
@ -160,6 +163,14 @@ void ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device
m_devices.emplace_back(std::move(device));
}
void ControllerInterface::RemoveDevice(std::function<bool(const ciface::Core::Device*)> callback)
{
std::lock_guard<std::mutex> lk(m_devices_mutex);
m_devices.erase(std::remove_if(m_devices.begin(), m_devices.end(),
[&callback](const auto& dev) { return callback(dev.get()); }),
m_devices.end());
}
//
// UpdateInput
//
@ -167,9 +178,35 @@ void ControllerInterface::AddDevice(std::shared_ptr<ciface::Core::Device> device
//
void ControllerInterface::UpdateInput()
{
std::lock_guard<std::mutex> lk(m_devices_mutex);
for (const auto& d : m_devices)
d->UpdateInput();
// Don't block the UI or CPU thread (to avoid a short but noticeable frame drop)
if (m_devices_mutex.try_lock())
{
std::lock_guard<std::mutex> lk(m_devices_mutex, std::adopt_lock);
for (const auto& d : m_devices)
d->UpdateInput();
}
}
//
// RegisterHotplugCallback
//
// Register a callback to be called from the input backends' hotplug thread
// when there is a new device
//
void ControllerInterface::RegisterHotplugCallback(std::function<void()> callback)
{
m_hotplug_callbacks.emplace_back(std::move(callback));
}
//
// InvokeHotplugCallbacks
//
// Invoke all callbacks that were registered
//
void ControllerInterface::InvokeHotplugCallbacks() const
{
for (const auto& callback : m_hotplug_callbacks)
callback();
}
//

View file

@ -122,12 +122,17 @@ public:
void Reinitialize();
void Shutdown();
void AddDevice(std::shared_ptr<ciface::Core::Device> device);
void RemoveDevice(std::function<bool(const ciface::Core::Device*)> callback);
bool IsInit() const { return m_is_init; }
void UpdateReference(ControlReference* control,
const ciface::Core::DeviceQualifier& default_device) const;
void UpdateInput();
void RegisterHotplugCallback(std::function<void(void)> callback);
void InvokeHotplugCallbacks() const;
private:
std::vector<std::function<void()>> m_hotplug_callbacks;
bool m_is_init;
void* m_hwnd;
};

View file

@ -98,6 +98,7 @@ public:
virtual std::string GetName() const = 0;
virtual std::string GetSource() const = 0;
virtual void UpdateInput() {}
virtual bool IsValid() const { return true; }
const std::vector<Input*>& Inputs() const { return m_inputs; }
const std::vector<Output*>& Outputs() const { return m_outputs; }
Input* FindInput(const std::string& name) const;

View file

@ -235,8 +235,9 @@ public:
ControlQualifier qualifier;
Device::Control* control;
ControlExpression(ControlQualifier qualifier_, Device::Control* control_)
: qualifier(qualifier_), control(control_)
ControlExpression(ControlQualifier qualifier_, std::shared_ptr<Device> device,
Device::Control* control_)
: qualifier(qualifier_), control(control_), m_device(device)
{
}
@ -244,6 +245,8 @@ public:
void SetValue(ControlState value) override { control->ToOutput()->SetGatedState(value); }
int CountNumControls() override { return 1; }
operator std::string() override { return "`" + (std::string)qualifier + "`"; }
private:
std::shared_ptr<Device> m_device;
};
class BinaryExpression : public ExpressionNode
@ -393,14 +396,15 @@ private:
{
case TOK_CONTROL:
{
std::shared_ptr<Device> device = finder.FindDevice(tok.qualifier);
Device::Control* control = finder.FindControl(tok.qualifier);
if (control == nullptr)
{
*expr_out = new DummyExpression(tok.qualifier);
return EXPRESSION_PARSE_SUCCESS;
return EXPRESSION_PARSE_NO_DEVICE;
}
*expr_out = new ControlExpression(tok.qualifier, control);
*expr_out = new ControlExpression(tok.qualifier, device, control);
return EXPRESSION_PARSE_SUCCESS;
}
case TOK_LPAREN:
@ -423,13 +427,12 @@ private:
ExpressionParseStatus Unary(ExpressionNode** expr_out)
{
ExpressionParseStatus status;
if (IsUnaryExpression(Peek().type))
{
Token tok = Chew();
ExpressionNode* atom_expr;
if ((status = Atom(&atom_expr)) != EXPRESSION_PARSE_SUCCESS)
ExpressionParseStatus status = Atom(&atom_expr);
if (status == EXPRESSION_PARSE_SYNTAX_ERROR)
return status;
*expr_out = new UnaryExpression(tok.type, atom_expr);
return EXPRESSION_PARSE_SUCCESS;
@ -453,16 +456,16 @@ private:
ExpressionParseStatus Binary(ExpressionNode** expr_out)
{
ExpressionParseStatus status;
if ((status = Unary(expr_out)) != EXPRESSION_PARSE_SUCCESS)
ExpressionParseStatus status = Unary(expr_out);
if (status == EXPRESSION_PARSE_SYNTAX_ERROR)
return status;
while (IsBinaryToken(Peek().type))
{
Token tok = Chew();
ExpressionNode* unary_expr;
if ((status = Unary(&unary_expr)) != EXPRESSION_PARSE_SUCCESS)
status = Unary(&unary_expr);
if (status == EXPRESSION_PARSE_SYNTAX_ERROR)
{
delete *expr_out;
return status;
@ -550,10 +553,11 @@ ExpressionParseStatus ParseExpression(const std::string& str, ControlFinder& fin
qualifier.control_name = str;
qualifier.has_device = false;
std::shared_ptr<Device> device = finder.FindDevice(qualifier);
Device::Control* control = finder.FindControl(qualifier);
if (control)
{
*expr_out = new Expression(new ControlExpression(qualifier, control));
*expr_out = new Expression(new ControlExpression(qualifier, device, control));
return EXPRESSION_PARSE_SUCCESS;
}

View file

@ -37,10 +37,10 @@ public:
: container(container_), default_device(default_), is_input(is_input_)
{
}
std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier);
Core::Device::Control* FindControl(ControlQualifier qualifier);
private:
std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier);
const Core::DeviceContainer& container;
const Core::DeviceQualifier& default_device;
bool is_input;

Some files were not shown because too many files have changed in this diff Show more