Merge latest master changes
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 176 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 234 B |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 234 B |
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
using namespace FifoAnalyzer;
|
||||
|
||||
void FifoRecordAnalyzer::Initialize(u32* cpMem)
|
||||
void FifoRecordAnalyzer::Initialize(const u32* cpMem)
|
||||
{
|
||||
s_DrawingObject = false;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ enum
|
|||
};
|
||||
|
||||
// UDSPControl
|
||||
#define DSP_CONTROL_MASK 0x0C07
|
||||
constexpr u16 DSP_CONTROL_MASK = 0x0C07;
|
||||
union UDSPControl {
|
||||
u16 Hex;
|
||||
struct
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,5 +17,5 @@ void LoadConfig();
|
|||
|
||||
InputConfig* GetConfig();
|
||||
|
||||
void GetStatus(u8 port, KeyboardStatus* keyboard_status);
|
||||
KeyboardStatus GetStatus(u8 port);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
|
||||
void ClockSync();
|
||||
|
||||
void Send(u8* si_buffer);
|
||||
void Send(const u8* si_buffer);
|
||||
int Receive(u8* si_buffer);
|
||||
|
||||
private:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
57
Source/Core/Core/HW/WiimoteReal/IOAndroid.h
Normal 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
|
20
Source/Core/Core/HW/WiimoteReal/IODummy.h
Normal 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 {}
|
||||
};
|
||||
}
|
260
Source/Core/Core/HW/WiimoteReal/IOLinux.cpp
Normal 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
|
57
Source/Core/Core/HW/WiimoteReal/IOLinux.h
Normal 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
|
|
@ -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)
|
||||
{
|
||||
|
|
66
Source/Core/Core/HW/WiimoteReal/IOWin.h
Normal 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
|
106
Source/Core/Core/HW/WiimoteReal/IOdarwin.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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&);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|