Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-master

This commit is contained in:
Nayla Hanegan 2024-08-23 13:38:17 -04:00
commit 7e6752e8fc
516 changed files with 60670 additions and 270317 deletions

View file

@ -20,7 +20,7 @@ AlsaSound::~AlsaSound()
m_thread_status.store(ALSAThreadStatus::STOPPING);
// Immediately lock and unlock mutex to prevent cv race.
std::unique_lock<std::mutex>{cv_m};
std::unique_lock<std::mutex>{cv_m}.unlock();
// Give the opportunity to the audio thread
// to realize we are stopping the emulation
@ -82,7 +82,7 @@ bool AlsaSound::SetRunning(bool running)
m_thread_status.store(running ? ALSAThreadStatus::RUNNING : ALSAThreadStatus::PAUSED);
// Immediately lock and unlock mutex to prevent cv race.
std::unique_lock<std::mutex>{cv_m};
std::unique_lock<std::mutex>{cv_m}.unlock();
// Notify thread that status has changed
cv.notify_one();

View file

@ -137,6 +137,12 @@ OpenSLESStream::~OpenSLESStream()
}
}
bool OpenSLESStream::SetRunning(bool running)
{
SLuint32 new_state = running ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED;
return (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, new_state) == SL_RESULT_SUCCESS;
}
void OpenSLESStream::SetVolume(int volume)
{
const SLmillibel attenuation =

View file

@ -14,7 +14,7 @@ class OpenSLESStream final : public SoundStream
public:
~OpenSLESStream() override;
bool Init() override;
bool SetRunning(bool running) override { return true; }
bool SetRunning(bool running) override;
void SetVolume(int volume) override;
static bool IsValid() { return true; }

View file

@ -2,12 +2,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "AudioCommon/WaveFile.h"
#include "AudioCommon/Mixer.h"
#include <string>
#include <fmt/format.h>
#include "AudioCommon/Mixer.h"
#include "Common/CommonTypes.h"
#include "Common/FileUtil.h"
#include "Common/IOFile.h"

View file

@ -30,3 +30,59 @@ endif()
if (WIN32 AND ENABLE_AUTOUPDATE)
add_subdirectory(WinUpdater)
endif()
if (APPLE AND ENABLE_QT)
set(DOLPHIN_MAC_BUNDLE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Dolphin.app")
add_custom_target(build_final_bundle ALL
COMMAND ${CMAKE_COMMAND} -E remove_directory
${DOLPHIN_MAC_BUNDLE}
COMMAND cp -R
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/DolphinQt.app
${DOLPHIN_MAC_BUNDLE}
# HACK: The Updater does not support setting the executable bit on new files,
# so don't use the new executable name, and instead continue to use "Dolphin".
COMMAND ${CMAKE_COMMAND} -E rename
${DOLPHIN_MAC_BUNDLE}/Contents/MacOS/DolphinQt
${DOLPHIN_MAC_BUNDLE}/Contents/MacOS/Dolphin
COMMAND plutil
-replace CFBundleExecutable -string Dolphin
${DOLPHIN_MAC_BUNDLE}/Contents/Info.plist
DEPENDS dolphin-emu)
if (ENABLE_AUTOUPDATE)
add_dependencies(build_final_bundle MacUpdater)
add_custom_command(TARGET build_final_bundle
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory
"${DOLPHIN_MAC_BUNDLE}/Contents/Helpers"
COMMAND cp -R
"$<TARGET_BUNDLE_DIR:MacUpdater>"
"${DOLPHIN_MAC_BUNDLE}/Contents/Helpers/Dolphin Updater.app")
if (MACOS_CODE_SIGNING)
add_custom_command(TARGET build_final_bundle
POST_BUILD
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
"-t"
"${MACOS_CODE_SIGNING_IDENTITY}"
"${DOLPHIN_MAC_BUNDLE}/Contents/Helpers/Dolphin Updater.app")
endif()
endif()
if (MACOS_CODE_SIGNING)
add_custom_command(TARGET build_final_bundle
POST_BUILD
COMMAND "${CMAKE_SOURCE_DIR}/Tools/mac-codesign.sh"
"-t"
"-e" "${CMAKE_SOURCE_DIR}/Source/Core/DolphinQt/DolphinEmu$<$<CONFIG:Debug>:Debug>.entitlements"
"${MACOS_CODE_SIGNING_IDENTITY}"
"${DOLPHIN_MAC_BUNDLE}")
endif()
endif()

View file

@ -87,26 +87,6 @@ void ARM64XEmitter::SetCodePtr(u8* ptr, u8* end, bool write_failed)
m_lastCacheFlushEnd = ptr;
}
const u8* ARM64XEmitter::GetCodePtr() const
{
return m_code;
}
u8* ARM64XEmitter::GetWritableCodePtr()
{
return m_code;
}
const u8* ARM64XEmitter::GetCodeEnd() const
{
return m_code_end;
}
u8* ARM64XEmitter::GetWritableCodeEnd()
{
return m_code_end;
}
void ARM64XEmitter::ReserveCodeSpace(u32 bytes)
{
for (u32 i = 0; i < bytes / 4; i++)

View file

@ -680,10 +680,10 @@ public:
void SetCodePtr(u8* ptr, u8* end, bool write_failed = false);
void SetCodePtrUnsafe(u8* ptr, u8* end, bool write_failed = false);
const u8* GetCodePtr() const;
u8* GetWritableCodePtr();
const u8* GetCodeEnd() const;
u8* GetWritableCodeEnd();
const u8* GetCodePtr() const { return m_code; }
u8* GetWritableCodePtr() { return m_code; }
const u8* GetCodeEnd() const { return m_code_end; }
u8* GetWritableCodeEnd() { return m_code_end; }
void ReserveCodeSpace(u32 bytes);
u8* AlignCode16();
u8* AlignCodePage();

View file

@ -15,11 +15,16 @@
#include <Windows.h>
#include <arm64intr.h>
#include "Common/WindowsRegistry.h"
#else
#ifndef __FreeBSD__
#elif defined(__linux__)
#include <asm/hwcap.h>
#endif
#include <sys/auxv.h>
#elif defined(__FreeBSD__)
#include <sys/auxv.h>
#elif defined(__OpenBSD__)
#include <machine/armreg.h>
#include <machine/cpu.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#endif
#include <fmt/format.h>
@ -183,7 +188,7 @@ static bool Read_MIDR_EL1(u64* value)
#endif
#ifndef __APPLE__
#if defined(_WIN32) || defined(__linux__) || defined(__FreeBSD__)
static std::string MIDRToString(u64 midr)
{
@ -248,7 +253,7 @@ void CPUInfo::Detect()
{
cpu_id = MIDRToString(reg);
}
#else
#elif defined(__linux__) || defined(__FreeBSD__)
// Linux, Android, and FreeBSD
#if defined(__FreeBSD__)
@ -277,6 +282,33 @@ void CPUInfo::Detect()
{
cpu_id = MIDRToString(midr);
}
#elif defined(__OpenBSD__)
// OpenBSD
int mib[2];
size_t len;
char hwmodel[256];
uint64_t isar0;
mib[0] = CTL_HW;
mib[1] = HW_MODEL;
len = std::size(hwmodel);
if (sysctl(mib, 2, &hwmodel, &len, nullptr, 0) != -1)
model_name = std::string(hwmodel, len - 1);
mib[0] = CTL_MACHDEP;
mib[1] = CPU_ID_AA64ISAR0;
len = sizeof(isar0);
if (sysctl(mib, 2, &isar0, &len, nullptr, 0) != -1)
{
if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_BASE)
bAES = true;
if (ID_AA64ISAR0_SHA1(isar0) >= ID_AA64ISAR0_SHA1_BASE)
bSHA1 = true;
if (ID_AA64ISAR0_SHA2(isar0) >= ID_AA64ISAR0_SHA2_BASE)
bSHA2 = true;
if (ID_AA64ISAR0_CRC32(isar0) >= ID_AA64ISAR0_CRC32_BASE)
bCRC32 = true;
}
#endif
model_name = ReplaceAll(model_name, ",", "_");

View file

@ -42,40 +42,40 @@ constexpr ExtendedMnemonicDesc INVALID_EXT_MNEMONIC = {0, nullptr};
// All operands as referenced by the Gekko/Broadway user manual
// See section 12.1.2 under Chapter 12
constexpr OperandDesc _A = OperandDesc{Mask(11, 15), {16, false}};
constexpr OperandDesc _B = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc _BD = OperandDesc{Mask(16, 29), {0, true}};
constexpr OperandDesc _BI = OperandDesc{Mask(11, 15), {16, false}};
constexpr OperandDesc _BO = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc _C = OperandDesc{Mask(21, 25), {6, false}};
constexpr OperandDesc _Crba = OperandDesc{Mask(11, 15), {16, false}};
constexpr OperandDesc _Crbb = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc _Crbd = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc _Crfd = OperandDesc{Mask(6, 8), {23, false}};
constexpr OperandDesc _Crfs = OperandDesc{Mask(11, 13), {18, false}};
constexpr OperandDesc _CRM = OperandDesc{Mask(12, 19), {12, false}};
constexpr OperandDesc _D = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc _FM = OperandDesc{Mask(7, 14), {17, false}};
constexpr OperandDesc _W1 = OperandDesc{Mask(16, 16), {15, false}};
constexpr OperandDesc _W2 = OperandDesc{Mask(21, 21), {10, false}};
constexpr OperandDesc _IMM = OperandDesc{Mask(16, 19), {12, false}};
constexpr OperandDesc _L = OperandDesc{Mask(10, 10), {21, false}};
constexpr OperandDesc _LI = OperandDesc{Mask(6, 29), {0, true}};
constexpr OperandDesc _MB = OperandDesc{Mask(21, 25), {6, false}};
constexpr OperandDesc _ME = OperandDesc{Mask(26, 30), {1, false}};
constexpr OperandDesc _NB = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc _Offd = OperandDesc{Mask(16, 31), {0, true}};
constexpr OperandDesc _OffdPs = OperandDesc{Mask(20, 31), {0, true}};
constexpr OperandDesc _S = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc _SH = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc _SIMM = OperandDesc{Mask(16, 31), {0, true}};
constexpr OperandDesc _SPR = OperandDesc{Mask(11, 20), {11, false}};
constexpr OperandDesc _SR = OperandDesc{Mask(12, 15), {16, false}};
constexpr OperandDesc _TO = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc _TPR = OperandDesc{Mask(11, 20), {11, false}};
constexpr OperandDesc _UIMM = OperandDesc{Mask(16, 31), {0, false}};
constexpr OperandDesc _I1 = OperandDesc{Mask(17, 19), {12, false}};
constexpr OperandDesc _I2 = OperandDesc{Mask(22, 24), {7, false}};
constexpr OperandDesc OpDesc_A = OperandDesc{Mask(11, 15), {16, false}};
constexpr OperandDesc OpDesc_B = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc OpDesc_BD = OperandDesc{Mask(16, 29), {0, true}};
constexpr OperandDesc OpDesc_BI = OperandDesc{Mask(11, 15), {16, false}};
constexpr OperandDesc OpDesc_BO = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc OpDesc_C = OperandDesc{Mask(21, 25), {6, false}};
constexpr OperandDesc OpDesc_Crba = OperandDesc{Mask(11, 15), {16, false}};
constexpr OperandDesc OpDesc_Crbb = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc OpDesc_Crbd = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc OpDesc_Crfd = OperandDesc{Mask(6, 8), {23, false}};
constexpr OperandDesc OpDesc_Crfs = OperandDesc{Mask(11, 13), {18, false}};
constexpr OperandDesc OpDesc_CRM = OperandDesc{Mask(12, 19), {12, false}};
constexpr OperandDesc OpDesc_D = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc OpDesc_FM = OperandDesc{Mask(7, 14), {17, false}};
constexpr OperandDesc OpDesc_W1 = OperandDesc{Mask(16, 16), {15, false}};
constexpr OperandDesc OpDesc_W2 = OperandDesc{Mask(21, 21), {10, false}};
constexpr OperandDesc OpDesc_IMM = OperandDesc{Mask(16, 19), {12, false}};
constexpr OperandDesc OpDesc_L = OperandDesc{Mask(10, 10), {21, false}};
constexpr OperandDesc OpDesc_LI = OperandDesc{Mask(6, 29), {0, true}};
constexpr OperandDesc OpDesc_MB = OperandDesc{Mask(21, 25), {6, false}};
constexpr OperandDesc OpDesc_ME = OperandDesc{Mask(26, 30), {1, false}};
constexpr OperandDesc OpDesc_NB = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc OpDesc_Offd = OperandDesc{Mask(16, 31), {0, true}};
constexpr OperandDesc OpDesc_OffdPs = OperandDesc{Mask(20, 31), {0, true}};
constexpr OperandDesc OpDesc_S = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc OpDesc_SH = OperandDesc{Mask(16, 20), {11, false}};
constexpr OperandDesc OpDesc_SIMM = OperandDesc{Mask(16, 31), {0, true}};
constexpr OperandDesc OpDesc_SPR = OperandDesc{Mask(11, 20), {11, false}};
constexpr OperandDesc OpDesc_SR = OperandDesc{Mask(12, 15), {16, false}};
constexpr OperandDesc OpDesc_TO = OperandDesc{Mask(6, 10), {21, false}};
constexpr OperandDesc OpDesc_TPR = OperandDesc{Mask(11, 20), {11, false}};
constexpr OperandDesc OpDesc_UIMM = OperandDesc{Mask(16, 31), {0, false}};
constexpr OperandDesc OpDesc_I1 = OperandDesc{Mask(17, 19), {12, false}};
constexpr OperandDesc OpDesc_I2 = OperandDesc{Mask(22, 24), {7, false}};
} // namespace
void OperandList::Insert(size_t before, u32 val)
@ -675,288 +675,293 @@ extern const CaseInsensitiveDict<ParseInfo, '.', '_', '+', '-'> extended_mnemoni
// Defines all basic mnemonics that Broadway/Gekko supports
extern const std::array<MnemonicDesc, NUM_MNEMONICS* VARIANT_PERMUTATIONS> mnemonics = {
// A-2
OERC_MNEMONIC(31, InsertVal(266, 22, 30), _D, _A, _B), // add
OERC_MNEMONIC(31, InsertVal(10, 22, 30), _D, _A, _B), // addc
OERC_MNEMONIC(31, InsertVal(138, 22, 30), _D, _A, _B), // adde
BASIC_MNEMONIC(14, _D, _A, _SIMM), // addi
BASIC_MNEMONIC(12, _D, _A, _SIMM), // addic
BASIC_MNEMONIC(13, _D, _A, _SIMM), // addic.
BASIC_MNEMONIC(15, _D, _A, _SIMM), // addis
OERC_MNEMONIC(31, InsertVal(234, 22, 30), _D, _A), // addme
OERC_MNEMONIC(31, InsertVal(202, 22, 30), _D, _A), // addze
OERC_MNEMONIC(31, InsertVal(491, 22, 30), _D, _A, _B), // divw
OERC_MNEMONIC(31, InsertVal(459, 22, 30), _D, _A, _B), // divwu
RC_MNEMONIC(31, InsertVal(75, 22, 30), _D, _A, _B), // mulhw
RC_MNEMONIC(31, InsertVal(11, 22, 30), _D, _A, _B), // mulhwu
BASIC_MNEMONIC(7, _D, _A, _SIMM), // mulli
OERC_MNEMONIC(31, InsertVal(235, 22, 30), _D, _A, _B), // mullw
OERC_MNEMONIC(31, InsertVal(104, 22, 30), _D, _A), // neg
OERC_MNEMONIC(31, InsertVal(40, 22, 30), _D, _A, _B), // subf
OERC_MNEMONIC(31, InsertVal(8, 22, 30), _D, _A, _B), // subfc
OERC_MNEMONIC(31, InsertVal(136, 22, 30), _D, _A, _B), // subfe
BASIC_MNEMONIC(8, _D, _A, _SIMM), // subfic
OERC_MNEMONIC(31, InsertVal(232, 22, 30), _D, _A), // subfme
OERC_MNEMONIC(31, InsertVal(200, 22, 30), _D, _A), // subfze
OERC_MNEMONIC(31, InsertVal(266, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // add
OERC_MNEMONIC(31, InsertVal(10, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // addc
OERC_MNEMONIC(31, InsertVal(138, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // adde
BASIC_MNEMONIC(14, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addi
BASIC_MNEMONIC(12, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addic
BASIC_MNEMONIC(13, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addic.
BASIC_MNEMONIC(15, OpDesc_D, OpDesc_A, OpDesc_SIMM), // addis
OERC_MNEMONIC(31, InsertVal(234, 22, 30), OpDesc_D, OpDesc_A), // addme
OERC_MNEMONIC(31, InsertVal(202, 22, 30), OpDesc_D, OpDesc_A), // addze
OERC_MNEMONIC(31, InsertVal(491, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // divw
OERC_MNEMONIC(31, InsertVal(459, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // divwu
RC_MNEMONIC(31, InsertVal(75, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // mulhw
RC_MNEMONIC(31, InsertVal(11, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // mulhwu
BASIC_MNEMONIC(7, OpDesc_D, OpDesc_A, OpDesc_SIMM), // mulli
OERC_MNEMONIC(31, InsertVal(235, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // mullw
OERC_MNEMONIC(31, InsertVal(104, 22, 30), OpDesc_D, OpDesc_A), // neg
OERC_MNEMONIC(31, InsertVal(40, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // subf
OERC_MNEMONIC(31, InsertVal(8, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // subfc
OERC_MNEMONIC(31, InsertVal(136, 22, 30), OpDesc_D, OpDesc_A, OpDesc_B), // subfe
BASIC_MNEMONIC(8, OpDesc_D, OpDesc_A, OpDesc_SIMM), // subfic
OERC_MNEMONIC(31, InsertVal(232, 22, 30), OpDesc_D, OpDesc_A), // subfme
OERC_MNEMONIC(31, InsertVal(200, 22, 30), OpDesc_D, OpDesc_A), // subfze
// A-3
MNEMONIC(31, InsertVal(0, 21, 30), _Crfd, _L, _A, _B), // cmp
BASIC_MNEMONIC(11, _Crfd, _L, _A, _SIMM), // cmpi
MNEMONIC(31, InsertVal(32, 21, 30), _Crfd, _L, _A, _B), // cmpl
BASIC_MNEMONIC(10, _Crfd, _L, _A, _UIMM), // cmpli
MNEMONIC(31, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_B), // cmp
BASIC_MNEMONIC(11, OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_SIMM), // cmpi
MNEMONIC(31, InsertVal(32, 21, 30), OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_B), // cmpl
BASIC_MNEMONIC(10, OpDesc_Crfd, OpDesc_L, OpDesc_A, OpDesc_UIMM), // cmpli
// A-4
RC_MNEMONIC(31, InsertVal(28, 21, 30), _A, _S, _B), // and
RC_MNEMONIC(31, InsertVal(60, 21, 30), _A, _S, _B), // andc
BASIC_MNEMONIC(28, _A, _S, _UIMM), // andi.
BASIC_MNEMONIC(29, _A, _S, _UIMM), // andis.
RC_MNEMONIC(31, InsertVal(26, 21, 30), _A, _S), // cntlzw
RC_MNEMONIC(31, InsertVal(284, 21, 30), _A, _S, _B), // eqv
RC_MNEMONIC(31, InsertVal(954, 21, 30), _A, _S), // extsb
RC_MNEMONIC(31, InsertVal(922, 21, 30), _A, _S), // extsh
RC_MNEMONIC(31, InsertVal(476, 21, 30), _A, _S, _B), // nand
RC_MNEMONIC(31, InsertVal(124, 21, 30), _A, _S, _B), // nor
RC_MNEMONIC(31, InsertVal(444, 21, 30), _A, _S, _B), // or
RC_MNEMONIC(31, InsertVal(412, 21, 30), _A, _S, _B), // orc
BASIC_MNEMONIC(24, _A, _S, _UIMM), // ori
BASIC_MNEMONIC(25, _A, _S, _UIMM), // oris
RC_MNEMONIC(31, InsertVal(316, 21, 30), _A, _S, _B), // xor
BASIC_MNEMONIC(26, _A, _S, _UIMM), // xori
BASIC_MNEMONIC(27, _A, _S, _UIMM), // xoris
RC_MNEMONIC(31, InsertVal(28, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // and
RC_MNEMONIC(31, InsertVal(60, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // andc
BASIC_MNEMONIC(28, OpDesc_A, OpDesc_S, OpDesc_UIMM), // andi.
BASIC_MNEMONIC(29, OpDesc_A, OpDesc_S, OpDesc_UIMM), // andis.
RC_MNEMONIC(31, InsertVal(26, 21, 30), OpDesc_A, OpDesc_S), // cntlzw
RC_MNEMONIC(31, InsertVal(284, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // eqv
RC_MNEMONIC(31, InsertVal(954, 21, 30), OpDesc_A, OpDesc_S), // extsb
RC_MNEMONIC(31, InsertVal(922, 21, 30), OpDesc_A, OpDesc_S), // extsh
RC_MNEMONIC(31, InsertVal(476, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // nand
RC_MNEMONIC(31, InsertVal(124, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // nor
RC_MNEMONIC(31, InsertVal(444, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // or
RC_MNEMONIC(31, InsertVal(412, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // orc
BASIC_MNEMONIC(24, OpDesc_A, OpDesc_S, OpDesc_UIMM), // ori
BASIC_MNEMONIC(25, OpDesc_A, OpDesc_S, OpDesc_UIMM), // oris
RC_MNEMONIC(31, InsertVal(316, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // xor
BASIC_MNEMONIC(26, OpDesc_A, OpDesc_S, OpDesc_UIMM), // xori
BASIC_MNEMONIC(27, OpDesc_A, OpDesc_S, OpDesc_UIMM), // xoris
// A-5
RC_MNEMONIC(20, 0, _A, _S, _SH, _MB, _ME), // rlwimi
RC_MNEMONIC(21, 0, _A, _S, _SH, _MB, _ME), // rlwinm
RC_MNEMONIC(23, 0, _A, _S, _B, _MB, _ME), // rlwnm
RC_MNEMONIC(20, 0, OpDesc_A, OpDesc_S, OpDesc_SH, OpDesc_MB, OpDesc_ME), // rlwimi
RC_MNEMONIC(21, 0, OpDesc_A, OpDesc_S, OpDesc_SH, OpDesc_MB, OpDesc_ME), // rlwinm
RC_MNEMONIC(23, 0, OpDesc_A, OpDesc_S, OpDesc_B, OpDesc_MB, OpDesc_ME), // rlwnm
// A-6
RC_MNEMONIC(31, InsertVal(24, 21, 30), _A, _S, _B), // slw
RC_MNEMONIC(31, InsertVal(792, 21, 30), _A, _S, _B), // sraw
RC_MNEMONIC(31, InsertVal(824, 21, 30), _A, _S, _SH), // srawi
RC_MNEMONIC(31, InsertVal(536, 21, 30), _A, _S, _B), // srw
RC_MNEMONIC(31, InsertVal(24, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // slw
RC_MNEMONIC(31, InsertVal(792, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // sraw
RC_MNEMONIC(31, InsertVal(824, 21, 30), OpDesc_A, OpDesc_S, OpDesc_SH), // srawi
RC_MNEMONIC(31, InsertVal(536, 21, 30), OpDesc_A, OpDesc_S, OpDesc_B), // srw
// A-7
RC_MNEMONIC(63, InsertVal(21, 26, 30), _D, _A, _B), // fadd
RC_MNEMONIC(59, InsertVal(21, 26, 30), _D, _A, _B), // fadds
RC_MNEMONIC(63, InsertVal(18, 26, 30), _D, _A, _B), // fdiv
RC_MNEMONIC(59, InsertVal(18, 26, 30), _D, _A, _B), // fdivs
RC_MNEMONIC(63, InsertVal(25, 26, 30), _D, _A, _C), // fmul
RC_MNEMONIC(59, InsertVal(25, 26, 30), _D, _A, _C), // fmuls
RC_MNEMONIC(59, InsertVal(24, 26, 30), _D, _B), // fres
RC_MNEMONIC(63, InsertVal(26, 26, 30), _D, _B), // frsqrte
RC_MNEMONIC(63, InsertVal(20, 26, 30), _D, _A, _B), // fsub
RC_MNEMONIC(59, InsertVal(20, 26, 30), _D, _A, _B), // fsubs
RC_MNEMONIC(63, InsertVal(23, 26, 30), _D, _A, _C, _B), // fsel
RC_MNEMONIC(63, InsertVal(21, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fadd
RC_MNEMONIC(59, InsertVal(21, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fadds
RC_MNEMONIC(63, InsertVal(18, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fdiv
RC_MNEMONIC(59, InsertVal(18, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fdivs
RC_MNEMONIC(63, InsertVal(25, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // fmul
RC_MNEMONIC(59, InsertVal(25, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // fmuls
RC_MNEMONIC(59, InsertVal(24, 26, 30), OpDesc_D, OpDesc_B), // fres
RC_MNEMONIC(63, InsertVal(26, 26, 30), OpDesc_D, OpDesc_B), // frsqrte
RC_MNEMONIC(63, InsertVal(20, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fsub
RC_MNEMONIC(59, InsertVal(20, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // fsubs
RC_MNEMONIC(63, InsertVal(23, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fsel
// A-8
RC_MNEMONIC(63, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadd
RC_MNEMONIC(59, InsertVal(29, 26, 30), _D, _A, _C, _B), // fmadds
RC_MNEMONIC(63, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsub
RC_MNEMONIC(59, InsertVal(28, 26, 30), _D, _A, _C, _B), // fmsubs
RC_MNEMONIC(63, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadd
RC_MNEMONIC(59, InsertVal(31, 26, 30), _D, _A, _C, _B), // fnmadds
RC_MNEMONIC(63, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsub
RC_MNEMONIC(59, InsertVal(30, 26, 30), _D, _A, _C, _B), // fnmsubs
RC_MNEMONIC(63, InsertVal(29, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmadd
RC_MNEMONIC(59, InsertVal(29, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmadds
RC_MNEMONIC(63, InsertVal(28, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmsub
RC_MNEMONIC(59, InsertVal(28, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fmsubs
RC_MNEMONIC(63, InsertVal(31, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmadd
RC_MNEMONIC(59, InsertVal(31, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmadds
RC_MNEMONIC(63, InsertVal(30, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmsub
RC_MNEMONIC(59, InsertVal(30, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // fnmsubs
// A-9
RC_MNEMONIC(63, InsertVal(14, 21, 30), _D, _B), // fctiw
RC_MNEMONIC(63, InsertVal(15, 21, 30), _D, _B), // fctiwz
RC_MNEMONIC(63, InsertVal(12, 21, 30), _D, _B), // frsp
RC_MNEMONIC(63, InsertVal(14, 21, 30), OpDesc_D, OpDesc_B), // fctiw
RC_MNEMONIC(63, InsertVal(15, 21, 30), OpDesc_D, OpDesc_B), // fctiwz
RC_MNEMONIC(63, InsertVal(12, 21, 30), OpDesc_D, OpDesc_B), // frsp
// A-10
MNEMONIC(63, InsertVal(32, 21, 30), _Crfd, _A, _B), // fcmpo
MNEMONIC(63, InsertVal(0, 21, 30), _Crfd, _A, _B), // fcmpu
MNEMONIC(63, InsertVal(32, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // fcmpo
MNEMONIC(63, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // fcmpu
// A-11
MNEMONIC(63, InsertVal(64, 21, 30), _Crfd, _Crfs), // mcrfs
RC_MNEMONIC(63, InsertVal(583, 21, 30), _D), // mffs
RC_MNEMONIC(63, InsertVal(70, 21, 30), _Crbd), // mtfsb0
RC_MNEMONIC(63, InsertVal(38, 21, 30), _Crbd), // mtfsb1
RC_MNEMONIC(63, InsertVal(711, 21, 30), _FM, _B), // mtfsf
RC_MNEMONIC(63, InsertVal(134, 21, 30), _Crfd, _IMM), // mtfsfi
MNEMONIC(63, InsertVal(64, 21, 30), OpDesc_Crfd, OpDesc_Crfs), // mcrfs
RC_MNEMONIC(63, InsertVal(583, 21, 30), OpDesc_D), // mffs
RC_MNEMONIC(63, InsertVal(70, 21, 30), OpDesc_Crbd), // mtfsb0
RC_MNEMONIC(63, InsertVal(38, 21, 30), OpDesc_Crbd), // mtfsb1
RC_MNEMONIC(63, InsertVal(711, 21, 30), OpDesc_FM, OpDesc_B), // mtfsf
RC_MNEMONIC(63, InsertVal(134, 21, 30), OpDesc_Crfd, OpDesc_IMM), // mtfsfi
// A-12
BASIC_MNEMONIC(34, _D, _Offd, _A), // lbz
BASIC_MNEMONIC(35, _D, _Offd, _A), // lbzu
MNEMONIC(31, InsertVal(119, 21, 30), _D, _A, _B), // lbzux
MNEMONIC(31, InsertVal(87, 21, 30), _D, _A, _B), // lbzx
BASIC_MNEMONIC(42, _D, _Offd, _A), // lha
BASIC_MNEMONIC(43, _D, _Offd, _A), // lhau
MNEMONIC(31, InsertVal(375, 21, 30), _D, _A, _B), // lhaux
MNEMONIC(31, InsertVal(343, 21, 30), _D, _A, _B), // lhax
BASIC_MNEMONIC(40, _D, _Offd, _A), // lhz
BASIC_MNEMONIC(41, _D, _Offd, _A), // lhzu
MNEMONIC(31, InsertVal(311, 21, 30), _D, _A, _B), // lhzux
MNEMONIC(31, InsertVal(279, 21, 30), _D, _A, _B), // lhzx
BASIC_MNEMONIC(32, _D, _Offd, _A), // lwz
BASIC_MNEMONIC(33, _D, _Offd, _A), // lwzu
MNEMONIC(31, InsertVal(55, 21, 30), _D, _A, _B), // lwzux
MNEMONIC(31, InsertVal(23, 21, 30), _D, _A, _B), // lwzx
BASIC_MNEMONIC(34, OpDesc_D, OpDesc_Offd, OpDesc_A), // lbz
BASIC_MNEMONIC(35, OpDesc_D, OpDesc_Offd, OpDesc_A), // lbzu
MNEMONIC(31, InsertVal(119, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lbzux
MNEMONIC(31, InsertVal(87, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lbzx
BASIC_MNEMONIC(42, OpDesc_D, OpDesc_Offd, OpDesc_A), // lha
BASIC_MNEMONIC(43, OpDesc_D, OpDesc_Offd, OpDesc_A), // lhau
MNEMONIC(31, InsertVal(375, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhaux
MNEMONIC(31, InsertVal(343, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhax
BASIC_MNEMONIC(40, OpDesc_D, OpDesc_Offd, OpDesc_A), // lhz
BASIC_MNEMONIC(41, OpDesc_D, OpDesc_Offd, OpDesc_A), // lhzu
MNEMONIC(31, InsertVal(311, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhzux
MNEMONIC(31, InsertVal(279, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhzx
BASIC_MNEMONIC(32, OpDesc_D, OpDesc_Offd, OpDesc_A), // lwz
BASIC_MNEMONIC(33, OpDesc_D, OpDesc_Offd, OpDesc_A), // lwzu
MNEMONIC(31, InsertVal(55, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwzux
MNEMONIC(31, InsertVal(23, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwzx
// A-13
BASIC_MNEMONIC(38, _S, _Offd, _A), // stb
BASIC_MNEMONIC(39, _S, _Offd, _A), // stbu
MNEMONIC(31, InsertVal(247, 21, 30), _S, _A, _B), // stbux
MNEMONIC(31, InsertVal(215, 21, 30), _S, _A, _B), // stbx
BASIC_MNEMONIC(44, _S, _Offd, _A), // sth
BASIC_MNEMONIC(45, _S, _Offd, _A), // sthu
MNEMONIC(31, InsertVal(439, 21, 30), _S, _A, _B), // sthux
MNEMONIC(31, InsertVal(407, 21, 30), _S, _A, _B), // sthx
BASIC_MNEMONIC(36, _S, _Offd, _A), // stw
BASIC_MNEMONIC(37, _S, _Offd, _A), // stwu
MNEMONIC(31, InsertVal(183, 21, 30), _S, _A, _B), // stwux
MNEMONIC(31, InsertVal(151, 21, 30), _S, _A, _B), // stwx
BASIC_MNEMONIC(38, OpDesc_S, OpDesc_Offd, OpDesc_A), // stb
BASIC_MNEMONIC(39, OpDesc_S, OpDesc_Offd, OpDesc_A), // stbu
MNEMONIC(31, InsertVal(247, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stbux
MNEMONIC(31, InsertVal(215, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stbx
BASIC_MNEMONIC(44, OpDesc_S, OpDesc_Offd, OpDesc_A), // sth
BASIC_MNEMONIC(45, OpDesc_S, OpDesc_Offd, OpDesc_A), // sthu
MNEMONIC(31, InsertVal(439, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // sthux
MNEMONIC(31, InsertVal(407, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // sthx
BASIC_MNEMONIC(36, OpDesc_S, OpDesc_Offd, OpDesc_A), // stw
BASIC_MNEMONIC(37, OpDesc_S, OpDesc_Offd, OpDesc_A), // stwu
MNEMONIC(31, InsertVal(183, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stwux
MNEMONIC(31, InsertVal(151, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stwx
// A-14
MNEMONIC(31, InsertVal(790, 21, 30), _D, _A, _B), // lhbrx
MNEMONIC(31, InsertVal(534, 21, 30), _D, _A, _B), // lwbrx
MNEMONIC(31, InsertVal(918, 21, 30), _S, _A, _B), // sthbrx
MNEMONIC(31, InsertVal(662, 21, 30), _S, _A, _B), // stwbrx
MNEMONIC(31, InsertVal(790, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lhbrx
MNEMONIC(31, InsertVal(534, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwbrx
MNEMONIC(31, InsertVal(918, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // sthbrx
MNEMONIC(31, InsertVal(662, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stwbrx
// A-15
BASIC_MNEMONIC(46, _D, _Offd, _A), // lmw
BASIC_MNEMONIC(47, _S, _Offd, _A), // stmw
BASIC_MNEMONIC(46, OpDesc_D, OpDesc_Offd, OpDesc_A), // lmw
BASIC_MNEMONIC(47, OpDesc_S, OpDesc_Offd, OpDesc_A), // stmw
// A-16
MNEMONIC(31, InsertVal(597, 21, 30), _D, _A, _NB), // lswi
MNEMONIC(31, InsertVal(533, 21, 30), _D, _A, _B), // lswx
MNEMONIC(31, InsertVal(725, 21, 30), _S, _A, _NB), // stswi
MNEMONIC(31, InsertVal(661, 21, 30), _S, _A, _B), // stswx
MNEMONIC(31, InsertVal(597, 21, 30), OpDesc_D, OpDesc_A, OpDesc_NB), // lswi
MNEMONIC(31, InsertVal(533, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lswx
MNEMONIC(31, InsertVal(725, 21, 30), OpDesc_S, OpDesc_A, OpDesc_NB), // stswi
MNEMONIC(31, InsertVal(661, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stswx
// A-17
MNEMONIC(31, InsertVal(854, 21, 30)), // eieio
MNEMONIC(19, InsertVal(150, 21, 30)), // isync
MNEMONIC(31, InsertVal(20, 21, 30), _D, _A, _B), // lwarx
MNEMONIC(31, InsertVal(150, 21, 30) | InsertVal(1, 31, 31), _S, _A, _B), // stwcx.
MNEMONIC(31, InsertVal(598, 21, 30)), // sync
MNEMONIC(31, InsertVal(854, 21, 30)), // eieio
MNEMONIC(19, InsertVal(150, 21, 30)), // isync
MNEMONIC(31, InsertVal(20, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lwarx
MNEMONIC(31, InsertVal(150, 21, 30) | InsertVal(1, 31, 31), OpDesc_S, OpDesc_A,
OpDesc_B), // stwcx.
MNEMONIC(31, InsertVal(598, 21, 30)), // sync
// A-18
BASIC_MNEMONIC(50, _D, _Offd, _A), // lfd
BASIC_MNEMONIC(51, _D, _Offd, _A), // lfdu
MNEMONIC(31, InsertVal(631, 21, 30), _D, _A, _B), // lfdux
MNEMONIC(31, InsertVal(599, 21, 30), _D, _A, _B), // lfdx
BASIC_MNEMONIC(48, _D, _Offd, _A), // lfs
BASIC_MNEMONIC(49, _D, _Offd, _A), // lfsu
MNEMONIC(31, InsertVal(567, 21, 30), _D, _A, _B), // lfsux
MNEMONIC(31, InsertVal(535, 21, 30), _D, _A, _B), // lfsx
BASIC_MNEMONIC(50, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfd
BASIC_MNEMONIC(51, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfdu
MNEMONIC(31, InsertVal(631, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfdux
MNEMONIC(31, InsertVal(599, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfdx
BASIC_MNEMONIC(48, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfs
BASIC_MNEMONIC(49, OpDesc_D, OpDesc_Offd, OpDesc_A), // lfsu
MNEMONIC(31, InsertVal(567, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfsux
MNEMONIC(31, InsertVal(535, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // lfsx
// A-19
BASIC_MNEMONIC(54, _S, _Offd, _A), // stfd
BASIC_MNEMONIC(55, _S, _Offd, _A), // stfdu
MNEMONIC(31, InsertVal(759, 21, 30), _S, _A, _B), // stfdux
MNEMONIC(31, InsertVal(727, 21, 30), _S, _A, _B), // stfdx
MNEMONIC(31, InsertVal(983, 21, 30), _S, _A, _B), // stfiwx
BASIC_MNEMONIC(52, _S, _Offd, _A), // stfs
BASIC_MNEMONIC(53, _S, _Offd, _A), // stfsu
MNEMONIC(31, InsertVal(695, 21, 30), _S, _A, _B), // stfsux
MNEMONIC(31, InsertVal(663, 21, 30), _S, _A, _B), // stfsx
BASIC_MNEMONIC(54, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfd
BASIC_MNEMONIC(55, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfdu
MNEMONIC(31, InsertVal(759, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfdux
MNEMONIC(31, InsertVal(727, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfdx
MNEMONIC(31, InsertVal(983, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfiwx
BASIC_MNEMONIC(52, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfs
BASIC_MNEMONIC(53, OpDesc_S, OpDesc_Offd, OpDesc_A), // stfsu
MNEMONIC(31, InsertVal(695, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfsux
MNEMONIC(31, InsertVal(663, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // stfsx
// A-20
RC_MNEMONIC(63, InsertVal(264, 21, 30), _D, _B), // fabs
RC_MNEMONIC(63, InsertVal(72, 21, 30), _D, _B), // fmr
RC_MNEMONIC(63, InsertVal(136, 21, 30), _D, _B), // fnabs
RC_MNEMONIC(63, InsertVal(40, 21, 30), _D, _B), // fneg
RC_MNEMONIC(63, InsertVal(264, 21, 30), OpDesc_D, OpDesc_B), // fabs
RC_MNEMONIC(63, InsertVal(72, 21, 30), OpDesc_D, OpDesc_B), // fmr
RC_MNEMONIC(63, InsertVal(136, 21, 30), OpDesc_D, OpDesc_B), // fnabs
RC_MNEMONIC(63, InsertVal(40, 21, 30), OpDesc_D, OpDesc_B), // fneg
// A-21
AALK_MNEMONIC(18, 0, _LI), // b
AALK_MNEMONIC(16, 0, _BO, _BI, _BD), // bc
LK_MNEMONIC(19, InsertVal(528, 21, 30), _BO, _BI), // bcctr
LK_MNEMONIC(19, InsertVal(16, 21, 30), _BO, _BI), // bclr
AALK_MNEMONIC(18, 0, OpDesc_LI), // b
AALK_MNEMONIC(16, 0, OpDesc_BO, OpDesc_BI, OpDesc_BD), // bc
LK_MNEMONIC(19, InsertVal(528, 21, 30), OpDesc_BO, OpDesc_BI), // bcctr
LK_MNEMONIC(19, InsertVal(16, 21, 30), OpDesc_BO, OpDesc_BI), // bclr
// A-22
MNEMONIC(19, InsertVal(257, 21, 30), _Crbd, _Crba, _Crbb), // crand
MNEMONIC(19, InsertVal(129, 21, 30), _Crbd, _Crba, _Crbb), // crandc
MNEMONIC(19, InsertVal(289, 21, 30), _Crbd, _Crba, _Crbb), // creqv
MNEMONIC(19, InsertVal(225, 21, 30), _Crbd, _Crba, _Crbb), // crnand
MNEMONIC(19, InsertVal(33, 21, 30), _Crbd, _Crba, _Crbb), // crnor
MNEMONIC(19, InsertVal(449, 21, 30), _Crbd, _Crba, _Crbb), // cror
MNEMONIC(19, InsertVal(417, 21, 30), _Crbd, _Crba, _Crbb), // crorc
MNEMONIC(19, InsertVal(193, 21, 30), _Crbd, _Crba, _Crbb), // crxor
MNEMONIC(19, InsertVal(0, 21, 30), _Crfd, _Crfs), // mcrf
MNEMONIC(19, InsertVal(257, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crand
MNEMONIC(19, InsertVal(129, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crandc
MNEMONIC(19, InsertVal(289, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // creqv
MNEMONIC(19, InsertVal(225, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crnand
MNEMONIC(19, InsertVal(33, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crnor
MNEMONIC(19, InsertVal(449, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // cror
MNEMONIC(19, InsertVal(417, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crorc
MNEMONIC(19, InsertVal(193, 21, 30), OpDesc_Crbd, OpDesc_Crba, OpDesc_Crbb), // crxor
MNEMONIC(19, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_Crfs), // mcrf
// A-23
MNEMONIC(19, InsertVal(50, 21, 30)), // rfi
MNEMONIC(17, InsertVal(1, 30, 30)), // sc
// A-24
MNEMONIC(31, InsertVal(4, 21, 30), _TO, _A, _B), // tw
BASIC_MNEMONIC(3, _TO, _A, _SIMM), // twi
MNEMONIC(31, InsertVal(4, 21, 30), OpDesc_TO, OpDesc_A, OpDesc_B), // tw
BASIC_MNEMONIC(3, OpDesc_TO, OpDesc_A, OpDesc_SIMM), // twi
// A-25
MNEMONIC(31, InsertVal(512, 21, 30), _Crfd), // mcrxr
MNEMONIC(31, InsertVal(19, 21, 30), _D), // mfcr
MNEMONIC(31, InsertVal(83, 21, 30), _D), // mfmsr
MNEMONIC(31, InsertVal(339, 21, 30), _D, _SPR), // mfspr
MNEMONIC(31, InsertVal(371, 21, 30), _D, _TPR), // mftb
MNEMONIC(31, InsertVal(144, 21, 30), _CRM, _S), // mtcrf
MNEMONIC(31, InsertVal(146, 21, 30), _S), // mtmsr
MNEMONIC(31, InsertVal(467, 21, 30), _SPR, _D), // mtspr
MNEMONIC(31, InsertVal(512, 21, 30), OpDesc_Crfd), // mcrxr
MNEMONIC(31, InsertVal(19, 21, 30), OpDesc_D), // mfcr
MNEMONIC(31, InsertVal(83, 21, 30), OpDesc_D), // mfmsr
MNEMONIC(31, InsertVal(339, 21, 30), OpDesc_D, OpDesc_SPR), // mfspr
MNEMONIC(31, InsertVal(371, 21, 30), OpDesc_D, OpDesc_TPR), // mftb
MNEMONIC(31, InsertVal(144, 21, 30), OpDesc_CRM, OpDesc_S), // mtcrf
MNEMONIC(31, InsertVal(146, 21, 30), OpDesc_S), // mtmsr
MNEMONIC(31, InsertVal(467, 21, 30), OpDesc_SPR, OpDesc_D), // mtspr
// A-26
MNEMONIC(31, InsertVal(86, 21, 30), _A, _B), // dcbf
MNEMONIC(31, InsertVal(470, 21, 30), _A, _B), // dcbi
MNEMONIC(31, InsertVal(54, 21, 30), _A, _B), // dcbst
MNEMONIC(31, InsertVal(278, 21, 30), _A, _B), // dcbt
MNEMONIC(31, InsertVal(246, 21, 30), _A, _B), // dcbtst
MNEMONIC(31, InsertVal(1014, 21, 30), _A, _B), // dcbz
MNEMONIC(31, InsertVal(982, 21, 30), _A, _B), // icbi
MNEMONIC(31, InsertVal(86, 21, 30), OpDesc_A, OpDesc_B), // dcbf
MNEMONIC(31, InsertVal(470, 21, 30), OpDesc_A, OpDesc_B), // dcbi
MNEMONIC(31, InsertVal(54, 21, 30), OpDesc_A, OpDesc_B), // dcbst
MNEMONIC(31, InsertVal(278, 21, 30), OpDesc_A, OpDesc_B), // dcbt
MNEMONIC(31, InsertVal(246, 21, 30), OpDesc_A, OpDesc_B), // dcbtst
MNEMONIC(31, InsertVal(1014, 21, 30), OpDesc_A, OpDesc_B), // dcbz
MNEMONIC(31, InsertVal(982, 21, 30), OpDesc_A, OpDesc_B), // icbi
// A-27
MNEMONIC(31, InsertVal(595, 21, 30), _D, _SR), // mfsr
MNEMONIC(31, InsertVal(659, 21, 30), _D, _B), // mfsrin
MNEMONIC(31, InsertVal(210, 21, 30), _SR, _S), // mtsr
MNEMONIC(31, InsertVal(242, 21, 30), _S, _B), // mtsrin
MNEMONIC(31, InsertVal(595, 21, 30), OpDesc_D, OpDesc_SR), // mfsr
MNEMONIC(31, InsertVal(659, 21, 30), OpDesc_D, OpDesc_B), // mfsrin
MNEMONIC(31, InsertVal(210, 21, 30), OpDesc_SR, OpDesc_S), // mtsr
MNEMONIC(31, InsertVal(242, 21, 30), OpDesc_S, OpDesc_B), // mtsrin
// A-28
MNEMONIC(31, InsertVal(306, 21, 30), _B), // tlbie
MNEMONIC(31, InsertVal(566, 21, 30)), // tlbsync
MNEMONIC(31, InsertVal(306, 21, 30), OpDesc_B), // tlbie
MNEMONIC(31, InsertVal(566, 21, 30)), // tlbsync
// A-29
MNEMONIC(31, InsertVal(310, 21, 30), _D, _A, _B), // eciwx
MNEMONIC(31, InsertVal(438, 21, 30), _S, _A, _B), // ecowx
MNEMONIC(31, InsertVal(310, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // eciwx
MNEMONIC(31, InsertVal(438, 21, 30), OpDesc_S, OpDesc_A, OpDesc_B), // ecowx
// A-30
MNEMONIC(4, InsertVal(6, 25, 30), _D, _A, _B, _W2, _I2), // psq_lx
MNEMONIC(4, InsertVal(7, 25, 30), _S, _A, _B, _W2, _I2), // psq_stx
MNEMONIC(4, InsertVal(38, 25, 30), _D, _A, _B, _W2, _I2), // psq_lux
MNEMONIC(4, InsertVal(39, 25, 30), _S, _A, _B, _W2, _I2), // psq_stux
BASIC_MNEMONIC(56, _D, _OffdPs, _A, _W1, _I1), // psq_l
BASIC_MNEMONIC(57, _D, _OffdPs, _A, _W1, _I1), // psq_lu
BASIC_MNEMONIC(60, _S, _OffdPs, _A, _W1, _I1), // psq_st
BASIC_MNEMONIC(61, _S, _OffdPs, _A, _W1, _I1), // psq_stu
MNEMONIC(4, InsertVal(6, 25, 30), OpDesc_D, OpDesc_A, OpDesc_B, OpDesc_W2,
OpDesc_I2), // psq_lx
MNEMONIC(4, InsertVal(7, 25, 30), OpDesc_S, OpDesc_A, OpDesc_B, OpDesc_W2,
OpDesc_I2), // psq_stx
MNEMONIC(4, InsertVal(38, 25, 30), OpDesc_D, OpDesc_A, OpDesc_B, OpDesc_W2,
OpDesc_I2), // psq_lux
MNEMONIC(4, InsertVal(39, 25, 30), OpDesc_S, OpDesc_A, OpDesc_B, OpDesc_W2,
OpDesc_I2), // psq_stux
BASIC_MNEMONIC(56, OpDesc_D, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_l
BASIC_MNEMONIC(57, OpDesc_D, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_lu
BASIC_MNEMONIC(60, OpDesc_S, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_st
BASIC_MNEMONIC(61, OpDesc_S, OpDesc_OffdPs, OpDesc_A, OpDesc_W1, OpDesc_I1), // psq_stu
// A-31
RC_MNEMONIC(4, InsertVal(18, 26, 30), _D, _A, _B), // ps_div
RC_MNEMONIC(4, InsertVal(20, 26, 30), _D, _A, _B), // ps_sub
RC_MNEMONIC(4, InsertVal(21, 26, 30), _D, _A, _B), // ps_add
RC_MNEMONIC(4, InsertVal(23, 26, 30), _D, _A, _C, _B), // ps_sel
RC_MNEMONIC(4, InsertVal(24, 26, 30), _D, _B), // ps_res
RC_MNEMONIC(4, InsertVal(25, 26, 30), _D, _A, _C), // ps_mul
RC_MNEMONIC(4, InsertVal(26, 26, 30), _D, _B), // ps_rsqrte
RC_MNEMONIC(4, InsertVal(28, 26, 30), _D, _A, _C, _B), // ps_msub
RC_MNEMONIC(4, InsertVal(29, 26, 30), _D, _A, _C, _B), // ps_madd
RC_MNEMONIC(4, InsertVal(30, 26, 30), _D, _A, _C, _B), // ps_nmsub
RC_MNEMONIC(4, InsertVal(31, 26, 30), _D, _A, _C, _B), // ps_nmadd
RC_MNEMONIC(4, InsertVal(40, 21, 30), _D, _B), // ps_neg
RC_MNEMONIC(4, InsertVal(72, 21, 30), _D, _B), // ps_mr
RC_MNEMONIC(4, InsertVal(136, 21, 30), _D, _B), // ps_nabs
RC_MNEMONIC(4, InsertVal(264, 21, 30), _D, _B), // ps_abs
RC_MNEMONIC(4, InsertVal(18, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_div
RC_MNEMONIC(4, InsertVal(20, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_sub
RC_MNEMONIC(4, InsertVal(21, 26, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_add
RC_MNEMONIC(4, InsertVal(23, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_sel
RC_MNEMONIC(4, InsertVal(24, 26, 30), OpDesc_D, OpDesc_B), // ps_res
RC_MNEMONIC(4, InsertVal(25, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // ps_mul
RC_MNEMONIC(4, InsertVal(26, 26, 30), OpDesc_D, OpDesc_B), // ps_rsqrte
RC_MNEMONIC(4, InsertVal(28, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_msub
RC_MNEMONIC(4, InsertVal(29, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_madd
RC_MNEMONIC(4, InsertVal(30, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_nmsub
RC_MNEMONIC(4, InsertVal(31, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_nmadd
RC_MNEMONIC(4, InsertVal(40, 21, 30), OpDesc_D, OpDesc_B), // ps_neg
RC_MNEMONIC(4, InsertVal(72, 21, 30), OpDesc_D, OpDesc_B), // ps_mr
RC_MNEMONIC(4, InsertVal(136, 21, 30), OpDesc_D, OpDesc_B), // ps_nabs
RC_MNEMONIC(4, InsertVal(264, 21, 30), OpDesc_D, OpDesc_B), // ps_abs
// A-32
RC_MNEMONIC(4, InsertVal(10, 26, 30), _D, _A, _C, _B), // ps_sum0
RC_MNEMONIC(4, InsertVal(11, 26, 30), _D, _A, _C, _B), // ps_sum1
RC_MNEMONIC(4, InsertVal(12, 26, 30), _D, _A, _C), // ps_muls0
RC_MNEMONIC(4, InsertVal(13, 26, 30), _D, _A, _C), // ps_muls1
RC_MNEMONIC(4, InsertVal(14, 26, 30), _D, _A, _C, _B), // ps_madds0
RC_MNEMONIC(4, InsertVal(15, 26, 30), _D, _A, _C, _B), // ps_madds1
MNEMONIC(4, InsertVal(0, 21, 30), _Crfd, _A, _B), // ps_cmpu0
MNEMONIC(4, InsertVal(32, 21, 30), _Crfd, _A, _B), // ps_cmpo0
MNEMONIC(4, InsertVal(64, 21, 30), _Crfd, _A, _B), // ps_cmpu1
MNEMONIC(4, InsertVal(96, 21, 30), _Crfd, _A, _B), // ps_cmpo1
RC_MNEMONIC(4, InsertVal(528, 21, 30), _D, _A, _B), // ps_merge00
RC_MNEMONIC(4, InsertVal(560, 21, 30), _D, _A, _B), // ps_merge01
RC_MNEMONIC(4, InsertVal(592, 21, 30), _D, _A, _B), // ps_merge10
RC_MNEMONIC(4, InsertVal(624, 21, 30), _D, _A, _B), // ps_merge11
MNEMONIC(4, InsertVal(1014, 21, 30), _A, _B), // dcbz_l
RC_MNEMONIC(4, InsertVal(10, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_sum0
RC_MNEMONIC(4, InsertVal(11, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_sum1
RC_MNEMONIC(4, InsertVal(12, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // ps_muls0
RC_MNEMONIC(4, InsertVal(13, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C), // ps_muls1
RC_MNEMONIC(4, InsertVal(14, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_madds0
RC_MNEMONIC(4, InsertVal(15, 26, 30), OpDesc_D, OpDesc_A, OpDesc_C, OpDesc_B), // ps_madds1
MNEMONIC(4, InsertVal(0, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpu0
MNEMONIC(4, InsertVal(32, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpo0
MNEMONIC(4, InsertVal(64, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpu1
MNEMONIC(4, InsertVal(96, 21, 30), OpDesc_Crfd, OpDesc_A, OpDesc_B), // ps_cmpo1
RC_MNEMONIC(4, InsertVal(528, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge00
RC_MNEMONIC(4, InsertVal(560, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge01
RC_MNEMONIC(4, InsertVal(592, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge10
RC_MNEMONIC(4, InsertVal(624, 21, 30), OpDesc_D, OpDesc_A, OpDesc_B), // ps_merge11
MNEMONIC(4, InsertVal(1014, 21, 30), OpDesc_A, OpDesc_B), // dcbz_l
};
namespace

View file

@ -4,6 +4,7 @@
#include "Common/Assembler/GekkoLexer.h"
#include "Common/Assert.h"
#include "Common/StringUtil.h"
#include <iterator>
#include <numeric>
@ -181,6 +182,11 @@ std::optional<T> EvalIntegral(TokenType tp, std::string_view val)
case TokenType::BinaryLit:
return std::accumulate(val.begin() + 2, val.end(), T{0}, bin_step);
case TokenType::GPR:
if (CaseInsensitiveEquals(val, "sp"))
return T{1};
if (CaseInsensitiveEquals(val, "rtoc"))
return T{2};
[[fallthrough]];
case TokenType::FPR:
return std::accumulate(val.begin() + 1, val.end(), T{0}, dec_step);
case TokenType::CRField:
@ -643,50 +649,43 @@ TokenType Lexer::ClassifyAlnum() const
if (rn[0] == '3')
{
return rn[1] <= '2';
return rn[1] < '2';
}
}
return false;
};
constexpr auto eq_nocase = [](std::string_view str, std::string_view lwr) {
auto it_l = str.cbegin(), it_r = lwr.cbegin();
for (; it_l != str.cend() && it_r != lwr.cend(); it_l++, it_r++)
{
if (std::tolower(*it_l) != *it_r)
{
return false;
}
}
return it_l == str.end() && it_r == lwr.end();
};
if (std::tolower(alnum[0]) == 'r' && valid_regnum(alnum.substr(1)))
{
return TokenType::GPR;
}
else if ((CaseInsensitiveEquals(alnum, "sp")) || (CaseInsensitiveEquals(alnum, "rtoc")))
{
return TokenType::GPR;
}
else if (std::tolower(alnum[0]) == 'f' && valid_regnum(alnum.substr(1)))
{
return TokenType::FPR;
}
else if (alnum.length() == 3 && eq_nocase(alnum.substr(0, 2), "cr") && alnum[2] >= '0' &&
alnum[2] <= '7')
else if (alnum.length() == 3 && CaseInsensitiveEquals(alnum.substr(0, 2), "cr") &&
alnum[2] >= '0' && alnum[2] <= '7')
{
return TokenType::CRField;
}
else if (eq_nocase(alnum, "lt"))
else if (CaseInsensitiveEquals(alnum, "lt"))
{
return TokenType::Lt;
}
else if (eq_nocase(alnum, "gt"))
else if (CaseInsensitiveEquals(alnum, "gt"))
{
return TokenType::Gt;
}
else if (eq_nocase(alnum, "eq"))
else if (CaseInsensitiveEquals(alnum, "eq"))
{
return TokenType::Eq;
}
else if (eq_nocase(alnum, "so"))
else if (CaseInsensitiveEquals(alnum, "so"))
{
return TokenType::So;
}

View file

@ -312,7 +312,7 @@ class BitFieldArrayConstRef
friend class BitFieldArrayConstIterator<position, bits, size, T, S>;
public:
constexpr T Value() const { return m_array->Value(m_index); };
constexpr T Value() const { return m_array->Value(m_index); }
constexpr operator T() const { return Value(); }
private:
@ -333,7 +333,7 @@ class BitFieldArrayRef
friend class BitFieldArrayIterator<position, bits, size, T, S>;
public:
constexpr T Value() const { return m_array->Value(m_index); };
constexpr T Value() const { return m_array->Value(m_index); }
constexpr operator T() const { return Value(); }
T operator=(const BitFieldArrayRef<position, bits, size, T, S>& value) const
{

View file

@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <bit>
#include <climits>
#include <cstddef>
#include <cstdint>
@ -166,50 +167,10 @@ inline auto BitCastPtr(PtrType* ptr) noexcept -> BitCastPtrType<T, PtrType>
}
// Similar to BitCastPtr, but specifically for aliasing structs to arrays.
template <typename ArrayType, typename T,
typename Container = std::array<ArrayType, sizeof(T) / sizeof(ArrayType)>>
inline auto BitCastToArray(const T& obj) noexcept -> Container
template <typename ValueType, typename From>
[[nodiscard]] constexpr auto BitCastToArray(const From& obj) noexcept
{
static_assert(sizeof(T) % sizeof(ArrayType) == 0,
"Size of array type must be a factor of size of source type.");
static_assert(std::is_trivially_copyable<T>(),
"BitCastToArray source type must be trivially copyable.");
static_assert(std::is_trivially_copyable<Container>(),
"BitCastToArray array type must be trivially copyable.");
Container result;
std::memcpy(result.data(), &obj, sizeof(T));
return result;
}
template <typename ArrayType, typename T,
typename Container = std::array<ArrayType, sizeof(T) / sizeof(ArrayType)>>
inline void BitCastFromArray(const Container& array, T& obj) noexcept
{
static_assert(sizeof(T) % sizeof(ArrayType) == 0,
"Size of array type must be a factor of size of destination type.");
static_assert(std::is_trivially_copyable<Container>(),
"BitCastFromArray array type must be trivially copyable.");
static_assert(std::is_trivially_copyable<T>(),
"BitCastFromArray destination type must be trivially copyable.");
std::memcpy(&obj, array.data(), sizeof(T));
}
template <typename ArrayType, typename T,
typename Container = std::array<ArrayType, sizeof(T) / sizeof(ArrayType)>>
inline auto BitCastFromArray(const Container& array) noexcept -> T
{
static_assert(sizeof(T) % sizeof(ArrayType) == 0,
"Size of array type must be a factor of size of destination type.");
static_assert(std::is_trivially_copyable<Container>(),
"BitCastFromArray array type must be trivially copyable.");
static_assert(std::is_trivially_copyable<T>(),
"BitCastFromArray destination type must be trivially copyable.");
T obj;
std::memcpy(&obj, array.data(), sizeof(T));
return obj;
return std::bit_cast<std::array<ValueType, sizeof(From) / sizeof(ValueType)>>(obj);
}
template <typename T>

View file

@ -87,6 +87,7 @@ add_library(common
JitRegister.cpp
JitRegister.h
JsonUtil.h
JsonUtil.cpp
Lazy.h
LinearDiskCache.h
Logging/ConsoleListener.h
@ -143,6 +144,7 @@ add_library(common
TraversalClient.h
TraversalProto.h
TypeUtils.h
Unreachable.h
UPnP.cpp
UPnP.h
VariantUtil.h

View file

@ -17,7 +17,7 @@ namespace Common
// having to prefix them with gen-> or something similar.
// Example implementation:
// class JIT : public CodeBlock<ARMXEmitter> {}
template <class T>
template <class T, bool executable = true>
class CodeBlock : public T
{
private:
@ -53,7 +53,10 @@ public:
{
region_size = size;
total_region_size = size;
region = static_cast<u8*>(Common::AllocateExecutableMemory(total_region_size));
if constexpr (executable)
region = static_cast<u8*>(Common::AllocateExecutableMemory(total_region_size));
else
region = static_cast<u8*>(Common::AllocateMemoryPages(total_region_size));
T::SetCodePtr(region, region + size);
}

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/ColorUtil.h"
#include "Common/Swap.h"
namespace Common

View file

@ -37,7 +37,7 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
{ \
DebugBreak(); \
}
#endif // WIN32 ndef
#endif // _WIN32
namespace Common
{

View file

@ -11,7 +11,6 @@
#define ROOT_DIR "."
// The normal user directory
#ifndef STEAM
#ifdef _WIN32
#define NORMAL_USER_DIR "Dolphin Emulator"
#elif defined(__APPLE__)
@ -21,15 +20,6 @@
#else
#define NORMAL_USER_DIR "dolphin-emu"
#endif
#else // ifndef STEAM
#ifdef _WIN32
#define NORMAL_USER_DIR "Dolphin Emulator (Steam)"
#elif defined(__APPLE__)
#define NORMAL_USER_DIR "Library/Application Support/Dolphin (Steam)"
#else
#define NORMAL_USER_DIR "dolphin-emu-steam"
#endif
#endif
// The portable user directory
#ifdef _WIN32
@ -63,6 +53,7 @@
#define COVERCACHE_DIR "GameCovers"
#define REDUMPCACHE_DIR "Redump"
#define SHADERCACHE_DIR "Shaders"
#define RETROACHIEVEMENTSCACHE_DIR "RetroAchievements"
#define STATESAVES_DIR "StateSaves"
#define SCREENSHOTS_DIR "ScreenShots"
#define LOAD_DIR "Load"

View file

@ -261,4 +261,4 @@ int __cdecl EnableCompatPatches()
extern "C" {
__declspec(allocate(".CRT$XCZ")) decltype(&EnableCompatPatches)
enableCompatPatches = EnableCompatPatches;
};
}

View file

@ -1,6 +1,8 @@
// Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/Crypto/AES.h"
#include <array>
#include <bit>
#include <memory>
@ -9,7 +11,6 @@
#include "Common/Assert.h"
#include "Common/CPUDetect.h"
#include "Common/Crypto/AES.h"
#ifdef _MSC_VER
#include <intrin.h>

View file

@ -385,4 +385,20 @@ Digest CalculateDigest(const u8* msg, size_t len)
ctx->Update(msg, len);
return ctx->Finish();
}
std::string DigestToString(const Digest& digest)
{
static constexpr std::array<char, 16> lookup = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
std::string hash;
hash.reserve(digest.size() * 2);
for (size_t i = 0; i < digest.size(); ++i)
{
const u8 upper = static_cast<u8>((digest[i] >> 4) & 0xf);
const u8 lower = static_cast<u8>(digest[i] & 0xf);
hash.push_back(lookup[upper]);
hash.push_back(lookup[lower]);
}
return hash;
}
} // namespace Common::SHA1

View file

@ -6,6 +6,7 @@
#include <array>
#include <limits>
#include <memory>
#include <span>
#include <string_view>
#include <type_traits>
#include <vector>
@ -23,7 +24,11 @@ class Context
public:
virtual ~Context() = default;
virtual void Update(const u8* msg, size_t len) = 0;
void Update(const std::vector<u8>& msg) { return Update(msg.data(), msg.size()); }
void Update(std::span<const u8> msg) { return Update(msg.data(), msg.size()); }
void Update(std::string_view msg)
{
return Update(reinterpret_cast<const u8*>(msg.data()), msg.size());
}
virtual Digest Finish() = 0;
virtual bool HwAccelerated() const = 0;
};
@ -51,4 +56,6 @@ inline Digest CalculateDigest(const std::array<T, Size>& msg)
static_assert(std::is_trivially_copyable_v<T>);
return CalculateDigest(reinterpret_cast<const u8*>(msg.data()), sizeof(msg));
}
std::string DigestToString(const Digest& digest);
} // namespace Common::SHA1

View file

@ -14,7 +14,7 @@
namespace Core
{
class CPUThreadGuard;
};
}
namespace Common::Debug
{

View file

@ -553,7 +553,7 @@ bool SyncSDFolderToSDImage(const std::function<bool()>& cancelled, bool determin
}
MKFS_PARM options = {};
options.fmt = FM_FAT32;
options.fmt = FM_FAT32 | FM_SFD;
options.n_fat = 0; // Number of FATs: automatic
options.align = 1; // Alignment of the data region (in sectors)
options.n_root = 0; // Number of root directory entries: automatic (and unused for FAT32)

View file

@ -60,6 +60,10 @@
#include "jni/AndroidCommon/AndroidCommon.h"
#endif
#if defined(__FreeBSD__)
#include <sys/sysctl.h>
#endif
namespace fs = std::filesystem;
namespace File
@ -738,6 +742,15 @@ std::string GetExePath()
return PathToString(exe_path_absolute);
#elif defined(__APPLE__)
return GetBundleDirectory();
#elif defined(__FreeBSD__)
int name[4]{CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
size_t length = 0;
if (sysctl(name, 4, nullptr, &length, nullptr, 0) != 0 || length == 0)
return {};
std::string dolphin_exe_path(length, '\0');
if (sysctl(name, 4, dolphin_exe_path.data(), &length, nullptr, 0) != 0)
return {};
return dolphin_exe_path;
#else
char dolphin_exe_path[PATH_MAX];
ssize_t len = ::readlink("/proc/self/exe", dolphin_exe_path, sizeof(dolphin_exe_path));
@ -843,6 +856,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_COVERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + COVERCACHE_DIR DIR_SEP;
s_user_paths[D_REDUMPCACHE_IDX] = s_user_paths[D_CACHE_IDX] + REDUMPCACHE_DIR DIR_SEP;
s_user_paths[D_SHADERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + SHADERCACHE_DIR DIR_SEP;
s_user_paths[D_RETROACHIEVEMENTSCACHE_IDX] =
s_user_paths[D_CACHE_IDX] + RETROACHIEVEMENTSCACHE_DIR DIR_SEP;
s_user_paths[D_SHADERS_IDX] = s_user_paths[D_USER_IDX] + SHADERS_DIR DIR_SEP;
s_user_paths[D_STATESAVES_IDX] = s_user_paths[D_USER_IDX] + STATESAVES_DIR DIR_SEP;
s_user_paths[D_SCREENSHOTS_IDX] = s_user_paths[D_USER_IDX] + SCREENSHOTS_DIR DIR_SEP;
@ -926,6 +941,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
s_user_paths[D_COVERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + COVERCACHE_DIR DIR_SEP;
s_user_paths[D_REDUMPCACHE_IDX] = s_user_paths[D_CACHE_IDX] + REDUMPCACHE_DIR DIR_SEP;
s_user_paths[D_SHADERCACHE_IDX] = s_user_paths[D_CACHE_IDX] + SHADERCACHE_DIR DIR_SEP;
s_user_paths[D_RETROACHIEVEMENTSCACHE_IDX] =
s_user_paths[D_CACHE_IDX] + RETROACHIEVEMENTSCACHE_DIR DIR_SEP;
break;
case D_GCUSER_IDX:

View file

@ -40,6 +40,7 @@ enum
D_COVERCACHE_IDX,
D_REDUMPCACHE_IDX,
D_SHADERCACHE_IDX,
D_RETROACHIEVEMENTSCACHE_IDX,
D_SHADERS_IDX,
D_STATESAVES_IDX,
D_SCREENSHOTS_IDX,

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLInterface/AGL.h"
#include "Common/Logging/Log.h"
// UpdateCachedDimensions and AttachContextToView contain calls to UI APIs, so they must only be

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Common/GL/GLX11Window.h"
#include "Common/GL/GLContext.h"
GLX11Window::GLX11Window(Display* display, Window parent_window, Colormap color_map, Window window,

View file

@ -75,7 +75,7 @@ bool IniFile::Section::Get(std::string_view key, std::string* value,
bool IniFile::Section::Exists(std::string_view key) const
{
return values.find(key) != values.end();
return values.contains(key);
}
bool IniFile::Section::Delete(std::string_view key)

View file

@ -3,6 +3,10 @@
#include "Common/JsonUtil.h"
#include <fstream>
#include "Common/FileUtil.h"
picojson::object ToJsonObject(const Common::Vec3& vec)
{
picojson::object obj;
@ -38,3 +42,27 @@ std::optional<bool> ReadBoolFromJson(const picojson::object& obj, const std::str
return std::nullopt;
return it->second.get<bool>();
}
bool JsonToFile(const std::string& filename, const picojson::value& root, bool prettify)
{
std::ofstream json_stream;
File::OpenFStream(json_stream, filename, std::ios_base::out);
if (!json_stream.is_open())
{
return false;
}
json_stream << root.serialize(prettify);
return true;
}
bool JsonFromFile(const std::string& filename, picojson::value* root, std::string* error)
{
std::string json_data;
if (!File::ReadFileToString(filename, json_data))
{
return false;
}
*error = picojson::parse(*root, json_data);
return error->empty();
}

View file

@ -9,7 +9,6 @@
#include <picojson.h>
#include "Common/MathUtil.h"
#include "Common/Matrix.h"
// Ideally this would use a concept like, 'template <std::ranges::range Range>' to constrain it,
@ -47,7 +46,7 @@ std::optional<Type> ReadNumericFromJson(const picojson::object& obj, const std::
return std::nullopt;
if (!it->second.is<double>())
return std::nullopt;
return MathUtil::SaturatingCast<Type>(it->second.get<double>());
return static_cast<Type>(it->second.get<double>());
}
std::optional<std::string> ReadStringFromJson(const picojson::object& obj, const std::string& key);
@ -56,3 +55,6 @@ std::optional<bool> ReadBoolFromJson(const picojson::object& obj, const std::str
picojson::object ToJsonObject(const Common::Vec3& vec);
void FromJson(const picojson::object& obj, Common::Vec3& vec);
bool JsonToFile(const std::string& filename, const picojson::value& root, bool prettify = false);
bool JsonFromFile(const std::string& filename, picojson::value* root, std::string* error);

View file

@ -78,7 +78,7 @@ public:
{
static LdrDllNotifier notifier;
return notifier;
};
}
void Install(LdrObserver* observer);
void Uninstall(LdrObserver* observer);

View file

@ -54,7 +54,7 @@ public:
private:
Profiler* m_p;
};
}; // namespace Common
} // namespace Common
// Warning: This profiler isn't thread safe. Only profile functions which doesn't run simultaneously
#define PROFILE(name) \

View file

@ -3,6 +3,9 @@
#include "Common/SocketContext.h"
#include "Common/Logging/Log.h"
#include "Common/Network.h"
namespace Common
{
#ifdef _WIN32
@ -11,7 +14,23 @@ SocketContext::SocketContext()
std::lock_guard<std::mutex> g(s_lock);
if (s_num_objects == 0)
{
static_cast<void>(WSAStartup(MAKEWORD(2, 2), &s_data));
const int ret = WSAStartup(MAKEWORD(2, 2), &s_data);
if (ret == 0)
{
INFO_LOG_FMT(COMMON, "WSAStartup succeeded, wVersion={}.{}, wHighVersion={}.{}",
int(LOBYTE(s_data.wVersion)), int(HIBYTE(s_data.wVersion)),
int(LOBYTE(s_data.wHighVersion)), int(HIBYTE(s_data.wHighVersion)));
}
else
{
// The WSAStartup function directly returns the extended error code in the return value.
// A call to the WSAGetLastError function is not needed and should not be used.
//
// Source:
// https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsastartup
ERROR_LOG_FMT(COMMON, "WSAStartup failed with error {}: {}", ret,
Common::DecodeNetworkError(ret));
}
}
s_num_objects++;
}

View file

@ -195,7 +195,7 @@ std::from_chars_result FromChars(std::string_view sv, T& value,
const char* const last = first + sv.size();
return std::from_chars(first, last, value, fmt);
}
}; // namespace Common
} // namespace Common
std::string TabsToSpaces(int tab_size, std::string str);

View file

@ -82,17 +82,4 @@ static_assert(!IsNOf<int, 1, int, int>::value);
static_assert(IsNOf<int, 2, int, int>::value);
static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed
static_assert(!IsNOf<int, 2, int, char*>::value);
// TODO: This can be replaced with std::array's fill() once C++20 is fully supported.
// Prior to C++20, std::array's fill() function is, unfortunately, not constexpr.
// Ditto for <algorithm>'s std::fill. Although Dolphin targets C++20, Android doesn't
// seem to properly support constexpr fill(), so we need this for now.
template <typename T1, size_t N, typename T2>
constexpr void Fill(std::array<T1, N>& array, const T2& value)
{
for (auto& entry : array)
{
entry = value;
}
}
} // namespace Common

View file

@ -0,0 +1,21 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "Common/CommonFuncs.h"
namespace Common
{
// TODO C++23: Replace with std::unreachable.
[[noreturn]] inline void Unreachable()
{
#ifdef _DEBUG
Crash();
#elif defined(_MSC_VER) && !defined(__clang__)
__assume(false);
#else
__builtin_unreachable();
#endif
}
} // namespace Common

View file

@ -9,6 +9,8 @@
namespace Common
{
#define EMULATOR_NAME "Dolphin"
#ifdef _DEBUG
#define BUILD_TYPE_STR "Debug "
#elif defined DEBUGFAST
@ -41,6 +43,12 @@ const std::string& GetScmBranchStr()
return scm_branch_str;
}
const std::string& GetUserAgentStr()
{
static const std::string user_agent_str = EMULATOR_NAME "/" SCM_DESC_STR;
return user_agent_str;
}
const std::string& GetScmDistributorStr()
{
static const std::string scm_distributor_str = SCM_DISTRIBUTOR_STR;

View file

@ -11,6 +11,7 @@ const std::string& GetScmDescStr();
const std::string& GetScmBranchStr();
const std::string& GetScmRevStr();
const std::string& GetScmRevGitStr();
const std::string& GetUserAgentStr();
const std::string& GetScmDistributorStr();
const std::string& GetScmUpdateTrackStr();
const std::string& GetNetplayDolphinVer();

View file

@ -69,4 +69,4 @@ OSVERSIONINFOW GetOSVersion()
}
return info;
}
}; // namespace WindowsRegistry
} // namespace WindowsRegistry

View file

@ -15,4 +15,4 @@ template <>
bool ReadValue(std::string* value, const std::string& subkey, const std::string& name);
OSVERSIONINFOW GetOSVersion();
}; // namespace WindowsRegistry
} // namespace WindowsRegistry

View file

@ -6,6 +6,7 @@ var cmd_revision = " rev-parse HEAD";
var cmd_describe = " rev-parse --short HEAD";
var cmd_branch = " rev-parse --abbrev-ref HEAD";
var cmd_commits_ahead = " rev-list --count HEAD ^master";
var cmd_get_tag = " describe --exact-match HEAD";
function GetGitExe()
{
@ -59,6 +60,25 @@ function GetFirstStdOutLine(cmd)
}
}
function AttemptToExecuteCommand(cmd)
{
try
{
var exec = wshShell.Exec(cmd)
// wait until the command has finished
while (exec.Status == 0) {}
return exec.ExitCode;
}
catch (e)
{
// catch "the system cannot find the file specified" error
WScript.Echo("Failed to exec " + cmd + " this should never happen");
WScript.Quit(1);
}
}
function GetFileContents(f)
{
try
@ -88,6 +108,12 @@ if (default_update_track == "%DOLPHIN_DEFAULT_UPDATE_TRACK%") default_update_tra
// remove hash (and trailing "-0" if needed) from description
describe = describe.replace(/(-0)?-[^-]+(-dirty)?$/, '$2');
// set commits ahead to zero if on a tag
if (AttemptToExecuteCommand(gitexe + cmd_get_tag) == 0)
{
commits_ahead = "0";
}
var out_contents =
"#define SCM_REV_STR \"" + revision + "\"\n" +
"#define SCM_DESC_STR \"" + describe + "\"\n" +

View file

@ -50,7 +50,7 @@
// FIXME: avoid pushing all 16 XMM registers when possible? most functions we call probably
// don't actually clobber them.
#define ABI_ALL_CALLER_SAVED (BitSet32{RAX, RCX, RDX, RDI, RSI, R8, R9, R10, R11} | ABI_ALL_FPRS)
#endif // WIN32
#endif // _WIN32
#define ABI_ALL_CALLEE_SAVED (~ABI_ALL_CALLER_SAVED)

View file

@ -107,26 +107,6 @@ void XEmitter::SetCodePtr(u8* ptr, u8* end, bool write_failed)
m_write_failed = write_failed;
}
const u8* XEmitter::GetCodePtr() const
{
return code;
}
u8* XEmitter::GetWritableCodePtr()
{
return code;
}
const u8* XEmitter::GetCodeEnd() const
{
return m_code_end;
}
u8* XEmitter::GetWritableCodeEnd()
{
return m_code_end;
}
void XEmitter::Write8(u8 value)
{
if (code >= m_code_end)

View file

@ -394,10 +394,10 @@ public:
u8* AlignCode4();
u8* AlignCode16();
u8* AlignCodePage();
const u8* GetCodePtr() const;
u8* GetWritableCodePtr();
const u8* GetCodeEnd() const;
u8* GetWritableCodeEnd();
const u8* GetCodePtr() const { return code; }
u8* GetWritableCodePtr() { return code; }
const u8* GetCodeEnd() const { return m_code_end; }
u8* GetWritableCodeEnd() { return m_code_end; }
void LockFlags() { flags_locked = true; }
void UnlockFlags() { flags_locked = false; }

View file

@ -13,20 +13,31 @@
#include <rcheevos/include/rc_api_info.h>
#include <rcheevos/include/rc_hash.h>
#include "Common/Assert.h"
#include "Common/BitUtils.h"
#include "Common/CommonPaths.h"
#include "Common/FileUtil.h"
#include "Common/IOFile.h"
#include "Common/Image.h"
#include "Common/Logging/Log.h"
#include "Common/ScopeGuard.h"
#include "Common/Version.h"
#include "Common/WorkQueueThread.h"
#include "Core/Config/AchievementSettings.h"
#include "Core/Core.h"
#include "Core/HW/Memmap.h"
#include "Core/HW/VideoInterface.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/MMU.h"
#include "Core/System.h"
#include "DiscIO/Blob.h"
#include "UICommon/DiscordPresence.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/OnScreenDisplay.h"
#include "VideoCommon/VideoEvents.h"
static std::unique_ptr<OSD::Icon> DecodeBadgeToOSDIcon(const AchievementManager::Badge& badge);
static const Common::HttpRequest::Headers USER_AGENT_HEADER = {
{"User-Agent", Common::GetUserAgentStr()}};
AchievementManager& AchievementManager::GetInstance()
{
@ -36,9 +47,13 @@ AchievementManager& AchievementManager::GetInstance()
void AchievementManager::Init()
{
LoadDefaultBadges();
if (!m_client && Config::Get(Config::RA_ENABLED))
{
m_client = rc_client_create(MemoryPeeker, Request);
{
std::lock_guard lg{m_lock};
m_client = rc_client_create(MemoryVerifier, Request);
}
std::string host_url = Config::Get(Config::RA_HOST_URL);
if (!host_url.empty())
rc_client_set_host(m_client, host_url.c_str());
@ -57,6 +72,33 @@ void AchievementManager::Init()
}
}
picojson::value AchievementManager::LoadApprovedList()
{
picojson::value temp;
std::string error;
if (!JsonFromFile(fmt::format("{}{}{}", File::GetSysDirectory(), DIR_SEP, APPROVED_LIST_FILENAME),
&temp, &error))
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load approved game settings list {}",
APPROVED_LIST_FILENAME);
WARN_LOG_FMT(ACHIEVEMENTS, "Error: {}", error);
return {};
}
auto context = Common::SHA1::CreateContext();
context->Update(temp.serialize());
auto digest = context->Finish();
if (digest != APPROVED_LIST_HASH)
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to verify approved game settings list {}",
APPROVED_LIST_FILENAME);
WARN_LOG_FMT(ACHIEVEMENTS, "Expected hash {}, found hash {}",
Common::SHA1::DigestToString(APPROVED_LIST_HASH),
Common::SHA1::DigestToString(digest));
return {};
}
return temp;
}
void AchievementManager::SetUpdateCallback(UpdateCallback callback)
{
m_update_callback = std::move(callback);
@ -138,6 +180,7 @@ void AchievementManager::LoadGame(const std::string& file_path, const DiscIO::Vo
}
else
{
rc_client_set_read_memory_function(m_client, MemoryVerifier);
rc_client_begin_identify_and_load_game(m_client, RC_CONSOLE_GAMECUBE, file_path.c_str(), NULL,
0, LoadGameCallback, NULL);
}
@ -149,6 +192,18 @@ bool AchievementManager::IsGameLoaded() const
return game_info && game_info->id != 0;
}
void AchievementManager::SetBackgroundExecutionAllowed(bool allowed)
{
m_background_execution_allowed = allowed;
Core::System* system = m_system.load(std::memory_order_acquire);
if (!system)
return;
if (allowed && Core::GetState(*system) == Core::State::Paused)
DoIdle();
}
void AchievementManager::FetchPlayerBadge()
{
FetchBadge(&m_player_badge, RC_IMAGE_TYPE_USER,
@ -220,7 +275,8 @@ void AchievementManager::DoFrame()
std::lock_guard lg{m_lock};
rc_client_do_frame(m_client);
}
if (!m_system)
Core::System* system = m_system.load(std::memory_order_acquire);
if (!system)
return;
auto current_time = std::chrono::steady_clock::now();
if (current_time - m_last_rp_time > std::chrono::seconds{10})
@ -233,6 +289,54 @@ void AchievementManager::DoFrame()
}
}
bool AchievementManager::CanPause()
{
u32 frames_to_next_pause = 0;
bool can_pause = rc_client_can_pause(m_client, &frames_to_next_pause);
if (!can_pause)
{
OSD::AddMessage(
fmt::format("RetroAchievements Hardcore Mode:\n"
"Cannot pause until another {:.2f} seconds have passed.",
static_cast<float>(frames_to_next_pause) /
Core::System::GetInstance().GetVideoInterface().GetTargetRefreshRate()),
OSD::Duration::VERY_LONG, OSD::Color::RED);
}
return can_pause;
}
void AchievementManager::DoIdle()
{
std::thread([this]() {
while (true)
{
Common::SleepCurrentThread(1000);
{
std::lock_guard lg{m_lock};
Core::System* system = m_system.load(std::memory_order_acquire);
if (!system || Core::GetState(*system) != Core::State::Paused)
return;
if (!m_background_execution_allowed)
return;
if (!m_client || !IsGameLoaded())
return;
}
// rc_client_idle peeks at memory to recalculate rich presence and therefore
// needs to be on host or CPU thread to access memory.
Core::QueueHostJob([this](Core::System& system) {
std::lock_guard lg{m_lock};
if (Core::GetState(system) != Core::State::Paused)
return;
if (!m_background_execution_allowed)
return;
if (!m_client || !IsGameLoaded())
return;
rc_client_idle(m_client);
});
}
}).detach();
}
std::recursive_mutex& AchievementManager::GetLock()
{
return m_lock;
@ -253,6 +357,62 @@ bool AchievementManager::IsHardcoreModeActive() const
return rc_client_is_processing_required(m_client);
}
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
const std::string& game_ini_id) const
{
if (patches.empty())
{
// There's nothing to verify, so let's save ourselves some work
return;
}
std::lock_guard lg{m_lock};
if (!IsHardcoreModeActive())
return;
const bool known_id = m_ini_root->contains(game_ini_id);
auto patch_itr = patches.begin();
while (patch_itr != patches.end())
{
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying patch {}", patch_itr->name);
bool verified = false;
if (known_id)
{
auto context = Common::SHA1::CreateContext();
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(patch_itr->entries.size())));
for (const auto& entry : patch_itr->entries)
{
context->Update(Common::BitCastToArray<u8>(entry.type));
context->Update(Common::BitCastToArray<u8>(entry.address));
context->Update(Common::BitCastToArray<u8>(entry.value));
context->Update(Common::BitCastToArray<u8>(entry.comparand));
context->Update(Common::BitCastToArray<u8>(entry.conditional));
}
auto digest = context->Finish();
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
}
if (!verified)
{
patch_itr = patches.erase(patch_itr);
OSD::AddMessage(
fmt::format("Failed to verify patch {} from file {}.", patch_itr->name, game_ini_id),
OSD::Duration::VERY_LONG, OSD::Color::RED);
OSD::AddMessage("Disable hardcore mode to enable this patch.", OSD::Duration::VERY_LONG,
OSD::Color::RED);
}
else
{
patch_itr++;
}
}
}
void AchievementManager::SetSpectatorMode()
{
rc_client_set_spectator_mode_enabled(m_client, Config::Get(Config::RA_SPECTATOR_ENABLED));
@ -278,9 +438,9 @@ u32 AchievementManager::GetPlayerScore() const
return user->score;
}
const AchievementManager::BadgeStatus& AchievementManager::GetPlayerBadge() const
const AchievementManager::Badge& AchievementManager::GetPlayerBadge() const
{
return m_player_badge;
return m_player_badge.data.empty() ? m_default_player_badge : m_player_badge;
}
std::string_view AchievementManager::GetGameDisplayName() const
@ -298,17 +458,19 @@ rc_api_fetch_game_data_response_t* AchievementManager::GetGameData()
return &m_game_data;
}
const AchievementManager::BadgeStatus& AchievementManager::GetGameBadge() const
const AchievementManager::Badge& AchievementManager::GetGameBadge() const
{
return m_game_badge;
return m_game_badge.data.empty() ? m_default_game_badge : m_game_badge;
}
const AchievementManager::BadgeStatus& AchievementManager::GetAchievementBadge(AchievementId id,
bool locked) const
const AchievementManager::Badge& AchievementManager::GetAchievementBadge(AchievementId id,
bool locked) const
{
auto& badge_list = locked ? m_locked_badges : m_locked_badges;
auto& badge_list = locked ? m_locked_badges : m_unlocked_badges;
auto itr = badge_list.find(id);
return (itr == badge_list.end()) ? m_default_badge : itr->second;
return (itr != badge_list.end() && itr->second.data.size() > 0) ?
itr->second :
(locked ? m_default_locked_badge : m_default_unlocked_badge);
}
const AchievementManager::LeaderboardStatus*
@ -317,8 +479,6 @@ AchievementManager::GetLeaderboardInfo(AchievementManager::AchievementId leaderb
if (const auto leaderboard_iter = m_leaderboard_map.find(leaderboard_id);
leaderboard_iter != m_leaderboard_map.end())
{
if (leaderboard_iter->second.entries.size() == 0)
FetchBoardInfo(leaderboard_id);
return &leaderboard_iter->second;
}
@ -330,7 +490,18 @@ AchievementManager::RichPresence AchievementManager::GetRichPresence() const
return m_rich_presence;
}
const AchievementManager::NamedIconMap& AchievementManager::GetChallengeIcons() const
bool AchievementManager::AreChallengesUpdated() const
{
return m_challenges_updated;
}
void AchievementManager::ResetChallengesUpdated()
{
m_challenges_updated = false;
}
const std::unordered_set<AchievementManager::AchievementId>&
AchievementManager::GetActiveChallenges() const
{
return m_active_challenges;
}
@ -393,29 +564,36 @@ void AchievementManager::CloseGame()
{
m_active_challenges.clear();
m_active_leaderboards.clear();
m_game_badge.name.clear();
m_game_badge.width = 0;
m_game_badge.height = 0;
m_game_badge.data.clear();
m_unlocked_badges.clear();
m_locked_badges.clear();
m_leaderboard_map.clear();
m_rich_presence.fill('\0');
rc_api_destroy_fetch_game_data_response(&m_game_data);
m_game_data = {};
m_queue.Cancel();
m_image_queue.Cancel();
rc_client_unload_game(m_client);
m_system = nullptr;
m_system.store(nullptr, std::memory_order_release);
if (Config::Get(Config::RA_DISCORD_PRESENCE_ENABLED))
Discord::UpdateDiscordPresence();
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
}
}
m_update_callback(UpdatedItems{.all = true});
INFO_LOG_FMT(ACHIEVEMENTS, "Game closed.");
}
void AchievementManager::Logout()
{
{
std::lock_guard lg{m_lock};
CloseGame();
m_player_badge.name.clear();
std::lock_guard lg{m_lock};
m_player_badge.width = 0;
m_player_badge.height = 0;
m_player_badge.data.clear();
Config::SetBaseOrCurrent(Config::RA_API_TOKEN, "");
}
@ -429,6 +607,7 @@ void AchievementManager::Shutdown()
{
CloseGame();
m_queue.Shutdown();
std::lock_guard lg{m_lock};
// DON'T log out - keep those credentials for next run.
rc_client_destroy(m_client);
m_client = nullptr;
@ -500,6 +679,53 @@ void AchievementManager::FilereaderClose(void* file_handle)
delete static_cast<FilereaderState*>(file_handle);
}
void AchievementManager::LoadDefaultBadges()
{
std::lock_guard lg{m_lock};
std::string directory = File::GetSysDirectory() + DIR_SEP + RESOURCES_DIR + DIR_SEP;
if (m_default_player_badge.data.empty())
{
if (!LoadPNGTexture(&m_default_player_badge,
fmt::format("{}{}", directory, DEFAULT_PLAYER_BADGE_FILENAME)))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Default player badge '{}' failed to load",
DEFAULT_PLAYER_BADGE_FILENAME);
}
}
if (m_default_game_badge.data.empty())
{
if (!LoadPNGTexture(&m_default_game_badge,
fmt::format("{}{}", directory, DEFAULT_GAME_BADGE_FILENAME)))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Default game badge '{}' failed to load",
DEFAULT_GAME_BADGE_FILENAME);
}
}
if (m_default_unlocked_badge.data.empty())
{
if (!LoadPNGTexture(&m_default_unlocked_badge,
fmt::format("{}{}", directory, DEFAULT_UNLOCKED_BADGE_FILENAME)))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Default unlocked achievement badge '{}' failed to load",
DEFAULT_UNLOCKED_BADGE_FILENAME);
}
}
if (m_default_locked_badge.data.empty())
{
if (!LoadPNGTexture(&m_default_locked_badge,
fmt::format("{}{}", directory, DEFAULT_LOCKED_BADGE_FILENAME)))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Default locked achievement badge '{}' failed to load",
DEFAULT_LOCKED_BADGE_FILENAME);
}
}
}
void AchievementManager::LoginCallback(int result, const char* error_message, rc_client_t* client,
void* userdata)
{
@ -578,6 +804,8 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
map_entry.username.assign(response_entry.user);
memcpy(map_entry.score.data(), response_entry.display, FORMAT_SIZE);
map_entry.rank = response_entry.rank;
if (ix == list->user_index)
leaderboard.player_index = response_entry.rank;
}
AchievementManager::GetInstance().m_update_callback({.leaderboards = {*leaderboard_id}});
}
@ -585,6 +813,7 @@ void AchievementManager::LeaderboardEntriesCallback(int result, const char* erro
void AchievementManager::LoadGameCallback(int result, const char* error_message,
rc_client_t* client, void* userdata)
{
AchievementManager::GetInstance().m_loading_volume.reset(nullptr);
if (result != RC_OK)
{
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to load data for current game.");
@ -603,20 +832,40 @@ void AchievementManager::LoadGameCallback(int result, const char* error_message,
}
INFO_LOG_FMT(ACHIEVEMENTS, "Loaded data for game ID {}.", game->id);
AchievementManager::GetInstance().m_display_welcome_message = true;
AchievementManager::GetInstance().FetchGameBadges();
AchievementManager::GetInstance().m_system = &Core::System::GetInstance();
AchievementManager::GetInstance().m_update_callback({.all = true});
auto& instance = AchievementManager::GetInstance();
rc_client_set_read_memory_function(instance.m_client, MemoryPeeker);
instance.m_display_welcome_message = true;
instance.FetchGameBadges();
instance.m_system.store(&Core::System::GetInstance(), std::memory_order_release);
instance.m_update_callback({.all = true});
// Set this to a value that will immediately trigger RP
AchievementManager::GetInstance().m_last_rp_time =
std::chrono::steady_clock::now() - std::chrono::minutes{2};
instance.m_last_rp_time = std::chrono::steady_clock::now() - std::chrono::minutes{2};
std::lock_guard lg{instance.GetLock()};
auto* leaderboard_list =
rc_client_create_leaderboard_list(client, RC_CLIENT_LEADERBOARD_LIST_GROUPING_NONE);
for (u32 bucket = 0; bucket < leaderboard_list->num_buckets; bucket++)
{
const auto& leaderboard_bucket = leaderboard_list->buckets[bucket];
for (u32 board = 0; board < leaderboard_bucket.num_leaderboards; board++)
{
const auto& leaderboard = leaderboard_bucket.leaderboards[board];
instance.m_leaderboard_map.insert(
std::pair(leaderboard->id, LeaderboardStatus{.name = leaderboard->title,
.description = leaderboard->description}));
}
}
rc_client_destroy_leaderboard_list(leaderboard_list);
}
void AchievementManager::ChangeMediaCallback(int result, const char* error_message,
rc_client_t* client, void* userdata)
{
AchievementManager::GetInstance().m_loading_volume.reset(nullptr);
if (result == RC_OK)
{
return;
}
if (result == RC_HARDCORE_DISABLED)
{
@ -637,11 +886,8 @@ void AchievementManager::DisplayWelcomeMessage()
m_display_welcome_message = false;
const u32 color =
rc_client_get_hardcore_enabled(m_client) ? OSD::Color::YELLOW : OSD::Color::CYAN;
if (Config::Get(Config::RA_BADGES_ENABLED) && !m_game_badge.name.empty())
{
OSD::AddMessage("", OSD::Duration::VERY_LONG, OSD::Color::GREEN,
DecodeBadgeToOSDIcon(m_game_badge.badge));
}
OSD::AddMessage("", OSD::Duration::VERY_LONG, OSD::Color::GREEN, &GetGameBadge());
auto info = rc_client_get_game_info(m_client);
if (!info)
{
@ -671,17 +917,15 @@ void AchievementManager::DisplayWelcomeMessage()
void AchievementManager::HandleAchievementTriggeredEvent(const rc_client_event_t* client_event)
{
const auto& instance = AchievementManager::GetInstance();
OSD::AddMessage(fmt::format("Unlocked: {} ({})", client_event->achievement->title,
client_event->achievement->points),
OSD::Duration::VERY_LONG,
(rc_client_get_hardcore_enabled(AchievementManager::GetInstance().m_client)) ?
OSD::Color::YELLOW :
OSD::Color::CYAN,
(Config::Get(Config::RA_BADGES_ENABLED)) ?
DecodeBadgeToOSDIcon(AchievementManager::GetInstance()
.m_unlocked_badges[client_event->achievement->id]
.badge) :
nullptr);
(rc_client_get_hardcore_enabled(instance.m_client)) ? OSD::Color::YELLOW :
OSD::Color::CYAN,
&instance.GetAchievementBadge(client_event->achievement->id, false));
AchievementManager::GetInstance().m_update_callback(
UpdatedItems{.achievements = {client_event->achievement->id}});
}
void AchievementManager::HandleLeaderboardStartedEvent(const rc_client_event_t* client_event)
@ -706,6 +950,8 @@ void AchievementManager::HandleLeaderboardSubmittedEvent(const rc_client_event_t
client_event->leaderboard->title),
OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
AchievementManager::GetInstance().FetchBoardInfo(client_event->leaderboard->id);
AchievementManager::GetInstance().m_update_callback(
UpdatedItems{.leaderboards = {client_event->leaderboard->id}});
}
void AchievementManager::HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event)
@ -738,36 +984,38 @@ void AchievementManager::HandleLeaderboardTrackerHideEvent(const rc_client_event
void AchievementManager::HandleAchievementChallengeIndicatorShowEvent(
const rc_client_event_t* client_event)
{
if (Config::Get(Config::RA_BADGES_ENABLED))
{
auto& unlocked_badges = AchievementManager::GetInstance().m_unlocked_badges;
if (const auto unlocked_iter = unlocked_badges.find(client_event->achievement->id);
unlocked_iter != unlocked_badges.end())
{
AchievementManager::GetInstance().m_active_challenges[client_event->achievement->badge_name] =
DecodeBadgeToOSDIcon(unlocked_iter->second.badge);
}
}
auto& instance = AchievementManager::GetInstance();
const auto [iter, inserted] = instance.m_active_challenges.insert(client_event->achievement->id);
if (inserted)
instance.m_challenges_updated = true;
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true});
}
void AchievementManager::HandleAchievementChallengeIndicatorHideEvent(
const rc_client_event_t* client_event)
{
AchievementManager::GetInstance().m_active_challenges.erase(
client_event->achievement->badge_name);
auto& instance = AchievementManager::GetInstance();
const auto removed = instance.m_active_challenges.erase(client_event->achievement->id);
if (removed > 0)
instance.m_challenges_updated = true;
AchievementManager::GetInstance().m_update_callback(UpdatedItems{.rich_presence = true});
}
void AchievementManager::HandleAchievementProgressIndicatorShowEvent(
const rc_client_event_t* client_event)
{
auto& instance = AchievementManager::GetInstance();
auto current_time = std::chrono::steady_clock::now();
const auto message_wait_time = std::chrono::milliseconds{OSD::Duration::SHORT};
if (current_time - instance.m_last_progress_message < message_wait_time)
return;
OSD::AddMessage(fmt::format("{} {}", client_event->achievement->title,
client_event->achievement->measured_progress),
OSD::Duration::SHORT, OSD::Color::GREEN,
(Config::Get(Config::RA_BADGES_ENABLED)) ?
DecodeBadgeToOSDIcon(AchievementManager::GetInstance()
.m_unlocked_badges[client_event->achievement->id]
.badge) :
nullptr);
&instance.GetAchievementBadge(client_event->achievement->id, false));
instance.m_last_progress_message = current_time;
AchievementManager::GetInstance().m_update_callback(
UpdatedItems{.achievements = {client_event->achievement->id}});
}
void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* client_event,
@ -784,9 +1032,7 @@ void AchievementManager::HandleGameCompletedEvent(const rc_client_event_t* clien
OSD::AddMessage(fmt::format("Congratulations! {} has {} {}", user_info->display_name,
hardcore ? "mastered" : "completed", game_info->title),
OSD::Duration::VERY_LONG, hardcore ? OSD::Color::YELLOW : OSD::Color::CYAN,
(Config::Get(Config::RA_BADGES_ENABLED)) ?
DecodeBadgeToOSDIcon(AchievementManager::GetInstance().m_game_badge.badge) :
nullptr);
&AchievementManager::GetInstance().GetGameBadge());
}
void AchievementManager::HandleResetEvent(const rc_client_event_t* client_event)
@ -801,62 +1047,65 @@ void AchievementManager::HandleServerErrorEvent(const rc_client_event_t* client_
client_event->server_error->api, client_event->server_error->error_message);
}
static std::unique_ptr<OSD::Icon> DecodeBadgeToOSDIcon(const AchievementManager::Badge& badge)
{
if (badge.empty())
return nullptr;
auto icon = std::make_unique<OSD::Icon>();
if (!Common::LoadPNG(badge, &icon->rgba_data, &icon->width, &icon->height))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Error decoding badge.");
return nullptr;
}
return icon;
}
void AchievementManager::Request(const rc_api_request_t* request,
rc_client_server_callback_t callback, void* callback_data,
rc_client_t* client)
{
std::string url = request->url;
std::string post_data = request->post_data;
AchievementManager::GetInstance().m_queue.EmplaceItem([url = std::move(url),
post_data = std::move(post_data),
callback = std::move(callback),
callback_data = std::move(callback_data)] {
const Common::HttpRequest::Headers USER_AGENT_HEADER = {{"User-Agent", "Dolphin/Placeholder"}};
AchievementManager::GetInstance().m_queue.EmplaceItem(
[url = std::move(url), post_data = std::move(post_data), callback = std::move(callback),
callback_data = std::move(callback_data)] {
Common::HttpRequest http_request;
Common::HttpRequest::Response http_response;
if (!post_data.empty())
{
http_response = http_request.Post(url, post_data, USER_AGENT_HEADER,
Common::HttpRequest::AllowedReturnCodes::All);
}
else
{
http_response = http_request.Get(url, USER_AGENT_HEADER,
Common::HttpRequest::AllowedReturnCodes::All);
}
Common::HttpRequest http_request;
Common::HttpRequest::Response http_response;
if (!post_data.empty())
{
http_response = http_request.Post(url, post_data, USER_AGENT_HEADER,
Common::HttpRequest::AllowedReturnCodes::All);
}
else
{
http_response =
http_request.Get(url, USER_AGENT_HEADER, Common::HttpRequest::AllowedReturnCodes::All);
}
rc_api_server_response_t server_response;
if (http_response.has_value() && http_response->size() > 0)
{
server_response.body = reinterpret_cast<const char*>(http_response->data());
server_response.body_length = http_response->size();
server_response.http_status_code = http_request.GetLastResponseCode();
}
else
{
static constexpr char error_message[] = "Failed HTTP request.";
server_response.body = error_message;
server_response.body_length = sizeof(error_message);
server_response.http_status_code = RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR;
}
rc_api_server_response_t server_response;
if (http_response.has_value() && http_response->size() > 0)
{
server_response.body = reinterpret_cast<const char*>(http_response->data());
server_response.body_length = http_response->size();
server_response.http_status_code = http_request.GetLastResponseCode();
}
else
{
constexpr char error_message[] = "Failed HTTP request.";
server_response.body = error_message;
server_response.body_length = sizeof(error_message);
server_response.http_status_code = RC_API_SERVER_RESPONSE_RETRYABLE_CLIENT_ERROR;
}
callback(&server_response, callback_data);
});
}
callback(&server_response, callback_data);
});
// Currently, when rc_client calls the memory peek method provided in its constructor (or in
// rc_client_set_read_memory_function) it will do so on the thread that calls DoFrame, which is
// currently the host thread, with one exception: an asynchronous callback in the load game process.
// This is done to validate/invalidate each memory reference in the downloaded assets, mark assets
// as unsupported, and notify the player upon startup that there are unsupported assets and how
// many. As such, all that call needs to do is return the number of bytes that can be read with this
// call. As only the CPU and host threads are allowed to read from memory, I provide a separate
// method for this verification. In lieu of a more convenient set of steps, I provide MemoryVerifier
// to rc_client at construction, and in the Load Game callback, after the verification has been
// complete, I call rc_client_set_read_memory_function to switch to the usual MemoryPeeker for all
// future synchronous calls.
u32 AchievementManager::MemoryVerifier(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client)
{
auto& system = Core::System::GetInstance();
u32 ram_size = system.GetMemory().GetRamSizeReal();
if (address >= ram_size)
return 0;
return std::min(ram_size - address, num_bytes);
}
u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client)
@ -864,6 +1113,11 @@ u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_
if (buffer == nullptr)
return 0u;
auto& system = Core::System::GetInstance();
if (!(Core::IsHostThread() || Core::IsCPUThread()))
{
ASSERT_MSG(ACHIEVEMENTS, false, "MemoryPeeker called from wrong thread");
return 0;
}
Core::CPUThreadGuard threadguard(system);
for (u32 num_read = 0; num_read < num_bytes; num_read++)
{
@ -876,11 +1130,11 @@ u32 AchievementManager::MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_
return num_bytes;
}
void AchievementManager::FetchBadge(AchievementManager::BadgeStatus* badge, u32 badge_type,
void AchievementManager::FetchBadge(AchievementManager::Badge* badge, u32 badge_type,
const AchievementManager::BadgeNameFunction function,
const UpdatedItems callback_data)
{
if (!m_client || !HasAPIToken() || !Config::Get(Config::RA_BADGES_ENABLED))
if (!m_client || !HasAPIToken())
{
m_update_callback(callback_data);
if (m_display_welcome_message && badge_type == RC_IMAGE_TYPE_GAME)
@ -902,40 +1156,71 @@ void AchievementManager::FetchBadge(AchievementManager::BadgeStatus* badge, u32
if (name_to_fetch.empty())
return;
}
rc_api_fetch_image_request_t icon_request = {.image_name = name_to_fetch.c_str(),
.image_type = badge_type};
Badge fetched_badge;
rc_api_request_t api_request;
Common::HttpRequest http_request;
if (rc_api_init_fetch_image_request(&api_request, &icon_request) != RC_OK)
const std::string cache_path = fmt::format(
"{}/badge-{}-{}.png", File::GetUserPath(D_RETROACHIEVEMENTSCACHE_IDX), badge_type,
Common::SHA1::DigestToString(Common::SHA1::CalculateDigest(name_to_fetch)));
AchievementManager::Badge tmp_badge;
if (!LoadPNGTexture(&tmp_badge, cache_path))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid request for image {}.", name_to_fetch);
return;
}
auto http_response = http_request.Get(api_request.url);
if (http_response.has_value() && http_response->size() <= 0)
{
WARN_LOG_FMT(ACHIEVEMENTS, "RetroAchievements connection failed on image request.\n URL: {}",
api_request.url);
rc_api_fetch_image_request_t icon_request = {.image_name = name_to_fetch.c_str(),
.image_type = badge_type};
Badge fetched_badge;
rc_api_request_t api_request;
Common::HttpRequest http_request;
if (rc_api_init_fetch_image_request(&api_request, &icon_request) != RC_OK)
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Invalid request for image {}.", name_to_fetch);
return;
}
auto http_response = http_request.Get(api_request.url, USER_AGENT_HEADER,
Common::HttpRequest::AllowedReturnCodes::All);
if (!http_response.has_value() || http_response->empty())
{
WARN_LOG_FMT(ACHIEVEMENTS,
"RetroAchievements connection failed on image request.\n URL: {}",
api_request.url);
rc_api_destroy_request(&api_request);
m_update_callback(callback_data);
return;
}
rc_api_destroy_request(&api_request);
m_update_callback(callback_data);
return;
INFO_LOG_FMT(ACHIEVEMENTS, "Successfully downloaded badge id {}.", name_to_fetch);
if (!LoadPNGTexture(&tmp_badge, *http_response))
{
ERROR_LOG_FMT(ACHIEVEMENTS, "Badge '{}' failed to load", name_to_fetch);
return;
}
std::string temp_path = fmt::format("{}.tmp", cache_path);
File::IOFile temp_file(temp_path, "wb");
if (!temp_file.IsOpen() ||
!temp_file.WriteBytes(http_response->data(), http_response->size()) ||
!temp_file.Close() || !File::Rename(temp_path, cache_path))
{
File::Delete(temp_path);
WARN_LOG_FMT(ACHIEVEMENTS, "Failed to store badge '{}' to cache", name_to_fetch);
}
}
rc_api_destroy_request(&api_request);
fetched_badge = std::move(*http_response);
INFO_LOG_FMT(ACHIEVEMENTS, "Successfully downloaded badge id {}.", name_to_fetch);
std::lock_guard lg{m_lock};
if (function(*this).empty() || name_to_fetch != function(*this))
{
INFO_LOG_FMT(ACHIEVEMENTS, "Requested outdated badge id {}.", name_to_fetch);
return;
}
badge->badge = std::move(fetched_badge);
badge->name = std::move(name_to_fetch);
*badge = std::move(tmp_badge);
m_update_callback(callback_data);
if (badge_type == RC_IMAGE_TYPE_ACHIEVEMENT &&
m_active_challenges.contains(*callback_data.achievements.begin()))
{
m_challenges_updated = true;
}
});
}

View file

@ -5,23 +5,34 @@
#ifdef USE_RETRO_ACHIEVEMENTS
#include <array>
#include <atomic>
#include <chrono>
#include <ctime>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <string_view>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <rcheevos/include/rc_api_runtime.h>
#include <rcheevos/include/rc_api_user.h>
#include <rcheevos/include/rc_client.h>
#include <rcheevos/include/rc_runtime.h>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
#include "Common/HttpRequest.h"
#include "Common/JsonUtil.h"
#include "Common/Lazy.h"
#include "Common/WorkQueueThread.h"
#include "DiscIO/Volume.h"
#include "VideoCommon/Assets/CustomTextureData.h"
namespace Core
{
@ -29,10 +40,10 @@ class CPUThreadGuard;
class System;
} // namespace Core
namespace OSD
namespace PatchEngine
{
struct Icon;
}
struct Patch;
} // namespace PatchEngine
class AchievementManager
{
@ -47,19 +58,20 @@ public:
using LeaderboardRank = u32;
static constexpr size_t RP_SIZE = 256;
using RichPresence = std::array<char, RP_SIZE>;
using Badge = std::vector<u8>;
using NamedIconMap = std::map<std::string, std::unique_ptr<OSD::Icon>, std::less<>>;
using Badge = VideoCommon::CustomTextureData::ArraySlice::Level;
static constexpr size_t MAX_DISPLAYED_LBOARDS = 4;
struct BadgeStatus
{
std::string name = "";
Badge badge{};
};
static constexpr std::string_view DEFAULT_PLAYER_BADGE_FILENAME = "achievements_player.png";
static constexpr std::string_view DEFAULT_GAME_BADGE_FILENAME = "achievements_game.png";
static constexpr std::string_view DEFAULT_LOCKED_BADGE_FILENAME = "achievements_locked.png";
static constexpr std::string_view DEFAULT_UNLOCKED_BADGE_FILENAME = "achievements_unlocked.png";
static constexpr std::string_view GRAY = "transparent";
static constexpr std::string_view GOLD = "#FFD700";
static constexpr std::string_view BLUE = "#0B71C1";
static constexpr std::string_view APPROVED_LIST_FILENAME = "ApprovedInis.json";
static const inline Common::SHA1::Digest APPROVED_LIST_HASH = {
0x50, 0x2F, 0x58, 0x02, 0x94, 0x60, 0x1B, 0x9F, 0x92, 0xC7,
0x04, 0x17, 0x50, 0x2E, 0xF3, 0x09, 0x8C, 0x8C, 0xD6, 0xC0};
struct LeaderboardEntry
{
@ -96,27 +108,36 @@ public:
bool HasAPIToken() const;
void LoadGame(const std::string& file_path, const DiscIO::Volume* volume);
bool IsGameLoaded() const;
void SetBackgroundExecutionAllowed(bool allowed);
void FetchPlayerBadge();
void FetchGameBadges();
void DoFrame();
bool CanPause();
void DoIdle();
std::recursive_mutex& GetLock();
void SetHardcoreMode();
bool IsHardcoreModeActive() const;
void SetGameIniId(const std::string& game_ini_id) { m_game_ini_id = game_ini_id; }
void FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
const std::string& game_ini_id) const;
void SetSpectatorMode();
std::string_view GetPlayerDisplayName() const;
u32 GetPlayerScore() const;
const BadgeStatus& GetPlayerBadge() const;
const Badge& GetPlayerBadge() const;
std::string_view GetGameDisplayName() const;
rc_client_t* GetClient();
rc_api_fetch_game_data_response_t* GetGameData();
const BadgeStatus& GetGameBadge() const;
const BadgeStatus& GetAchievementBadge(AchievementId id, bool locked) const;
const Badge& GetGameBadge() const;
const Badge& GetAchievementBadge(AchievementId id, bool locked) const;
const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id);
RichPresence GetRichPresence() const;
const NamedIconMap& GetChallengeIcons() const;
bool AreChallengesUpdated() const;
void ResetChallengesUpdated();
const std::unordered_set<AchievementId>& GetActiveChallenges() const;
std::vector<std::string> GetActiveLeaderboards() const;
void DoState(PointerWrap& p);
@ -134,7 +155,7 @@ private:
std::unique_ptr<DiscIO::Volume> volume;
};
const BadgeStatus m_default_badge;
static picojson::value LoadApprovedList();
static void* FilereaderOpenByFilepath(const char* path_utf8);
static void* FilereaderOpenByVolume(const char* path_utf8);
@ -143,12 +164,13 @@ private:
static size_t FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes);
static void FilereaderClose(void* file_handle);
void LoadDefaultBadges();
static void LoginCallback(int result, const char* error_message, rc_client_t* client,
void* userdata);
void FetchBoardInfo(AchievementId leaderboard_id);
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; };
std::unique_ptr<DiscIO::Volume>& GetLoadingVolume() { return m_loading_volume; }
static void LoadGameCallback(int result, const char* error_message, rc_client_t* client,
void* userdata);
@ -176,31 +198,42 @@ private:
static void Request(const rc_api_request_t* request, rc_client_server_callback_t callback,
void* callback_data, rc_client_t* client);
static u32 MemoryVerifier(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
static u32 MemoryPeeker(u32 address, u8* buffer, u32 num_bytes, rc_client_t* client);
void FetchBadge(BadgeStatus* badge, u32 badge_type, const BadgeNameFunction function,
void FetchBadge(Badge* badge, u32 badge_type, const BadgeNameFunction function,
const UpdatedItems callback_data);
static void EventHandler(const rc_client_event_t* event, rc_client_t* client);
rc_runtime_t m_runtime{};
rc_client_t* m_client{};
Core::System* m_system{};
std::atomic<Core::System*> m_system{};
bool m_is_runtime_initialized = false;
UpdateCallback m_update_callback = [](const UpdatedItems&) {};
std::unique_ptr<DiscIO::Volume> m_loading_volume;
BadgeStatus m_player_badge;
Badge m_default_player_badge;
Badge m_default_game_badge;
Badge m_default_unlocked_badge;
Badge m_default_locked_badge;
std::atomic_bool m_background_execution_allowed = true;
Badge m_player_badge;
Hash m_game_hash{};
u32 m_game_id = 0;
rc_api_fetch_game_data_response_t m_game_data{};
bool m_is_game_loaded = false;
BadgeStatus m_game_badge;
Badge m_game_badge;
bool m_display_welcome_message = false;
std::unordered_map<AchievementId, BadgeStatus> m_unlocked_badges;
std::unordered_map<AchievementId, BadgeStatus> m_locked_badges;
std::unordered_map<AchievementId, Badge> m_unlocked_badges;
std::unordered_map<AchievementId, Badge> m_locked_badges;
RichPresence m_rich_presence;
std::chrono::steady_clock::time_point m_last_rp_time = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point m_last_progress_message = std::chrono::steady_clock::now();
Common::Lazy<picojson::value> m_ini_root{LoadApprovedList};
std::string m_game_ini_id;
std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
NamedIconMap m_active_challenges;
bool m_challenges_updated = false;
std::unordered_set<AchievementId> m_active_challenges;
std::vector<rc_client_leaderboard_tracker_t> m_active_leaderboards;
Common::WorkQueueThread<std::function<void()>> m_queue;
@ -209,4 +242,33 @@ private:
std::recursive_mutex m_filereader_lock;
}; // class AchievementManager
#else // USE_RETRO_ACHIEVEMENTS
#include <string>
namespace DiscIO
{
class Volume;
}
class AchievementManager
{
public:
static AchievementManager& GetInstance()
{
static AchievementManager s_instance;
return s_instance;
}
constexpr bool IsHardcoreModeActive() { return false; }
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}
constexpr void DoFrame() {}
constexpr void CloseGame() {}
};
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -18,7 +18,7 @@ class IniFile;
namespace Core
{
class CPUThreadGuard;
};
}
namespace ActionReplay
{

View file

@ -235,7 +235,7 @@ std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(std::vector<std
static const std::unordered_set<std::string> disc_image_extensions = {
{".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz", ".wia", ".rvz", ".nfs", ".dol", ".elf"}};
if (disc_image_extensions.find(extension) != disc_image_extensions.end())
if (disc_image_extensions.contains(extension))
{
std::unique_ptr<DiscIO::VolumeDisc> disc = DiscIO::CreateDisc(path);
if (disc)
@ -575,9 +575,7 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
SetupGCMemory(system, guard);
}
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().LoadGame(executable.path, nullptr);
#endif // USE_RETRO_ACHIEVEMENTS
if (!executable.reader->LoadIntoMemory(system))
{

View file

@ -24,7 +24,7 @@ public:
bool IsValid() const override { return m_is_valid; }
bool IsWii() const override { return m_is_wii; }
bool IsAncast() const { return m_is_ancast; };
bool IsAncast() const { return m_is_ancast; }
u32 GetEntryPoint() const override { return m_dolheader.entryPoint; }
bool LoadIntoMemory(Core::System& system, bool only_in_mem1 = false) const override;
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const override

View file

@ -166,9 +166,7 @@ bool BootCore(Core::System& system, std::unique_ptr<BootParameters> boot,
}
}
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().CloseGame();
#endif // USE_RETRO_ACHIEVEMENTS
const bool load_ipl = !system.IsWii() && !Config::Get(Config::MAIN_SKIP_IPL) &&
std::holds_alternative<BootParameters::Disc>(boot->parameters);
@ -211,8 +209,7 @@ static void RestoreSYSCONF()
},
setting.config_info);
}
// Save the SYSCONF.
Config::GetLayer(Config::LayerType::Base)->Save();
ConfigLoaders::SaveToSYSCONF(Config::LayerType::Base);
}
void RestoreConfig()

View file

@ -485,8 +485,10 @@ add_library(core
PowerPC/BreakPoints.h
PowerPC/CachedInterpreter/CachedInterpreter.cpp
PowerPC/CachedInterpreter/CachedInterpreter.h
PowerPC/CachedInterpreter/InterpreterBlockCache.cpp
PowerPC/CachedInterpreter/InterpreterBlockCache.h
PowerPC/CachedInterpreter/CachedInterpreterBlockCache.cpp
PowerPC/CachedInterpreter/CachedInterpreterBlockCache.h
PowerPC/CachedInterpreter/CachedInterpreterEmitter.cpp
PowerPC/CachedInterpreter/CachedInterpreterEmitter.h
PowerPC/ConditionRegister.cpp
PowerPC/ConditionRegister.h
PowerPC/Expression.cpp
@ -783,7 +785,7 @@ if(MSVC)
endif()
if(USE_RETRO_ACHIEVEMENTS)
target_link_libraries(core PRIVATE rcheevos)
target_compile_definitions(core PRIVATE -DUSE_RETRO_ACHIEVEMENTS)
target_compile_definitions(core PRIVATE -DRC_CLIENT_SUPPORTS_HASH)
target_link_libraries(core PUBLIC rcheevos)
target_compile_definitions(core PUBLIC -DUSE_RETRO_ACHIEVEMENTS)
target_compile_definitions(core PUBLIC -DRC_CLIENT_SUPPORTS_HASH)
endif()

View file

@ -38,7 +38,7 @@ void OnConfigChanged()
}
}
}; // namespace
} // namespace
namespace CPUThreadConfigCallback
{
@ -73,4 +73,4 @@ void CheckForConfigChanges()
RunCallbacks();
}
}; // namespace CPUThreadConfigCallback
} // namespace CPUThreadConfigCallback

View file

@ -27,4 +27,4 @@ void RemoveConfigChangedCallback(ConfigChangedCallbackID callback_id);
// Should be called regularly from the CPU thread
void CheckForConfigChanges();
}; // namespace CPUThreadConfigCallback
} // namespace CPUThreadConfigCallback

View file

@ -207,10 +207,8 @@ Cheats::NewSearch(const Core::CPUThreadGuard& guard,
PowerPC::RequestedAddressSpace address_space, bool aligned,
const std::function<bool(const T& value)>& validator)
{
#ifdef USE_RETRO_ACHIEVEMENTS
if (AchievementManager::GetInstance().IsHardcoreModeActive())
return Cheats::SearchErrorCode::DisabledInHardcoreMode;
#endif // USE_RETRO_ACHIEVEMENTS
auto& system = guard.GetSystem();
std::vector<Cheats::SearchResult<T>> results;
const Core::State core_state = Core::GetState(system);
@ -262,10 +260,8 @@ Cheats::NextSearch(const Core::CPUThreadGuard& guard,
PowerPC::RequestedAddressSpace address_space,
const std::function<bool(const T& new_value, const T& old_value)>& validator)
{
#ifdef USE_RETRO_ACHIEVEMENTS
if (AchievementManager::GetInstance().IsHardcoreModeActive())
return Cheats::SearchErrorCode::DisabledInHardcoreMode;
#endif // USE_RETRO_ACHIEVEMENTS
auto& system = guard.GetSystem();
std::vector<Cheats::SearchResult<T>> results;
const Core::State core_state = Core::GetState(system);
@ -429,10 +425,8 @@ MakeCompareFunctionForLastValue(Cheats::CompareType op)
template <typename T>
Cheats::SearchErrorCode Cheats::CheatSearchSession<T>::RunSearch(const Core::CPUThreadGuard& guard)
{
#ifdef USE_RETRO_ACHIEVEMENTS
if (AchievementManager::GetInstance().IsHardcoreModeActive())
return Cheats::SearchErrorCode::DisabledInHardcoreMode;
#endif // USE_RETRO_ACHIEVEMENTS
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>> result =
Cheats::SearchErrorCode::InvalidParameters;
if (m_filter_type == FilterType::CompareAgainstSpecificValue)

View file

@ -17,7 +17,7 @@
namespace Core
{
class CPUThreadGuard;
};
}
namespace Cheats
{
@ -100,10 +100,8 @@ enum class SearchErrorCode
// currently off in the emulated game.
VirtualAddressesCurrentlyNotAccessible,
#ifdef USE_RETRO_ACHIEVEMENTS
// Cheats and memory reading are disabled in RetroAchievements hardcore mode.
DisabledInHardcoreMode,
#endif // USE_RETRO_ACHIEVEMENTS
};
// Returns the corresponding DataType enum for the value currently held by the given SearchValue.

View file

@ -26,7 +26,6 @@ const Info<bool> RA_DISCORD_PRESENCE_ENABLED{
{System::Achievements, "Achievements", "DiscordPresenceEnabled"}, false};
const Info<bool> RA_PROGRESS_ENABLED{{System::Achievements, "Achievements", "ProgressEnabled"},
false};
const Info<bool> RA_BADGES_ENABLED{{System::Achievements, "Achievements", "BadgesEnabled"}, false};
} // namespace Config
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -20,7 +20,6 @@ extern const Info<bool> RA_ENCORE_ENABLED;
extern const Info<bool> RA_SPECTATOR_ENABLED;
extern const Info<bool> RA_DISCORD_PRESENCE_ENABLED;
extern const Info<bool> RA_PROGRESS_ENABLED;
extern const Info<bool> RA_BADGES_ENABLED;
} // namespace Config
#endif // USE_RETRO_ACHIEVEMENTS

View file

@ -749,22 +749,14 @@ bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot)
bool AreCheatsEnabled()
{
#ifdef USE_RETRO_ACHIEVEMENTS
return Config::Get(::Config::MAIN_ENABLE_CHEATS) &&
!AchievementManager::GetInstance().IsHardcoreModeActive();
#else // USE_RETRO_ACHIEVEMENTS
return Config::Get(::Config::MAIN_ENABLE_CHEATS);
#endif // USE_RETRO_ACHIEVEMENTS
}
bool IsDebuggingEnabled()
{
#ifdef USE_RETRO_ACHIEVEMENTS
return Config::Get(::Config::MAIN_ENABLE_DEBUGGING) &&
!AchievementManager::GetInstance().IsHardcoreModeActive();
#else // USE_RETRO_ACHIEVEMENTS
return Config::Get(::Config::MAIN_ENABLE_DEBUGGING);
#endif // USE_RETRO_ACHIEVEMENTS
}
} // namespace Config

View file

@ -15,7 +15,7 @@ const Info<u32> SYSCONF_LANGUAGE{{System::SYSCONF, "IPL", "LNG"},
const Info<u32> SYSCONF_COUNTRY{{System::SYSCONF, "IPL", "SADR"}, GetDefaultCountry()};
const Info<bool> SYSCONF_WIDESCREEN{{System::SYSCONF, "IPL", "AR"}, true};
const Info<bool> SYSCONF_PROGRESSIVE_SCAN{{System::SYSCONF, "IPL", "PGS"}, true};
const Info<bool> SYSCONF_PAL60{{System::SYSCONF, "IPL", "E60"}, 0x01};
const Info<bool> SYSCONF_PAL60{{System::SYSCONF, "IPL", "E60"}, true};
const Info<u32> SYSCONF_SOUND_MODE{{System::SYSCONF, "IPL", "SND"}, 0x01};
// SYSCONF.BT

View file

@ -175,11 +175,6 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
if (!was_changed)
return;
#ifdef USE_RETRO_ACHIEVEMENTS
if (game_id != "00000000")
AchievementManager::GetInstance().CloseGame();
#endif // USE_RETRO_ACHIEVEMENTS
if (game_id == "00000000")
{
m_title_name.clear();
@ -187,6 +182,8 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
return;
}
AchievementManager::GetInstance().CloseGame();
const Core::TitleDatabase title_database;
auto& system = Core::System::GetInstance();
const DiscIO::Language language = GetLanguageAdjustedForRegion(system.IsWii(), region);
@ -194,22 +191,22 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
m_title_description = title_database.Describe(m_gametdb_id, language);
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
Host_TitleChanged();
if (Core::IsRunning(system))
{
const bool is_running_or_starting = Core::IsRunningOrStarting(system);
if (is_running_or_starting)
Core::UpdateTitle(system);
}
Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision));
if (Core::IsRunning(system))
if (is_running_or_starting)
DolphinAnalytics::Instance().ReportGameStart();
}
void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard& guard)
{
auto& system = guard.GetSystem();
if (!Core::IsRunning(system))
if (!Core::IsRunningOrStarting(system))
return;
auto& ppc_symbol_db = system.GetPPCSymbolDB();
@ -460,7 +457,14 @@ std::string SConfig::GetGameTDBImageRegionCode(bool wii, DiscIO::Region region)
switch (region)
{
case DiscIO::Region::NTSC_J:
{
// Taiwanese games share the Japanese region code however their title ID ends with 'W'.
// GameTDB differentiates the covers using the code "ZH".
if (m_game_id.size() >= 4 && m_game_id.at(3) == 'W')
return "ZH";
return "JA";
}
case DiscIO::Region::NTSC_U:
return "US";
case DiscIO::Region::NTSC_K:

View file

@ -101,10 +101,6 @@ namespace Core
static bool s_wants_determinism;
// Declarations and definitions
static bool s_is_stopping = false;
static bool s_hardware_initialized = false;
static bool s_is_started = false;
static Common::Flag s_is_booting;
static std::thread s_emu_thread;
static std::vector<StateChangedCallbackFunc> s_on_state_changed_callbacks;
@ -114,6 +110,10 @@ static std::atomic<double> s_last_actual_emulation_speed{1.0};
static bool s_frame_step = false;
static std::atomic<bool> s_stop_frame_step;
// The value Paused is never stored in this variable. The core is considered to be in
// the Paused state if this variable is Running and the CPU reports that it's stepping.
static std::atomic<State> s_state = State::Uninitialized;
#ifdef USE_MEMORYWATCHER
static std::unique_ptr<MemoryWatcher> s_memory_watcher;
#endif
@ -190,7 +190,7 @@ std::string StopMessage(bool main_thread, std::string_view message)
void DisplayMessage(std::string message, int time_in_ms)
{
if (!IsRunning(Core::System::GetInstance()))
if (!IsRunningOrStarting(Core::System::GetInstance()))
return;
// Actually displaying non-ASCII could cause things to go pear-shaped
@ -202,12 +202,13 @@ void DisplayMessage(std::string message, int time_in_ms)
bool IsRunning(Core::System& system)
{
return (GetState(system) != State::Uninitialized || s_hardware_initialized) && !s_is_stopping;
return s_state.load() == State::Running;
}
bool IsRunningAndStarted()
bool IsRunningOrStarting(Core::System& system)
{
return s_is_started && !s_is_stopping;
const State state = s_state.load();
return state == State::Running || state == State::Starting;
}
bool IsCPUThread()
@ -262,7 +263,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
g_video_backend->PrepareWindow(prepared_wsi);
// Start the emu thread
s_is_booting.Set();
s_state.store(State::Starting);
s_emu_thread = std::thread(EmuThread, std::ref(system), std::move(boot), prepared_wsi);
return true;
}
@ -281,17 +282,13 @@ static void ResetRumble()
// Called from GUI thread
void Stop(Core::System& system) // - Hammertime!
{
if (const State state = GetState(system);
state == State::Stopping || state == State::Uninitialized)
{
const State state = s_state.load();
if (state == State::Stopping || state == State::Uninitialized)
return;
}
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().CloseGame();
#endif // USE_RETRO_ACHIEVEMENTS
s_is_stopping = true;
s_state.store(State::Stopping);
CallOnStateChangedCallbacks(State::Stopping);
@ -356,7 +353,7 @@ static void CPUSetInitialExecutionState(bool force_paused = false)
// SetState must be called on the host thread, so we defer it for later.
QueueHostJob([force_paused](Core::System& system) {
bool paused = SConfig::GetInstance().bBootToPause || force_paused;
SetState(system, paused ? State::Paused : State::Running);
SetState(system, paused ? State::Paused : State::Running, true, true);
Host_UpdateDisasmDialog();
Host_UpdateMainFrame();
Host_Message(HostMessageID::WMUserCreate);
@ -396,11 +393,15 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
File::Delete(*savestate_path);
}
s_is_started = true;
// If s_state is Starting, change it to Running. But if it's already been set to Stopping
// by the host thread, don't change it.
State expected = State::Starting;
s_state.compare_exchange_strong(expected, State::Running);
{
#ifndef _WIN32
std::string gdb_socket = Config::Get(Config::MAIN_GDB_SOCKET);
if (!gdb_socket.empty())
if (!gdb_socket.empty() && !AchievementManager::GetInstance().IsHardcoreModeActive())
{
GDBStub::InitLocal(gdb_socket.data());
CPUSetInitialExecutionState(true);
@ -409,7 +410,7 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
#endif
{
int gdb_port = Config::Get(Config::MAIN_GDB_PORT);
if (gdb_port > 0)
if (gdb_port > 0 && !AchievementManager::GetInstance().IsHardcoreModeActive())
{
GDBStub::Init(gdb_port);
CPUSetInitialExecutionState(true);
@ -428,8 +429,6 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
s_memory_watcher.reset();
#endif
s_is_started = false;
if (exception_handler)
EMM::UninstallExceptionHandler();
@ -455,12 +454,15 @@ static void FifoPlayerThread(Core::System& system, const std::optional<std::stri
if (auto cpu_core = system.GetFifoPlayer().GetCPUCore())
{
system.GetPowerPC().InjectExternalCPUCore(cpu_core.get());
s_is_started = true;
// If s_state is Starting, change it to Running. But if it's already been set to Stopping
// by the host thread, don't change it.
State expected = State::Starting;
s_state.compare_exchange_strong(expected, State::Running);
CPUSetInitialExecutionState();
system.GetCPU().Run();
s_is_started = false;
system.GetPowerPC().InjectExternalCPUCore(nullptr);
system.GetFifoPlayer().Close();
}
@ -481,10 +483,7 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
{
CallOnStateChangedCallbacks(State::Starting);
Common::ScopeGuard flag_guard{[] {
s_is_booting.Clear();
s_is_started = false;
s_is_stopping = false;
s_wants_determinism = false;
s_state.store(State::Uninitialized);
CallOnStateChangedCallbacks(State::Uninitialized);
@ -562,8 +561,6 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
NetPlay::NetPlay_RegisterEvents();
Common::ScopeGuard hw_guard{[&system] {
// We must set up this flag before executing HW::Shutdown()
s_hardware_initialized = false;
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "Shutting down HW"));
HW::Shutdown(system);
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "HW shutdown"));
@ -607,10 +604,6 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
AudioCommon::PostInitSoundStream(system);
// The hardware is initialized.
s_hardware_initialized = true;
s_is_booting.Clear();
// Set execution state to known values (CPU/FIFO/Audio Paused)
system.GetCPU().Break();
@ -703,24 +696,32 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
// Set or get the running state
void SetState(Core::System& system, State state, bool report_state_change)
void SetState(Core::System& system, State state, bool report_state_change,
bool initial_execution_state)
{
// State cannot be controlled until the CPU Thread is operational
if (!IsRunningAndStarted())
if (s_state.load() != State::Running)
return;
switch (state)
{
case State::Paused:
#ifdef USE_RETRO_ACHIEVEMENTS
if (!initial_execution_state && !AchievementManager::GetInstance().CanPause())
return;
#endif // USE_RETRO_ACHIEVEMENTS
// NOTE: GetState() will return State::Paused immediately, even before anything has
// stopped (including the CPU).
system.GetCPU().EnableStepping(true); // Break
system.GetCPU().SetStepping(true); // Break
Wiimote::Pause();
ResetRumble();
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().DoIdle();
#endif // USE_RETRO_ACHIEVEMENTS
break;
case State::Running:
{
system.GetCPU().EnableStepping(false);
system.GetCPU().SetStepping(false);
Wiimote::Resume();
break;
}
@ -737,21 +738,11 @@ void SetState(Core::System& system, State state, bool report_state_change)
State GetState(Core::System& system)
{
if (s_is_stopping)
return State::Stopping;
if (s_hardware_initialized)
{
if (system.GetCPU().IsStepping())
return State::Paused;
return State::Running;
}
if (s_is_booting.IsSet())
return State::Starting;
return State::Uninitialized;
const State state = s_state.load();
if (state == State::Running && system.GetCPU().IsStepping())
return State::Paused;
else
return state;
}
static std::string GenerateScreenshotFolderPath()
@ -805,7 +796,7 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl
{
// WARNING: PauseAndLock is not fully threadsafe so is only valid on the Host Thread
if (!IsRunningAndStarted())
if (!IsRunning(system))
return true;
bool was_unpaused = true;
@ -833,7 +824,7 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl
// The CPU is responsible for managing the Audio and FIFO state so we use its
// mechanism to unpause them. If we unpaused the systems above when releasing
// the locks then they could call CPU::Break which would require detecting it
// and re-pausing with CPU::EnableStepping.
// and re-pausing with CPU::SetStepping.
was_unpaused = system.GetCPU().PauseAndLock(false, unpause_on_unlock, true);
}
@ -911,9 +902,7 @@ void Callback_NewField(Core::System& system)
}
}
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().DoFrame();
#endif // USE_RETRO_ACHIEVEMENTS
}
void UpdateTitle(Core::System& system)
@ -1034,13 +1023,12 @@ void HostDispatchJobs(Core::System& system)
HostJob job = std::move(s_host_jobs_queue.front());
s_host_jobs_queue.pop();
// NOTE: Memory ordering is important. The booting flag needs to be
// checked first because the state transition is:
// Core::State::Uninitialized: s_is_booting -> s_hardware_initialized
// We need to check variables in the same order as the state
// transition, otherwise we race and get transient failures.
if (!job.run_after_stop && !s_is_booting.IsSet() && !IsRunning(system))
continue;
if (!job.run_after_stop)
{
const State state = s_state.load();
if (state == State::Stopping || state == State::Uninitialized)
continue;
}
guard.unlock();
job.job(system);
@ -1051,13 +1039,11 @@ void HostDispatchJobs(Core::System& system)
// NOTE: Host Thread
void DoFrameStep(Core::System& system)
{
#ifdef USE_RETRO_ACHIEVEMENTS
if (AchievementManager::GetInstance().IsHardcoreModeActive())
{
OSD::AddMessage("Frame stepping is disabled in RetroAchievements hardcore mode");
return;
}
#endif // USE_RETRO_ACHIEVEMENTS
if (GetState(system) == State::Paused)
{
// if already paused, frame advance for 1 frame
@ -1076,7 +1062,8 @@ void UpdateInputGate(bool require_focus, bool require_full_focus)
{
// If the user accepts background input, controls should pass even if an on screen interface is on
const bool focus_passes =
!require_focus || (Host_RendererHasFocus() && !Host_UIBlocksControllerState());
!require_focus ||
((Host_RendererHasFocus() || Host_TASInputHasFocus()) && !Host_UIBlocksControllerState());
// Ignore full focus if we don't require basic focus
const bool full_focus_passes =
!require_focus || !require_full_focus || (focus_passes && Host_RendererHasFullFocus());

View file

@ -135,15 +135,16 @@ void UndeclareAsHostThread();
std::string StopMessage(bool main_thread, std::string_view message);
bool IsRunning(Core::System& system);
bool IsRunningAndStarted(); // is running and the CPU loop has been entered
bool IsCPUThread(); // this tells us whether we are the CPU thread.
bool IsRunningOrStarting(Core::System& system);
bool IsCPUThread(); // this tells us whether we are the CPU thread.
bool IsGPUThread();
bool IsHostThread();
bool WantsDeterminism();
// [NOT THREADSAFE] For use by Host only
void SetState(Core::System& system, State state, bool report_state_change = true);
void SetState(Core::System& system, State state, bool report_state_change = true,
bool initial_execution_state = false);
State GetState(Core::System& system);
void SaveScreenShot();

View file

@ -73,7 +73,7 @@ EventType* CoreTimingManager::RegisterEvent(const std::string& name, TimedCallba
{
// check for existing type with same name.
// we want event type names to remain unique so that we can use them for serialization.
ASSERT_MSG(POWERPC, m_event_types.find(name) == m_event_types.end(),
ASSERT_MSG(POWERPC, !m_event_types.contains(name),
"CoreTiming Event \"{}\" is already registered. Events should only be registered "
"during Init to avoid breaking save states.",
name);
@ -138,7 +138,6 @@ void CoreTimingManager::RefreshConfig()
m_max_variance = std::chrono::duration_cast<DT>(DT_ms(Config::Get(Config::MAIN_TIMING_VARIANCE)));
#ifdef USE_RETRO_ACHIEVEMENTS
if (AchievementManager::GetInstance().IsHardcoreModeActive() &&
Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f &&
Config::Get(Config::MAIN_EMULATION_SPEED) > 0.0f)
@ -147,7 +146,6 @@ void CoreTimingManager::RefreshConfig()
m_emulation_speed = 1.0f;
OSD::AddMessage("Minimum speed is 100% in Hardcore Mode");
}
#endif // USE_RETRO_ACHIEVEMENTS
m_emulation_speed = Config::Get(Config::MAIN_EMULATION_SPEED);
}
@ -305,13 +303,12 @@ void CoreTimingManager::ScheduleExternalEvent(u64 timepoint, EventType* event_ty
void CoreTimingManager::RemoveEvent(EventType* event_type)
{
auto itr = std::remove_if(m_event_queue.begin(), m_event_queue.end(),
[&](const Event& e) { return e.type == event_type; });
const size_t erased =
std::erase_if(m_event_queue, [&](const Event& e) { return e.type == event_type; });
// Removing random items breaks the invariant so we have to re-establish it.
if (itr != m_event_queue.end())
if (erased != 0)
{
m_event_queue.erase(itr, m_event_queue.end());
std::make_heap(m_event_queue.begin(), m_event_queue.end(), std::greater<Event>());
}
}

View file

@ -142,7 +142,7 @@ public:
m_collection_pf.size();
}
std::size_t GetBlacklistSize() const { return m_blacklist_size; }
Phase GetRecordingPhase() const { return m_recording_phase; };
Phase GetRecordingPhase() const { return m_recording_phase; }
// An empty selection in reduction mode can't be reconstructed when loading from a file.
bool CanSave() const { return !(m_recording_phase == Phase::Reduction && m_selection.empty()); }

View file

@ -189,7 +189,6 @@ AutoStepResults CodeTrace::AutoStepping(const Core::CPUThreadGuard& guard, bool
stop_condition = HitType::ACTIVE;
auto& power_pc = guard.GetSystem().GetPowerPC();
power_pc.GetBreakPoints().ClearAllTemporary();
using clock = std::chrono::steady_clock;
clock::time_point timeout = clock::now() + std::chrono::seconds(4);

View file

@ -73,8 +73,8 @@ public:
}
virtual bool IsAlive() const { return true; }
virtual bool IsBreakpoint(u32 /*address*/) const { return false; }
virtual void SetBreakpoint(u32 /*address*/) {}
virtual void ClearBreakpoint(u32 /*address*/) {}
virtual void AddBreakpoint(u32 /*address*/) {}
virtual void RemoveBreakpoint(u32 /*address*/) {}
virtual void ClearAllBreakpoints() {}
virtual void ToggleBreakpoint(u32 /*address*/) {}
virtual void ClearAllMemChecks() {}
@ -99,7 +99,7 @@ public:
virtual u32 GetPC() const { return 0; }
virtual void SetPC(u32 /*address*/) {}
virtual void Step() {}
virtual void RunToBreakpoint() {}
virtual void RunTo(u32 /*address*/) {}
virtual u32 GetColor(const CPUThreadGuard* /*guard*/, u32 /*address*/) const
{
return 0xFFFFFFFF;

View file

@ -13,7 +13,7 @@
namespace Core
{
class CPUThreadGuard;
};
}
namespace Core::Debug
{

View file

@ -20,6 +20,7 @@
#include "Core/Config/MainSettings.h"
#include "Core/Core.h"
#include "Core/Debugger/OSThread.h"
#include "Core/HW/CPU.h"
#include "Core/HW/DSP.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/MMU.h"
@ -30,10 +31,9 @@
void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, Common::Debug::MemoryPatch& patch,
bool store_existing_value)
{
#ifdef USE_RETRO_ACHIEVEMENTS
if (AchievementManager::GetInstance().IsHardcoreModeActive())
return;
#endif // USE_RETRO_ACHIEVEMENTS
if (patch.value.empty())
return;
@ -350,7 +350,7 @@ u32 PPCDebugInterface::ReadInstruction(const Core::CPUThreadGuard& guard, u32 ad
bool PPCDebugInterface::IsAlive() const
{
return Core::IsRunningAndStarted();
return Core::IsRunning(m_system);
}
bool PPCDebugInterface::IsBreakpoint(u32 address) const
@ -358,12 +358,12 @@ bool PPCDebugInterface::IsBreakpoint(u32 address) const
return m_system.GetPowerPC().GetBreakPoints().IsAddressBreakPoint(address);
}
void PPCDebugInterface::SetBreakpoint(u32 address)
void PPCDebugInterface::AddBreakpoint(u32 address)
{
m_system.GetPowerPC().GetBreakPoints().Add(address);
}
void PPCDebugInterface::ClearBreakpoint(u32 address)
void PPCDebugInterface::RemoveBreakpoint(u32 address)
{
m_system.GetPowerPC().GetBreakPoints().Remove(address);
}
@ -375,11 +375,7 @@ void PPCDebugInterface::ClearAllBreakpoints()
void PPCDebugInterface::ToggleBreakpoint(u32 address)
{
auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
if (breakpoints.IsAddressBreakPoint(address))
breakpoints.Remove(address);
else
breakpoints.Add(address);
m_system.GetPowerPC().GetBreakPoints().ToggleBreakPoint(address);
}
void PPCDebugInterface::ClearAllMemChecks()
@ -507,8 +503,11 @@ void PPCDebugInterface::SetPC(u32 address)
m_system.GetPPCState().pc = address;
}
void PPCDebugInterface::RunToBreakpoint()
void PPCDebugInterface::RunTo(u32 address)
{
auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
breakpoints.SetTemporary(address);
m_system.GetCPU().SetStepping(false);
}
std::shared_ptr<Core::NetworkCaptureLogger> PPCDebugInterface::NetworkLogger()

View file

@ -80,8 +80,8 @@ public:
u32 address) const override;
bool IsAlive() const override;
bool IsBreakpoint(u32 address) const override;
void SetBreakpoint(u32 address) override;
void ClearBreakpoint(u32 address) override;
void AddBreakpoint(u32 address) override;
void RemoveBreakpoint(u32 address) override;
void ClearAllBreakpoints() override;
void ToggleBreakpoint(u32 address) override;
void ClearAllMemChecks() override;
@ -100,7 +100,7 @@ public:
u32 GetPC() const override;
void SetPC(u32 address) override;
void Step() override {}
void RunToBreakpoint() override;
void RunTo(u32 address) override;
u32 GetColor(const Core::CPUThreadGuard* guard, u32 address) const override;
std::string_view GetDescription(u32 address) const override;

View file

@ -16,7 +16,7 @@ class PPCSymbolDB;
namespace Core
{
class CPUThreadGuard;
};
}
struct RSOEntry
{

View file

@ -228,7 +228,7 @@ public:
IsPlayingBackFifologWithBrokenEFBCopies = m_parent->m_File->HasBrokenEFBCopies();
// Without this call, we deadlock in initialization in dual core, as the FIFO is disabled and
// thus ClearEfb()'s call to WaitForGPUInactive() never returns
m_parent->m_system.GetCPU().EnableStepping(false);
m_parent->m_system.GetCPU().SetStepping(false);
m_parent->m_CurrentFrame = m_parent->m_FrameRangeStart;
m_parent->LoadMemory();
@ -243,7 +243,7 @@ public:
void SingleStep() override
{
// NOTE: AdvanceFrame() will get stuck forever in Dual Core because the FIFO
// is disabled by CPU::EnableStepping(true) so the frame never gets displayed.
// is disabled by CPU::SetStepping(true) so the frame never gets displayed.
PanicAlertFmtT("Cannot SingleStep the FIFO. Use Frame Advance instead.");
}

View file

@ -46,11 +46,7 @@ void Config::Refresh()
}
camera_config.control_type = ::Config::Get(::Config::FL1_CONTROL_TYPE);
#ifdef USE_RETRO_ACHIEVEMENTS
enabled = ::Config::Get(::Config::FREE_LOOK_ENABLED) &&
!AchievementManager::GetInstance().IsHardcoreModeActive();
#else // USE_RETRO_ACHIEVEMENTS
enabled = ::Config::Get(::Config::FREE_LOOK_ENABLED);
#endif // USE_RETRO_ACHIEVEMENTS
}
} // namespace FreeLook

View file

@ -14,7 +14,7 @@ class PointerWrap;
namespace Core
{
class CPUThreadGuard;
};
}
namespace Gecko
{

View file

@ -6,7 +6,7 @@
namespace Core
{
class CPUThreadGuard;
};
}
namespace HLE_Misc
{

View file

@ -11,7 +11,7 @@
namespace Core
{
class CPUThreadGuard;
};
}
namespace HLE_OS
{

View file

@ -2,11 +2,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HLE/HLE_VarArgs.h"
#include "Core/Core.h"
#include "Core/System.h"
#include "Common/Logging/Log.h"
#include "Core/Core.h"
#include "Core/System.h"
HLE::SystemVABI::VAList::~VAList() = default;
u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const

View file

@ -120,7 +120,7 @@ struct EffectiveAddressSpaceAccessors : Accessors
float ReadF32(const Core::CPUThreadGuard& guard, u32 address) const override
{
return PowerPC::MMU::HostRead_F32(guard, address);
};
}
bool Matches(const Core::CPUThreadGuard& guard, u32 haystack_start, const u8* needle_start,
std::size_t needle_size) const

View file

@ -85,22 +85,20 @@ void CPUManager::Run()
m_state_cpu_thread_active = true;
state_lock.unlock();
// Adjust PC for JIT when debugging
// Adjust PC when debugging
// SingleStep so that the "continue", "step over" and "step out" debugger functions
// work when the PC is at a breakpoint at the beginning of the block
// Don't use PowerPCManager::CheckBreakPoints, otherwise you get double logging
// If watchpoints are enabled, any instruction could be a breakpoint.
if (power_pc.GetMode() != PowerPC::CoreMode::Interpreter)
if (power_pc.GetBreakPoints().IsAddressBreakPoint(power_pc.GetPPCState().pc) ||
power_pc.GetMemChecks().HasAny())
{
if (power_pc.GetBreakPoints().IsAddressBreakPoint(power_pc.GetPPCState().pc) ||
power_pc.GetMemChecks().HasAny())
{
m_state = State::Stepping;
PowerPC::CoreMode old_mode = power_pc.GetMode();
power_pc.SetMode(PowerPC::CoreMode::Interpreter);
power_pc.SingleStep();
power_pc.SetMode(old_mode);
m_state = State::Running;
}
m_state = State::Stepping;
PowerPC::CoreMode old_mode = power_pc.GetMode();
power_pc.SetMode(PowerPC::CoreMode::Interpreter);
power_pc.SingleStep();
power_pc.SetMode(old_mode);
m_state = State::Running;
}
// Enter a fast runloop
@ -174,7 +172,7 @@ void CPUManager::Run()
// Requires holding m_state_change_lock
void CPUManager::RunAdjacentSystems(bool running)
{
// NOTE: We're assuming these will not try to call Break or EnableStepping.
// NOTE: We're assuming these will not try to call Break or SetStepping.
m_system.GetFifo().EmulatorState(running);
// Core is responsible for shutting down the sound stream.
if (m_state != State::PowerDown)
@ -243,11 +241,13 @@ bool CPUManager::SetStateLocked(State s)
{
if (m_state == State::PowerDown)
return false;
if (s == State::Stepping)
m_system.GetPowerPC().GetBreakPoints().ClearTemporary();
m_state = s;
return true;
}
void CPUManager::EnableStepping(bool stepping)
void CPUManager::SetStepping(bool stepping)
{
std::lock_guard stepping_lock(m_stepping_lock);
std::unique_lock state_lock(m_state_change_lock);
@ -290,7 +290,7 @@ void CPUManager::Break()
void CPUManager::Continue()
{
EnableStepping(false);
SetStepping(false);
Core::CallOnStateChangedCallbacks(Core::State::Running);
}

View file

@ -62,10 +62,10 @@ public:
void StepOpcode(Common::Event* event = nullptr);
// Enable or Disable Stepping. [Will deadlock if called from a system thread]
void EnableStepping(bool stepping);
void SetStepping(bool stepping);
// Breakpoint activation for system threads. Similar to EnableStepping(true).
// NOTE: Unlike EnableStepping, this does NOT synchronize with the CPU Thread
// Breakpoint activation for system threads. Similar to SetStepping(true).
// NOTE: Unlike SetStepping, this does NOT synchronize with the CPU Thread
// which enables it to avoid deadlocks but also makes it less safe so it
// should not be used by the Host.
void Break();
@ -91,7 +91,7 @@ public:
// Return value for do_lock == true is whether the state was State::Running or not.
// Return value for do_lock == false is whether the state was changed *to* State::Running or not.
// Cannot be used by System threads as it will deadlock. It is threadsafe otherwise.
// "control_adjacent" causes PauseAndLock to behave like EnableStepping by modifying the
// "control_adjacent" causes PauseAndLock to behave like SetStepping by modifying the
// state of the Audio and FIFO subsystems as well.
bool PauseAndLock(bool do_lock, bool unpause_on_unlock = true, bool control_adjacent = false);
@ -110,9 +110,9 @@ private:
// Read access is unsynchronized.
State m_state = State::PowerDown;
// Synchronizes EnableStepping and PauseAndLock so only one instance can be
// Synchronizes SetStepping and PauseAndLock so only one instance can be
// active at a time. Simplifies code by eliminating several edge cases where
// the EnableStepping(true)/PauseAndLock(true) case must release the state lock
// the SetStepping(true)/PauseAndLock(true) case must release the state lock
// and wait for the CPU Thread which would otherwise require additional flags.
// NOTE: When using the stepping lock, it must always be acquired first. If
// the lock is acquired after the state lock then that is guaranteed to

View file

@ -143,7 +143,7 @@ protected:
pb_mem[update_off] = update_val;
}
Common::BitCastFromArray<u16>(pb_mem, pb);
pb = std::bit_cast<PBType>(pb_mem);
}
virtual void HandleCommandList();

View file

@ -397,7 +397,7 @@ bool AXWiiUCode::ExtractUpdatesFields(AXPBWii& pb, u16* num_updates, u16* update
// Remove the updates data from the PB
memmove(&pb_mem[41], &pb_mem[46], sizeof(pb) - 2 * 46);
Common::BitCastFromArray<u16>(pb_mem, pb);
pb = std::bit_cast<AXPBWii>(pb_mem);
return true;
}
@ -416,7 +416,7 @@ void AXWiiUCode::ReinjectUpdatesFields(AXPBWii& pb, u16* num_updates, u32 update
pb_mem[44] = updates_addr >> 16;
pb_mem[45] = updates_addr & 0xFFFF;
Common::BitCastFromArray<u16>(pb_mem, pb);
pb = std::bit_cast<AXPBWii>(pb_mem);
}
void AXWiiUCode::ProcessPBList(u32 pb_addr)

View file

@ -7,6 +7,7 @@
#include <array>
#include <map>
#include "Common/BitField.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
#include "Common/Logging/Log.h"
@ -803,8 +804,13 @@ struct ZeldaAudioRenderer::VPB
// can be used for future linear interpolation.
s16 resample_buffer[4];
// TODO: document and implement.
s16 prev_input_samples[0x18];
s16 variable_fir_history[20];
// Biquad filter history.
s16 biquad_xn1;
s16 biquad_xn2;
s16 biquad_yn1;
s16 biquad_yn2;
// Values from the last decoded AFC block. The last two values are
// especially important since AFC decoding - as a variant of ADPCM -
@ -813,7 +819,12 @@ struct ZeldaAudioRenderer::VPB
s16 afc_remaining_samples[0x10];
s16* AFCYN2() { return &afc_remaining_samples[0xE]; }
s16* AFCYN1() { return &afc_remaining_samples[0xF]; }
u16 unk_68_80[0x80 - 0x68];
// Low-pass filter history.
s16 low_pass_yn1;
s16 low_pass_xn1;
u16 unk_6A_80[0x80 - 0x6A];
enum SamplesSourceType
{
@ -861,7 +872,11 @@ struct ZeldaAudioRenderer::VPB
s16 loop_yn1;
s16 loop_yn2;
u16 unk_84;
union
{
BitField<0, 5, u16> variable_fir_filter_size;
BitField<5, 1, u16> enable_biquad_filter;
};
// If true, ramp down quickly to a volume of zero, and end the voice (by
// setting VPB[1] done) when it reaches zero.
@ -890,6 +905,20 @@ struct ZeldaAudioRenderer::VPB
u16 base_address_l;
DEFINE_32BIT_ACCESSOR(base_address, BaseAddress)
u16 unk_8E;
u16 unk_8F;
u16 variable_fir_coeffs[20];
// Biquad filter coefficients.
s16 biquad_bn1;
s16 biquad_bn2;
s16 biquad_an1;
s16 biquad_an2;
// Low-pass filter coefficient.
u16 low_pass_coeff;
u16 padding[0xC0];
// These next two functions are terrible hacks used in order to support two
@ -1173,6 +1202,62 @@ ZeldaAudioRenderer::MixingBuffer* ZeldaAudioRenderer::BufferForID(u16 buffer_id)
}
}
void ZeldaAudioRenderer::ApplyLowPassFilter(MixingBuffer* buf, VPB* vpb)
{
s32 yn1 = vpb->reset_vpb ? 0 : vpb->low_pass_yn1;
s32 xn1 = vpb->reset_vpb ? 0 : vpb->low_pass_xn1;
// 9.7 format I think.
s32 coeff = vpb->low_pass_coeff;
for (int i = 0; i < 0x50; ++i)
{
s32 xn0 = (*buf)[i];
s64 tmp = xn0 - xn1;
tmp *= coeff;
tmp >>= 7;
tmp += yn1;
s16 yn0 = std::clamp<s64>(tmp, -0x8000, 0x7FFF);
(*buf)[i] = yn0;
yn1 = yn0;
xn1 = xn0;
}
vpb->low_pass_yn1 = yn1;
vpb->low_pass_xn1 = xn1;
}
void ZeldaAudioRenderer::ApplyBiquadFilter(MixingBuffer* buf, VPB* vpb)
{
s32 xn1 = vpb->biquad_xn1;
s32 xn2 = vpb->biquad_xn2;
s32 yn1 = vpb->biquad_yn1;
s32 yn2 = vpb->biquad_yn2;
for (int i = 0; i < 0x50; ++i)
{
s32 xn0 = (*buf)[i];
s64 tmp = 0;
tmp += vpb->biquad_bn1 * xn1;
tmp += vpb->biquad_bn2 * xn2;
tmp += vpb->biquad_an1 * yn1;
tmp += vpb->biquad_an2 * yn2;
s16 yn0 = std::clamp<s64>(tmp >> 15, -0x8000, 0x7FFF);
(*buf)[i] = yn0;
xn2 = xn1;
xn1 = xn0;
yn2 = yn1;
yn1 = yn0;
}
vpb->biquad_xn1 = xn1;
vpb->biquad_xn2 = xn2;
vpb->biquad_yn1 = yn1;
vpb->biquad_yn2 = yn2;
}
void ZeldaAudioRenderer::AddVoice(u16 voice_id)
{
VPB vpb;
@ -1184,9 +1269,23 @@ void ZeldaAudioRenderer::AddVoice(u16 voice_id)
MixingBuffer input_samples;
LoadInputSamples(&input_samples, &vpb);
// TODO: In place effects.
if (vpb.low_pass_coeff != 0)
{
ApplyLowPassFilter(&input_samples, &vpb);
}
// TODO: IIR filter.
#ifdef STRICT_ZELDA_HLE
if (vpb.variable_fir_filter_size != 0)
{
ERROR_LOG_FMT(DSPHLE, "TODO: variable FIR filter of size {}", vpb.variable_fir_filter_size);
}
#endif
if (vpb.enable_biquad_filter && (vpb.biquad_an2 != 0 || vpb.biquad_an1 != 0 ||
vpb.biquad_bn2 != 0 || vpb.biquad_bn1 != 0x7FFF))
{
ApplyBiquadFilter(&input_samples, &vpb);
}
if (vpb.use_dolby_volume)
{
@ -1407,12 +1506,13 @@ void ZeldaAudioRenderer::LoadInputSamples(MixingBuffer* buffer, VPB* vpb)
else
shift = 2;
u32 mask = (1 << shift) - 1;
u32 ratio = vpb->resampling_ratio << (shift - 1);
u32 pos = vpb->current_pos_frac << shift;
for (s16& sample : *buffer)
{
sample = ((pos >> 16) & mask) ? 0xC000 : 0x4000;
pos += vpb->resampling_ratio;
pos += ratio;
}
vpb->current_pos_frac = (pos >> shift) & 0xFFFF;
break;

View file

@ -185,6 +185,9 @@ private:
// behavior.
void DownloadRawSamplesFromMRAM(s16* dst, VPB* vpb, u16 requested_samples_count);
void ApplyLowPassFilter(MixingBuffer* buf, VPB* vpb);
void ApplyBiquadFilter(MixingBuffer* buf, VPB* vpb);
// Applies the reverb effect to Dolby mixed voices based on a set of
// per-buffer parameters. Is called twice: once before frame rendering and
// once after.

View file

@ -391,16 +391,14 @@ void DVDInterface::SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
if (auto_disc_change_paths)
{
ASSERT_MSG(DISCIO, (*auto_disc_change_paths).size() != 1,
ASSERT_MSG(DISCIO, auto_disc_change_paths->size() != 1,
"Cannot automatically change between one disc");
m_auto_disc_change_paths = *auto_disc_change_paths;
m_auto_disc_change_index = 0;
}
#ifdef USE_RETRO_ACHIEVEMENTS
AchievementManager::GetInstance().LoadGame("", disc.get());
#endif // USE_RETRO_ACHIEVEMENTS
// Assume that inserting a disc requires having an empty disc before
if (had_disc != has_disc)

View file

@ -43,7 +43,7 @@ static bool IsSoundFile(const std::string& filename)
".str", // Harry Potter & the Sorcerer's Stone
};
return extensions.find(extension) != extensions.end();
return extensions.contains(extension);
}
FileLogger::FileLogger() = default;

View file

@ -2,13 +2,13 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/EXI/BBA/TAP_Win32.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
#include "Common/Assert.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Core/HW/EXI/EXI_Device.h"
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
namespace Win32TAPHelper
{

View file

@ -42,7 +42,7 @@ distribution.
namespace ExpansionInterface
{
enum class Slot : int;
};
}
using CardFlashId = std::array<u8, 12>;

View file

@ -1,6 +1,8 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
#include <cstring>
#include <optional>
#include <type_traits>
@ -9,7 +11,6 @@
#include "Common/BitUtils.h"
#include "Common/CommonTypes.h"
#include "Core/HW/WiimoteEmu/DesiredWiimoteState.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/DrawsomeTablet.h"
#include "Core/HW/WiimoteEmu/Extension/Drums.h"

View file

@ -72,7 +72,7 @@ public:
using TriggerRawValue = ControllerEmu::RawValue<TriggerType, TRIGGER_BITS>;
// 6-bit X and Y values (0-63)
auto GetLeftStick() const { return LeftStickRawValue{StickType(lx, ly)}; };
auto GetLeftStick() const { return LeftStickRawValue{StickType(lx, ly)}; }
void SetLeftStick(const StickType& value)
{
lx = value.x;
@ -82,7 +82,7 @@ public:
auto GetRightStick() const
{
return RightStickRawValue{StickType(rx1 | rx2 << 1 | rx3 << 3, ry)};
};
}
void SetRightStick(const StickType& value)
{
rx1 = value.x & 0b1;

View file

@ -30,7 +30,7 @@ struct MPI : mbedtls_mpi
MPI() { mbedtls_mpi_init(this); }
~MPI() { mbedtls_mpi_free(this); }
mbedtls_mpi* Data() { return this; };
mbedtls_mpi* Data() { return this; }
template <std::size_t N>
bool ReadBinary(const u8 (&in_data)[N])
@ -719,7 +719,7 @@ void MotionPlus::ReversePassthroughModifications(PassthroughMode mode, u8* data)
// This is an overwritten unused button bit on the Classic Controller.
// Note it's a significant bit on the DJ Hero Turntable. (passthrough not feasible)
Common::SetBit<0>(data[4], 1);
Common::SetBit<0>(data[4], true);
}
}

View file

@ -1000,7 +1000,7 @@ bool IsBalanceBoardName(const std::string& name)
bool IsNewWiimote(const std::string& identifier)
{
std::lock_guard lk(s_known_ids_mutex);
return s_known_ids.count(identifier) == 0;
return !s_known_ids.contains(identifier);
}
void HandleWiimoteSourceChange(unsigned int index)

View file

@ -53,6 +53,7 @@ bool Host_UIBlocksControllerState();
bool Host_RendererHasFocus();
bool Host_RendererHasFullFocus();
bool Host_RendererIsFullscreen();
bool Host_TASInputHasFocus();
void Host_Message(HostMessageID id);
void Host_PPCSymbolsChanged();

View file

@ -38,6 +38,9 @@ constexpr std::array<const char*, NUM_HOTKEYS> s_hotkey_labels{{
_trans("Center Mouse"),
_trans("Activate NetPlay Chat"),
_trans("Control NetPlay Golf Mode"),
#ifdef USE_RETRO_ACHIEVEMENTS
_trans("Open Achievements"),
#endif // USE_RETRO_ACHIEVEMENTS
_trans("Volume Down"),
_trans("Volume Up"),
@ -330,7 +333,11 @@ struct HotkeyGroupInfo
};
constexpr std::array<HotkeyGroupInfo, NUM_HOTKEY_GROUPS> s_groups_info = {
#ifdef USE_RETRO_ACHIEVEMENTS
{{_trans("General"), HK_OPEN, HK_OPEN_ACHIEVEMENTS},
#else // USE_RETRO_ACHIEVEMENTS
{{_trans("General"), HK_OPEN, HK_REQUEST_GOLF_CONTROL},
#endif // USE_RETRO_ACHIEVEMENTS
{_trans("Volume"), HK_VOLUME_DOWN, HK_VOLUME_TOGGLE_MUTE},
{_trans("Emulation Speed"), HK_DECREASE_EMULATION_SPEED, HK_TOGGLE_THROTTLE},
{_trans("Frame Advance"), HK_FRAME_ADVANCE, HK_FRAME_ADVANCE_RESET_SPEED},
@ -448,6 +455,9 @@ void HotkeyManager::LoadDefaults(const ControllerInterface& ciface)
set_key_expression(HK_STOP, "Escape");
set_key_expression(HK_FULLSCREEN, hotkey_string({"Alt", "Return"}));
#endif
#ifdef USE_RETRO_ACHIEVEMENTS
set_key_expression(HK_OPEN_ACHIEVEMENTS, hotkey_string({"Alt", "A"}));
#endif // USE_RETRO_ACHIEVEMENTS
set_key_expression(HK_STEP, "F11");
set_key_expression(HK_STEP_OVER, hotkey_string({"Shift", "F10"}));
set_key_expression(HK_STEP_OUT, hotkey_string({"Shift", "F11"}));

View file

@ -32,6 +32,9 @@ enum Hotkey
HK_CENTER_MOUSE,
HK_ACTIVATE_CHAT,
HK_REQUEST_GOLF_CONTROL,
#ifdef USE_RETRO_ACHIEVEMENTS
HK_OPEN_ACHIEVEMENTS,
#endif // USE_RETRO_ACHIEVEMENTS
HK_VOLUME_DOWN,
HK_VOLUME_UP,

View file

@ -29,6 +29,7 @@ enum ReturnCode : s32
IPC_EMAX = -5, // Too many file descriptors open
IPC_ENOENT = -6, // File not found
IPC_EQUEUEFULL = -8, // Queue full
IPC_UNKNOWN = -9, // Unknown
IPC_EIO = -12, // ECC error
IPC_ENOMEM = -22, // Alloc failed during request
FS_EINVAL = -101, // Invalid path

View file

@ -112,9 +112,9 @@ ESCore::~ESCore() = default;
ESDevice::ESDevice(EmulationKernel& ios, ESCore& core, const std::string& device_name)
: EmulationDevice(ios, device_name), m_core(core)
{
if (Core::IsRunningAndStarted())
auto& system = ios.GetSystem();
if (Core::IsRunning(system))
{
auto& system = ios.GetSystem();
auto& core_timing = system.GetCoreTiming();
core_timing.RemoveEvent(s_finish_init_event);
core_timing.ScheduleEvent(GetESBootTicks(ios.GetVersion()), s_finish_init_event);
@ -446,7 +446,7 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
}
const u64 required_ios = tmd.GetIOSId();
if (!Core::IsRunningAndStarted())
if (!Core::IsRunning(system))
return LaunchTitle(required_ios, HangPPC::Yes);
core_timing.RemoveEvent(s_reload_ios_for_ppc_launch_event);
core_timing.ScheduleEvent(ticks, s_reload_ios_for_ppc_launch_event, required_ios);
@ -475,14 +475,12 @@ bool ESDevice::LaunchPPCTitle(u64 title_id)
return false;
m_pending_ppc_boot_content_path = m_core.GetContentPath(tmd.GetTitleId(), content);
if (!Core::IsRunningAndStarted())
if (!Core::IsRunning(system))
return BootstrapPPC();
#ifdef USE_RETRO_ACHIEVEMENTS
INFO_LOG_FMT(ACHIEVEMENTS,
"WAD and NAND formats not currently supported by Achievement Manager.");
AchievementManager::GetInstance().CloseGame();
#endif // USE_RETRO_ACHIEVEMENTS
core_timing.RemoveEvent(s_bootstrap_ppc_for_launch_event);
core_timing.ScheduleEvent(ticks, s_bootstrap_ppc_for_launch_event);
@ -1051,7 +1049,7 @@ ReturnCode ESCore::WriteNewCertToStore(const ES::CertReader& cert)
{
const std::map<std::string, ES::CertReader> certs = ES::ParseCertChain(current_store);
// The cert is already present in the store. Nothing to do.
if (certs.find(cert.GetName()) != certs.end())
if (certs.contains(cert.GetName()))
return IPC_SUCCESS;
}

View file

@ -340,7 +340,7 @@ bool ESCore::FinishImport(const ES::TMDReader& tmd)
// There should not be any directory in there. Remove it.
if (fs->ReadDirectory(PID_KERNEL, PID_KERNEL, absolute_path))
fs->Delete(PID_KERNEL, PID_KERNEL, absolute_path);
else if (expected_entries.find(name) == expected_entries.end())
else if (!expected_entries.contains(name))
fs->Delete(PID_KERNEL, PID_KERNEL, absolute_path);
}

View file

@ -290,7 +290,7 @@ void HostFileSystem::DoStateRead(PointerWrap& p, std::string start_directory_pat
File::CreateDir(path);
// now restore from the stream
while (1)
while (true)
{
char type = 0;
p.Do(type);

View file

@ -81,6 +81,12 @@ Result<FileHandle> HostFileSystem::OpenFile(Uid, Gid, const std::string& path, M
return ResultCode::NoFreeHandle;
const std::string host_path = BuildFilename(path).host_path;
if (File::IsDirectory(host_path))
{
*handle = Handle{};
return ResultCode::Invalid;
}
if (!File::IsFile(host_path))
{
*handle = Handle{};

View file

@ -518,7 +518,7 @@ bool EmulationKernel::BootIOS(const u64 ios_title_id, HangPPC hang_ppc,
if (hang_ppc == HangPPC::Yes)
ResetAndPausePPC(m_system);
if (Core::IsRunningAndStarted())
if (Core::IsRunning(m_system))
{
m_system.GetCoreTiming().ScheduleEvent(GetIOSBootTicks(GetVersion()), s_event_finish_ios_boot,
ios_title_id);
@ -533,7 +533,7 @@ bool EmulationKernel::BootIOS(const u64 ios_title_id, HangPPC hang_ppc,
void EmulationKernel::InitIPC()
{
if (!Core::IsRunning(m_system))
if (Core::GetState(m_system) == Core::State::Uninitialized)
return;
INFO_LOG_FMT(IOS, "IPC initialised.");

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