mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-09-11 20:15:48 +00:00
Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-master2
This commit is contained in:
commit
86f7aa4e6a
128 changed files with 5247 additions and 4422 deletions
|
@ -101,6 +101,10 @@ void Host_PPCSymbolsChanged()
|
|||
{
|
||||
}
|
||||
|
||||
void Host_PPCBreakpointsChanged()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_RefreshDSPDebuggerWindow()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ struct Symbol
|
|||
|
||||
std::string name;
|
||||
std::string function_name; // stripped function name
|
||||
std::string object_name; // name of object/source file symbol belongs to
|
||||
std::vector<SCall> callers; // addresses of functions that call this function
|
||||
std::vector<SCall> calls; // addresses of functions that are called by this function
|
||||
u32 hash = 0; // use for HLE function finding
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "Common/Assert.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonPaths.h"
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/Image.h"
|
||||
|
@ -23,10 +24,12 @@
|
|||
#include "Common/ScopeGuard.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Config/FreeLookSettings.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/GeckoCode.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/VideoInterface.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
|
@ -64,6 +67,7 @@ void AchievementManager::Init()
|
|||
[](const char* message, const rc_client_t* client) {
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "{}", message);
|
||||
});
|
||||
Config::AddConfigChangedCallback([this] { SetHardcoreMode(); });
|
||||
SetHardcoreMode();
|
||||
m_queue.Reset("AchievementManagerQueue", [](const std::function<void()>& func) { func(); });
|
||||
m_image_queue.Reset("AchievementManagerImageQueue",
|
||||
|
@ -368,7 +372,6 @@ void AchievementManager::SetHardcoreMode()
|
|||
if (Config::Get(Config::MAIN_EMULATION_SPEED) < 1.0f)
|
||||
Config::SetBaseOrCurrent(Config::MAIN_EMULATION_SPEED, 1.0f);
|
||||
Config::SetBaseOrCurrent(Config::FREE_LOOK_ENABLED, false);
|
||||
Config::SetBaseOrCurrent(Config::MAIN_ENABLE_CHEATS, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -382,10 +385,11 @@ 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
|
||||
template <typename T>
|
||||
void AchievementManager::FilterApprovedIni(std::vector<T>& codes,
|
||||
const std::string& game_ini_id) const
|
||||
{
|
||||
if (patches.empty())
|
||||
if (codes.empty())
|
||||
{
|
||||
// There's nothing to verify, so let's save ourselves some work
|
||||
return;
|
||||
|
@ -396,46 +400,120 @@ void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>&
|
|||
if (!IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
// Approved codes list failed to hash
|
||||
if (!m_ini_root->is<picojson::value::object>())
|
||||
{
|
||||
codes.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& code : codes)
|
||||
{
|
||||
if (code.enabled && !CheckApprovedCode(code, game_ini_id))
|
||||
code.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool AchievementManager::CheckApprovedCode(const T& code, const std::string& game_ini_id) const
|
||||
{
|
||||
if (!IsHardcoreModeActive())
|
||||
return true;
|
||||
|
||||
// Approved codes list failed to hash
|
||||
if (!m_ini_root->is<picojson::value::object>())
|
||||
return false;
|
||||
|
||||
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 code {}", code.name);
|
||||
|
||||
bool verified = false;
|
||||
|
||||
if (known_id)
|
||||
{
|
||||
INFO_LOG_FMT(ACHIEVEMENTS, "Verifying patch {}", patch_itr->name);
|
||||
auto digest = GetCodeHash(code);
|
||||
|
||||
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++;
|
||||
}
|
||||
verified = m_ini_root->get(game_ini_id).contains(Common::SHA1::DigestToString(digest));
|
||||
}
|
||||
|
||||
if (!verified)
|
||||
{
|
||||
OSD::AddMessage(fmt::format("Failed to verify code {} from file {}.", code.name, game_ini_id),
|
||||
OSD::Duration::VERY_LONG, OSD::Color::RED);
|
||||
OSD::AddMessage("Disable hardcore mode to enable this code.", OSD::Duration::VERY_LONG,
|
||||
OSD::Color::RED);
|
||||
}
|
||||
return verified;
|
||||
}
|
||||
|
||||
Common::SHA1::Digest AchievementManager::GetCodeHash(const PatchEngine::Patch& patch) const
|
||||
{
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(patch.entries.size())));
|
||||
for (const auto& entry : patch.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));
|
||||
}
|
||||
return context->Finish();
|
||||
}
|
||||
|
||||
Common::SHA1::Digest AchievementManager::GetCodeHash(const Gecko::GeckoCode& code) const
|
||||
{
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.codes.size())));
|
||||
for (const auto& entry : code.codes)
|
||||
{
|
||||
context->Update(Common::BitCastToArray<u8>(entry.address));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.data));
|
||||
}
|
||||
return context->Finish();
|
||||
}
|
||||
|
||||
Common::SHA1::Digest AchievementManager::GetCodeHash(const ActionReplay::ARCode& code) const
|
||||
{
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.ops.size())));
|
||||
for (const auto& entry : code.ops)
|
||||
{
|
||||
context->Update(Common::BitCastToArray<u8>(entry.cmd_addr));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.value));
|
||||
}
|
||||
return context->Finish();
|
||||
}
|
||||
|
||||
void AchievementManager::FilterApprovedPatches(std::vector<PatchEngine::Patch>& patches,
|
||||
const std::string& game_ini_id) const
|
||||
{
|
||||
FilterApprovedIni(patches, game_ini_id);
|
||||
}
|
||||
|
||||
void AchievementManager::FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes,
|
||||
const std::string& game_ini_id) const
|
||||
{
|
||||
FilterApprovedIni(codes, game_ini_id);
|
||||
}
|
||||
|
||||
void AchievementManager::FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes,
|
||||
const std::string& game_ini_id) const
|
||||
{
|
||||
FilterApprovedIni(codes, game_ini_id);
|
||||
}
|
||||
|
||||
bool AchievementManager::CheckApprovedGeckoCode(const Gecko::GeckoCode& code,
|
||||
const std::string& game_ini_id) const
|
||||
{
|
||||
return CheckApprovedCode(code, game_ini_id);
|
||||
}
|
||||
|
||||
bool AchievementManager::CheckApprovedARCode(const ActionReplay::ARCode& code,
|
||||
const std::string& game_ini_id) const
|
||||
{
|
||||
return CheckApprovedCode(code, game_ini_id);
|
||||
}
|
||||
|
||||
void AchievementManager::SetSpectatorMode()
|
||||
|
|
|
@ -45,6 +45,16 @@ namespace PatchEngine
|
|||
struct Patch;
|
||||
} // namespace PatchEngine
|
||||
|
||||
namespace Gecko
|
||||
{
|
||||
class GeckoCode;
|
||||
} // namespace Gecko
|
||||
|
||||
namespace ActionReplay
|
||||
{
|
||||
struct ARCode;
|
||||
} // namespace ActionReplay
|
||||
|
||||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
|
@ -70,8 +80,8 @@ public:
|
|||
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};
|
||||
0xA4, 0x98, 0x59, 0x23, 0x10, 0x56, 0x45, 0x30, 0xA9, 0xC5,
|
||||
0x68, 0x5A, 0xB6, 0x47, 0x67, 0xF8, 0xF0, 0x7D, 0x1D, 0x14};
|
||||
|
||||
struct LeaderboardEntry
|
||||
{
|
||||
|
@ -125,8 +135,16 @@ public:
|
|||
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 FilterApprovedGeckoCodes(std::vector<Gecko::GeckoCode>& codes,
|
||||
const std::string& game_ini_id) const;
|
||||
void FilterApprovedARCodes(std::vector<ActionReplay::ARCode>& codes,
|
||||
const std::string& game_ini_id) const;
|
||||
bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code, const std::string& game_ini_id) const;
|
||||
bool CheckApprovedARCode(const ActionReplay::ARCode& code, const std::string& game_ini_id) const;
|
||||
|
||||
void SetSpectatorMode();
|
||||
std::string_view GetPlayerDisplayName() const;
|
||||
u32 GetPlayerScore() const;
|
||||
|
@ -181,6 +199,14 @@ private:
|
|||
void* userdata);
|
||||
void DisplayWelcomeMessage();
|
||||
|
||||
template <typename T>
|
||||
void FilterApprovedIni(std::vector<T>& codes, const std::string& game_ini_id) const;
|
||||
template <typename T>
|
||||
bool CheckApprovedCode(const T& code, const std::string& game_ini_id) const;
|
||||
Common::SHA1::Digest GetCodeHash(const PatchEngine::Patch& patch) const;
|
||||
Common::SHA1::Digest GetCodeHash(const Gecko::GeckoCode& code) const;
|
||||
Common::SHA1::Digest GetCodeHash(const ActionReplay::ARCode& code) const;
|
||||
|
||||
static void LeaderboardEntriesCallback(int result, const char* error_message,
|
||||
rc_client_leaderboard_entry_list_t* list,
|
||||
rc_client_t* client, void* userdata);
|
||||
|
@ -249,11 +275,21 @@ private:
|
|||
|
||||
#include <string>
|
||||
|
||||
namespace ActionReplay
|
||||
{
|
||||
struct ARCode;
|
||||
}
|
||||
|
||||
namespace DiscIO
|
||||
{
|
||||
class Volume;
|
||||
}
|
||||
|
||||
namespace Gecko
|
||||
{
|
||||
class GeckoCode;
|
||||
}
|
||||
|
||||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
|
@ -265,6 +301,18 @@ public:
|
|||
|
||||
constexpr bool IsHardcoreModeActive() { return false; }
|
||||
|
||||
constexpr bool CheckApprovedGeckoCode(const Gecko::GeckoCode& code,
|
||||
const std::string& game_ini_id)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
constexpr bool CheckApprovedARCode(const ActionReplay::ARCode& code,
|
||||
const std::string& game_ini_id)
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
constexpr void LoadGame(const std::string&, const DiscIO::Volume*) {}
|
||||
|
||||
constexpr void SetBackgroundExecutionAllowed(bool allowed) {}
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include "Common/MsgHandler.h"
|
||||
|
||||
#include "Core/ARDecrypt.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/CheatCodes.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
|
@ -112,7 +113,7 @@ struct ARAddr
|
|||
|
||||
// ----------------------
|
||||
// AR Remote Functions
|
||||
void ApplyCodes(std::span<const ARCode> codes)
|
||||
void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id)
|
||||
{
|
||||
if (!Config::AreCheatsEnabled())
|
||||
return;
|
||||
|
@ -121,7 +122,10 @@ void ApplyCodes(std::span<const ARCode> codes)
|
|||
s_disable_logging = false;
|
||||
s_active_codes.clear();
|
||||
std::copy_if(codes.begin(), codes.end(), std::back_inserter(s_active_codes),
|
||||
[](const ARCode& code) { return code.enabled; });
|
||||
[&game_id](const ARCode& code) {
|
||||
return code.enabled &&
|
||||
AchievementManager::GetInstance().CheckApprovedARCode(code, game_id);
|
||||
});
|
||||
s_active_codes.shrink_to_fit();
|
||||
}
|
||||
|
||||
|
@ -169,9 +173,10 @@ void AddCode(ARCode code)
|
|||
}
|
||||
}
|
||||
|
||||
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini)
|
||||
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini,
|
||||
const std::string& game_id)
|
||||
{
|
||||
ApplyCodes(LoadCodes(global_ini, local_ini));
|
||||
ApplyCodes(LoadCodes(global_ini, local_ini), game_id);
|
||||
}
|
||||
|
||||
// Parses the Action Replay section of a game ini file.
|
||||
|
|
|
@ -45,12 +45,13 @@ struct ARCode
|
|||
|
||||
void RunAllActive(const Core::CPUThreadGuard& cpu_guard);
|
||||
|
||||
void ApplyCodes(std::span<const ARCode> codes);
|
||||
void ApplyCodes(std::span<const ARCode> codes, const std::string& game_id);
|
||||
void SetSyncedCodesAsActive();
|
||||
void UpdateSyncedCodes(std::span<const ARCode> codes);
|
||||
std::vector<ARCode> ApplyAndReturnCodes(std::span<const ARCode> codes);
|
||||
void AddCode(ARCode new_code);
|
||||
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini);
|
||||
void LoadAndApplyCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini,
|
||||
const std::string& game_id);
|
||||
|
||||
std::vector<ARCode> LoadCodes(const Common::IniFile& global_ini, const Common::IniFile& local_ini);
|
||||
void SaveCodes(Common::IniFile* local_ini, std::span<const ARCode> codes);
|
||||
|
|
|
@ -605,7 +605,9 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||
|
||||
ppc_state.pc = executable.reader->GetEntryPoint();
|
||||
|
||||
if (executable.reader->LoadSymbols(guard, system.GetPPCSymbolDB()))
|
||||
const std::string filename = PathToFileName(executable.path);
|
||||
|
||||
if (executable.reader->LoadSymbols(guard, system.GetPPCSymbolDB(), filename))
|
||||
{
|
||||
Host_PPCSymbolsChanged();
|
||||
HLE::PatchFunctions(system);
|
||||
|
|
|
@ -215,7 +215,8 @@ public:
|
|||
virtual bool IsValid() const = 0;
|
||||
virtual bool IsWii() const = 0;
|
||||
virtual bool LoadIntoMemory(Core::System& system, bool only_in_mem1 = false) const = 0;
|
||||
virtual bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const = 0;
|
||||
virtual bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
|
||||
const std::string& filename) const = 0;
|
||||
|
||||
protected:
|
||||
std::vector<u8> m_bytes;
|
||||
|
|
|
@ -27,7 +27,8 @@ public:
|
|||
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
|
||||
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
|
||||
const std::string& filename) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -180,7 +180,8 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const
|
||||
bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
|
||||
const std::string& filename) const
|
||||
{
|
||||
bool hasSymbols = false;
|
||||
SectionID sec = GetSectionByName(".symtab");
|
||||
|
@ -218,7 +219,7 @@ bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_
|
|||
default:
|
||||
continue;
|
||||
}
|
||||
ppc_symbol_db.AddKnownSymbol(guard, value, size, name, symtype);
|
||||
ppc_symbol_db.AddKnownSymbol(guard, value, size, name, filename, symtype);
|
||||
hasSymbols = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,8 @@ public:
|
|||
u32 GetEntryPoint() const override { return entryPoint; }
|
||||
u32 GetFlags() const { return (u32)(header->e_flags); }
|
||||
bool LoadIntoMemory(Core::System& system, bool only_in_mem1 = false) const override;
|
||||
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const override;
|
||||
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db,
|
||||
const std::string& filename) const override;
|
||||
// TODO: actually check for validity.
|
||||
bool IsValid() const override { return true; }
|
||||
bool IsWii() const override;
|
||||
|
|
|
@ -751,8 +751,7 @@ bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot)
|
|||
|
||||
bool AreCheatsEnabled()
|
||||
{
|
||||
return Config::Get(::Config::MAIN_ENABLE_CHEATS) &&
|
||||
!AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
return Config::Get(::Config::MAIN_ENABLE_CHEATS);
|
||||
}
|
||||
|
||||
bool IsDebuggingEnabled()
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "Common/Config/Config.h"
|
||||
#include "Common/FileUtil.h"
|
||||
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/SYSCONFSettings.h"
|
||||
|
@ -33,6 +34,9 @@ public:
|
|||
layer->Set(Config::MAIN_CPU_THREAD, m_settings.cpu_thread);
|
||||
layer->Set(Config::MAIN_CPU_CORE, m_settings.cpu_core);
|
||||
layer->Set(Config::MAIN_ENABLE_CHEATS, m_settings.enable_cheats);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
layer->Set(Config::RA_HARDCORE_ENABLED, m_settings.enable_hardcore);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
layer->Set(Config::MAIN_GC_LANGUAGE, m_settings.selected_language);
|
||||
layer->Set(Config::MAIN_OVERRIDE_REGION_SETTINGS, m_settings.override_region_settings);
|
||||
layer->Set(Config::MAIN_DSP_HLE, m_settings.dsp_hle);
|
||||
|
|
|
@ -375,6 +375,7 @@ void RSOView::LoadAll(const Core::CPUThreadGuard& guard, u32 address)
|
|||
|
||||
void RSOView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) const
|
||||
{
|
||||
const std::string rso_name = GetName();
|
||||
for (const RSOExport& rso_export : GetExports())
|
||||
{
|
||||
u32 address = GetExportAddress(rso_export);
|
||||
|
@ -389,15 +390,17 @@ void RSOView::Apply(const Core::CPUThreadGuard& guard, PPCSymbolDB* symbol_db) c
|
|||
{
|
||||
// Function symbol
|
||||
symbol->Rename(export_name);
|
||||
symbol->object_name = rso_name;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Data symbol
|
||||
symbol_db->AddKnownSymbol(guard, address, 0, export_name, Common::Symbol::Type::Data);
|
||||
symbol_db->AddKnownSymbol(guard, address, 0, export_name, rso_name,
|
||||
Common::Symbol::Type::Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_LOG_FMT(SYMBOLS, "RSO({}): {} symbols applied", GetName(), GetExportsCount());
|
||||
DEBUG_LOG_FMT(SYMBOLS, "RSO({}): {} symbols applied", rso_name, GetExportsCount());
|
||||
}
|
||||
|
||||
u32 RSOView::GetNextEntry() const
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "Common/Config/Config.h"
|
||||
#include "Common/FileUtil.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Host.h"
|
||||
|
@ -53,7 +54,7 @@ static std::vector<GeckoCode> s_active_codes;
|
|||
static std::vector<GeckoCode> s_synced_codes;
|
||||
static std::mutex s_active_codes_lock;
|
||||
|
||||
void SetActiveCodes(std::span<const GeckoCode> gcodes)
|
||||
void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id)
|
||||
{
|
||||
std::lock_guard lk(s_active_codes_lock);
|
||||
|
||||
|
@ -61,8 +62,12 @@ void SetActiveCodes(std::span<const GeckoCode> gcodes)
|
|||
if (Config::AreCheatsEnabled())
|
||||
{
|
||||
s_active_codes.reserve(gcodes.size());
|
||||
|
||||
std::copy_if(gcodes.begin(), gcodes.end(), std::back_inserter(s_active_codes),
|
||||
[](const GeckoCode& code) { return code.enabled; });
|
||||
[&game_id](const GeckoCode& code) {
|
||||
return code.enabled &&
|
||||
AchievementManager::GetInstance().CheckApprovedGeckoCode(code, game_id);
|
||||
});
|
||||
}
|
||||
s_active_codes.shrink_to_fit();
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ constexpr u32 HLE_TRAMPOLINE_ADDRESS = INSTALLER_END_ADDRESS - 4;
|
|||
// preserve the emulation performance.
|
||||
constexpr u32 MAGIC_GAMEID = 0xD01F1BAD;
|
||||
|
||||
void SetActiveCodes(std::span<const GeckoCode> gcodes);
|
||||
void SetActiveCodes(std::span<const GeckoCode> gcodes, const std::string& game_id);
|
||||
void SetSyncedCodesAsActive();
|
||||
void UpdateSyncedCodes(std::span<const GeckoCode> gcodes);
|
||||
std::vector<GeckoCode> SetAndReturnActiveCodes(std::span<const GeckoCode> gcodes);
|
||||
|
|
|
@ -418,7 +418,7 @@ void AXWiiUCode::WritePB(Memory::MemoryManager& memory, u32 addr, const AXPBWii&
|
|||
case 0xadbc06bd:
|
||||
memory.CopyToEmuSwapped<u16>(addr, (const u16*)src, updates_begin);
|
||||
memory.CopyToEmuSwapped<u16>(addr + updates_begin, (const u16*)(src + updates_end),
|
||||
sizeof(PBUpdatesWii));
|
||||
gap_begin - updates_end);
|
||||
memory.CopyToEmuSwapped<u16>(addr + gap_begin, (const u16*)(src + gap_end),
|
||||
sizeof(pb) - gap_end);
|
||||
break;
|
||||
|
|
|
@ -834,7 +834,7 @@ BbaTcpSocket::ConnectingState BbaTcpSocket::Connected(StackRef* ref)
|
|||
fd_set read_fds;
|
||||
fd_set write_fds;
|
||||
fd_set except_fds;
|
||||
struct timeval t = {0, 0};
|
||||
timeval t = {0, 0};
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&except_fds);
|
||||
|
@ -965,7 +965,7 @@ sf::Socket::Status BbaUdpSocket::Bind(u16 port, u32 net_ip)
|
|||
|
||||
// Subscribe to the SSDP multicast group
|
||||
// NB: Other groups aren't supported because of HLE
|
||||
struct ip_mreq mreq;
|
||||
ip_mreq mreq;
|
||||
mreq.imr_multiaddr.s_addr = std::bit_cast<u32>(Common::IP_ADDR_SSDP);
|
||||
mreq.imr_interface.s_addr = net_ip;
|
||||
if (setsockopt(getHandle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<const char*>(&mreq),
|
||||
|
|
|
@ -99,6 +99,10 @@ void ProcessorInterfaceManager::RegisterMMIO(MMIO::Mapping* mmio, u32 base)
|
|||
{
|
||||
system.GetGPFifo().ResetGatherPipe();
|
||||
|
||||
// Assume that all bytes that made it into the GPU fifo did in fact execute
|
||||
// before this MMIO write takes effect.
|
||||
system.GetFifo().SyncGPUForRegisterAccess();
|
||||
|
||||
// Call Fifo::ResetVideoBuffer() from the video thread. Since that function
|
||||
// resets various pointers used by the video thread, we can't call it directly
|
||||
// from the CPU thread, so queue a task to do it instead. In single-core mode,
|
||||
|
|
|
@ -8,21 +8,12 @@
|
|||
#include "AudioCommon/AudioCommon.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
|
||||
#include "Core/System.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
|
||||
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
|
||||
|
||||
//#define WIIMOTE_SPEAKER_DUMP
|
||||
#ifdef WIIMOTE_SPEAKER_DUMP
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include "AudioCommon/WaveFile.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#endif
|
||||
|
||||
namespace WiimoteEmu
|
||||
{
|
||||
// Yamaha ADPCM decoder code based on The ffmpeg Project (Copyright (s) 2001-2003)
|
||||
|
@ -60,17 +51,6 @@ static s16 adpcm_yamaha_expand_nibble(ADPCMState& s, u8 nibble)
|
|||
return s.predictor;
|
||||
}
|
||||
|
||||
#ifdef WIIMOTE_SPEAKER_DUMP
|
||||
std::ofstream ofile;
|
||||
WaveFileWriter wav;
|
||||
|
||||
void stopdamnwav()
|
||||
{
|
||||
wav.Stop();
|
||||
ofile.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
|
||||
{
|
||||
// TODO: should we still process samples for the decoder state?
|
||||
|
@ -151,28 +131,6 @@ void SpeakerLogic::SpeakerData(const u8* data, int length, float speaker_pan)
|
|||
const unsigned int sample_rate = sample_rate_dividend / reg_data.sample_rate;
|
||||
sound_stream->GetMixer()->PushWiimoteSpeakerSamples(
|
||||
samples.get(), sample_length, Mixer::FIXED_SAMPLE_RATE_DIVIDEND / (sample_rate * 2));
|
||||
|
||||
#ifdef WIIMOTE_SPEAKER_DUMP
|
||||
static int num = 0;
|
||||
|
||||
if (num == 0)
|
||||
{
|
||||
File::Delete("rmtdump.wav");
|
||||
File::Delete("rmtdump.bin");
|
||||
atexit(stopdamnwav);
|
||||
File::OpenFStream(ofile, "rmtdump.bin", ofile.binary | ofile.out);
|
||||
wav.Start("rmtdump.wav", 6000);
|
||||
}
|
||||
wav.AddMonoSamples(samples.get(), length * 2);
|
||||
if (ofile.good())
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
ofile << data[i];
|
||||
}
|
||||
}
|
||||
num++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SpeakerLogic::Reset()
|
||||
|
|
|
@ -197,8 +197,8 @@ void init_lib()
|
|||
|
||||
namespace WiimoteReal
|
||||
{
|
||||
int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, enum WinWriteMethod& stack,
|
||||
const u8* buf, size_t len, DWORD* written);
|
||||
int IOWrite(HANDLE& dev_handle, OVERLAPPED& hid_overlap_write, WinWriteMethod& stack, const u8* buf,
|
||||
size_t len, DWORD* written);
|
||||
int IORead(HANDLE& dev_handle, OVERLAPPED& hid_overlap_read, u8* buf, int index);
|
||||
|
||||
template <typename T>
|
||||
|
|
|
@ -57,6 +57,7 @@ bool Host_TASInputHasFocus();
|
|||
|
||||
void Host_Message(HostMessageID id);
|
||||
void Host_PPCSymbolsChanged();
|
||||
void Host_PPCBreakpointsChanged();
|
||||
void Host_RefreshDSPDebuggerWindow();
|
||||
void Host_RequestRenderWindowSize(int width, int height);
|
||||
void Host_UpdateDisasmDialog();
|
||||
|
|
|
@ -798,7 +798,7 @@ IPCReply NetIPTopDevice::HandleInetAToNRequest(const IOCtlRequest& request)
|
|||
auto& memory = system.GetMemory();
|
||||
|
||||
const std::string hostname = memory.GetString(request.buffer_in);
|
||||
struct hostent* remoteHost = gethostbyname(hostname.c_str());
|
||||
hostent* remoteHost = gethostbyname(hostname.c_str());
|
||||
|
||||
if (remoteHost == nullptr || remoteHost->h_addr_list == nullptr ||
|
||||
remoteHost->h_addr_list[0] == nullptr)
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
static DRESULT read_vff_header(IOS::HLE::FS::FileHandle* vff, FATFS* fs)
|
||||
{
|
||||
struct IOS::HLE::NWC24::VFFHeader header;
|
||||
IOS::HLE::NWC24::VFFHeader header;
|
||||
if (!vff->Read(&header, 1))
|
||||
{
|
||||
ERROR_LOG_FMT(IOS_WC24, "Failed to read VFF header.");
|
||||
|
|
|
@ -770,7 +770,7 @@ WiiSocket::ConnectingState WiiSocket::GetConnectingState() const
|
|||
fd_set read_fds;
|
||||
fd_set write_fds;
|
||||
fd_set except_fds;
|
||||
struct timeval t = {0, 0};
|
||||
timeval t = {0, 0};
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&except_fds);
|
||||
|
@ -998,7 +998,7 @@ void WiiSockMan::Update()
|
|||
{
|
||||
s32 nfds = 0;
|
||||
fd_set read_fds, write_fds, except_fds;
|
||||
struct timeval t = {0, 0};
|
||||
timeval t = {0, 0};
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&except_fds);
|
||||
|
|
|
@ -2505,7 +2505,7 @@ struct hci_filter
|
|||
uint32_t mask[8]; /* 256 bits */
|
||||
};
|
||||
|
||||
static __inline void hci_filter_set(uint8_t bit, struct hci_filter* filter)
|
||||
static __inline void hci_filter_set(uint8_t bit, hci_filter* filter)
|
||||
{
|
||||
uint8_t off = bit - 1;
|
||||
|
||||
|
@ -2513,7 +2513,7 @@ static __inline void hci_filter_set(uint8_t bit, struct hci_filter* filter)
|
|||
filter->mask[off] |= (1 << ((bit - 1) & 0x1f));
|
||||
}
|
||||
|
||||
static __inline void hci_filter_clr(uint8_t bit, struct hci_filter* filter)
|
||||
static __inline void hci_filter_clr(uint8_t bit, hci_filter* filter)
|
||||
{
|
||||
uint8_t off = bit - 1;
|
||||
|
||||
|
@ -2581,7 +2581,7 @@ struct btreq
|
|||
uint16_t btri_link_policy; /* Link Policy */
|
||||
uint16_t btri_packet_type; /* Packet Type */
|
||||
} btri;
|
||||
struct bt_stats btrs; /* unit stats */
|
||||
bt_stats btrs; /* unit stats */
|
||||
} btru;
|
||||
};
|
||||
|
||||
|
|
|
@ -96,10 +96,11 @@ std::optional<IPCReply> USB_HIDv4::GetDeviceChange(const IOCtlRequest& request)
|
|||
m_devicechange_hook_request = std::make_unique<IOCtlRequest>(GetSystem(), request.address);
|
||||
// If there are pending changes, the reply is sent immediately (instead of on device
|
||||
// insertion/removal).
|
||||
if (m_has_pending_changes)
|
||||
if (m_has_pending_changes || m_is_shut_down)
|
||||
{
|
||||
TriggerDeviceChangeReply();
|
||||
m_has_pending_changes = false;
|
||||
m_is_shut_down = false;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -114,6 +115,7 @@ IPCReply USB_HIDv4::Shutdown(const IOCtlRequest& request)
|
|||
memory.Write_U32(0xffffffff, m_devicechange_hook_request->buffer_out);
|
||||
GetEmulationKernel().EnqueueIPCReply(*m_devicechange_hook_request, -1);
|
||||
m_devicechange_hook_request.reset();
|
||||
m_is_shut_down = true;
|
||||
}
|
||||
return IPCReply(IPC_SUCCESS);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ private:
|
|||
static constexpr u8 HID_CLASS = 0x03;
|
||||
|
||||
bool m_has_pending_changes = true;
|
||||
bool m_is_shut_down = false;
|
||||
std::mutex m_devicechange_hook_address_mutex;
|
||||
std::unique_ptr<IOCtlRequest> m_devicechange_hook_request;
|
||||
|
||||
|
|
|
@ -873,6 +873,7 @@ void NetPlayClient::OnStartGame(sf::Packet& packet)
|
|||
packet >> m_net_settings.cpu_thread;
|
||||
packet >> m_net_settings.cpu_core;
|
||||
packet >> m_net_settings.enable_cheats;
|
||||
packet >> m_net_settings.enable_hardcore;
|
||||
packet >> m_net_settings.selected_language;
|
||||
packet >> m_net_settings.override_region_settings;
|
||||
packet >> m_net_settings.dsp_enable_jit;
|
||||
|
|
|
@ -35,6 +35,7 @@ struct NetSettings
|
|||
bool cpu_thread = false;
|
||||
PowerPC::CPUCore cpu_core{};
|
||||
bool enable_cheats = false;
|
||||
bool enable_hardcore = false;
|
||||
int selected_language = 0;
|
||||
bool override_region_settings = false;
|
||||
bool dsp_hle = false;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "Common/UPnP.h"
|
||||
#include "Common/Version.h"
|
||||
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/Boot/Boot.h"
|
||||
#include "Core/Config/GraphicsSettings.h"
|
||||
|
@ -1412,6 +1413,7 @@ bool NetPlayServer::SetupNetSettings()
|
|||
settings.cpu_thread = Config::Get(Config::MAIN_CPU_THREAD);
|
||||
settings.cpu_core = Config::Get(Config::MAIN_CPU_CORE);
|
||||
settings.enable_cheats = Config::AreCheatsEnabled();
|
||||
settings.enable_hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
settings.selected_language = Config::Get(Config::MAIN_GC_LANGUAGE);
|
||||
settings.override_region_settings = Config::Get(Config::MAIN_OVERRIDE_REGION_SETTINGS);
|
||||
settings.dsp_hle = Config::Get(Config::MAIN_DSP_HLE);
|
||||
|
@ -1640,6 +1642,7 @@ bool NetPlayServer::StartGame()
|
|||
spac << m_settings.cpu_thread;
|
||||
spac << m_settings.cpu_core;
|
||||
spac << m_settings.enable_cheats;
|
||||
spac << m_settings.enable_hardcore;
|
||||
spac << m_settings.selected_language;
|
||||
spac << m_settings.override_region_settings;
|
||||
spac << m_settings.dsp_enable_jit;
|
||||
|
@ -2121,13 +2124,18 @@ bool NetPlayServer::SyncCodes()
|
|||
}
|
||||
// Sync Gecko Codes
|
||||
{
|
||||
std::vector<Gecko::GeckoCode> codes = Gecko::LoadCodes(globalIni, localIni);
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().FilterApprovedGeckoCodes(codes, game_id);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// Create a Gecko Code Vector with just the active codes
|
||||
std::vector<Gecko::GeckoCode> s_active_codes =
|
||||
Gecko::SetAndReturnActiveCodes(Gecko::LoadCodes(globalIni, localIni));
|
||||
std::vector<Gecko::GeckoCode> active_codes = Gecko::SetAndReturnActiveCodes(codes);
|
||||
|
||||
// Determine Codelist Size
|
||||
u16 codelines = 0;
|
||||
for (const Gecko::GeckoCode& active_code : s_active_codes)
|
||||
for (const Gecko::GeckoCode& active_code : active_codes)
|
||||
{
|
||||
INFO_LOG_FMT(NETPLAY, "Indexing {}", active_code.name);
|
||||
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
||||
|
@ -2156,7 +2164,7 @@ bool NetPlayServer::SyncCodes()
|
|||
pac << SyncCodeID::GeckoData;
|
||||
std::vector<std::string> v_ActiveGeckoCodes = {};
|
||||
// Iterate through the active code vector and send each codeline
|
||||
for (const Gecko::GeckoCode& active_code : s_active_codes)
|
||||
for (const Gecko::GeckoCode& active_code : active_codes)
|
||||
{
|
||||
INFO_LOG_FMT(NETPLAY, "Sending {}", active_code.name);
|
||||
for (const Gecko::GeckoCode::Code& code : active_code.codes)
|
||||
|
@ -2180,13 +2188,16 @@ bool NetPlayServer::SyncCodes()
|
|||
|
||||
// Sync AR Codes
|
||||
{
|
||||
std::vector<ActionReplay::ARCode> codes = ActionReplay::LoadCodes(globalIni, localIni);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().FilterApprovedARCodes(codes, game_id);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
// Create an AR Code Vector with just the active codes
|
||||
std::vector<ActionReplay::ARCode> s_active_codes =
|
||||
ActionReplay::ApplyAndReturnCodes(ActionReplay::LoadCodes(globalIni, localIni));
|
||||
std::vector<ActionReplay::ARCode> active_codes = ActionReplay::ApplyAndReturnCodes(codes);
|
||||
|
||||
// Determine Codelist Size
|
||||
u16 codelines = 0;
|
||||
for (const ActionReplay::ARCode& active_code : s_active_codes)
|
||||
for (const ActionReplay::ARCode& active_code : active_codes)
|
||||
{
|
||||
INFO_LOG_FMT(NETPLAY, "Indexing {}", active_code.name);
|
||||
for (const ActionReplay::AREntry& op : active_code.ops)
|
||||
|
|
|
@ -198,8 +198,8 @@ void LoadPatches()
|
|||
}
|
||||
else
|
||||
{
|
||||
Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni));
|
||||
ActionReplay::LoadAndApplyCodes(globalIni, localIni);
|
||||
Gecko::SetActiveCodes(Gecko::LoadCodes(globalIni, localIni), sconfig.GetGameID());
|
||||
ActionReplay::LoadAndApplyCodes(globalIni, localIni, sconfig.GetGameID());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -246,9 +246,6 @@ static void ApplyPatches(const Core::CPUThreadGuard& guard, const std::vector<Pa
|
|||
static void ApplyMemoryPatches(const Core::CPUThreadGuard& guard,
|
||||
std::span<const std::size_t> memory_patch_indices)
|
||||
{
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return;
|
||||
|
||||
std::lock_guard lock(s_on_frame_memory_mutex);
|
||||
for (std::size_t index : memory_patch_indices)
|
||||
{
|
||||
|
@ -338,7 +335,7 @@ bool ApplyFramePatches(Core::System& system)
|
|||
void Shutdown()
|
||||
{
|
||||
s_on_frame.clear();
|
||||
ActionReplay::ApplyCodes({});
|
||||
ActionReplay::ApplyCodes({}, "");
|
||||
Gecko::Shutdown();
|
||||
}
|
||||
|
||||
|
|
|
@ -178,6 +178,7 @@ static void RemoveBreakpoint(BreakpointType type, u32 addr, u32 len)
|
|||
INFO_LOG_FMT(GDB_STUB, "gdb: removed a memcheck: {:08x} bytes at {:08x}", len, addr);
|
||||
}
|
||||
}
|
||||
Host_PPCBreakpointsChanged();
|
||||
}
|
||||
|
||||
static void Nack()
|
||||
|
@ -258,7 +259,7 @@ static void ReadCommand()
|
|||
|
||||
static bool IsDataAvailable()
|
||||
{
|
||||
struct timeval t;
|
||||
timeval t;
|
||||
fd_set _fds, *fds = &_fds;
|
||||
|
||||
FD_ZERO(fds);
|
||||
|
@ -896,6 +897,7 @@ static bool AddBreakpoint(BreakpointType type, u32 addr, u32 len)
|
|||
INFO_LOG_FMT(GDB_STUB, "gdb: added {} memcheck: {:08x} bytes at {:08x}", static_cast<int>(type),
|
||||
len, addr);
|
||||
}
|
||||
Host_PPCBreakpointsChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -350,8 +350,8 @@ void Jit64::Shutdown()
|
|||
|
||||
void Jit64::FallBackToInterpreter(UGeckoInstruction inst)
|
||||
{
|
||||
gpr.Flush();
|
||||
fpr.Flush();
|
||||
gpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
|
||||
fpr.Flush(BitSet32(0xFFFFFFFF), RegCache::IgnoreDiscardedRegisters::Yes);
|
||||
|
||||
if (js.op->canEndBlock)
|
||||
{
|
||||
|
|
|
@ -391,7 +391,7 @@ void RegCache::Discard(BitSet32 pregs)
|
|||
}
|
||||
}
|
||||
|
||||
void RegCache::Flush(BitSet32 pregs)
|
||||
void RegCache::Flush(BitSet32 pregs, IgnoreDiscardedRegisters ignore_discarded_registers)
|
||||
{
|
||||
ASSERT_MSG(
|
||||
DYNA_REC,
|
||||
|
@ -410,7 +410,8 @@ void RegCache::Flush(BitSet32 pregs)
|
|||
case PPCCachedReg::LocationType::Default:
|
||||
break;
|
||||
case PPCCachedReg::LocationType::Discarded:
|
||||
ASSERT_MSG(DYNA_REC, false, "Attempted to flush discarded PPC reg {}", i);
|
||||
ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No,
|
||||
"Attempted to flush discarded PPC reg {}", i);
|
||||
break;
|
||||
case PPCCachedReg::LocationType::SpeculativeImmediate:
|
||||
// We can have a cached value without a host register through speculative constants.
|
||||
|
|
|
@ -126,6 +126,12 @@ public:
|
|||
MaintainState,
|
||||
};
|
||||
|
||||
enum class IgnoreDiscardedRegisters
|
||||
{
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
explicit RegCache(Jit64& jit);
|
||||
virtual ~RegCache() = default;
|
||||
|
||||
|
@ -168,7 +174,8 @@ public:
|
|||
|
||||
RCForkGuard Fork();
|
||||
void Discard(BitSet32 pregs);
|
||||
void Flush(BitSet32 pregs = BitSet32::AllTrue(32));
|
||||
void Flush(BitSet32 pregs = BitSet32::AllTrue(32),
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No);
|
||||
void Reset(BitSet32 pregs);
|
||||
void Revert();
|
||||
void Commit();
|
||||
|
|
|
@ -256,8 +256,8 @@ void JitArm64::Shutdown()
|
|||
void JitArm64::FallBackToInterpreter(UGeckoInstruction inst)
|
||||
{
|
||||
FlushCarry();
|
||||
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
||||
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG);
|
||||
gpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
|
||||
fpr.Flush(FlushMode::All, ARM64Reg::INVALID_REG, IgnoreDiscardedRegisters::Yes);
|
||||
|
||||
if (js.op->canEndBlock)
|
||||
{
|
||||
|
|
|
@ -181,7 +181,8 @@ void JitArm64::SafeStoreFromReg(s32 dest, u32 value, s32 regOffset, u32 flags, s
|
|||
if (!jo.fastmem)
|
||||
gpr.Lock(ARM64Reg::W0);
|
||||
|
||||
ARM64Reg RS = gpr.R(value);
|
||||
// Don't materialize zero.
|
||||
ARM64Reg RS = gpr.IsImm(value, 0) ? ARM64Reg::WZR : gpr.R(value);
|
||||
|
||||
ARM64Reg reg_dest = ARM64Reg::INVALID_REG;
|
||||
ARM64Reg reg_off = ARM64Reg::INVALID_REG;
|
||||
|
|
|
@ -241,12 +241,16 @@ void Arm64GPRCache::FlushRegister(size_t index, FlushMode mode, ARM64Reg tmp_reg
|
|||
}
|
||||
}
|
||||
|
||||
void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_reg)
|
||||
void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers)
|
||||
{
|
||||
for (auto iter = regs.begin(); iter != regs.end(); ++iter)
|
||||
{
|
||||
const int i = *iter;
|
||||
ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded,
|
||||
|
||||
ASSERT_MSG(DYNA_REC,
|
||||
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
|
||||
m_guest_registers[GUEST_GPR_OFFSET + i].GetType() != RegType::Discarded,
|
||||
"Attempted to flush discarded register");
|
||||
|
||||
if (i + 1 < int(GUEST_GPR_COUNT) && regs[i + 1])
|
||||
|
@ -288,11 +292,14 @@ void Arm64GPRCache::FlushRegisters(BitSet32 regs, FlushMode mode, ARM64Reg tmp_r
|
|||
}
|
||||
}
|
||||
|
||||
void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, FlushMode mode, ARM64Reg tmp_reg)
|
||||
void Arm64GPRCache::FlushCRRegisters(BitSet8 regs, FlushMode mode, ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers)
|
||||
{
|
||||
for (int i : regs)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, m_guest_registers[GUEST_CR_OFFSET + i].GetType() != RegType::Discarded,
|
||||
ASSERT_MSG(DYNA_REC,
|
||||
ignore_discarded_registers != IgnoreDiscardedRegisters::No ||
|
||||
m_guest_registers[GUEST_CR_OFFSET + i].GetType() != RegType::Discarded,
|
||||
"Attempted to flush discarded register");
|
||||
|
||||
FlushRegister(GUEST_CR_OFFSET + i, mode, tmp_reg);
|
||||
|
@ -318,10 +325,11 @@ void Arm64GPRCache::ResetCRRegisters(BitSet8 regs)
|
|||
}
|
||||
}
|
||||
|
||||
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
|
||||
void Arm64GPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers)
|
||||
{
|
||||
FlushRegisters(BitSet32(0xFFFFFFFF), mode, tmp_reg);
|
||||
FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg);
|
||||
FlushRegisters(BitSet32(0xFFFFFFFF), mode, tmp_reg, ignore_discarded_registers);
|
||||
FlushCRRegisters(BitSet8(0xFF), mode, tmp_reg, ignore_discarded_registers);
|
||||
}
|
||||
|
||||
ARM64Reg Arm64GPRCache::R(const GuestRegInfo& guest_reg)
|
||||
|
@ -498,14 +506,19 @@ Arm64FPRCache::Arm64FPRCache() : Arm64RegCache(GUEST_FPR_COUNT)
|
|||
{
|
||||
}
|
||||
|
||||
void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg)
|
||||
void Arm64FPRCache::Flush(FlushMode mode, ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers)
|
||||
{
|
||||
for (size_t i = 0; i < m_guest_registers.size(); ++i)
|
||||
{
|
||||
const RegType reg_type = m_guest_registers[i].GetType();
|
||||
|
||||
if (reg_type != RegType::NotLoaded && reg_type != RegType::Discarded &&
|
||||
reg_type != RegType::Immediate)
|
||||
if (reg_type == RegType::Discarded)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, ignore_discarded_registers != IgnoreDiscardedRegisters::No,
|
||||
"Attempted to flush discarded register");
|
||||
}
|
||||
else if (reg_type != RegType::NotLoaded && reg_type != RegType::Immediate)
|
||||
{
|
||||
FlushRegister(i, mode, tmp_reg);
|
||||
}
|
||||
|
|
|
@ -81,6 +81,12 @@ enum class FlushMode : bool
|
|||
MaintainState,
|
||||
};
|
||||
|
||||
enum class IgnoreDiscardedRegisters
|
||||
{
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
class OpArg
|
||||
{
|
||||
public:
|
||||
|
@ -169,7 +175,8 @@ public:
|
|||
// Flushes the register cache in different ways depending on the mode.
|
||||
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState,
|
||||
// but in other cases it can be set to ARM64Reg::INVALID_REG when convenient for the caller.
|
||||
virtual void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) = 0;
|
||||
virtual void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers) = 0;
|
||||
|
||||
virtual BitSet32 GetCallerSavedUsed() const = 0;
|
||||
|
||||
|
@ -315,7 +322,9 @@ public:
|
|||
// Flushes the register cache in different ways depending on the mode.
|
||||
// A temporary register must be supplied when flushing GPRs with FlushMode::MaintainState,
|
||||
// but in other cases it can be set to ARM64Reg::INVALID_REG when convenient for the caller.
|
||||
void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) override;
|
||||
void Flush(
|
||||
FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No) override;
|
||||
|
||||
// Returns a guest GPR inside of a host register.
|
||||
// Will dump an immediate to the host register as well.
|
||||
|
@ -381,12 +390,12 @@ public:
|
|||
|
||||
void StoreRegisters(BitSet32 regs, Arm64Gen::ARM64Reg tmp_reg = Arm64Gen::ARM64Reg::INVALID_REG)
|
||||
{
|
||||
FlushRegisters(regs, FlushMode::All, tmp_reg);
|
||||
FlushRegisters(regs, FlushMode::All, tmp_reg, IgnoreDiscardedRegisters::No);
|
||||
}
|
||||
|
||||
void StoreCRRegisters(BitSet8 regs, Arm64Gen::ARM64Reg tmp_reg = Arm64Gen::ARM64Reg::INVALID_REG)
|
||||
{
|
||||
FlushCRRegisters(regs, FlushMode::All, tmp_reg);
|
||||
FlushCRRegisters(regs, FlushMode::All, tmp_reg, IgnoreDiscardedRegisters::No);
|
||||
}
|
||||
|
||||
void DiscardCRRegisters(BitSet8 regs);
|
||||
|
@ -421,8 +430,10 @@ private:
|
|||
void SetImmediate(const GuestRegInfo& guest_reg, u32 imm, bool dirty);
|
||||
void BindToRegister(const GuestRegInfo& guest_reg, bool will_read, bool will_write = true);
|
||||
|
||||
void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg);
|
||||
void FlushCRRegisters(BitSet8 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg);
|
||||
void FlushRegisters(BitSet32 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers);
|
||||
void FlushCRRegisters(BitSet8 regs, FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers);
|
||||
};
|
||||
|
||||
class Arm64FPRCache : public Arm64RegCache
|
||||
|
@ -432,7 +443,9 @@ public:
|
|||
|
||||
// Flushes the register cache in different ways depending on the mode.
|
||||
// The temporary register can be set to ARM64Reg::INVALID_REG when convenient for the caller.
|
||||
void Flush(FlushMode mode, Arm64Gen::ARM64Reg tmp_reg) override;
|
||||
void Flush(
|
||||
FlushMode mode, Arm64Gen::ARM64Reg tmp_reg,
|
||||
IgnoreDiscardedRegisters ignore_discarded_registers = IgnoreDiscardedRegisters::No) override;
|
||||
|
||||
// Returns a guest register inside of a host register
|
||||
// Will dump an immediate to the host register as well
|
||||
|
|
|
@ -257,6 +257,12 @@ void ByteswapAfterLoad(ARM64XEmitter* emit, ARM64FloatEmitter* float_emit, ARM64
|
|||
ARM64Reg ByteswapBeforeStore(ARM64XEmitter* emit, ARM64FloatEmitter* float_emit, ARM64Reg tmp_reg,
|
||||
ARM64Reg src_reg, u32 flags, bool want_reversed)
|
||||
{
|
||||
// Byteswapping zero is still zero.
|
||||
// We'd typically expect a writable register to be passed in, but recognize
|
||||
// WZR for optimization purposes.
|
||||
if ((flags & BackPatchInfo::FLAG_FLOAT) == 0 && src_reg == ARM64Reg::WZR)
|
||||
return ARM64Reg::WZR;
|
||||
|
||||
ARM64Reg dst_reg = src_reg;
|
||||
|
||||
if (want_reversed == !(flags & BackPatchInfo::FLAG_REVERSE))
|
||||
|
|
|
@ -49,7 +49,8 @@ Common::Symbol* PPCSymbolDB::AddFunction(const Core::CPUThreadGuard& guard, u32
|
|||
}
|
||||
|
||||
void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
|
||||
const std::string& name, Common::Symbol::Type type)
|
||||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type)
|
||||
{
|
||||
auto iter = m_functions.find(startAddr);
|
||||
if (iter != m_functions.end())
|
||||
|
@ -57,6 +58,7 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
|||
// already got it, let's just update name, checksum & size to be sure.
|
||||
Common::Symbol* tempfunc = &iter->second;
|
||||
tempfunc->Rename(name);
|
||||
tempfunc->object_name = object_name;
|
||||
tempfunc->hash = HashSignatureDB::ComputeCodeChecksum(guard, startAddr, startAddr + size - 4);
|
||||
tempfunc->type = type;
|
||||
tempfunc->size = size;
|
||||
|
@ -65,6 +67,7 @@ void PPCSymbolDB::AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAdd
|
|||
{
|
||||
// new symbol. run analyze.
|
||||
auto& new_symbol = m_functions.emplace(startAddr, name).first->second;
|
||||
new_symbol.object_name = object_name;
|
||||
new_symbol.type = type;
|
||||
new_symbol.address = startAddr;
|
||||
|
||||
|
@ -399,6 +402,13 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
if (name[strlen(name) - 1] == '\r')
|
||||
name[strlen(name) - 1] = 0;
|
||||
|
||||
// Split the current name string into separate parts, and get the object name
|
||||
// if it exists.
|
||||
const std::vector<std::string> parts = SplitString(name, '\t');
|
||||
const std::string name_string(StripWhitespace(parts[0]));
|
||||
const std::string object_filename_string =
|
||||
parts.size() > 1 ? std::string(StripWhitespace(parts[1])) : "";
|
||||
|
||||
// Check if this is a valid entry.
|
||||
if (strlen(name) > 0)
|
||||
{
|
||||
|
@ -435,7 +445,7 @@ bool PPCSymbolDB::LoadMap(const Core::CPUThreadGuard& guard, const std::string&
|
|||
if (good)
|
||||
{
|
||||
++good_count;
|
||||
AddKnownSymbol(guard, vaddress, size, name, type);
|
||||
AddKnownSymbol(guard, vaddress, size, name_string, object_filename_string, type);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -473,8 +483,13 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
|
|||
for (const auto& symbol : function_symbols)
|
||||
{
|
||||
// Write symbol address, size, virtual address, alignment, name
|
||||
f.WriteString(fmt::format("{0:08x} {1:08x} {2:08x} {3} {4}\n", symbol->address, symbol->size,
|
||||
symbol->address, 0, symbol->name));
|
||||
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
|
||||
symbol->address, 0, symbol->name);
|
||||
// Also write the object name if it exists
|
||||
if (!symbol->object_name.empty())
|
||||
line += fmt::format(" \t{0}", symbol->object_name);
|
||||
line += "\n";
|
||||
f.WriteString(line);
|
||||
}
|
||||
|
||||
// Write .data section
|
||||
|
@ -482,8 +497,13 @@ bool PPCSymbolDB::SaveSymbolMap(const std::string& filename) const
|
|||
for (const auto& symbol : data_symbols)
|
||||
{
|
||||
// Write symbol address, size, virtual address, alignment, name
|
||||
f.WriteString(fmt::format("{0:08x} {1:08x} {2:08x} {3} {4}\n", symbol->address, symbol->size,
|
||||
symbol->address, 0, symbol->name));
|
||||
std::string line = fmt::format("{0:08x} {1:06x} {2:08x} {3} {4}", symbol->address, symbol->size,
|
||||
symbol->address, 0, symbol->name);
|
||||
// Also write the object name if it exists
|
||||
if (!symbol->object_name.empty())
|
||||
line += fmt::format(" \t{0}", symbol->object_name);
|
||||
line += "\n";
|
||||
f.WriteString(line);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
|
||||
Common::Symbol* AddFunction(const Core::CPUThreadGuard& guard, u32 start_addr) override;
|
||||
void AddKnownSymbol(const Core::CPUThreadGuard& guard, u32 startAddr, u32 size,
|
||||
const std::string& name,
|
||||
const std::string& name, const std::string& object_name,
|
||||
Common::Symbol::Type type = Common::Symbol::Type::Function);
|
||||
|
||||
Common::Symbol* GetSymbolFromAddr(u32 addr) override;
|
||||
|
|
|
@ -61,6 +61,10 @@ void Host_PPCSymbolsChanged()
|
|||
{
|
||||
}
|
||||
|
||||
void Host_PPCBreakpointsChanged()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_RefreshDSPDebuggerWindow()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -329,7 +329,6 @@ void AchievementSettingsWidget::ToggleProgress()
|
|||
|
||||
void AchievementSettingsWidget::UpdateHardcoreMode()
|
||||
{
|
||||
AchievementManager::GetInstance().SetHardcoreMode();
|
||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||
{
|
||||
Settings::Instance().SetDebugModeEnabled(false);
|
||||
|
|
|
@ -115,7 +115,7 @@ void ARCodeWidget::OnItemChanged(QListWidgetItem* item)
|
|||
m_ar_codes[m_code_list->row(item)].enabled = (item->checkState() == Qt::Checked);
|
||||
|
||||
if (!m_restart_required)
|
||||
ActionReplay::ApplyCodes(m_ar_codes);
|
||||
ActionReplay::ApplyCodes(m_ar_codes, m_game_id);
|
||||
|
||||
UpdateList();
|
||||
SaveCodes();
|
||||
|
|
|
@ -206,7 +206,7 @@ void GeckoCodeWidget::OnItemChanged(QListWidgetItem* item)
|
|||
m_gecko_codes[index].enabled = (item->checkState() == Qt::Checked);
|
||||
|
||||
if (!m_restart_required)
|
||||
Gecko::SetActiveCodes(m_gecko_codes);
|
||||
Gecko::SetActiveCodes(m_gecko_codes, m_game_id);
|
||||
|
||||
SaveCodes();
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ void HardcoreWarningWidget::CreateWidgets()
|
|||
auto* icon = new QLabel;
|
||||
icon->setPixmap(warning_icon);
|
||||
|
||||
m_text = new QLabel(tr("This feature is disabled in hardcore mode."));
|
||||
m_text = new QLabel(tr("Only approved codes will be applied in hardcore mode."));
|
||||
m_settings_button = new QPushButton(tr("Achievement Settings"));
|
||||
|
||||
auto* layout = new QHBoxLayout;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
|
@ -19,12 +18,10 @@
|
|||
|
||||
#include "Core/HW/WiimoteEmu/Camera.h"
|
||||
|
||||
#include "InputCommon/ControlReference/ControlReference.h"
|
||||
#include "InputCommon/ControllerEmu/Control/Control.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Cursor.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/Force.h"
|
||||
#include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h"
|
||||
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
|
||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||
|
||||
#include "DolphinQt/Config/Mapping/MappingWidget.h"
|
||||
|
@ -289,7 +286,14 @@ void GenerateFibonacciSphere(int point_count, F&& callback)
|
|||
|
||||
void MappingIndicator::paintEvent(QPaintEvent*)
|
||||
{
|
||||
constexpr float max_elapsed_seconds = 0.1f;
|
||||
|
||||
const auto now = Clock::now();
|
||||
const float elapsed_seconds = std::chrono::duration_cast<DT_s>(now - m_last_update).count();
|
||||
m_last_update = now;
|
||||
|
||||
const auto lock = ControllerEmu::EmulatedController::GetStateLock();
|
||||
Update(std::min(elapsed_seconds, max_elapsed_seconds));
|
||||
Draw();
|
||||
}
|
||||
|
||||
|
@ -321,7 +325,7 @@ void SquareIndicator::TransformPainter(QPainter& p)
|
|||
p.setRenderHint(QPainter::Antialiasing, true);
|
||||
p.setRenderHint(QPainter::SmoothPixmapTransform, true);
|
||||
|
||||
p.translate(width() / 2, height() / 2);
|
||||
p.translate(width() / 2.0, height() / 2.0);
|
||||
const auto scale = GetContentsScale();
|
||||
p.scale(scale, scale);
|
||||
}
|
||||
|
@ -413,10 +417,13 @@ void AnalogStickIndicator::Draw()
|
|||
(adj_coord.x || adj_coord.y) ? std::make_optional(adj_coord) : std::nullopt);
|
||||
}
|
||||
|
||||
void TiltIndicator::Update(float elapsed_seconds)
|
||||
{
|
||||
WiimoteEmu::EmulateTilt(&m_motion_state, &m_group, elapsed_seconds);
|
||||
}
|
||||
|
||||
void TiltIndicator::Draw()
|
||||
{
|
||||
WiimoteEmu::EmulateTilt(&m_motion_state, &m_group, 1.f / INDICATOR_UPDATE_FREQ);
|
||||
|
||||
auto adj_coord = Common::DVec2{-m_motion_state.angle.y, m_motion_state.angle.x} / MathUtil::PI;
|
||||
|
||||
// Angle values after dividing by pi.
|
||||
|
@ -564,28 +571,54 @@ void SwingIndicator::DrawUnderGate(QPainter& p)
|
|||
}
|
||||
}
|
||||
|
||||
void SwingIndicator::Update(float elapsed_seconds)
|
||||
{
|
||||
WiimoteEmu::EmulateSwing(&m_motion_state, &m_swing_group, elapsed_seconds);
|
||||
}
|
||||
|
||||
void SwingIndicator::Draw()
|
||||
{
|
||||
auto& force = m_swing_group;
|
||||
WiimoteEmu::EmulateSwing(&m_motion_state, &force, 1.f / INDICATOR_UPDATE_FREQ);
|
||||
|
||||
DrawReshapableInput(force, SWING_GATE_COLOR,
|
||||
DrawReshapableInput(m_swing_group, SWING_GATE_COLOR,
|
||||
Common::DVec2{-m_motion_state.position.x, m_motion_state.position.z});
|
||||
}
|
||||
|
||||
void ShakeMappingIndicator::Update(float elapsed_seconds)
|
||||
{
|
||||
WiimoteEmu::EmulateShake(&m_motion_state, &m_shake_group, elapsed_seconds);
|
||||
|
||||
for (auto& sample : m_position_samples)
|
||||
sample.age += elapsed_seconds;
|
||||
|
||||
m_position_samples.erase(
|
||||
std::ranges::find_if(m_position_samples,
|
||||
[](const ShakeSample& sample) { return sample.age > 1.f; }),
|
||||
m_position_samples.end());
|
||||
|
||||
constexpr float MAX_DISTANCE = 0.5f;
|
||||
|
||||
m_position_samples.push_front(ShakeSample{m_motion_state.position / MAX_DISTANCE});
|
||||
|
||||
const bool any_non_zero_samples =
|
||||
std::any_of(m_position_samples.begin(), m_position_samples.end(),
|
||||
[](const ShakeSample& s) { return s.state.LengthSquared() != 0.0; });
|
||||
|
||||
// Only start moving the line if there's non-zero data.
|
||||
if (m_grid_line_position || any_non_zero_samples)
|
||||
{
|
||||
m_grid_line_position += elapsed_seconds;
|
||||
|
||||
if (m_grid_line_position > 1.f)
|
||||
{
|
||||
if (any_non_zero_samples)
|
||||
m_grid_line_position = std::fmod(m_grid_line_position, 1.f);
|
||||
else
|
||||
m_grid_line_position = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShakeMappingIndicator::Draw()
|
||||
{
|
||||
constexpr std::size_t HISTORY_COUNT = INDICATOR_UPDATE_FREQ;
|
||||
|
||||
WiimoteEmu::EmulateShake(&m_motion_state, &m_shake_group, 1.f / INDICATOR_UPDATE_FREQ);
|
||||
|
||||
constexpr float MAX_DISTANCE = 0.5f;
|
||||
|
||||
m_position_samples.push_front(m_motion_state.position / MAX_DISTANCE);
|
||||
// This also holds the current state so +1.
|
||||
if (m_position_samples.size() > HISTORY_COUNT + 1)
|
||||
m_position_samples.pop_back();
|
||||
|
||||
QPainter p(this);
|
||||
DrawBoundingBox(p);
|
||||
TransformPainter(p);
|
||||
|
@ -610,15 +643,7 @@ void ShakeMappingIndicator::Draw()
|
|||
p.drawPoint(QPointF{-0.5 + c * 0.5, raw_coord.data[c]});
|
||||
}
|
||||
|
||||
// Grid line.
|
||||
if (m_grid_line_position ||
|
||||
std::any_of(m_position_samples.begin(), m_position_samples.end(),
|
||||
[](const Common::Vec3& v) { return v.LengthSquared() != 0.0; }))
|
||||
{
|
||||
// Only start moving the line if there's non-zero data.
|
||||
m_grid_line_position = (m_grid_line_position + 1) % HISTORY_COUNT;
|
||||
}
|
||||
const double grid_line_x = 1.0 - m_grid_line_position * 2.0 / HISTORY_COUNT;
|
||||
const double grid_line_x = 1.0 - m_grid_line_position * 2.0;
|
||||
p.setPen(QPen(GetRawInputColor(), 0));
|
||||
p.drawLine(QPointF{grid_line_x, -1.0}, QPointF{grid_line_x, 1.0});
|
||||
|
||||
|
@ -629,12 +654,8 @@ void ShakeMappingIndicator::Draw()
|
|||
{
|
||||
QPolygonF polyline;
|
||||
|
||||
int i = 0;
|
||||
for (auto& sample : m_position_samples)
|
||||
{
|
||||
polyline.append(QPointF{1.0 - i * 2.0 / HISTORY_COUNT, sample.data[c]});
|
||||
++i;
|
||||
}
|
||||
polyline.append(QPointF{1.0 - sample.age * 2.0, sample.state.data[c]});
|
||||
|
||||
p.setPen(QPen(component_colors[c], 0));
|
||||
p.drawPolyline(polyline);
|
||||
|
@ -692,7 +713,7 @@ void AccelerometerMappingIndicator::Draw()
|
|||
p.setBrush(Qt::NoBrush);
|
||||
|
||||
p.resetTransform();
|
||||
p.translate(width() / 2, height() / 2);
|
||||
p.translate(width() / 2.0, height() / 2.0);
|
||||
|
||||
// Red dot upright target.
|
||||
p.setPen(GetAdjustedInputColor());
|
||||
|
@ -717,6 +738,28 @@ void AccelerometerMappingIndicator::Draw()
|
|||
fmt::format("{:.2f} g", state.Length() / WiimoteEmu::GRAVITY_ACCELERATION)));
|
||||
}
|
||||
|
||||
void GyroMappingIndicator::Update(float elapsed_seconds)
|
||||
{
|
||||
const auto gyro_state = m_gyro_group.GetState();
|
||||
const auto angular_velocity = gyro_state.value_or(Common::Vec3{});
|
||||
m_state *= WiimoteEmu::GetRotationFromGyroscope(angular_velocity * Common::Vec3(-1, +1, -1) *
|
||||
elapsed_seconds);
|
||||
m_state = m_state.Normalized();
|
||||
|
||||
// Reset orientation when stable for a bit:
|
||||
constexpr float STABLE_RESET_SECONDS = 1.f;
|
||||
// Consider device stable when data (with deadzone applied) is zero.
|
||||
const bool is_stable = !angular_velocity.LengthSquared();
|
||||
|
||||
if (!is_stable)
|
||||
m_stable_time = 0;
|
||||
else if (m_stable_time < STABLE_RESET_SECONDS)
|
||||
m_stable_time += elapsed_seconds;
|
||||
|
||||
if (m_stable_time >= STABLE_RESET_SECONDS)
|
||||
m_state = Common::Quaternion::Identity();
|
||||
}
|
||||
|
||||
void GyroMappingIndicator::Draw()
|
||||
{
|
||||
const auto gyro_state = m_gyro_group.GetState();
|
||||
|
@ -725,23 +768,9 @@ void GyroMappingIndicator::Draw()
|
|||
const auto jitter = raw_gyro_state - m_previous_velocity;
|
||||
m_previous_velocity = raw_gyro_state;
|
||||
|
||||
m_state *= WiimoteEmu::GetRotationFromGyroscope(angular_velocity * Common::Vec3(-1, +1, -1) /
|
||||
INDICATOR_UPDATE_FREQ);
|
||||
m_state = m_state.Normalized();
|
||||
|
||||
// Reset orientation when stable for a bit:
|
||||
constexpr u32 STABLE_RESET_STEPS = INDICATOR_UPDATE_FREQ;
|
||||
// Consider device stable when data (with deadzone applied) is zero.
|
||||
const bool is_stable = !angular_velocity.LengthSquared();
|
||||
|
||||
if (!is_stable)
|
||||
m_stable_steps = 0;
|
||||
else if (m_stable_steps != STABLE_RESET_STEPS)
|
||||
++m_stable_steps;
|
||||
|
||||
if (STABLE_RESET_STEPS == m_stable_steps)
|
||||
m_state = Common::Quaternion::Identity();
|
||||
|
||||
// Use an empty rotation matrix if gyroscope data is not present.
|
||||
const auto rotation =
|
||||
(gyro_state.has_value() ? Common::Matrix33::FromQuaternion(m_state) : Common::Matrix33{});
|
||||
|
@ -814,7 +843,7 @@ void GyroMappingIndicator::Draw()
|
|||
p.setBrush(Qt::NoBrush);
|
||||
|
||||
p.resetTransform();
|
||||
p.translate(width() / 2, height() / 2);
|
||||
p.translate(width() / 2.0, height() / 2.0);
|
||||
|
||||
// Red dot upright target.
|
||||
p.setPen(GetAdjustedInputColor());
|
||||
|
|
|
@ -45,9 +45,12 @@ public:
|
|||
|
||||
protected:
|
||||
virtual void Draw() {}
|
||||
virtual void Update(float elapsed_seconds) {}
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent*) override;
|
||||
|
||||
Clock::time_point m_last_update = Clock::now();
|
||||
};
|
||||
|
||||
class SquareIndicator : public MappingIndicator
|
||||
|
@ -98,6 +101,7 @@ public:
|
|||
|
||||
private:
|
||||
void Draw() override;
|
||||
void Update(float elapsed_seconds) override;
|
||||
|
||||
ControllerEmu::Tilt& m_group;
|
||||
WiimoteEmu::MotionState m_motion_state{};
|
||||
|
@ -132,6 +136,7 @@ public:
|
|||
|
||||
private:
|
||||
void Draw() override;
|
||||
void Update(float elapsed_seconds) override;
|
||||
|
||||
void DrawUnderGate(QPainter& p) override;
|
||||
|
||||
|
@ -146,11 +151,19 @@ public:
|
|||
|
||||
private:
|
||||
void Draw() override;
|
||||
void Update(float elapsed_seconds) override;
|
||||
|
||||
ControllerEmu::Shake& m_shake_group;
|
||||
WiimoteEmu::MotionState m_motion_state{};
|
||||
std::deque<ControllerEmu::Shake::StateData> m_position_samples;
|
||||
int m_grid_line_position = 0;
|
||||
|
||||
struct ShakeSample
|
||||
{
|
||||
ControllerEmu::Shake::StateData state;
|
||||
float age = 0.f;
|
||||
};
|
||||
|
||||
std::deque<ShakeSample> m_position_samples;
|
||||
float m_grid_line_position = 0;
|
||||
};
|
||||
|
||||
class AccelerometerMappingIndicator : public SquareIndicator
|
||||
|
@ -174,11 +187,12 @@ public:
|
|||
|
||||
private:
|
||||
void Draw() override;
|
||||
void Update(float elapsed_seconds) override;
|
||||
|
||||
ControllerEmu::IMUGyroscope& m_gyro_group;
|
||||
Common::Quaternion m_state = Common::Quaternion::Identity();
|
||||
Common::Vec3 m_previous_velocity = {};
|
||||
u32 m_stable_steps = 0;
|
||||
float m_stable_time = 0;
|
||||
};
|
||||
|
||||
class IRPassthroughMappingIndicator : public SquareIndicator
|
||||
|
|
|
@ -228,6 +228,8 @@ void MappingWidget::AddSettingWidgets(QFormLayout* layout, ControllerEmu::Contro
|
|||
const auto hbox = new QHBoxLayout;
|
||||
|
||||
hbox->addWidget(setting_widget);
|
||||
setting_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
||||
|
||||
hbox->addWidget(CreateSettingAdvancedMappingButton(*setting));
|
||||
|
||||
layout->addRow(tr(setting->GetUIName()), hbox);
|
||||
|
|
|
@ -27,8 +27,6 @@ class NumericSettingBase;
|
|||
enum class SettingVisibility;
|
||||
} // namespace ControllerEmu
|
||||
|
||||
constexpr int INDICATOR_UPDATE_FREQ = 30;
|
||||
|
||||
class MappingWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QScreen>
|
||||
#include <QTabWidget>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HotkeyManager.h"
|
||||
|
||||
#include "Common/CommonPaths.h"
|
||||
|
@ -73,12 +73,15 @@ MappingWindow::MappingWindow(QWidget* parent, Type type, int port_num)
|
|||
SetMappingType(type);
|
||||
|
||||
const auto timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, this, [this] {
|
||||
connect(timer, &QTimer::timeout, this, [this, timer] {
|
||||
const double refresh_rate = screen()->refreshRate();
|
||||
timer->setInterval(1000 / refresh_rate);
|
||||
|
||||
const auto lock = GetController()->GetStateLock();
|
||||
emit Update();
|
||||
});
|
||||
|
||||
timer->start(1000 / INDICATOR_UPDATE_FREQ);
|
||||
timer->start(100);
|
||||
|
||||
const auto lock = GetController()->GetStateLock();
|
||||
emit ConfigChanged();
|
||||
|
|
|
@ -379,6 +379,12 @@ void CodeWidget::UpdateSymbols()
|
|||
{
|
||||
QString name = QString::fromStdString(symbol.second.name);
|
||||
|
||||
// If the symbol has an object name, add it to the entry name.
|
||||
if (!symbol.second.object_name.empty())
|
||||
{
|
||||
name += QString::fromStdString(fmt::format(" ({})", symbol.second.object_name));
|
||||
}
|
||||
|
||||
auto* item = new QListWidgetItem(name);
|
||||
if (name == selection)
|
||||
item->setSelected(true);
|
||||
|
@ -408,8 +414,17 @@ void CodeWidget::UpdateFunctionCalls(const Common::Symbol* symbol)
|
|||
|
||||
if (call_symbol)
|
||||
{
|
||||
const QString name =
|
||||
QString::fromStdString(fmt::format("> {} ({:08x})", call_symbol->name, addr));
|
||||
QString name;
|
||||
|
||||
if (!call_symbol->object_name.empty())
|
||||
{
|
||||
name = QString::fromStdString(
|
||||
fmt::format("< {} ({}, {:08x})", call_symbol->name, call_symbol->object_name, addr));
|
||||
}
|
||||
else
|
||||
{
|
||||
name = QString::fromStdString(fmt::format("< {} ({:08x})", call_symbol->name, addr));
|
||||
}
|
||||
|
||||
if (!name.contains(filter, Qt::CaseInsensitive))
|
||||
continue;
|
||||
|
@ -433,8 +448,17 @@ void CodeWidget::UpdateFunctionCallers(const Common::Symbol* symbol)
|
|||
|
||||
if (caller_symbol)
|
||||
{
|
||||
const QString name =
|
||||
QString::fromStdString(fmt::format("< {} ({:08x})", caller_symbol->name, addr));
|
||||
QString name;
|
||||
|
||||
if (!caller_symbol->object_name.empty())
|
||||
{
|
||||
name = QString::fromStdString(fmt::format("< {} ({}, {:08x})", caller_symbol->name,
|
||||
caller_symbol->object_name, addr));
|
||||
}
|
||||
else
|
||||
{
|
||||
name = QString::fromStdString(fmt::format("< {} ({:08x})", caller_symbol->name, addr));
|
||||
}
|
||||
|
||||
if (!name.contains(filter, Qt::CaseInsensitive))
|
||||
continue;
|
||||
|
|
|
@ -271,6 +271,12 @@ void Host_PPCSymbolsChanged()
|
|||
QueueOnObject(QApplication::instance(), [] { emit Host::GetInstance()->PPCSymbolsChanged(); });
|
||||
}
|
||||
|
||||
void Host_PPCBreakpointsChanged()
|
||||
{
|
||||
QueueOnObject(QApplication::instance(),
|
||||
[] { emit Host::GetInstance()->PPCBreakpointsChanged(); });
|
||||
}
|
||||
|
||||
// We ignore these, and their purpose should be questioned individually.
|
||||
// In particular, RequestRenderWindowSize, RequestFullscreen, and
|
||||
// UpdateMainFrame should almost certainly be removed.
|
||||
|
|
|
@ -82,12 +82,7 @@ void GeneralPane::OnEmulationStateChanged(Core::State state)
|
|||
const bool running = state != Core::State::Uninitialized;
|
||||
|
||||
m_checkbox_dualcore->setEnabled(!running);
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
bool hardcore = AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
m_checkbox_cheats->setEnabled(!running && !hardcore);
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
m_checkbox_cheats->setEnabled(!running);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
m_checkbox_override_region_settings->setEnabled(!running);
|
||||
#ifdef USE_DISCORD_PRESENCE
|
||||
m_checkbox_discord_presence->setEnabled(!running);
|
||||
|
|
|
@ -25,14 +25,14 @@ IRWidget::IRWidget(QWidget* parent) : QWidget(parent)
|
|||
|
||||
void IRWidget::SetX(u16 x)
|
||||
{
|
||||
m_x = std::min(ir_max_x, x);
|
||||
m_x = std::min(IR_MAX_X, x);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void IRWidget::SetY(u16 y)
|
||||
{
|
||||
m_y = std::min(ir_max_y, y);
|
||||
m_y = std::min(IR_MAX_Y, y);
|
||||
|
||||
update();
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ void IRWidget::paintEvent(QPaintEvent* event)
|
|||
painter.drawLine(PADDING + w / 2, PADDING, PADDING + w / 2, PADDING + h);
|
||||
|
||||
// convert from value space to widget space
|
||||
u16 x = PADDING + ((m_x * w) / ir_max_x);
|
||||
u16 y = PADDING + (h - (m_y * h) / ir_max_y);
|
||||
u16 x = PADDING + ((m_x * w) / IR_MAX_X);
|
||||
u16 y = PADDING + (h - (m_y * h) / IR_MAX_Y);
|
||||
|
||||
painter.drawLine(PADDING + w / 2, PADDING + h / 2, x, y);
|
||||
|
||||
|
@ -84,17 +84,17 @@ void IRWidget::handleMouseEvent(QMouseEvent* event)
|
|||
|
||||
if (event->button() == Qt::RightButton)
|
||||
{
|
||||
m_x = std::round(ir_max_x / 2.);
|
||||
m_y = std::round(ir_max_y / 2.);
|
||||
m_x = std::round(IR_MAX_X / 2.);
|
||||
m_y = std::round(IR_MAX_Y / 2.);
|
||||
}
|
||||
else
|
||||
{
|
||||
// convert from widget space to value space
|
||||
int new_x = (event->pos().x() * ir_max_x) / width();
|
||||
int new_y = ir_max_y - (event->pos().y() * ir_max_y) / height();
|
||||
int new_x = (event->pos().x() * IR_MAX_X) / width();
|
||||
int new_y = IR_MAX_Y - (event->pos().y() * IR_MAX_Y) / height();
|
||||
|
||||
m_x = std::max(0, std::min(static_cast<int>(ir_max_x), new_x));
|
||||
m_y = std::max(0, std::min(static_cast<int>(ir_max_y), new_y));
|
||||
m_x = std::max(0, std::min(static_cast<int>(IR_MAX_X), new_x));
|
||||
m_y = std::max(0, std::min(static_cast<int>(IR_MAX_Y), new_y));
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
|
|
@ -13,6 +13,11 @@ class IRWidget : public QWidget
|
|||
public:
|
||||
explicit IRWidget(QWidget* parent);
|
||||
|
||||
static constexpr u16 IR_MIN_X = 0;
|
||||
static constexpr u16 IR_MIN_Y = 0;
|
||||
static constexpr u16 IR_MAX_X = 1023;
|
||||
static constexpr u16 IR_MAX_Y = 767;
|
||||
|
||||
signals:
|
||||
void ChangedX(u16 x);
|
||||
void ChangedY(u16 y);
|
||||
|
@ -32,9 +37,3 @@ private:
|
|||
u16 m_y = 0;
|
||||
bool m_ignore_movement = false;
|
||||
};
|
||||
|
||||
// Should be part of class but fails to compile on mac os
|
||||
static const u16 ir_min_x = 0;
|
||||
static const u16 ir_min_y = 0;
|
||||
static const u16 ir_max_x = 1023;
|
||||
static const u16 ir_max_y = 767;
|
||||
|
|
|
@ -51,20 +51,20 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
|
|||
ir_x_shortcut_key_sequence.toString(QKeySequence::NativeText),
|
||||
ir_y_shortcut_key_sequence.toString(QKeySequence::NativeText)));
|
||||
|
||||
const int ir_x_center = static_cast<int>(std::round(ir_max_x / 2.));
|
||||
const int ir_y_center = static_cast<int>(std::round(ir_max_y / 2.));
|
||||
const int ir_x_center = static_cast<int>(std::round(IRWidget::IR_MAX_X / 2.));
|
||||
const int ir_y_center = static_cast<int>(std::round(IRWidget::IR_MAX_Y / 2.));
|
||||
|
||||
auto* x_layout = new QHBoxLayout;
|
||||
m_ir_x_value = CreateSliderValuePair(
|
||||
WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE,
|
||||
&m_wiimote_overrider, x_layout, ir_x_center, ir_x_center, ir_min_x, ir_max_x,
|
||||
ir_x_shortcut_key_sequence, Qt::Horizontal, m_ir_box);
|
||||
&m_wiimote_overrider, x_layout, ir_x_center, ir_x_center, IRWidget::IR_MIN_X,
|
||||
IRWidget::IR_MAX_X, ir_x_shortcut_key_sequence, Qt::Horizontal, m_ir_box);
|
||||
|
||||
auto* y_layout = new QVBoxLayout;
|
||||
m_ir_y_value = CreateSliderValuePair(
|
||||
WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE,
|
||||
&m_wiimote_overrider, y_layout, ir_y_center, ir_y_center, ir_min_y, ir_max_y,
|
||||
ir_y_shortcut_key_sequence, Qt::Vertical, m_ir_box);
|
||||
&m_wiimote_overrider, y_layout, ir_y_center, ir_y_center, IRWidget::IR_MIN_Y,
|
||||
IRWidget::IR_MAX_Y, ir_y_shortcut_key_sequence, Qt::Vertical, m_ir_box);
|
||||
m_ir_y_value->setMaximumWidth(60);
|
||||
|
||||
auto* visual = new IRWidget(this);
|
||||
|
@ -76,7 +76,7 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
|
|||
connect(visual, &IRWidget::ChangedX, m_ir_x_value, &QSpinBox::setValue);
|
||||
connect(visual, &IRWidget::ChangedY, m_ir_y_value, &QSpinBox::setValue);
|
||||
|
||||
auto* visual_ar = new AspectRatioWidget(visual, ir_max_x, ir_max_y);
|
||||
auto* visual_ar = new AspectRatioWidget(visual, IRWidget::IR_MAX_X, IRWidget::IR_MAX_Y);
|
||||
|
||||
auto* visual_layout = new QHBoxLayout;
|
||||
visual_layout->addWidget(visual_ar);
|
||||
|
|
|
@ -25,6 +25,10 @@ void Host_PPCSymbolsChanged()
|
|||
{
|
||||
}
|
||||
|
||||
void Host_PPCBreakpointsChanged()
|
||||
{
|
||||
}
|
||||
|
||||
void Host_RefreshDSPDebuggerWindow()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -133,8 +133,10 @@ int VerifyCommand(const std::vector<std::string>& args)
|
|||
hashes_to_calculate.md5 = true;
|
||||
else if (algorithm == "sha1")
|
||||
hashes_to_calculate.sha1 = true;
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
else if (algorithm == "rchash")
|
||||
rc_hash_calculate = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!hashes_to_calculate.crc32 && !hashes_to_calculate.md5 && !hashes_to_calculate.sha1 &&
|
||||
|
@ -163,11 +165,13 @@ int VerifyCommand(const std::vector<std::string>& args)
|
|||
verifier.Finish();
|
||||
const DiscIO::VolumeVerifier::Result& result = verifier.GetResult();
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
// Calculate rcheevos hash
|
||||
if (rc_hash_calculate)
|
||||
{
|
||||
rc_hash_result = AchievementManager::CalculateHash(input_file_path);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Print the report
|
||||
if (!algorithm_is_set)
|
||||
|
|
|
@ -56,6 +56,38 @@ bool IsTriggerAxis(int index)
|
|||
return index >= 4;
|
||||
}
|
||||
|
||||
ControlState GetBatteryValueFromSDLPowerLevel(SDL_JoystickPowerLevel sdl_power_level)
|
||||
{
|
||||
// Values come from comments in SDL_joystick.h
|
||||
// A proper percentage will be exposed in SDL3.
|
||||
ControlState result;
|
||||
switch (sdl_power_level)
|
||||
{
|
||||
case SDL_JOYSTICK_POWER_EMPTY:
|
||||
result = 0.025;
|
||||
break;
|
||||
case SDL_JOYSTICK_POWER_LOW:
|
||||
result = 0.125;
|
||||
break;
|
||||
case SDL_JOYSTICK_POWER_MEDIUM:
|
||||
result = 0.45;
|
||||
break;
|
||||
case SDL_JOYSTICK_POWER_FULL:
|
||||
result = 0.85;
|
||||
break;
|
||||
case SDL_JOYSTICK_POWER_WIRED:
|
||||
case SDL_JOYSTICK_POWER_MAX:
|
||||
result = 1.0;
|
||||
break;
|
||||
case SDL_JOYSTICK_POWER_UNKNOWN:
|
||||
default:
|
||||
result = 0.0;
|
||||
break;
|
||||
}
|
||||
|
||||
return result * ciface::BATTERY_INPUT_MAX_VALUE;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ciface::SDL
|
||||
|
@ -142,6 +174,18 @@ private:
|
|||
const u8 m_direction;
|
||||
};
|
||||
|
||||
class BatteryInput final : public Input
|
||||
{
|
||||
public:
|
||||
explicit BatteryInput(const ControlState* battery_value) : m_battery_value(*battery_value) {}
|
||||
std::string GetName() const override { return "Battery"; }
|
||||
ControlState GetState() const override { return m_battery_value; }
|
||||
bool IsDetectable() const override { return false; }
|
||||
|
||||
private:
|
||||
const ControlState& m_battery_value;
|
||||
};
|
||||
|
||||
// Rumble
|
||||
template <int LowEnable, int HighEnable, int SuffixIndex>
|
||||
class GenericMotor : public Output
|
||||
|
@ -239,6 +283,39 @@ private:
|
|||
const Motor m_motor;
|
||||
};
|
||||
|
||||
class NormalizedInput : public Input
|
||||
{
|
||||
public:
|
||||
NormalizedInput(const char* name, const float* state) : m_name{std::move(name)}, m_state{*state}
|
||||
{
|
||||
}
|
||||
|
||||
std::string GetName() const override { return std::string{m_name}; }
|
||||
ControlState GetState() const override { return m_state; }
|
||||
|
||||
private:
|
||||
const char* const m_name;
|
||||
const float& m_state;
|
||||
};
|
||||
|
||||
template <int Scale>
|
||||
class NonDetectableDirectionalInput : public Input
|
||||
{
|
||||
public:
|
||||
NonDetectableDirectionalInput(const char* name, const float* state)
|
||||
: m_name{std::move(name)}, m_state{*state}
|
||||
{
|
||||
}
|
||||
|
||||
std::string GetName() const override { return std::string{m_name} + (Scale > 0 ? '+' : '-'); }
|
||||
bool IsDetectable() const override { return false; }
|
||||
ControlState GetState() const override { return m_state * Scale; }
|
||||
|
||||
private:
|
||||
const char* const m_name;
|
||||
const float& m_state;
|
||||
};
|
||||
|
||||
class MotionInput : public Input
|
||||
{
|
||||
public:
|
||||
|
@ -269,12 +346,32 @@ public:
|
|||
std::string GetName() const override;
|
||||
std::string GetSource() const override;
|
||||
int GetSDLInstanceID() const;
|
||||
Core::DeviceRemoval UpdateInput() override
|
||||
{
|
||||
m_battery_value = GetBatteryValueFromSDLPowerLevel(SDL_JoystickCurrentPowerLevel(m_joystick));
|
||||
|
||||
// We only support one touchpad and one finger.
|
||||
const int touchpad_index = 0;
|
||||
const int finger_index = 0;
|
||||
|
||||
Uint8 state = 0;
|
||||
SDL_GameControllerGetTouchpadFinger(m_gamecontroller, touchpad_index, finger_index, &state,
|
||||
&m_touchpad_x, &m_touchpad_y, &m_touchpad_pressure);
|
||||
m_touchpad_x = m_touchpad_x * 2 - 1;
|
||||
m_touchpad_y = m_touchpad_y * 2 - 1;
|
||||
|
||||
return Core::DeviceRemoval::Keep;
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_GameController* const m_gamecontroller;
|
||||
std::string m_name;
|
||||
SDL_Joystick* const m_joystick;
|
||||
SDL_Haptic* m_haptic = nullptr;
|
||||
ControlState m_battery_value;
|
||||
float m_touchpad_x = 0.f;
|
||||
float m_touchpad_y = 0.f;
|
||||
float m_touchpad_pressure = 0.f;
|
||||
};
|
||||
|
||||
class InputBackend final : public ciface::InputBackend
|
||||
|
@ -434,6 +531,8 @@ InputBackend::InputBackend(ControllerInterface* controller_interface)
|
|||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
// We want buttons to come in as positions, not labels
|
||||
SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0");
|
||||
// We have our own WGI backend. Enabling SDL's WGI handling creates even more redundant devices.
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_WGI, "0");
|
||||
|
||||
// Disable DualSense Player LEDs; We already colorize the Primary LED
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_PLAYER_LED, "0");
|
||||
|
@ -660,6 +759,18 @@ GameController::GameController(SDL_GameController* const gamecontroller,
|
|||
AddOutput(new MotorR(m_gamecontroller));
|
||||
}
|
||||
|
||||
// Touchpad
|
||||
if (SDL_GameControllerGetNumTouchpads(m_gamecontroller) > 0)
|
||||
{
|
||||
const char* const name_x = "Touchpad X";
|
||||
AddInput(new NonDetectableDirectionalInput<-1>(name_x, &m_touchpad_x));
|
||||
AddInput(new NonDetectableDirectionalInput<+1>(name_x, &m_touchpad_x));
|
||||
const char* const name_y = "Touchpad Y";
|
||||
AddInput(new NonDetectableDirectionalInput<-1>(name_y, &m_touchpad_y));
|
||||
AddInput(new NonDetectableDirectionalInput<+1>(name_y, &m_touchpad_y));
|
||||
AddInput(new NormalizedInput("Touchpad Pressure", &m_touchpad_pressure));
|
||||
}
|
||||
|
||||
// Motion
|
||||
const auto add_sensor = [this](SDL_SensorType type, std::string_view sensor_name,
|
||||
const SDLMotionAxisList& axes) {
|
||||
|
@ -767,6 +878,17 @@ GameController::GameController(SDL_GameController* const gamecontroller,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Needed to make the below power level not "UNKNOWN".
|
||||
SDL_JoystickUpdate();
|
||||
|
||||
// Battery
|
||||
if (SDL_JoystickPowerLevel const power_level = SDL_JoystickCurrentPowerLevel(m_joystick);
|
||||
power_level != SDL_JOYSTICK_POWER_UNKNOWN)
|
||||
{
|
||||
m_battery_value = GetBatteryValueFromSDLPowerLevel(power_level);
|
||||
AddInput(new BatteryInput{&m_battery_value});
|
||||
}
|
||||
}
|
||||
|
||||
GameController::~GameController()
|
||||
|
|
|
@ -66,7 +66,13 @@ static void AddGCAdapter(libusb_device* device);
|
|||
static void ResetRumbleLockNeeded();
|
||||
#endif
|
||||
|
||||
static void Reset();
|
||||
enum class CalledFromReadThread
|
||||
{
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
static void Reset(CalledFromReadThread called_from_read_thread);
|
||||
static void Setup();
|
||||
static void ProcessInputPayload(const u8* data, std::size_t size);
|
||||
static void ReadThreadFunc();
|
||||
|
@ -123,6 +129,7 @@ static std::atomic<int> s_controller_write_payload_size{0};
|
|||
|
||||
static std::thread s_read_adapter_thread;
|
||||
static Common::Flag s_read_adapter_thread_running;
|
||||
static Common::Flag s_read_adapter_thread_needs_joining;
|
||||
static std::thread s_write_adapter_thread;
|
||||
static Common::Flag s_write_adapter_thread_running;
|
||||
static Common::Event s_write_happened;
|
||||
|
@ -324,7 +331,7 @@ static int HotplugCallback(libusb_context* ctx, libusb_device* dev, libusb_hotpl
|
|||
else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT)
|
||||
{
|
||||
if (s_handle != nullptr && libusb_get_device(s_handle) == dev)
|
||||
Reset();
|
||||
Reset(CalledFromReadThread::No);
|
||||
|
||||
// Reset a potential error status now that the adapter is unplugged
|
||||
if (s_status == AdapterStatus::Error)
|
||||
|
@ -516,8 +523,11 @@ static void Setup()
|
|||
s_detected = true;
|
||||
|
||||
// Make sure the thread isn't in the middle of shutting down while starting a new one
|
||||
if (s_read_adapter_thread_running.TestAndClear())
|
||||
if (s_read_adapter_thread_needs_joining.TestAndClear() ||
|
||||
s_read_adapter_thread_running.TestAndClear())
|
||||
{
|
||||
s_read_adapter_thread.join();
|
||||
}
|
||||
|
||||
s_read_adapter_thread_running.Set(true);
|
||||
s_read_adapter_thread = std::thread(ReadThreadFunc);
|
||||
|
@ -682,7 +692,7 @@ void Shutdown()
|
|||
libusb_hotplug_deregister_callback(*s_libusb_context, s_hotplug_handle);
|
||||
#endif
|
||||
#endif
|
||||
Reset();
|
||||
Reset(CalledFromReadThread::No);
|
||||
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
s_libusb_context.reset();
|
||||
|
@ -696,7 +706,7 @@ void Shutdown()
|
|||
}
|
||||
}
|
||||
|
||||
static void Reset()
|
||||
static void Reset(CalledFromReadThread called_from_read_thread)
|
||||
{
|
||||
#if GCADAPTER_USE_LIBUSB_IMPLEMENTATION
|
||||
std::unique_lock lock(s_init_mutex, std::defer_lock);
|
||||
|
@ -709,8 +719,16 @@ static void Reset()
|
|||
return;
|
||||
#endif
|
||||
|
||||
if (s_read_adapter_thread_running.TestAndClear())
|
||||
s_read_adapter_thread.join();
|
||||
if (called_from_read_thread == CalledFromReadThread::No)
|
||||
{
|
||||
if (s_read_adapter_thread_running.TestAndClear())
|
||||
s_read_adapter_thread.join();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_read_adapter_thread_needs_joining.Set();
|
||||
s_read_adapter_thread_running.Clear();
|
||||
}
|
||||
// The read thread will close the write thread
|
||||
|
||||
s_port_states.fill({});
|
||||
|
@ -790,7 +808,7 @@ void ProcessInputPayload(const u8* data, std::size_t size)
|
|||
ERROR_LOG_FMT(CONTROLLERINTERFACE, "error reading payload (size: {}, type: {:02x})", size,
|
||||
data[0]);
|
||||
#if GCADAPTER_USE_ANDROID_IMPLEMENTATION
|
||||
Reset();
|
||||
Reset(CalledFromReadThread::Yes);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
|
|
@ -16,6 +16,9 @@ std::vector<std::string> Host_GetPreferredLocales()
|
|||
void Host_PPCSymbolsChanged()
|
||||
{
|
||||
}
|
||||
void Host_PPCBreakpointsChanged()
|
||||
{
|
||||
}
|
||||
void Host_RefreshDSPDebuggerWindow()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
#include "Common/IOFile.h"
|
||||
#include "Common/IniFile.h"
|
||||
#include "Common/JsonUtil.h"
|
||||
#include "Core/ActionReplay.h"
|
||||
#include "Core/CheatCodes.h"
|
||||
#include "Core/GeckoCode.h"
|
||||
#include "Core/GeckoCodeConfig.h"
|
||||
#include "Core/PatchEngine.h"
|
||||
|
||||
struct GameHashes
|
||||
|
@ -27,6 +30,11 @@ struct GameHashes
|
|||
std::map<std::string /*hash*/, std::string /*patch name*/> hashes;
|
||||
};
|
||||
|
||||
using AllowList = std::map<std::string /*ID*/, GameHashes>;
|
||||
|
||||
void CheckHash(const std::string& game_id, GameHashes* game_hashes, const std::string& hash,
|
||||
const std::string& patch_name);
|
||||
|
||||
TEST(PatchAllowlist, VerifyHashes)
|
||||
{
|
||||
// Load allowlist
|
||||
|
@ -42,9 +50,9 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||
const auto& list_filepath = fmt::format("{}{}{}", sys_directory, DIR_SEP, APPROVED_LIST_FILENAME);
|
||||
ASSERT_TRUE(JsonFromFile(list_filepath, &json_tree, &error))
|
||||
<< "Failed to open file at " << list_filepath;
|
||||
// Parse allowlist - Map<game id, Map<hash, name>
|
||||
// Parse allowlist - Map<game id, Map<hash, name>>
|
||||
ASSERT_TRUE(json_tree.is<picojson::object>());
|
||||
std::map<std::string /*ID*/, GameHashes> allow_list;
|
||||
AllowList allow_list;
|
||||
for (const auto& entry : json_tree.get<picojson::object>())
|
||||
{
|
||||
ASSERT_TRUE(entry.second.is<picojson::object>());
|
||||
|
@ -69,12 +77,25 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||
std::string game_id = file.virtualName.substr(0, file.virtualName.find_first_of('.'));
|
||||
std::vector<PatchEngine::Patch> patches;
|
||||
PatchEngine::LoadPatchSection("OnFrame", &patches, ini_file, Common::IniFile());
|
||||
std::vector<Gecko::GeckoCode> geckos = Gecko::LoadCodes(Common::IniFile(), ini_file);
|
||||
std::vector<ActionReplay::ARCode> action_replays =
|
||||
ActionReplay::LoadCodes(Common::IniFile(), ini_file);
|
||||
// Filter patches for RetroAchievements approved
|
||||
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "OnFrame", false, &patches);
|
||||
for (auto& patch : patches)
|
||||
patch.enabled = false;
|
||||
ReadEnabledOrDisabled<PatchEngine::Patch>(ini_file, "Patches_RetroAchievements_Verified", true,
|
||||
&patches);
|
||||
for (auto& code : geckos)
|
||||
code.enabled = false;
|
||||
ReadEnabledOrDisabled<Gecko::GeckoCode>(ini_file, "Gecko_RetroAchievements_Verified", true,
|
||||
&geckos);
|
||||
for (auto& code : action_replays)
|
||||
code.enabled = false;
|
||||
ReadEnabledOrDisabled<ActionReplay::ARCode>(ini_file, "AR_RetroAchievements_Verified", true,
|
||||
&action_replays);
|
||||
// Get game section from allow list
|
||||
auto game_itr = allow_list.find(game_id);
|
||||
bool itr_end = (game_itr == allow_list.end());
|
||||
// Iterate over approved patches
|
||||
for (const auto& patch : patches)
|
||||
{
|
||||
|
@ -92,38 +113,51 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||
context->Update(Common::BitCastToArray<u8>(entry.conditional));
|
||||
}
|
||||
auto digest = context->Finish();
|
||||
std::string hash = Common::SHA1::DigestToString(digest);
|
||||
// Check patch in list
|
||||
if (game_itr == allow_list.end())
|
||||
{
|
||||
// Report: no patches in game found in list
|
||||
ADD_FAILURE() << "Approved hash missing from list." << std::endl
|
||||
<< "Game ID: " << game_id << std::endl
|
||||
<< "Patch: \"" << hash << "\" : \"" << patch.name << "\"";
|
||||
CheckHash(game_id, itr_end ? nullptr : &game_itr->second,
|
||||
Common::SHA1::DigestToString(digest), patch.name);
|
||||
}
|
||||
// Iterate over approved geckos
|
||||
for (const auto& code : geckos)
|
||||
{
|
||||
if (!code.enabled)
|
||||
continue;
|
||||
}
|
||||
auto hash_itr = game_itr->second.hashes.find(hash);
|
||||
if (hash_itr == game_itr->second.hashes.end())
|
||||
// Hash patch
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.codes.size())));
|
||||
for (const auto& entry : code.codes)
|
||||
{
|
||||
// Report: patch not found in list
|
||||
ADD_FAILURE() << "Approved hash missing from list." << std::endl
|
||||
<< "Game ID: " << game_id << ":" << game_itr->second.game_title << std::endl
|
||||
<< "Patch: \"" << hash << "\" : \"" << patch.name << "\"";
|
||||
context->Update(Common::BitCastToArray<u8>(entry.address));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.data));
|
||||
}
|
||||
else
|
||||
auto digest = context->Finish();
|
||||
CheckHash(game_id, itr_end ? nullptr : &game_itr->second,
|
||||
Common::SHA1::DigestToString(digest), code.name);
|
||||
}
|
||||
// Iterate over approved AR codes
|
||||
for (const auto& code : action_replays)
|
||||
{
|
||||
if (!code.enabled)
|
||||
continue;
|
||||
// Hash patch
|
||||
auto context = Common::SHA1::CreateContext();
|
||||
context->Update(Common::BitCastToArray<u8>(static_cast<u64>(code.ops.size())));
|
||||
for (const auto& entry : code.ops)
|
||||
{
|
||||
// Remove patch from map if found
|
||||
game_itr->second.hashes.erase(hash_itr);
|
||||
context->Update(Common::BitCastToArray<u8>(entry.cmd_addr));
|
||||
context->Update(Common::BitCastToArray<u8>(entry.value));
|
||||
}
|
||||
auto digest = context->Finish();
|
||||
CheckHash(game_id, itr_end ? nullptr : &game_itr->second,
|
||||
Common::SHA1::DigestToString(digest), code.name);
|
||||
}
|
||||
// Report missing patches in map
|
||||
if (game_itr == allow_list.end())
|
||||
if (itr_end)
|
||||
continue;
|
||||
for (auto& remaining_hashes : game_itr->second.hashes)
|
||||
{
|
||||
ADD_FAILURE() << "Hash in list not approved in ini." << std::endl
|
||||
<< "Game ID: " << game_id << ":" << game_itr->second.game_title << std::endl
|
||||
<< "Patch: " << remaining_hashes.second << ":" << remaining_hashes.first;
|
||||
<< "Code: " << remaining_hashes.second << ":" << remaining_hashes.first;
|
||||
}
|
||||
// Remove section from map
|
||||
allow_list.erase(game_itr);
|
||||
|
@ -136,3 +170,30 @@ TEST(PatchAllowlist, VerifyHashes)
|
|||
<< remaining_games.second.game_title;
|
||||
}
|
||||
}
|
||||
|
||||
void CheckHash(const std::string& game_id, GameHashes* game_hashes, const std::string& hash,
|
||||
const std::string& patch_name)
|
||||
{
|
||||
// Check patch in list
|
||||
if (game_hashes == nullptr)
|
||||
{
|
||||
// Report: no patches in game found in list
|
||||
ADD_FAILURE() << "Approved hash missing from list." << std::endl
|
||||
<< "Game ID: " << game_id << std::endl
|
||||
<< "Code: \"" << hash << "\": \"" << patch_name << "\"";
|
||||
return;
|
||||
}
|
||||
auto hash_itr = game_hashes->hashes.find(hash);
|
||||
if (hash_itr == game_hashes->hashes.end())
|
||||
{
|
||||
// Report: patch not found in list
|
||||
ADD_FAILURE() << "Approved hash missing from list." << std::endl
|
||||
<< "Game ID: " << game_id << ":" << game_hashes->game_title << std::endl
|
||||
<< "Code: \"" << hash << "\": \"" << patch_name << "\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove patch from map if found
|
||||
game_hashes->hashes.erase(hash_itr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ std::vector<std::string> Host_GetPreferredLocales()
|
|||
void Host_PPCSymbolsChanged()
|
||||
{
|
||||
}
|
||||
void Host_PPCBreakpointsChanged()
|
||||
{
|
||||
}
|
||||
void Host_RefreshDSPDebuggerWindow()
|
||||
{
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue