mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-31 05:08:57 +00:00
Merge branch 'master' of https://github.com/dolphin-emu/dolphin into dolphin-emu-master
This commit is contained in:
commit
0a2d2c624b
511 changed files with 74722 additions and 58925 deletions
|
@ -100,7 +100,9 @@ std::string GetDefaultSoundBackend()
|
|||
#elif defined __linux__
|
||||
if (AlsaSound::IsValid())
|
||||
backend = BACKEND_ALSA;
|
||||
#elif defined(__APPLE__) || defined(_WIN32)
|
||||
else
|
||||
backend = BACKEND_CUBEB;
|
||||
#elif defined(__APPLE__) || defined(_WIN32) || defined(__OpenBSD__)
|
||||
backend = BACKEND_CUBEB;
|
||||
#endif
|
||||
return backend;
|
||||
|
|
|
@ -25,7 +25,8 @@ static void LogCallback(const char* format, ...)
|
|||
return;
|
||||
|
||||
constexpr auto log_type = Common::Log::LogType::AUDIO;
|
||||
if (!instance->IsEnabled(log_type))
|
||||
constexpr auto log_level = Common::Log::LogLevel::LINFO;
|
||||
if (!instance->IsEnabled(log_type, log_level))
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
|
@ -36,8 +37,7 @@ static void LogCallback(const char* format, ...)
|
|||
const std::string message = StringFromFormatV(adapted_format.c_str(), args);
|
||||
va_end(args);
|
||||
|
||||
instance->LogWithFullPath(Common::Log::LogLevel::LNOTICE, log_type, filename, lineno,
|
||||
message.c_str());
|
||||
instance->LogWithFullPath(log_level, log_type, filename, lineno, message.c_str());
|
||||
}
|
||||
|
||||
static void DestroyContext(cubeb* ctx)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
|
@ -15,7 +16,6 @@
|
|||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/SmallVector.h"
|
||||
|
@ -51,12 +51,12 @@ float FPImm8ToFloat(u8 bits)
|
|||
const u32 mantissa = (bits & 0xF) << 19;
|
||||
const u32 f = (sign << 31) | (exp << 23) | mantissa;
|
||||
|
||||
return Common::BitCast<float>(f);
|
||||
return std::bit_cast<float>(f);
|
||||
}
|
||||
|
||||
std::optional<u8> FPImm8FromFloat(float value)
|
||||
{
|
||||
const u32 f = Common::BitCast<u32>(value);
|
||||
const u32 f = std::bit_cast<u32>(value);
|
||||
const u32 mantissa4 = (f & 0x7FFFFF) >> 19;
|
||||
const u32 exponent = (f >> 23) & 0xFF;
|
||||
const u32 sign = f >> 31;
|
||||
|
@ -1809,16 +1809,16 @@ void ARM64XEmitter::ParallelMoves(RegisterMove* begin, RegisterMove* end,
|
|||
{
|
||||
bool removed_moves_during_this_loop_iteration = false;
|
||||
|
||||
RegisterMove* move = end;
|
||||
while (move != begin)
|
||||
RegisterMove* current_move = end;
|
||||
while (current_move != begin)
|
||||
{
|
||||
RegisterMove* prev_move = move;
|
||||
--move;
|
||||
if ((*source_gpr_usages)[DecodeReg(move->dst)] == 0)
|
||||
RegisterMove* prev_move = current_move;
|
||||
--current_move;
|
||||
if ((*source_gpr_usages)[DecodeReg(current_move->dst)] == 0)
|
||||
{
|
||||
MOV(move->dst, move->src);
|
||||
(*source_gpr_usages)[DecodeReg(move->src)]--;
|
||||
std::move(prev_move, end, move);
|
||||
MOV(current_move->dst, current_move->src);
|
||||
(*source_gpr_usages)[DecodeReg(current_move->src)]--;
|
||||
std::move(prev_move, end, current_move);
|
||||
--end;
|
||||
removed_moves_during_this_loop_iteration = true;
|
||||
}
|
||||
|
@ -1832,7 +1832,7 @@ void ARM64XEmitter::ParallelMoves(RegisterMove* begin, RegisterMove* end,
|
|||
while ((*source_gpr_usages)[temp_reg] != 0)
|
||||
{
|
||||
++temp_reg;
|
||||
ASSERT_MSG(COMMON, temp_reg != temp_reg_end, "Out of registers");
|
||||
ASSERT_MSG(DYNA_REC, temp_reg != temp_reg_end, "Out of registers");
|
||||
}
|
||||
|
||||
const ARM64Reg src = begin->src;
|
||||
|
@ -3893,6 +3893,8 @@ void ARM64FloatEmitter::ABI_PushRegisters(BitSet32 registers, ARM64Reg tmp)
|
|||
|
||||
if (bundled_loadstore && tmp != ARM64Reg::INVALID_REG)
|
||||
{
|
||||
DEBUG_ASSERT_MSG(DYNA_REC, Is64Bit(tmp), "Expected a 64-bit temporary register!");
|
||||
|
||||
int num_regs = registers.Count();
|
||||
m_emit->SUB(ARM64Reg::SP, ARM64Reg::SP, num_regs * 16);
|
||||
m_emit->ADD(tmp, ARM64Reg::SP, 0);
|
||||
|
@ -4410,7 +4412,7 @@ void ARM64FloatEmitter::MOVI2F(ARM64Reg Rd, float value, ARM64Reg scratch, bool
|
|||
if (negate)
|
||||
value = -value;
|
||||
|
||||
const u32 ival = Common::BitCast<u32>(value);
|
||||
const u32 ival = std::bit_cast<u32>(value);
|
||||
m_emit->MOVI2R(scratch, ival);
|
||||
FMOV(Rd, scratch);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Common/Assembler/GekkoIRGen.h"
|
||||
|
||||
#include <bit>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
|
@ -436,13 +437,13 @@ void GekkoIRPlugin::AddBytes(T val)
|
|||
else if constexpr (std::is_same_v<T, float>)
|
||||
{
|
||||
static_assert(sizeof(double) == sizeof(u64));
|
||||
AddBytes(BitCast<u32>(val));
|
||||
AddBytes(std::bit_cast<u32>(val));
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::is_same_v<T, double>
|
||||
static_assert(sizeof(double) == sizeof(u64));
|
||||
AddBytes(BitCast<u64>(val));
|
||||
AddBytes(std::bit_cast<u64>(val));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,39 +125,6 @@ constexpr bool IsValidLowMask(const T mask) noexcept
|
|||
return (mask & (mask + 1)) == 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// Reinterpret objects of one type as another by bit-casting between object representations.
|
||||
///
|
||||
/// @remark This is the example implementation of std::bit_cast which is to be included
|
||||
/// in C++2a. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0476r2.html
|
||||
/// for more details. The only difference is this variant is not constexpr,
|
||||
/// as the mechanism for bit_cast requires a compiler built-in to have that quality.
|
||||
///
|
||||
/// @param source The source object to convert to another representation.
|
||||
///
|
||||
/// @tparam To The type to reinterpret source as.
|
||||
/// @tparam From The initial type representation of source.
|
||||
///
|
||||
/// @return The representation of type From as type To.
|
||||
///
|
||||
/// @pre Both To and From types must be the same size
|
||||
/// @pre Both To and From types must satisfy the TriviallyCopyable concept.
|
||||
///
|
||||
template <typename To, typename From>
|
||||
inline To BitCast(const From& source) noexcept
|
||||
{
|
||||
static_assert(sizeof(From) == sizeof(To),
|
||||
"BitCast source and destination types must be equal in size.");
|
||||
static_assert(std::is_trivially_copyable<From>(),
|
||||
"BitCast source type must be trivially copyable.");
|
||||
static_assert(std::is_trivially_copyable<To>(),
|
||||
"BitCast destination type must be trivially copyable.");
|
||||
|
||||
alignas(To) std::byte storage[sizeof(To)];
|
||||
std::memcpy(&storage, &source, sizeof(storage));
|
||||
return reinterpret_cast<To&>(storage);
|
||||
}
|
||||
|
||||
template <typename T, typename PtrType>
|
||||
class BitCastPtrType
|
||||
{
|
||||
|
|
|
@ -108,8 +108,6 @@ add_library(common
|
|||
Network.h
|
||||
PcapFile.cpp
|
||||
PcapFile.h
|
||||
PerformanceCounter.cpp
|
||||
PerformanceCounter.h
|
||||
Profiler.cpp
|
||||
Profiler.h
|
||||
QoSSession.cpp
|
||||
|
@ -128,6 +126,7 @@ add_library(common
|
|||
SmallVector.h
|
||||
SocketContext.cpp
|
||||
SocketContext.h
|
||||
SpanUtils.h
|
||||
SPSCQueue.h
|
||||
StringLiteral.h
|
||||
StringUtil.cpp
|
||||
|
|
|
@ -82,6 +82,10 @@ public:
|
|||
}
|
||||
|
||||
bool IsInSpace(const u8* ptr) const { return ptr >= region && ptr < (region + region_size); }
|
||||
bool IsInSpaceOrChildSpace(const u8* ptr) const
|
||||
{
|
||||
return ptr >= region && ptr < (region + total_region_size);
|
||||
}
|
||||
void WriteProtect(bool allow_execute)
|
||||
{
|
||||
Common::WriteProtectMemory(region, region_size, allow_execute);
|
||||
|
@ -106,7 +110,7 @@ public:
|
|||
bool HasChildren() const { return region_size != total_region_size; }
|
||||
u8* AllocChildCodeSpace(size_t child_size)
|
||||
{
|
||||
ASSERT_MSG(DYNA_REC, child_size < GetSpaceLeft(), "Insufficient space for child allocation.");
|
||||
ASSERT_MSG(DYNA_REC, child_size <= GetSpaceLeft(), "Insufficient space for child allocation.");
|
||||
u8* child_region = region + region_size - child_size;
|
||||
region_size -= child_size;
|
||||
ResetCodePtr();
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
#define DUMP_SSL_DIR "SSL"
|
||||
#define DUMP_DEBUG_DIR "Debug"
|
||||
#define DUMP_DEBUG_BRANCHWATCH_DIR "BranchWatch"
|
||||
#define DUMP_DEBUG_JITBLOCKS_DIR "JitBlocks"
|
||||
#define LOGS_DIR "Logs"
|
||||
#define MAIL_LOGS_DIR "Mail"
|
||||
#define SHADERS_DIR "Shaders"
|
||||
|
|
|
@ -859,6 +859,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||
s_user_paths[D_DUMPDEBUG_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DEBUG_DIR DIR_SEP;
|
||||
s_user_paths[D_DUMPDEBUG_BRANCHWATCH_IDX] =
|
||||
s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_BRANCHWATCH_DIR DIR_SEP;
|
||||
s_user_paths[D_DUMPDEBUG_JITBLOCKS_IDX] =
|
||||
s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_JITBLOCKS_DIR DIR_SEP;
|
||||
s_user_paths[D_LOGS_IDX] = s_user_paths[D_USER_IDX] + LOGS_DIR DIR_SEP;
|
||||
s_user_paths[D_MAILLOGS_IDX] = s_user_paths[D_LOGS_IDX] + MAIL_LOGS_DIR DIR_SEP;
|
||||
s_user_paths[D_THEMES_IDX] = s_user_paths[D_USER_IDX] + THEMES_DIR DIR_SEP;
|
||||
|
@ -916,6 +918,8 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||
s_user_paths[F_DUALSHOCKUDPCLIENTCONFIG_IDX] =
|
||||
s_user_paths[D_CONFIG_IDX] + DUALSHOCKUDPCLIENT_CONFIG;
|
||||
s_user_paths[F_FREELOOKCONFIG_IDX] = s_user_paths[D_CONFIG_IDX] + FREELOOK_CONFIG;
|
||||
s_user_paths[F_RETROACHIEVEMENTSCONFIG_IDX] =
|
||||
s_user_paths[D_CONFIG_IDX] + RETROACHIEVEMENTS_CONFIG;
|
||||
break;
|
||||
|
||||
case D_CACHE_IDX:
|
||||
|
@ -937,7 +941,9 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||
s_user_paths[D_DUMPSSL_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_SSL_DIR DIR_SEP;
|
||||
s_user_paths[D_DUMPDEBUG_IDX] = s_user_paths[D_DUMP_IDX] + DUMP_DEBUG_DIR DIR_SEP;
|
||||
s_user_paths[D_DUMPDEBUG_BRANCHWATCH_IDX] =
|
||||
s_user_paths[D_DUMP_IDX] + DUMP_DEBUG_BRANCHWATCH_DIR DIR_SEP;
|
||||
s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_BRANCHWATCH_DIR DIR_SEP;
|
||||
s_user_paths[D_DUMPDEBUG_JITBLOCKS_IDX] =
|
||||
s_user_paths[D_DUMPDEBUG_IDX] + DUMP_DEBUG_JITBLOCKS_DIR DIR_SEP;
|
||||
s_user_paths[F_MEM1DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM1_DUMP;
|
||||
s_user_paths[F_MEM2DUMP_IDX] = s_user_paths[D_DUMP_IDX] + MEM2_DUMP;
|
||||
s_user_paths[F_ARAMDUMP_IDX] = s_user_paths[D_DUMP_IDX] + ARAM_DUMP;
|
||||
|
|
|
@ -54,6 +54,7 @@ enum
|
|||
D_DUMPSSL_IDX,
|
||||
D_DUMPDEBUG_IDX,
|
||||
D_DUMPDEBUG_BRANCHWATCH_IDX,
|
||||
D_DUMPDEBUG_JITBLOCKS_IDX,
|
||||
D_LOAD_IDX,
|
||||
D_LOGS_IDX,
|
||||
D_MAILLOGS_IDX,
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
|
||||
#include "Common/FloatUtils.h"
|
||||
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
u32 ClassifyDouble(double dvalue)
|
||||
{
|
||||
const u64 ivalue = BitCast<u64>(dvalue);
|
||||
const u64 ivalue = std::bit_cast<u64>(dvalue);
|
||||
const u64 sign = ivalue & DOUBLE_SIGN;
|
||||
const u64 exp = ivalue & DOUBLE_EXP;
|
||||
|
||||
|
@ -43,7 +42,7 @@ u32 ClassifyDouble(double dvalue)
|
|||
|
||||
u32 ClassifyFloat(float fvalue)
|
||||
{
|
||||
const u32 ivalue = BitCast<u32>(fvalue);
|
||||
const u32 ivalue = std::bit_cast<u32>(fvalue);
|
||||
const u32 sign = ivalue & FLOAT_SIGN;
|
||||
const u32 exp = ivalue & FLOAT_EXP;
|
||||
|
||||
|
@ -86,7 +85,7 @@ const std::array<BaseAndDec, 32> frsqrte_expected = {{
|
|||
|
||||
double ApproximateReciprocalSquareRoot(double val)
|
||||
{
|
||||
s64 integral = BitCast<s64>(val);
|
||||
s64 integral = std::bit_cast<s64>(val);
|
||||
s64 mantissa = integral & ((1LL << 52) - 1);
|
||||
const s64 sign = integral & (1ULL << 63);
|
||||
s64 exponent = integral & (0x7FFLL << 52);
|
||||
|
@ -136,7 +135,7 @@ double ApproximateReciprocalSquareRoot(double val)
|
|||
const auto& entry = frsqrte_expected[i / 2048];
|
||||
integral |= static_cast<s64>(entry.m_base + entry.m_dec * (i % 2048)) << 26;
|
||||
|
||||
return BitCast<double>(integral);
|
||||
return std::bit_cast<double>(integral);
|
||||
}
|
||||
|
||||
const std::array<BaseAndDec, 32> fres_expected = {{
|
||||
|
@ -152,7 +151,7 @@ const std::array<BaseAndDec, 32> fres_expected = {{
|
|||
// Used by fres and ps_res.
|
||||
double ApproximateReciprocal(double val)
|
||||
{
|
||||
s64 integral = BitCast<s64>(val);
|
||||
s64 integral = std::bit_cast<s64>(val);
|
||||
const s64 mantissa = integral & ((1LL << 52) - 1);
|
||||
const s64 sign = integral & (1ULL << 63);
|
||||
s64 exponent = integral & (0x7FFLL << 52);
|
||||
|
@ -184,7 +183,7 @@ double ApproximateReciprocal(double val)
|
|||
integral = sign | exponent;
|
||||
integral |= static_cast<s64>(entry.m_base - (entry.m_dec * (i % 1024) + 1) / 2) << 29;
|
||||
|
||||
return BitCast<double>(integral);
|
||||
return std::bit_cast<double>(integral);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <limits>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Common
|
||||
|
@ -35,37 +35,37 @@ static constexpr int FLOAT_FRAC_WIDTH = 23;
|
|||
|
||||
inline bool IsQNAN(double d)
|
||||
{
|
||||
const u64 i = BitCast<u64>(d);
|
||||
const u64 i = std::bit_cast<u64>(d);
|
||||
return ((i & DOUBLE_EXP) == DOUBLE_EXP) && ((i & DOUBLE_QBIT) == DOUBLE_QBIT);
|
||||
}
|
||||
|
||||
inline bool IsSNAN(double d)
|
||||
{
|
||||
const u64 i = BitCast<u64>(d);
|
||||
const u64 i = std::bit_cast<u64>(d);
|
||||
return ((i & DOUBLE_EXP) == DOUBLE_EXP) && ((i & DOUBLE_FRAC) != DOUBLE_ZERO) &&
|
||||
((i & DOUBLE_QBIT) == DOUBLE_ZERO);
|
||||
}
|
||||
|
||||
inline float FlushToZero(float f)
|
||||
{
|
||||
u32 i = BitCast<u32>(f);
|
||||
u32 i = std::bit_cast<u32>(f);
|
||||
if ((i & FLOAT_EXP) == 0)
|
||||
{
|
||||
// Turn into signed zero
|
||||
i &= FLOAT_SIGN;
|
||||
}
|
||||
return BitCast<float>(i);
|
||||
return std::bit_cast<float>(i);
|
||||
}
|
||||
|
||||
inline double FlushToZero(double d)
|
||||
{
|
||||
u64 i = BitCast<u64>(d);
|
||||
u64 i = std::bit_cast<u64>(d);
|
||||
if ((i & DOUBLE_EXP) == 0)
|
||||
{
|
||||
// Turn into signed zero
|
||||
i &= DOUBLE_SIGN;
|
||||
}
|
||||
return BitCast<double>(i);
|
||||
return std::bit_cast<double>(i);
|
||||
}
|
||||
|
||||
enum PPCFpClass
|
||||
|
|
|
@ -14,7 +14,27 @@ picojson::object ToJsonObject(const Common::Vec3& vec)
|
|||
|
||||
void FromJson(const picojson::object& obj, Common::Vec3& vec)
|
||||
{
|
||||
vec.x = ReadNumericOrDefault<float>(obj, "x");
|
||||
vec.y = ReadNumericOrDefault<float>(obj, "y");
|
||||
vec.z = ReadNumericOrDefault<float>(obj, "z");
|
||||
vec.x = ReadNumericFromJson<float>(obj, "x").value_or(0.0f);
|
||||
vec.y = ReadNumericFromJson<float>(obj, "y").value_or(0.0f);
|
||||
vec.z = ReadNumericFromJson<float>(obj, "z").value_or(0.0f);
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadStringFromJson(const picojson::object& obj, const std::string& key)
|
||||
{
|
||||
const auto it = obj.find(key);
|
||||
if (it == obj.end())
|
||||
return std::nullopt;
|
||||
if (!it->second.is<std::string>())
|
||||
return std::nullopt;
|
||||
return it->second.to_str();
|
||||
}
|
||||
|
||||
std::optional<bool> ReadBoolFromJson(const picojson::object& obj, const std::string& key)
|
||||
{
|
||||
const auto it = obj.find(key);
|
||||
if (it == obj.end())
|
||||
return std::nullopt;
|
||||
if (!it->second.is<bool>())
|
||||
return std::nullopt;
|
||||
return it->second.get<bool>();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include <picojson.h>
|
||||
|
||||
|
@ -17,28 +19,40 @@
|
|||
template <typename Range>
|
||||
picojson::array ToJsonArray(const Range& data)
|
||||
{
|
||||
using RangeUnderlyingType = typename Range::value_type;
|
||||
picojson::array result;
|
||||
result.reserve(std::size(data));
|
||||
|
||||
for (const auto& value : data)
|
||||
{
|
||||
result.emplace_back(static_cast<double>(value));
|
||||
if constexpr (std::is_integral_v<RangeUnderlyingType> ||
|
||||
std::is_floating_point_v<RangeUnderlyingType>)
|
||||
{
|
||||
result.emplace_back(static_cast<double>(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.emplace_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
Type ReadNumericOrDefault(const picojson::object& obj, const std::string& key,
|
||||
Type default_value = Type{})
|
||||
std::optional<Type> ReadNumericFromJson(const picojson::object& obj, const std::string& key)
|
||||
{
|
||||
const auto it = obj.find(key);
|
||||
if (it == obj.end())
|
||||
return default_value;
|
||||
return std::nullopt;
|
||||
if (!it->second.is<double>())
|
||||
return default_value;
|
||||
return std::nullopt;
|
||||
return MathUtil::SaturatingCast<Type>(it->second.get<double>());
|
||||
}
|
||||
|
||||
std::optional<std::string> ReadStringFromJson(const picojson::object& obj, const std::string& key);
|
||||
|
||||
std::optional<bool> ReadBoolFromJson(const picojson::object& obj, const std::string& key);
|
||||
|
||||
picojson::object ToJsonObject(const Common::Vec3& vec);
|
||||
void FromJson(const picojson::object& obj, Common::Vec3& vec);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Common/Network.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
|
@ -307,8 +308,8 @@ u16 ComputeNetworkChecksum(const void* data, u16 length, u32 initial_value)
|
|||
u16 ComputeTCPNetworkChecksum(const IPAddress& from, const IPAddress& to, const void* data,
|
||||
u16 length, u8 protocol)
|
||||
{
|
||||
const u32 source_addr = ntohl(Common::BitCast<u32>(from));
|
||||
const u32 destination_addr = ntohl(Common::BitCast<u32>(to));
|
||||
const u32 source_addr = ntohl(std::bit_cast<u32>(from));
|
||||
const u32 destination_addr = ntohl(std::bit_cast<u32>(to));
|
||||
const u32 initial_value = (source_addr >> 16) + (source_addr & 0xFFFF) +
|
||||
(destination_addr >> 16) + (destination_addr & 0xFFFF) + protocol +
|
||||
length;
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include "Common/PerformanceCounter.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
|
||||
#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0
|
||||
#define DOLPHIN_CLOCK CLOCK_MONOTONIC
|
||||
#else
|
||||
#define DOLPHIN_CLOCK CLOCK_REALTIME
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool QueryPerformanceCounter(u64* out)
|
||||
{
|
||||
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
|
||||
timespec tp;
|
||||
if (clock_gettime(DOLPHIN_CLOCK, &tp))
|
||||
return false;
|
||||
*out = (u64)tp.tv_nsec + (u64)1000000000 * (u64)tp.tv_sec;
|
||||
return true;
|
||||
#else
|
||||
*out = 0;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QueryPerformanceFrequency(u64* out)
|
||||
{
|
||||
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0
|
||||
*out = 1000000000;
|
||||
return true;
|
||||
#else
|
||||
*out = 1;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,16 +0,0 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
typedef u64 LARGE_INTEGER;
|
||||
bool QueryPerformanceCounter(u64* out);
|
||||
bool QueryPerformanceFrequency(u64* lpFrequency);
|
||||
|
||||
#endif
|
|
@ -17,14 +17,14 @@
|
|||
|
||||
namespace Common
|
||||
{
|
||||
SettingsHandler::SettingsHandler()
|
||||
SettingsHandler::SettingsHandler() : m_buffer{}, m_position{0}, m_key{INITIAL_SEED}, decoded{""}
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
SettingsHandler::SettingsHandler(Buffer&& buffer)
|
||||
SettingsHandler::SettingsHandler(const Buffer& buffer) : SettingsHandler()
|
||||
{
|
||||
SetBytes(std::move(buffer));
|
||||
m_buffer = buffer;
|
||||
Decrypt();
|
||||
}
|
||||
|
||||
const SettingsHandler::Buffer& SettingsHandler::GetBytes() const
|
||||
|
@ -32,13 +32,6 @@ const SettingsHandler::Buffer& SettingsHandler::GetBytes() const
|
|||
return m_buffer;
|
||||
}
|
||||
|
||||
void SettingsHandler::SetBytes(Buffer&& buffer)
|
||||
{
|
||||
Reset();
|
||||
m_buffer = std::move(buffer);
|
||||
Decrypt();
|
||||
}
|
||||
|
||||
std::string SettingsHandler::GetValue(std::string_view key) const
|
||||
{
|
||||
constexpr char delim[] = "\n";
|
||||
|
@ -84,14 +77,6 @@ void SettingsHandler::Decrypt()
|
|||
std::erase(decoded, '\x0d');
|
||||
}
|
||||
|
||||
void SettingsHandler::Reset()
|
||||
{
|
||||
decoded = "";
|
||||
m_position = 0;
|
||||
m_key = INITIAL_SEED;
|
||||
m_buffer = {};
|
||||
}
|
||||
|
||||
void SettingsHandler::AddSetting(std::string_view key, std::string_view value)
|
||||
{
|
||||
WriteLine(fmt::format("{}={}\r\n", key, value));
|
||||
|
|
|
@ -25,19 +25,17 @@ public:
|
|||
|
||||
using Buffer = std::array<u8, SETTINGS_SIZE>;
|
||||
SettingsHandler();
|
||||
explicit SettingsHandler(Buffer&& buffer);
|
||||
explicit SettingsHandler(const Buffer& buffer);
|
||||
|
||||
void AddSetting(std::string_view key, std::string_view value);
|
||||
|
||||
const Buffer& GetBytes() const;
|
||||
void SetBytes(Buffer&& buffer);
|
||||
std::string GetValue(std::string_view key) const;
|
||||
|
||||
void Decrypt();
|
||||
void Reset();
|
||||
static std::string GenerateSerialNumber();
|
||||
|
||||
private:
|
||||
void Decrypt();
|
||||
void WriteLine(std::string_view str);
|
||||
void WriteByte(u8 b);
|
||||
|
||||
|
|
|
@ -8,12 +8,27 @@ namespace Common
|
|||
#ifdef _WIN32
|
||||
SocketContext::SocketContext()
|
||||
{
|
||||
static_cast<void>(WSAStartup(MAKEWORD(2, 2), &m_data));
|
||||
std::lock_guard<std::mutex> g(s_lock);
|
||||
if (s_num_objects == 0)
|
||||
{
|
||||
static_cast<void>(WSAStartup(MAKEWORD(2, 2), &s_data));
|
||||
}
|
||||
s_num_objects++;
|
||||
}
|
||||
SocketContext::~SocketContext()
|
||||
{
|
||||
WSACleanup();
|
||||
std::lock_guard<std::mutex> g(s_lock);
|
||||
s_num_objects--;
|
||||
if (s_num_objects == 0)
|
||||
{
|
||||
WSACleanup();
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex SocketContext::s_lock;
|
||||
size_t SocketContext::s_num_objects = 0;
|
||||
WSADATA SocketContext::s_data;
|
||||
|
||||
#else
|
||||
SocketContext::SocketContext() = default;
|
||||
SocketContext::~SocketContext() = default;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
namespace Common
|
||||
|
@ -23,7 +24,9 @@ public:
|
|||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
WSADATA m_data;
|
||||
static std::mutex s_lock;
|
||||
static size_t s_num_objects;
|
||||
static WSADATA s_data;
|
||||
#endif
|
||||
};
|
||||
} // namespace Common
|
||||
|
|
41
Source/Core/Common/SpanUtils.h
Normal file
41
Source/Core/Common/SpanUtils.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
// Like std::span::subspan, except undefined behavior is replaced with returning a 0-length span.
|
||||
template <class T>
|
||||
[[nodiscard]] constexpr std::span<T> SafeSubspan(std::span<T> span, size_t offset,
|
||||
size_t count = std::dynamic_extent)
|
||||
{
|
||||
if (count == std::dynamic_extent || offset > span.size())
|
||||
return span.subspan(std::min(offset, span.size()));
|
||||
else
|
||||
return span.subspan(offset, std::min(count, span.size() - offset));
|
||||
}
|
||||
|
||||
// Default-constructs an object of type T, then copies data into it from the specified offset in
|
||||
// the specified span. Out-of-bounds reads will be skipped, meaning that specifying a too large
|
||||
// offset results in the object partially or entirely remaining default constructed.
|
||||
template <class T>
|
||||
[[nodiscard]] T SafeSpanRead(std::span<const u8> span, size_t offset)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable<T>());
|
||||
|
||||
const std::span<const u8> subspan = SafeSubspan(span, offset);
|
||||
T result{};
|
||||
std::memcpy(&result, subspan.data(), std::min(subspan.size(), sizeof(result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Common
|
|
@ -35,9 +35,6 @@
|
|||
constexpr u32 CODEPAGE_SHIFT_JIS = 932;
|
||||
constexpr u32 CODEPAGE_WINDOWS_1252 = 1252;
|
||||
#else
|
||||
#if defined(__NetBSD__)
|
||||
#define LIBICONV_PLUG
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <iconv.h>
|
||||
#include <locale.h>
|
||||
|
@ -528,13 +525,8 @@ std::string CodeTo(const char* tocode, const char* fromcode, std::basic_string_v
|
|||
while (src_bytes != 0)
|
||||
{
|
||||
size_t const iconv_result =
|
||||
#if defined(__NetBSD__)
|
||||
iconv(conv_desc, reinterpret_cast<const char**>(&src_buffer), &src_bytes, &dst_buffer,
|
||||
&dst_bytes);
|
||||
#else
|
||||
iconv(conv_desc, const_cast<char**>(reinterpret_cast<const char**>(&src_buffer)),
|
||||
&src_bytes, &dst_buffer, &dst_bytes);
|
||||
#endif
|
||||
if ((size_t)-1 == iconv_result)
|
||||
{
|
||||
if (EILSEQ == errno || EINVAL == errno)
|
||||
|
|
|
@ -67,4 +67,10 @@ const std::string& GetNetplayDolphinVer()
|
|||
return netplay_dolphin_ver;
|
||||
}
|
||||
|
||||
int GetScmCommitsAheadMaster()
|
||||
{
|
||||
// Note this macro can be empty if the master branch does not exist.
|
||||
return SCM_COMMITS_AHEAD_MASTER + 0;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -14,4 +14,5 @@ const std::string& GetScmRevGitStr();
|
|||
const std::string& GetScmDistributorStr();
|
||||
const std::string& GetScmUpdateTrackStr();
|
||||
const std::string& GetNetplayDolphinVer();
|
||||
int GetScmCommitsAheadMaster();
|
||||
} // namespace Common
|
||||
|
|
|
@ -5,6 +5,7 @@ var outfile = "./scmrev.h";
|
|||
var cmd_revision = " rev-parse HEAD";
|
||||
var cmd_describe = " describe --always --long --dirty";
|
||||
var cmd_branch = " rev-parse --abbrev-ref HEAD";
|
||||
var cmd_commits_ahead = " rev-list --count HEAD ^master";
|
||||
|
||||
function GetGitExe()
|
||||
{
|
||||
|
@ -76,7 +77,7 @@ var gitexe = GetGitExe();
|
|||
var revision = GetFirstStdOutLine(gitexe + cmd_revision);
|
||||
var describe = GetFirstStdOutLine(gitexe + cmd_describe);
|
||||
var branch = GetFirstStdOutLine(gitexe + cmd_branch);
|
||||
var isStable = +("master" == branch || "stable" == branch);
|
||||
var commits_ahead = GetFirstStdOutLine(gitexe + cmd_commits_ahead);
|
||||
|
||||
// Get environment information.
|
||||
var distributor = "Mario Party Netplay";
|
||||
|
@ -91,7 +92,7 @@ var out_contents =
|
|||
"#define SCM_REV_STR \"" + revision + "\"\n" +
|
||||
"#define SCM_DESC_STR \"" + describe + "\"\n" +
|
||||
"#define SCM_BRANCH_STR \"" + branch + "\"\n" +
|
||||
"#define SCM_IS_MASTER " + isStable + "\n" +
|
||||
"#define SCM_COMMITS_AHEAD_MASTER " + commits_ahead + "\n" +
|
||||
"#define SCM_DISTRIBUTOR_STR \"" + distributor + "\"\n" +
|
||||
"#define SCM_UPDATE_TRACK_STR \"" + default_update_track + "\"\n";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#define SCM_REV_STR "${DOLPHIN_WC_REVISION}"
|
||||
#define SCM_DESC_STR "${DOLPHIN_WC_DESCRIBE}"
|
||||
#define SCM_BRANCH_STR "${DOLPHIN_WC_BRANCH}"
|
||||
#define SCM_IS_MASTER ${DOLPHIN_WC_IS_STABLE}
|
||||
#define SCM_COMMITS_AHEAD_MASTER ${DOLPHIN_WC_COMMITS_AHEAD_MASTER}
|
||||
#define SCM_DISTRIBUTOR_STR "${DISTRIBUTOR}"
|
||||
#define SCM_UPDATE_TRACK_STR "${DOLPHIN_DEFAULT_UPDATE_TRACK}"
|
||||
|
|
|
@ -1082,6 +1082,14 @@ public:
|
|||
ABI_CallFunction(func);
|
||||
}
|
||||
|
||||
template <typename FunctionPointer>
|
||||
void ABI_CallFunctionPP(FunctionPointer func, const void* param1, const void* param2)
|
||||
{
|
||||
MOV(64, R(ABI_PARAM1), Imm64(reinterpret_cast<u64>(param1)));
|
||||
MOV(64, R(ABI_PARAM2), Imm64(reinterpret_cast<u64>(param2)));
|
||||
ABI_CallFunction(func);
|
||||
}
|
||||
|
||||
template <typename FunctionPointer>
|
||||
void ABI_CallFunctionPC(FunctionPointer func, const void* param1, u32 param2)
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,16 +15,19 @@
|
|||
|
||||
#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/Event.h"
|
||||
#include "Common/HttpRequest.h"
|
||||
#include "Common/WorkQueueThread.h"
|
||||
#include "DiscIO/Volume.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
class System;
|
||||
}
|
||||
} // namespace Core
|
||||
|
||||
namespace OSD
|
||||
{
|
||||
|
@ -34,30 +37,7 @@ struct Icon;
|
|||
class AchievementManager
|
||||
{
|
||||
public:
|
||||
enum class ResponseType
|
||||
{
|
||||
SUCCESS,
|
||||
NOT_ENABLED,
|
||||
MANAGER_NOT_INITIALIZED,
|
||||
INVALID_REQUEST,
|
||||
INVALID_CREDENTIALS,
|
||||
CONNECTION_FAILED,
|
||||
MALFORMED_OBJECT,
|
||||
EXPIRED_CONTEXT,
|
||||
UNKNOWN_FAILURE
|
||||
};
|
||||
using ResponseCallback = std::function<void(ResponseType)>;
|
||||
using UpdateCallback = std::function<void()>;
|
||||
|
||||
struct PointSpread
|
||||
{
|
||||
u32 total_count;
|
||||
u32 total_points;
|
||||
u32 hard_unlocks;
|
||||
u32 hard_points;
|
||||
u32 soft_unlocks;
|
||||
u32 soft_points;
|
||||
};
|
||||
using BadgeNameFunction = std::function<std::string(const AchievementManager&)>;
|
||||
|
||||
static constexpr size_t HASH_SIZE = 33;
|
||||
using Hash = std::array<char, HASH_SIZE>;
|
||||
|
@ -69,6 +49,7 @@ public:
|
|||
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<>>;
|
||||
static constexpr size_t MAX_DISPLAYED_LBOARDS = 4;
|
||||
|
||||
struct BadgeStatus
|
||||
{
|
||||
|
@ -76,22 +57,6 @@ public:
|
|||
Badge badge{};
|
||||
};
|
||||
|
||||
struct UnlockStatus
|
||||
{
|
||||
AchievementId game_data_index = 0;
|
||||
enum class UnlockType
|
||||
{
|
||||
LOCKED,
|
||||
SOFTCORE,
|
||||
HARDCORE
|
||||
} remote_unlock_status = UnlockType::LOCKED;
|
||||
u32 session_unlock_count = 0;
|
||||
u32 points = 0;
|
||||
BadgeStatus locked_badge;
|
||||
BadgeStatus unlocked_badge;
|
||||
u32 category = RC_ACHIEVEMENT_CATEGORY_CORE;
|
||||
};
|
||||
|
||||
static constexpr std::string_view GRAY = "transparent";
|
||||
static constexpr std::string_view GOLD = "#FFD700";
|
||||
static constexpr std::string_view BLUE = "#0B71C1";
|
||||
|
@ -111,43 +76,50 @@ public:
|
|||
std::unordered_map<u32, LeaderboardEntry> entries;
|
||||
};
|
||||
|
||||
struct UpdatedItems
|
||||
{
|
||||
bool all = false;
|
||||
bool player_icon = false;
|
||||
bool game_icon = false;
|
||||
bool all_achievements = false;
|
||||
std::set<AchievementId> achievements{};
|
||||
bool all_leaderboards = false;
|
||||
std::set<AchievementId> leaderboards{};
|
||||
bool rich_presence = false;
|
||||
};
|
||||
using UpdateCallback = std::function<void(const UpdatedItems&)>;
|
||||
|
||||
static AchievementManager& GetInstance();
|
||||
void Init();
|
||||
void SetUpdateCallback(UpdateCallback callback);
|
||||
ResponseType Login(const std::string& password);
|
||||
void LoginAsync(const std::string& password, const ResponseCallback& callback);
|
||||
bool IsLoggedIn() const;
|
||||
void HashGame(const std::string& file_path, const ResponseCallback& callback);
|
||||
void HashGame(const DiscIO::Volume* volume, const ResponseCallback& callback);
|
||||
void Login(const std::string& password);
|
||||
bool HasAPIToken() const;
|
||||
void LoadGame(const std::string& file_path, const DiscIO::Volume* volume);
|
||||
bool IsGameLoaded() const;
|
||||
|
||||
void LoadUnlockData(const ResponseCallback& callback);
|
||||
void ActivateDeactivateAchievements();
|
||||
void ActivateDeactivateLeaderboards();
|
||||
void ActivateDeactivateRichPresence();
|
||||
void FetchBadges();
|
||||
void FetchPlayerBadge();
|
||||
void FetchGameBadges();
|
||||
|
||||
void DoFrame();
|
||||
u32 MemoryPeeker(u32 address, u32 num_bytes, void* ud);
|
||||
void AchievementEventHandler(const rc_runtime_event_t* runtime_event);
|
||||
|
||||
std::recursive_mutex& GetLock();
|
||||
void SetHardcoreMode();
|
||||
bool IsHardcoreModeActive() const;
|
||||
std::string GetPlayerDisplayName() const;
|
||||
void SetSpectatorMode();
|
||||
std::string_view GetPlayerDisplayName() const;
|
||||
u32 GetPlayerScore() const;
|
||||
const BadgeStatus& GetPlayerBadge() const;
|
||||
std::string GetGameDisplayName() const;
|
||||
PointSpread TallyScore() const;
|
||||
std::string_view GetGameDisplayName() const;
|
||||
rc_client_t* GetClient();
|
||||
rc_api_fetch_game_data_response_t* GetGameData();
|
||||
const BadgeStatus& GetGameBadge() const;
|
||||
const UnlockStatus& GetUnlockStatus(AchievementId achievement_id) const;
|
||||
AchievementManager::ResponseType GetAchievementProgress(AchievementId achievement_id, u32* value,
|
||||
u32* target);
|
||||
const std::unordered_map<AchievementId, LeaderboardStatus>& GetLeaderboardsInfo() const;
|
||||
const BadgeStatus& GetAchievementBadge(AchievementId id, bool locked) const;
|
||||
const LeaderboardStatus* GetLeaderboardInfo(AchievementId leaderboard_id);
|
||||
RichPresence GetRichPresence() const;
|
||||
bool IsDisabled() const { return m_disabled; };
|
||||
void SetDisabled(bool disabled);
|
||||
const NamedIconMap& GetChallengeIcons() const;
|
||||
std::vector<std::string> GetActiveLeaderboards() const;
|
||||
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void CloseGame();
|
||||
void Logout();
|
||||
|
@ -162,6 +134,8 @@ private:
|
|||
std::unique_ptr<DiscIO::Volume> volume;
|
||||
};
|
||||
|
||||
const BadgeStatus m_default_badge;
|
||||
|
||||
static void* FilereaderOpenByFilepath(const char* path_utf8);
|
||||
static void* FilereaderOpenByVolume(const char* path_utf8);
|
||||
static void FilereaderSeek(void* file_handle, int64_t offset, int origin);
|
||||
|
@ -169,60 +143,65 @@ private:
|
|||
static size_t FilereaderRead(void* file_handle, void* buffer, size_t requested_bytes);
|
||||
static void FilereaderClose(void* file_handle);
|
||||
|
||||
ResponseType VerifyCredentials(const std::string& password);
|
||||
ResponseType ResolveHash(const Hash& game_hash, u32* game_id);
|
||||
void LoadGameSync(const ResponseCallback& callback);
|
||||
ResponseType StartRASession();
|
||||
ResponseType FetchGameData();
|
||||
ResponseType FetchUnlockData(bool hardcore);
|
||||
ResponseType FetchBoardInfo(AchievementId leaderboard_id);
|
||||
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; };
|
||||
|
||||
void ActivateDeactivateAchievement(AchievementId id, bool enabled, bool unofficial, bool encore);
|
||||
void GenerateRichPresence();
|
||||
|
||||
ResponseType AwardAchievement(AchievementId achievement_id);
|
||||
ResponseType SubmitLeaderboard(AchievementId leaderboard_id, int value);
|
||||
ResponseType PingRichPresence(const RichPresence& rich_presence);
|
||||
|
||||
static void LoadGameCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata);
|
||||
static void ChangeMediaCallback(int result, const char* error_message, rc_client_t* client,
|
||||
void* userdata);
|
||||
void DisplayWelcomeMessage();
|
||||
|
||||
void HandleAchievementTriggeredEvent(const rc_runtime_event_t* runtime_event);
|
||||
void HandleAchievementProgressUpdatedEvent(const rc_runtime_event_t* runtime_event);
|
||||
void HandleAchievementPrimedEvent(const rc_runtime_event_t* runtime_event);
|
||||
void HandleAchievementUnprimedEvent(const rc_runtime_event_t* runtime_event);
|
||||
void HandleLeaderboardStartedEvent(const rc_runtime_event_t* runtime_event);
|
||||
void HandleLeaderboardCanceledEvent(const rc_runtime_event_t* runtime_event);
|
||||
void HandleLeaderboardTriggeredEvent(const rc_runtime_event_t* runtime_event);
|
||||
static void LeaderboardEntriesCallback(int result, const char* error_message,
|
||||
rc_client_leaderboard_entry_list_t* list,
|
||||
rc_client_t* client, void* userdata);
|
||||
|
||||
template <typename RcRequest, typename RcResponse>
|
||||
ResponseType Request(RcRequest rc_request, RcResponse* rc_response,
|
||||
const std::function<int(rc_api_request_t*, const RcRequest*)>& init_request,
|
||||
const std::function<int(RcResponse*, const char*)>& process_response);
|
||||
ResponseType RequestImage(rc_api_fetch_image_request_t rc_request, Badge* rc_response);
|
||||
static void HandleAchievementTriggeredEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardStartedEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardFailedEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardSubmittedEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardTrackerUpdateEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardTrackerShowEvent(const rc_client_event_t* client_event);
|
||||
static void HandleLeaderboardTrackerHideEvent(const rc_client_event_t* client_event);
|
||||
static void HandleAchievementChallengeIndicatorShowEvent(const rc_client_event_t* client_event);
|
||||
static void HandleAchievementChallengeIndicatorHideEvent(const rc_client_event_t* client_event);
|
||||
static void HandleAchievementProgressIndicatorShowEvent(const rc_client_event_t* client_event);
|
||||
static void HandleGameCompletedEvent(const rc_client_event_t* client_event, rc_client_t* client);
|
||||
static void HandleResetEvent(const rc_client_event_t* client_event);
|
||||
static void HandleServerErrorEvent(const rc_client_event_t* client_event);
|
||||
|
||||
static void Request(const rc_api_request_t* request, rc_client_server_callback_t callback,
|
||||
void* callback_data, 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,
|
||||
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{};
|
||||
bool m_is_runtime_initialized = false;
|
||||
UpdateCallback m_update_callback = [] {};
|
||||
UpdateCallback m_update_callback = [](const UpdatedItems&) {};
|
||||
std::unique_ptr<DiscIO::Volume> m_loading_volume;
|
||||
bool m_disabled = false;
|
||||
std::string m_display_name;
|
||||
u32 m_player_score = 0;
|
||||
BadgeStatus 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;
|
||||
u32 m_framecount = 0;
|
||||
BadgeStatus 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;
|
||||
RichPresence m_rich_presence;
|
||||
time_t m_last_ping_time = 0;
|
||||
std::chrono::steady_clock::time_point m_last_rp_time = std::chrono::steady_clock::now();
|
||||
|
||||
std::unordered_map<AchievementId, UnlockStatus> m_unlock_map;
|
||||
std::unordered_map<AchievementId, LeaderboardStatus> m_leaderboard_map;
|
||||
NamedIconMap m_active_challenges;
|
||||
std::vector<rc_client_leaderboard_tracker_t> m_active_leaderboards;
|
||||
|
||||
Common::WorkQueueThread<std::function<void()>> m_queue;
|
||||
Common::WorkQueueThread<std::function<void()>> m_image_queue;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
@ -31,7 +32,6 @@
|
|||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Config/Config.h"
|
||||
#include "Common/IniFile.h"
|
||||
|
@ -519,10 +519,10 @@ static bool Subtype_AddCode(const Core::CPUThreadGuard& guard, const ARAddr& add
|
|||
LogInfo("--------");
|
||||
|
||||
const u32 read = PowerPC::MMU::HostRead_U32(guard, new_addr);
|
||||
const float read_float = Common::BitCast<float>(read);
|
||||
const float read_float = std::bit_cast<float>(read);
|
||||
// data contains an (unsigned?) integer value
|
||||
const float fread = read_float + static_cast<float>(data);
|
||||
const u32 newval = Common::BitCast<u32>(fread);
|
||||
const u32 newval = std::bit_cast<u32>(fread);
|
||||
PowerPC::MMU::HostWrite_U32(guard, newval, new_addr);
|
||||
LogInfo("Old Value {:08x}", read);
|
||||
LogInfo("Increment {:08x}", data);
|
||||
|
|
|
@ -351,11 +351,6 @@ bool CBoot::DVDReadDiscID(Core::System& system, const DiscIO::VolumeDisc& disc,
|
|||
return true;
|
||||
}
|
||||
|
||||
void CBoot::UpdateDebugger_MapLoaded()
|
||||
{
|
||||
Host_NotifyMapLoaded();
|
||||
}
|
||||
|
||||
// Get map file paths for the active title.
|
||||
bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_map_file)
|
||||
{
|
||||
|
@ -376,13 +371,13 @@ bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_ma
|
|||
return false;
|
||||
}
|
||||
|
||||
bool CBoot::LoadMapFromFilename(const Core::CPUThreadGuard& guard)
|
||||
bool CBoot::LoadMapFromFilename(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db)
|
||||
{
|
||||
std::string strMapFilename;
|
||||
bool found = FindMapFile(&strMapFilename, nullptr);
|
||||
if (found && g_symbolDB.LoadMap(guard, strMapFilename))
|
||||
if (found && ppc_symbol_db.LoadMap(guard, strMapFilename))
|
||||
{
|
||||
UpdateDebugger_MapLoaded();
|
||||
Host_PPCSymbolsChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -514,10 +509,10 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||
{
|
||||
SConfig& config = SConfig::GetInstance();
|
||||
|
||||
if (!g_symbolDB.IsEmpty())
|
||||
if (auto& ppc_symbol_db = system.GetPPCSymbolDB(); !ppc_symbol_db.IsEmpty())
|
||||
{
|
||||
g_symbolDB.Clear();
|
||||
UpdateDebugger_MapLoaded();
|
||||
ppc_symbol_db.Clear();
|
||||
Host_PPCSymbolsChanged();
|
||||
}
|
||||
|
||||
// PAL Wii uses NTSC framerate and linecount in 60Hz modes
|
||||
|
@ -581,8 +576,7 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().HashGame(executable.path,
|
||||
[](AchievementManager::ResponseType r_type) {});
|
||||
AchievementManager::GetInstance().LoadGame(executable.path, nullptr);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (!executable.reader->LoadIntoMemory(system))
|
||||
|
@ -595,9 +589,9 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||
|
||||
ppc_state.pc = executable.reader->GetEntryPoint();
|
||||
|
||||
if (executable.reader->LoadSymbols(guard))
|
||||
if (executable.reader->LoadSymbols(guard, system.GetPPCSymbolDB()))
|
||||
{
|
||||
UpdateDebugger_MapLoaded();
|
||||
Host_PPCSymbolsChanged();
|
||||
HLE::PatchFunctions(system);
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "DiscIO/VolumeDisc.h"
|
||||
#include "DiscIO/VolumeWad.h"
|
||||
|
||||
class PPCSymbolDB;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
|
@ -168,7 +170,7 @@ public:
|
|||
//
|
||||
// Returns true if a map file exists, false if none could be found.
|
||||
static bool FindMapFile(std::string* existing_map_file, std::string* writable_map_file);
|
||||
static bool LoadMapFromFilename(const Core::CPUThreadGuard& guard);
|
||||
static bool LoadMapFromFilename(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db);
|
||||
|
||||
private:
|
||||
static bool DVDRead(Core::System& system, const DiscIO::VolumeDisc& disc, u64 dvd_offset,
|
||||
|
@ -177,8 +179,6 @@ private:
|
|||
u32 output_address);
|
||||
static void RunFunction(Core::System& system, u32 address);
|
||||
|
||||
static void UpdateDebugger_MapLoaded();
|
||||
|
||||
static bool Boot_WiiWAD(Core::System& system, const DiscIO::VolumeWAD& wad);
|
||||
static bool BootNANDTitle(Core::System& system, u64 title_id);
|
||||
|
||||
|
@ -215,7 +215,7 @@ 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) const = 0;
|
||||
virtual bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const = 0;
|
||||
|
||||
protected:
|
||||
std::vector<u8> m_bytes;
|
||||
|
|
|
@ -363,7 +363,6 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
|
|||
auto entryPos = region_settings.find(SConfig::GetInstance().m_region);
|
||||
RegionSetting region_setting = entryPos->second;
|
||||
|
||||
Common::SettingsHandler gen;
|
||||
std::string serno;
|
||||
std::string model = "RVL-001(" + region_setting.area + ")";
|
||||
CreateSystemMenuTitleDirs();
|
||||
|
@ -377,9 +376,9 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
|
|||
IOS::HLE::FS::Mode::Read);
|
||||
if (file && file->Read(data.data(), data.size()))
|
||||
{
|
||||
gen.SetBytes(std::move(data));
|
||||
serno = gen.GetValue("SERNO");
|
||||
model = gen.GetValue("MODEL");
|
||||
Common::SettingsHandler settings_reader(data);
|
||||
serno = settings_reader.GetValue("SERNO");
|
||||
model = settings_reader.GetValue("MODEL");
|
||||
|
||||
bool region_matches = false;
|
||||
if (Config::Get(Config::MAIN_OVERRIDE_REGION_SETTINGS))
|
||||
|
@ -388,15 +387,16 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
|
|||
}
|
||||
else
|
||||
{
|
||||
const std::string code = gen.GetValue("CODE");
|
||||
const std::string code = settings_reader.GetValue("CODE");
|
||||
if (code.size() >= 2 && CodeRegion(code[1]) == SConfig::GetInstance().m_region)
|
||||
region_matches = true;
|
||||
}
|
||||
|
||||
if (region_matches)
|
||||
{
|
||||
region_setting = RegionSetting{gen.GetValue("AREA"), gen.GetValue("VIDEO"),
|
||||
gen.GetValue("GAME"), gen.GetValue("CODE")};
|
||||
region_setting =
|
||||
RegionSetting{settings_reader.GetValue("AREA"), settings_reader.GetValue("VIDEO"),
|
||||
settings_reader.GetValue("GAME"), settings_reader.GetValue("CODE")};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -404,8 +404,6 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
|
|||
if (parenthesis_pos != std::string::npos)
|
||||
model = model.substr(0, parenthesis_pos) + '(' + region_setting.area + ')';
|
||||
}
|
||||
|
||||
gen.Reset();
|
||||
}
|
||||
}
|
||||
fs->Delete(IOS::SYSMENU_UID, IOS::SYSMENU_GID, settings_file_path);
|
||||
|
@ -423,6 +421,7 @@ bool CBoot::SetupWiiMemory(Core::System& system, IOS::HLE::IOSC::ConsoleType con
|
|||
INFO_LOG_FMT(BOOT, "Using serial number: {}", serno);
|
||||
}
|
||||
|
||||
Common::SettingsHandler gen;
|
||||
gen.AddSetting("AREA", region_setting.area);
|
||||
gen.AddSetting("MODEL", model);
|
||||
gen.AddSetting("DVD", "0");
|
||||
|
|
|
@ -27,7 +27,10 @@ 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) const override { return false; }
|
||||
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
enum
|
||||
|
|
|
@ -180,7 +180,7 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard) const
|
||||
bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const
|
||||
{
|
||||
bool hasSymbols = false;
|
||||
SectionID sec = GetSectionByName(".symtab");
|
||||
|
@ -218,11 +218,11 @@ bool ElfReader::LoadSymbols(const Core::CPUThreadGuard& guard) const
|
|||
default:
|
||||
continue;
|
||||
}
|
||||
g_symbolDB.AddKnownSymbol(guard, value, size, name, symtype);
|
||||
ppc_symbol_db.AddKnownSymbol(guard, value, size, name, symtype);
|
||||
hasSymbols = true;
|
||||
}
|
||||
}
|
||||
g_symbolDB.Index();
|
||||
ppc_symbol_db.Index();
|
||||
return hasSymbols;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ 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) const override;
|
||||
bool LoadSymbols(const Core::CPUThreadGuard& guard, PPCSymbolDB& ppc_symbol_db) const override;
|
||||
// TODO: actually check for validity.
|
||||
bool IsValid() const override { return true; }
|
||||
bool IsWii() const override;
|
||||
|
|
|
@ -55,12 +55,12 @@
|
|||
namespace BootManager
|
||||
{
|
||||
// Boot the ISO or file
|
||||
bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
|
||||
bool BootCore(Core::System& system, std::unique_ptr<BootParameters> boot,
|
||||
const WindowSystemInfo& wsi)
|
||||
{
|
||||
if (!boot)
|
||||
return false;
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
SConfig& StartUp = SConfig::GetInstance();
|
||||
|
||||
if (!StartUp.SetPathsAndGameMetadata(system, *boot))
|
||||
|
@ -146,7 +146,7 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
|
|||
|
||||
system.Initialize();
|
||||
|
||||
Core::UpdateWantDeterminism(/*initial*/ true);
|
||||
Core::UpdateWantDeterminism(system, /*initial*/ true);
|
||||
|
||||
if (system.IsWii())
|
||||
{
|
||||
|
@ -167,7 +167,7 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
|
|||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().SetDisabled(false);
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
const bool load_ipl = !system.IsWii() && !Config::Get(Config::MAIN_SKIP_IPL) &&
|
||||
|
|
|
@ -5,12 +5,17 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class System;
|
||||
}
|
||||
struct BootParameters;
|
||||
struct WindowSystemInfo;
|
||||
|
||||
namespace BootManager
|
||||
{
|
||||
bool BootCore(std::unique_ptr<BootParameters> parameters, const WindowSystemInfo& wsi);
|
||||
bool BootCore(Core::System& system, std::unique_ptr<BootParameters> parameters,
|
||||
const WindowSystemInfo& wsi);
|
||||
|
||||
// Synchronise Dolphin's configuration with the SYSCONF (which may have changed during emulation),
|
||||
// and restore settings that were overriden by per-game INIs or for some other reason.
|
||||
|
|
|
@ -189,6 +189,12 @@ add_library(core
|
|||
HW/DVD/DVDThread.h
|
||||
HW/DVD/FileMonitor.cpp
|
||||
HW/DVD/FileMonitor.h
|
||||
HW/EXI/BBA/TAPServerConnection.cpp
|
||||
HW/EXI/BBA/TAPServerBBA.cpp
|
||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||
HW/EXI/BBA/BuiltIn.cpp
|
||||
HW/EXI/BBA/BuiltIn.h
|
||||
HW/EXI/Modem/TAPServerModem.cpp
|
||||
HW/EXI/EXI_Channel.cpp
|
||||
HW/EXI/EXI_Channel.h
|
||||
HW/EXI/EXI_Device.cpp
|
||||
|
@ -209,6 +215,8 @@ add_library(core
|
|||
HW/EXI/EXI_DeviceMemoryCard.h
|
||||
HW/EXI/EXI_DeviceMic.cpp
|
||||
HW/EXI/EXI_DeviceMic.h
|
||||
HW/EXI/EXI_DeviceModem.cpp
|
||||
HW/EXI/EXI_DeviceModem.h
|
||||
HW/EXI/EXI.cpp
|
||||
HW/EXI/EXI.h
|
||||
HW/GBAPad.cpp
|
||||
|
@ -519,7 +527,6 @@ add_library(core
|
|||
PowerPC/PPCSymbolDB.h
|
||||
PowerPC/PPCTables.cpp
|
||||
PowerPC/PPCTables.h
|
||||
PowerPC/Profiler.h
|
||||
PowerPC/SignatureDB/CSVSignatureDB.cpp
|
||||
PowerPC/SignatureDB/CSVSignatureDB.h
|
||||
PowerPC/SignatureDB/DSYSignatureDB.cpp
|
||||
|
@ -700,9 +707,6 @@ if(WIN32)
|
|||
target_sources(core PRIVATE
|
||||
HW/EXI/BBA/TAP_Win32.cpp
|
||||
HW/EXI/BBA/TAP_Win32.h
|
||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||
HW/EXI/BBA/BuiltIn.cpp
|
||||
HW/EXI/BBA/BuiltIn.h
|
||||
HW/WiimoteReal/IOWin.cpp
|
||||
HW/WiimoteReal/IOWin.h
|
||||
)
|
||||
|
@ -716,18 +720,11 @@ if(WIN32)
|
|||
elseif(APPLE)
|
||||
target_sources(core PRIVATE
|
||||
HW/EXI/BBA/TAP_Apple.cpp
|
||||
HW/EXI/BBA/TAPServer_Apple.cpp
|
||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||
HW/EXI/BBA/BuiltIn.cpp
|
||||
HW/EXI/BBA/BuiltIn.h
|
||||
)
|
||||
target_link_libraries(core PUBLIC ${IOB_LIBRARY})
|
||||
elseif(UNIX)
|
||||
target_sources(core PRIVATE
|
||||
HW/EXI/BBA/TAP_Unix.cpp
|
||||
HW/EXI/BBA/XLINK_KAI_BBA.cpp
|
||||
HW/EXI/BBA/BuiltIn.cpp
|
||||
HW/EXI/BBA/BuiltIn.h
|
||||
)
|
||||
if(ANDROID)
|
||||
target_sources(core PRIVATE
|
||||
|
@ -758,6 +755,12 @@ if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD")
|
||||
# OpenBSD doesn't allow memory to be both writable and executable by default.
|
||||
# The JIT currently needs this.
|
||||
target_link_options(core PUBLIC -Wl,-zwxneeded)
|
||||
endif()
|
||||
|
||||
if(TARGET Hidapi::Hidapi)
|
||||
target_sources(core PRIVATE
|
||||
HW/WiimoteReal/IOhidapi.cpp
|
||||
|
@ -782,4 +785,5 @@ endif()
|
|||
if(USE_RETRO_ACHIEVEMENTS)
|
||||
target_link_libraries(core PRIVATE rcheevos)
|
||||
target_compile_definitions(core PRIVATE -DUSE_RETRO_ACHIEVEMENTS)
|
||||
endif()
|
||||
target_compile_definitions(core PRIVATE -DRC_CLIENT_SUPPORTS_HASH)
|
||||
endif()
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "Core/CheatSearch.h"
|
||||
|
||||
#include <bit>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
@ -13,10 +14,9 @@
|
|||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
|
@ -83,17 +83,17 @@ std::vector<u8> Cheats::GetValueAsByteVector(const Cheats::SearchValue& value)
|
|||
case Cheats::DataType::U64:
|
||||
return ToByteVector(Common::swap64(std::get<u64>(value.m_value)));
|
||||
case Cheats::DataType::S8:
|
||||
return {Common::BitCast<u8>(std::get<s8>(value.m_value))};
|
||||
return {std::bit_cast<u8>(std::get<s8>(value.m_value))};
|
||||
case Cheats::DataType::S16:
|
||||
return ToByteVector(Common::swap16(Common::BitCast<u16>(std::get<s16>(value.m_value))));
|
||||
return ToByteVector(Common::swap16(std::bit_cast<u16>(std::get<s16>(value.m_value))));
|
||||
case Cheats::DataType::S32:
|
||||
return ToByteVector(Common::swap32(Common::BitCast<u32>(std::get<s32>(value.m_value))));
|
||||
return ToByteVector(Common::swap32(std::bit_cast<u32>(std::get<s32>(value.m_value))));
|
||||
case Cheats::DataType::S64:
|
||||
return ToByteVector(Common::swap64(Common::BitCast<u64>(std::get<s64>(value.m_value))));
|
||||
return ToByteVector(Common::swap64(std::bit_cast<u64>(std::get<s64>(value.m_value))));
|
||||
case Cheats::DataType::F32:
|
||||
return ToByteVector(Common::swap32(Common::BitCast<u32>(std::get<float>(value.m_value))));
|
||||
return ToByteVector(Common::swap32(std::bit_cast<u32>(std::get<float>(value.m_value))));
|
||||
case Cheats::DataType::F64:
|
||||
return ToByteVector(Common::swap64(Common::BitCast<u64>(std::get<double>(value.m_value))));
|
||||
return ToByteVector(Common::swap64(std::bit_cast<u64>(std::get<double>(value.m_value))));
|
||||
default:
|
||||
DEBUG_ASSERT(false);
|
||||
return {};
|
||||
|
@ -147,7 +147,7 @@ TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
|
|||
auto tmp = PowerPC::MMU::HostTryReadU8(guard, addr, space);
|
||||
if (!tmp)
|
||||
return std::nullopt;
|
||||
return PowerPC::ReadResult<s8>(tmp->translated, Common::BitCast<s8>(tmp->value));
|
||||
return PowerPC::ReadResult<s8>(tmp->translated, std::bit_cast<s8>(tmp->value));
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -158,7 +158,7 @@ TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
|
|||
auto tmp = PowerPC::MMU::HostTryReadU16(guard, addr, space);
|
||||
if (!tmp)
|
||||
return std::nullopt;
|
||||
return PowerPC::ReadResult<s16>(tmp->translated, Common::BitCast<s16>(tmp->value));
|
||||
return PowerPC::ReadResult<s16>(tmp->translated, std::bit_cast<s16>(tmp->value));
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -169,7 +169,7 @@ TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
|
|||
auto tmp = PowerPC::MMU::HostTryReadU32(guard, addr, space);
|
||||
if (!tmp)
|
||||
return std::nullopt;
|
||||
return PowerPC::ReadResult<s32>(tmp->translated, Common::BitCast<s32>(tmp->value));
|
||||
return PowerPC::ReadResult<s32>(tmp->translated, std::bit_cast<s32>(tmp->value));
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -180,7 +180,7 @@ TryReadValueFromEmulatedMemory(const Core::CPUThreadGuard& guard, u32 addr,
|
|||
auto tmp = PowerPC::MMU::HostTryReadU64(guard, addr, space);
|
||||
if (!tmp)
|
||||
return std::nullopt;
|
||||
return PowerPC::ReadResult<s64>(tmp->translated, Common::BitCast<s64>(tmp->value));
|
||||
return PowerPC::ReadResult<s64>(tmp->translated, std::bit_cast<s64>(tmp->value));
|
||||
}
|
||||
|
||||
template <>
|
||||
|
@ -208,15 +208,16 @@ Cheats::NewSearch(const Core::CPUThreadGuard& guard,
|
|||
const std::function<bool(const T& value)>& validator)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||
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();
|
||||
const Core::State core_state = Core::GetState(system);
|
||||
if (core_state != Core::State::Running && core_state != Core::State::Paused)
|
||||
return Cheats::SearchErrorCode::NoEmulationActive;
|
||||
|
||||
const auto& ppc_state = guard.GetSystem().GetPPCState();
|
||||
const auto& ppc_state = system.GetPPCState();
|
||||
if (address_space == PowerPC::RequestedAddressSpace::Virtual && !ppc_state.msr.DR)
|
||||
return Cheats::SearchErrorCode::VirtualAddressesCurrentlyNotAccessible;
|
||||
|
||||
|
@ -262,15 +263,16 @@ Cheats::NextSearch(const Core::CPUThreadGuard& guard,
|
|||
const std::function<bool(const T& new_value, const T& old_value)>& validator)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||
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();
|
||||
const Core::State core_state = Core::GetState(system);
|
||||
if (core_state != Core::State::Running && core_state != Core::State::Paused)
|
||||
return Cheats::SearchErrorCode::NoEmulationActive;
|
||||
|
||||
const auto& ppc_state = guard.GetSystem().GetPPCState();
|
||||
const auto& ppc_state = system.GetPPCState();
|
||||
if (address_space == PowerPC::RequestedAddressSpace::Virtual && !ppc_state.msr.DR)
|
||||
return Cheats::SearchErrorCode::VirtualAddressesCurrentlyNotAccessible;
|
||||
|
||||
|
@ -428,7 +430,7 @@ template <typename T>
|
|||
Cheats::SearchErrorCode Cheats::CheatSearchSession<T>::RunSearch(const Core::CPUThreadGuard& guard)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return Cheats::SearchErrorCode::DisabledInHardcoreMode;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
Common::Result<SearchErrorCode, std::vector<SearchResult<T>>> result =
|
||||
|
@ -570,11 +572,19 @@ std::string Cheats::CheatSearchSession<T>::GetResultValueAsString(size_t index,
|
|||
if (hex)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, float>)
|
||||
return fmt::format("0x{0:08x}", Common::BitCast<u32>(m_search_results[index].m_value));
|
||||
{
|
||||
return fmt::format("0x{0:08x}", std::bit_cast<s32>(m_search_results[index].m_value));
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, double>)
|
||||
return fmt::format("0x{0:016x}", Common::BitCast<u64>(m_search_results[index].m_value));
|
||||
{
|
||||
return fmt::format("0x{0:016x}", std::bit_cast<s64>(m_search_results[index].m_value));
|
||||
}
|
||||
else
|
||||
return fmt::format("0x{0:0{1}x}", m_search_results[index].m_value, sizeof(T) * 2);
|
||||
{
|
||||
return fmt::format("0x{0:0{1}x}",
|
||||
std::bit_cast<std::make_unsigned_t<T>>(m_search_results[index].m_value),
|
||||
sizeof(T) * 2);
|
||||
}
|
||||
}
|
||||
|
||||
return fmt::format("{}", m_search_results[index].m_value);
|
||||
|
|
|
@ -15,20 +15,16 @@ const Info<bool> RA_ENABLED{{System::Achievements, "Achievements", "Enabled"}, f
|
|||
const Info<std::string> RA_HOST_URL{{System::Achievements, "Achievements", "HostUrl"}, ""};
|
||||
const Info<std::string> RA_USERNAME{{System::Achievements, "Achievements", "Username"}, ""};
|
||||
const Info<std::string> RA_API_TOKEN{{System::Achievements, "Achievements", "ApiToken"}, ""};
|
||||
const Info<bool> RA_ACHIEVEMENTS_ENABLED{
|
||||
{System::Achievements, "Achievements", "AchievementsEnabled"}, false};
|
||||
const Info<bool> RA_LEADERBOARDS_ENABLED{
|
||||
{System::Achievements, "Achievements", "LeaderboardsEnabled"}, false};
|
||||
const Info<bool> RA_RICH_PRESENCE_ENABLED{
|
||||
{System::Achievements, "Achievements", "RichPresenceEnabled"}, false};
|
||||
const Info<bool> RA_HARDCORE_ENABLED{{System::Achievements, "Achievements", "HardcoreEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_PROGRESS_ENABLED{{System::Achievements, "Achievements", "ProgressEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_BADGES_ENABLED{{System::Achievements, "Achievements", "BadgesEnabled"}, false};
|
||||
true};
|
||||
const Info<bool> RA_UNOFFICIAL_ENABLED{{System::Achievements, "Achievements", "UnofficialEnabled"},
|
||||
false};
|
||||
const Info<bool> RA_ENCORE_ENABLED{{System::Achievements, "Achievements", "EncoreEnabled"}, false};
|
||||
const Info<bool> RA_SPECTATOR_ENABLED{{System::Achievements, "Achievements", "SpectatorEnabled"},
|
||||
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
|
||||
|
|
|
@ -14,14 +14,12 @@ extern const Info<bool> RA_ENABLED;
|
|||
extern const Info<std::string> RA_USERNAME;
|
||||
extern const Info<std::string> RA_HOST_URL;
|
||||
extern const Info<std::string> RA_API_TOKEN;
|
||||
extern const Info<bool> RA_ACHIEVEMENTS_ENABLED;
|
||||
extern const Info<bool> RA_LEADERBOARDS_ENABLED;
|
||||
extern const Info<bool> RA_RICH_PRESENCE_ENABLED;
|
||||
extern const Info<bool> RA_HARDCORE_ENABLED;
|
||||
extern const Info<bool> RA_PROGRESS_ENABLED;
|
||||
extern const Info<bool> RA_BADGES_ENABLED;
|
||||
extern const Info<bool> RA_UNOFFICIAL_ENABLED;
|
||||
extern const Info<bool> RA_ENCORE_ENABLED;
|
||||
extern const Info<bool> RA_SPECTATOR_ENABLED;
|
||||
extern const Info<bool> RA_PROGRESS_ENABLED;
|
||||
extern const Info<bool> RA_BADGES_ENABLED;
|
||||
} // namespace Config
|
||||
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -71,8 +71,9 @@ const Info<std::string> GFX_DUMP_PIXEL_FORMAT{{System::GFX, "Settings", "DumpPix
|
|||
const Info<std::string> GFX_DUMP_ENCODER{{System::GFX, "Settings", "DumpEncoder"}, ""};
|
||||
const Info<std::string> GFX_DUMP_PATH{{System::GFX, "Settings", "DumpPath"}, ""};
|
||||
const Info<int> GFX_BITRATE_KBPS{{System::GFX, "Settings", "BitrateKbps"}, 25000};
|
||||
const Info<bool> GFX_INTERNAL_RESOLUTION_FRAME_DUMPS{
|
||||
{System::GFX, "Settings", "InternalResolutionFrameDumps"}, false};
|
||||
const Info<FrameDumpResolutionType> GFX_FRAME_DUMPS_RESOLUTION_TYPE{
|
||||
{System::GFX, "Settings", "FrameDumpsResolutionType"},
|
||||
FrameDumpResolutionType::XFBAspectRatioCorrectedResolution};
|
||||
const Info<int> GFX_PNG_COMPRESSION_LEVEL{{System::GFX, "Settings", "PNGCompressionLevel"}, 6};
|
||||
const Info<bool> GFX_ENABLE_GPU_TEXTURE_DECODING{
|
||||
{System::GFX, "Settings", "EnableGPUTextureDecoding"}, false};
|
||||
|
|
|
@ -14,6 +14,7 @@ enum class TextureFilteringMode : int;
|
|||
enum class OutputResamplingMode : int;
|
||||
enum class ColorCorrectionRegion : int;
|
||||
enum class TriState : int;
|
||||
enum class FrameDumpResolutionType : int;
|
||||
|
||||
namespace Config
|
||||
{
|
||||
|
@ -68,7 +69,7 @@ extern const Info<std::string> GFX_DUMP_PIXEL_FORMAT;
|
|||
extern const Info<std::string> GFX_DUMP_ENCODER;
|
||||
extern const Info<std::string> GFX_DUMP_PATH;
|
||||
extern const Info<int> GFX_BITRATE_KBPS;
|
||||
extern const Info<bool> GFX_INTERNAL_RESOLUTION_FRAME_DUMPS;
|
||||
extern const Info<FrameDumpResolutionType> GFX_FRAME_DUMPS_RESOLUTION_TYPE;
|
||||
extern const Info<int> GFX_PNG_COMPRESSION_LEVEL;
|
||||
extern const Info<bool> GFX_ENABLE_GPU_TEXTURE_DECODING;
|
||||
extern const Info<bool> GFX_ENABLE_PIXEL_LIGHTING;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "Common/MathUtil.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/DefaultLocale.h"
|
||||
#include "Core/HW/EXI/EXI.h"
|
||||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
|
@ -137,6 +137,10 @@ const Info<bool> MAIN_BBA_XLINK_CHAT_OSD{{System::Main, "Core", "BBA_XLINK_CHAT_
|
|||
// Schthack PSO Server - https://schtserv.com/
|
||||
const Info<std::string> MAIN_BBA_BUILTIN_DNS{{System::Main, "Core", "BBA_BUILTIN_DNS"},
|
||||
"3.18.217.27"};
|
||||
const Info<std::string> MAIN_BBA_TAPSERVER_DESTINATION{
|
||||
{System::Main, "Core", "BBA_TAPSERVER_DESTINATION"}, "/tmp/dolphin-tap"};
|
||||
const Info<std::string> MAIN_MODEM_TAPSERVER_DESTINATION{
|
||||
{System::Main, "Core", "MODEM_TAPSERVER_DESTINATION"}, "/tmp/dolphin-modem-tap"};
|
||||
const Info<std::string> MAIN_BBA_BUILTIN_IP{{System::Main, "Core", "BBA_BUILTIN_IP"}, ""};
|
||||
|
||||
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel)
|
||||
|
@ -505,6 +509,8 @@ const Info<bool> MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF{
|
|||
const Info<bool> MAIN_DEBUG_JIT_BRANCH_OFF{{System::Main, "Debug", "JitBranchOff"}, false};
|
||||
const Info<bool> MAIN_DEBUG_JIT_REGISTER_CACHE_OFF{{System::Main, "Debug", "JitRegisterCacheOff"},
|
||||
false};
|
||||
const Info<bool> MAIN_DEBUG_JIT_ENABLE_PROFILING{{System::Main, "Debug", "JitEnableProfiling"},
|
||||
false};
|
||||
|
||||
// Main.BluetoothPassthrough
|
||||
|
||||
|
@ -744,7 +750,8 @@ bool IsDefaultGCIFolderPathConfigured(ExpansionInterface::Slot slot)
|
|||
bool AreCheatsEnabled()
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
return Config::Get(::Config::MAIN_ENABLE_CHEATS) && !::Config::Get(::Config::RA_HARDCORE_ENABLED);
|
||||
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
|
||||
|
@ -754,7 +761,7 @@ bool IsDebuggingEnabled()
|
|||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
return Config::Get(::Config::MAIN_ENABLE_DEBUGGING) &&
|
||||
!::Config::Get(::Config::RA_HARDCORE_ENABLED);
|
||||
!AchievementManager::GetInstance().IsHardcoreModeActive();
|
||||
#else // USE_RETRO_ACHIEVEMENTS
|
||||
return Config::Get(::Config::MAIN_ENABLE_DEBUGGING);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
|
|
@ -96,6 +96,8 @@ extern const Info<std::string> MAIN_BBA_XLINK_IP;
|
|||
extern const Info<bool> MAIN_BBA_XLINK_CHAT_OSD;
|
||||
extern const Info<std::string> MAIN_BBA_BUILTIN_DNS;
|
||||
extern const Info<std::string> MAIN_BBA_BUILTIN_IP;
|
||||
extern const Info<std::string> MAIN_BBA_TAPSERVER_DESTINATION;
|
||||
extern const Info<std::string> MAIN_MODEM_TAPSERVER_DESTINATION;
|
||||
const Info<SerialInterface::SIDevices>& GetInfoForSIDevice(int channel);
|
||||
const Info<bool>& GetInfoForAdapterRumble(int channel);
|
||||
const Info<bool>& GetInfoForSimulateKonga(int channel);
|
||||
|
@ -333,6 +335,7 @@ extern const Info<bool> MAIN_DEBUG_JIT_PAIRED_OFF;
|
|||
extern const Info<bool> MAIN_DEBUG_JIT_SYSTEM_REGISTERS_OFF;
|
||||
extern const Info<bool> MAIN_DEBUG_JIT_BRANCH_OFF;
|
||||
extern const Info<bool> MAIN_DEBUG_JIT_REGISTER_CACHE_OFF;
|
||||
extern const Info<bool> MAIN_DEBUG_JIT_ENABLE_PROFILING;
|
||||
|
||||
// Main.BluetoothPassthrough
|
||||
|
||||
|
|
|
@ -27,12 +27,13 @@
|
|||
#include "Core/IOS/IOS.h"
|
||||
#include "Core/IOS/USB/Bluetooth/BTBase.h"
|
||||
#include "Core/SysConf.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace ConfigLoaders
|
||||
{
|
||||
void SaveToSYSCONF(Config::LayerType layer, std::function<bool(const Config::Location&)> predicate)
|
||||
{
|
||||
if (Core::IsRunning())
|
||||
if (Core::IsRunning(Core::System::GetInstance()))
|
||||
return;
|
||||
|
||||
IOS::HLE::Kernel ios;
|
||||
|
@ -182,7 +183,7 @@ public:
|
|||
private:
|
||||
void LoadFromSYSCONF(Config::Layer* layer)
|
||||
{
|
||||
if (Core::IsRunning())
|
||||
if (Core::IsRunning(Core::System::GetInstance()))
|
||||
return;
|
||||
|
||||
IOS::HLE::Kernel ios;
|
||||
|
|
|
@ -208,8 +208,9 @@ private:
|
|||
if (m_id == "00000000")
|
||||
return;
|
||||
|
||||
const std::array<std::tuple<std::string, std::string, Config::System>, 2> profile_info = {{
|
||||
const std::array<std::tuple<std::string, std::string, Config::System>, 3> profile_info = {{
|
||||
std::make_tuple("Pad", "GCPad", Config::System::GCPad),
|
||||
std::make_tuple("GBA", "GBA", Config::System::GCPad),
|
||||
std::make_tuple("Wiimote", "Wiimote", Config::System::WiiPad),
|
||||
}};
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
|||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (game_id != "00000000")
|
||||
AchievementManager::GetInstance().SetDisabled(true);
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
if (game_id == "00000000")
|
||||
|
@ -188,30 +188,31 @@ 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())
|
||||
if (Core::IsRunning(system))
|
||||
{
|
||||
Core::UpdateTitle();
|
||||
Core::UpdateTitle(system);
|
||||
}
|
||||
|
||||
Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
|
||||
Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision));
|
||||
|
||||
if (Core::IsRunning())
|
||||
if (Core::IsRunning(system))
|
||||
DolphinAnalytics::Instance().ReportGameStart();
|
||||
}
|
||||
|
||||
void SConfig::OnNewTitleLoad(const Core::CPUThreadGuard& guard)
|
||||
{
|
||||
if (!Core::IsRunning())
|
||||
auto& system = guard.GetSystem();
|
||||
if (!Core::IsRunning(system))
|
||||
return;
|
||||
|
||||
if (!g_symbolDB.IsEmpty())
|
||||
auto& ppc_symbol_db = system.GetPPCSymbolDB();
|
||||
if (!ppc_symbol_db.IsEmpty())
|
||||
{
|
||||
g_symbolDB.Clear();
|
||||
Host_NotifyMapLoaded();
|
||||
ppc_symbol_db.Clear();
|
||||
Host_PPCSymbolsChanged();
|
||||
}
|
||||
CBoot::LoadMapFromFilename(guard);
|
||||
auto& system = Core::System::GetInstance();
|
||||
CBoot::LoadMapFromFilename(guard, ppc_symbol_db);
|
||||
HLE::Reload(system);
|
||||
PatchEngine::Reload();
|
||||
HiresTexture::Update();
|
||||
|
|
|
@ -120,7 +120,7 @@ static std::unique_ptr<MemoryWatcher> s_memory_watcher;
|
|||
|
||||
struct HostJob
|
||||
{
|
||||
std::function<void()> job;
|
||||
std::function<void(Core::System&)> job;
|
||||
bool run_after_stop;
|
||||
};
|
||||
static std::mutex s_host_jobs_lock;
|
||||
|
@ -166,13 +166,13 @@ void FrameUpdateOnCPUThread()
|
|||
NetPlay::NetPlayClient::SendTimeBase();
|
||||
}
|
||||
|
||||
void OnFrameEnd()
|
||||
void OnFrameEnd(Core::System& system)
|
||||
{
|
||||
#ifdef USE_MEMORYWATCHER
|
||||
if (s_memory_watcher)
|
||||
{
|
||||
ASSERT(IsCPUThread());
|
||||
CPUThreadGuard guard(Core::System::GetInstance());
|
||||
const CPUThreadGuard guard(system);
|
||||
|
||||
s_memory_watcher->Step(guard);
|
||||
}
|
||||
|
@ -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())
|
||||
if (!IsRunning(Core::System::GetInstance()))
|
||||
return;
|
||||
|
||||
// Actually displaying non-ASCII could cause things to go pear-shaped
|
||||
|
@ -200,9 +200,9 @@ void DisplayMessage(std::string message, int time_in_ms)
|
|||
OSD::AddMessage(std::move(message), time_in_ms);
|
||||
}
|
||||
|
||||
bool IsRunning()
|
||||
bool IsRunning(Core::System& system)
|
||||
{
|
||||
return (GetState() != State::Uninitialized || s_hardware_initialized) && !s_is_stopping;
|
||||
return (GetState(system) != State::Uninitialized || s_hardware_initialized) && !s_is_stopping;
|
||||
}
|
||||
|
||||
bool IsRunningAndStarted()
|
||||
|
@ -210,11 +210,6 @@ bool IsRunningAndStarted()
|
|||
return s_is_started && !s_is_stopping;
|
||||
}
|
||||
|
||||
bool IsRunningInCurrentThread()
|
||||
{
|
||||
return IsRunning() && IsCPUThread();
|
||||
}
|
||||
|
||||
bool IsCPUThread()
|
||||
{
|
||||
return tls_is_cpu_thread;
|
||||
|
@ -241,7 +236,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
|
|||
{
|
||||
if (s_emu_thread.joinable())
|
||||
{
|
||||
if (IsRunning())
|
||||
if (IsRunning(system))
|
||||
{
|
||||
PanicAlertFmtT("Emu Thread already running");
|
||||
return false;
|
||||
|
@ -252,7 +247,7 @@ bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const Wind
|
|||
}
|
||||
|
||||
// Drain any left over jobs
|
||||
HostDispatchJobs();
|
||||
HostDispatchJobs(system);
|
||||
|
||||
INFO_LOG_FMT(BOOT, "Starting core = {} mode", system.IsWii() ? "Wii" : "GameCube");
|
||||
INFO_LOG_FMT(BOOT, "CPU Thread separate = {}", system.IsDualCoreMode() ? "Yes" : "No");
|
||||
|
@ -284,14 +279,16 @@ static void ResetRumble()
|
|||
}
|
||||
|
||||
// Called from GUI thread
|
||||
void Stop() // - Hammertime!
|
||||
void Stop(Core::System& system) // - Hammertime!
|
||||
{
|
||||
if (GetState() == State::Stopping || GetState() == State::Uninitialized)
|
||||
if (const State state = GetState(system);
|
||||
state == State::Stopping || state == State::Uninitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().CloseGame();
|
||||
AchievementManager::GetInstance().SetDisabled(false);
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
s_is_stopping = true;
|
||||
|
@ -299,9 +296,7 @@ void Stop() // - Hammertime!
|
|||
CallOnStateChangedCallbacks(State::Stopping);
|
||||
|
||||
// Dump left over jobs
|
||||
HostDispatchJobs();
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
HostDispatchJobs(system);
|
||||
|
||||
system.GetFifo().EmulatorState(false);
|
||||
|
||||
|
@ -359,9 +354,9 @@ static void CPUSetInitialExecutionState(bool force_paused = false)
|
|||
{
|
||||
// The CPU starts in stepping state, and will wait until a new state is set before executing.
|
||||
// SetState must be called on the host thread, so we defer it for later.
|
||||
QueueHostJob([force_paused]() {
|
||||
QueueHostJob([force_paused](Core::System& system) {
|
||||
bool paused = SConfig::GetInstance().bBootToPause || force_paused;
|
||||
SetState(paused ? State::Paused : State::Running);
|
||||
SetState(system, paused ? State::Paused : State::Running);
|
||||
Host_UpdateDisasmDialog();
|
||||
Host_UpdateMainFrame();
|
||||
Host_Message(HostMessageID::WMUserCreate);
|
||||
|
@ -396,7 +391,7 @@ static void CpuThread(Core::System& system, const std::optional<std::string>& sa
|
|||
|
||||
if (savestate_path)
|
||||
{
|
||||
::State::LoadAs(*savestate_path);
|
||||
::State::LoadAs(system, *savestate_path);
|
||||
if (delete_savestate)
|
||||
File::Delete(*savestate_path);
|
||||
}
|
||||
|
@ -666,7 +661,7 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
system.GetPowerPC().SetMode(PowerPC::CoreMode::Interpreter);
|
||||
}
|
||||
|
||||
UpdateTitle();
|
||||
UpdateTitle(system);
|
||||
|
||||
// ENTER THE VIDEO THREAD LOOP
|
||||
if (system.IsDualCoreMode())
|
||||
|
@ -708,13 +703,12 @@ static void EmuThread(Core::System& system, std::unique_ptr<BootParameters> boot
|
|||
|
||||
// Set or get the running state
|
||||
|
||||
void SetState(State state, bool report_state_change)
|
||||
void SetState(Core::System& system, State state, bool report_state_change)
|
||||
{
|
||||
// State cannot be controlled until the CPU Thread is operational
|
||||
if (!IsRunningAndStarted())
|
||||
return;
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
switch (state)
|
||||
{
|
||||
case State::Paused:
|
||||
|
@ -738,17 +732,16 @@ void SetState(State state, bool report_state_change)
|
|||
// Certain callers only change the state momentarily. Sending a callback for them causes
|
||||
// unwanted updates, such as the Pause/Play button flickering between states on frame advance.
|
||||
if (report_state_change)
|
||||
CallOnStateChangedCallbacks(GetState());
|
||||
CallOnStateChangedCallbacks(GetState(system));
|
||||
}
|
||||
|
||||
State GetState()
|
||||
State GetState(Core::System& system)
|
||||
{
|
||||
if (s_is_stopping)
|
||||
return State::Stopping;
|
||||
|
||||
if (s_hardware_initialized)
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
if (system.GetCPU().IsStepping())
|
||||
return State::Paused;
|
||||
|
||||
|
@ -798,14 +791,14 @@ static std::string GenerateScreenshotName()
|
|||
|
||||
void SaveScreenShot()
|
||||
{
|
||||
Core::RunAsCPUThread([] { g_frame_dumper->SaveScreenshot(GenerateScreenshotName()); });
|
||||
const Core::CPUThreadGuard guard(Core::System::GetInstance());
|
||||
g_frame_dumper->SaveScreenshot(GenerateScreenshotName());
|
||||
}
|
||||
|
||||
void SaveScreenShot(std::string_view name)
|
||||
{
|
||||
Core::RunAsCPUThread([&name] {
|
||||
g_frame_dumper->SaveScreenshot(fmt::format("{}{}.png", GenerateScreenshotFolderPath(), name));
|
||||
});
|
||||
const Core::CPUThreadGuard guard(Core::System::GetInstance());
|
||||
g_frame_dumper->SaveScreenshot(fmt::format("{}{}.png", GenerateScreenshotFolderPath(), name));
|
||||
}
|
||||
|
||||
static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unlock)
|
||||
|
@ -847,31 +840,15 @@ static bool PauseAndLock(Core::System& system, bool do_lock, bool unpause_on_unl
|
|||
return was_unpaused;
|
||||
}
|
||||
|
||||
void RunAsCPUThread(std::function<void()> function)
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
const bool is_cpu_thread = IsCPUThread();
|
||||
bool was_unpaused = false;
|
||||
if (!is_cpu_thread)
|
||||
was_unpaused = PauseAndLock(system, true, true);
|
||||
|
||||
function();
|
||||
|
||||
if (!is_cpu_thread)
|
||||
PauseAndLock(system, false, was_unpaused);
|
||||
}
|
||||
|
||||
void RunOnCPUThread(std::function<void()> function, bool wait_for_completion)
|
||||
void RunOnCPUThread(Core::System& system, std::function<void()> function, bool wait_for_completion)
|
||||
{
|
||||
// If the CPU thread is not running, assume there is no active CPU thread we can race against.
|
||||
if (!IsRunning() || IsCPUThread())
|
||||
if (!IsRunning(system) || IsCPUThread())
|
||||
{
|
||||
function();
|
||||
return;
|
||||
}
|
||||
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
||||
// Pause the CPU (set it to stepping mode).
|
||||
const bool was_running = PauseAndLock(system, true, true);
|
||||
|
||||
|
@ -930,7 +907,7 @@ void Callback_NewField(Core::System& system)
|
|||
{
|
||||
s_frame_step = false;
|
||||
system.GetCPU().Break();
|
||||
CallOnStateChangedCallbacks(Core::GetState());
|
||||
CallOnStateChangedCallbacks(Core::GetState(system));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -939,13 +916,12 @@ void Callback_NewField(Core::System& system)
|
|||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
}
|
||||
|
||||
void UpdateTitle()
|
||||
void UpdateTitle(Core::System& system)
|
||||
{
|
||||
// Settings are shown the same for both extended and summary info
|
||||
const std::string SSettings = fmt::format(
|
||||
"{} {} | {} | {}", Core::System::GetInstance().GetPowerPC().GetCPUName(),
|
||||
Core::System::GetInstance().IsDualCoreMode() ? "DC" : "SC", g_video_backend->GetDisplayName(),
|
||||
Config::Get(Config::MAIN_DSP_HLE) ? "HLE" : "LLE");
|
||||
"{} {} | {} | {}", system.GetPowerPC().GetCPUName(), system.IsDualCoreMode() ? "DC" : "SC",
|
||||
g_video_backend->GetDisplayName(), Config::Get(Config::MAIN_DSP_HLE) ? "HLE" : "LLE");
|
||||
|
||||
std::string message = fmt::format("{} | {}", Common::GetScmRevStr(), SSettings);
|
||||
if (Config::Get(Config::MAIN_SHOW_ACTIVE_TITLE))
|
||||
|
@ -958,7 +934,7 @@ void UpdateTitle()
|
|||
Host_UpdateTitle(message);
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
void Shutdown(Core::System& system)
|
||||
{
|
||||
// During shutdown DXGI expects us to handle some messages on the UI thread.
|
||||
// Therefore we can't immediately block and wait for the emu thread to shut
|
||||
|
@ -970,7 +946,7 @@ void Shutdown()
|
|||
s_emu_thread.join();
|
||||
|
||||
// Make sure there's nothing left over in case we're about to exit.
|
||||
HostDispatchJobs();
|
||||
HostDispatchJobs(system);
|
||||
}
|
||||
|
||||
int AddOnStateChangedCallback(StateChangedCallbackFunc callback)
|
||||
|
@ -1007,33 +983,31 @@ void CallOnStateChangedCallbacks(Core::State state)
|
|||
}
|
||||
}
|
||||
|
||||
void UpdateWantDeterminism(bool initial)
|
||||
void UpdateWantDeterminism(Core::System& system, bool initial)
|
||||
{
|
||||
// For now, this value is not itself configurable. Instead, individual
|
||||
// settings that depend on it, such as GPU determinism mode. should have
|
||||
// override options for testing,
|
||||
auto& system = Core::System::GetInstance();
|
||||
bool new_want_determinism = system.GetMovie().IsMovieActive() || NetPlay::IsNetPlayRunning();
|
||||
if (new_want_determinism != s_wants_determinism || initial)
|
||||
{
|
||||
NOTICE_LOG_FMT(COMMON, "Want determinism <- {}", new_want_determinism ? "true" : "false");
|
||||
|
||||
RunAsCPUThread([&] {
|
||||
s_wants_determinism = new_want_determinism;
|
||||
const auto ios = system.GetIOS();
|
||||
if (ios)
|
||||
ios->UpdateWantDeterminism(new_want_determinism);
|
||||
const Core::CPUThreadGuard guard(system);
|
||||
s_wants_determinism = new_want_determinism;
|
||||
const auto ios = system.GetIOS();
|
||||
if (ios)
|
||||
ios->UpdateWantDeterminism(new_want_determinism);
|
||||
|
||||
system.GetFifo().UpdateWantDeterminism(new_want_determinism);
|
||||
system.GetFifo().UpdateWantDeterminism(new_want_determinism);
|
||||
|
||||
// We need to clear the cache because some parts of the JIT depend on want_determinism,
|
||||
// e.g. use of FMA.
|
||||
system.GetJitInterface().ClearCache();
|
||||
});
|
||||
// We need to clear the cache because some parts of the JIT depend on want_determinism,
|
||||
// e.g. use of FMA.
|
||||
system.GetJitInterface().ClearCache(guard);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueHostJob(std::function<void()> job, bool run_during_stop)
|
||||
void QueueHostJob(std::function<void(Core::System&)> job, bool run_during_stop)
|
||||
{
|
||||
if (!job)
|
||||
return;
|
||||
|
@ -1049,7 +1023,7 @@ void QueueHostJob(std::function<void()> job, bool run_during_stop)
|
|||
Host_Message(HostMessageID::WMUserJobDispatch);
|
||||
}
|
||||
|
||||
void HostDispatchJobs()
|
||||
void HostDispatchJobs(Core::System& system)
|
||||
{
|
||||
// WARNING: This should only run on the Host Thread.
|
||||
// NOTE: This function is potentially re-entrant. If a job calls
|
||||
|
@ -1065,17 +1039,17 @@ void HostDispatchJobs()
|
|||
// 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())
|
||||
if (!job.run_after_stop && !s_is_booting.IsSet() && !IsRunning(system))
|
||||
continue;
|
||||
|
||||
guard.unlock();
|
||||
job.job();
|
||||
job.job(system);
|
||||
guard.lock();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Host Thread
|
||||
void DoFrameStep()
|
||||
void DoFrameStep(Core::System& system)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
|
@ -1084,17 +1058,17 @@ void DoFrameStep()
|
|||
return;
|
||||
}
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
if (GetState() == State::Paused)
|
||||
if (GetState(system) == State::Paused)
|
||||
{
|
||||
// if already paused, frame advance for 1 frame
|
||||
s_stop_frame_step = false;
|
||||
s_frame_step = true;
|
||||
SetState(State::Running, false);
|
||||
SetState(system, State::Running, false);
|
||||
}
|
||||
else if (!s_frame_step)
|
||||
{
|
||||
// if not paused yet, pause immediately instead
|
||||
SetState(State::Paused);
|
||||
SetState(system, State::Paused);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,13 +94,10 @@ enum class ConsoleType : u32
|
|||
ReservedTDEVSystem = 0x20000007,
|
||||
};
|
||||
|
||||
// Run a function as the CPU thread. This is an RAII alternative to the RunAsCPUThread function.
|
||||
//
|
||||
// If constructed from the Host thread, the CPU thread is paused and the current thread temporarily
|
||||
// becomes the CPU thread.
|
||||
// If constructed from the CPU thread, nothing special happens.
|
||||
//
|
||||
// This should only be constructed from the CPU thread or the host thread.
|
||||
// This is an RAII alternative to using PauseAndLock. If constructed from the host thread, the CPU
|
||||
// thread is paused, and the current thread temporarily becomes the CPU thread. If constructed from
|
||||
// the CPU thread, nothing special happens. This should only be constructed on the CPU thread or the
|
||||
// host thread.
|
||||
//
|
||||
// Some functions use a parameter of this type to indicate that the function should only be called
|
||||
// from the CPU thread. If the parameter is a pointer, the function has a fallback for being called
|
||||
|
@ -125,8 +122,8 @@ private:
|
|||
};
|
||||
|
||||
bool Init(Core::System& system, std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi);
|
||||
void Stop();
|
||||
void Shutdown();
|
||||
void Stop(Core::System& system);
|
||||
void Shutdown(Core::System& system);
|
||||
|
||||
void DeclareAsCPUThread();
|
||||
void UndeclareAsCPUThread();
|
||||
|
@ -137,18 +134,17 @@ void UndeclareAsHostThread();
|
|||
|
||||
std::string StopMessage(bool main_thread, std::string_view message);
|
||||
|
||||
bool IsRunning();
|
||||
bool IsRunningAndStarted(); // is running and the CPU loop has been entered
|
||||
bool IsRunningInCurrentThread(); // this tells us whether we are running in the CPU thread.
|
||||
bool IsCPUThread(); // this tells us whether we are the CPU thread.
|
||||
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 IsGPUThread();
|
||||
bool IsHostThread();
|
||||
|
||||
bool WantsDeterminism();
|
||||
|
||||
// [NOT THREADSAFE] For use by Host only
|
||||
void SetState(State state, bool report_state_change = true);
|
||||
State GetState();
|
||||
void SetState(Core::System& system, State state, bool report_state_change = true);
|
||||
State GetState(Core::System& system);
|
||||
|
||||
void SaveScreenShot();
|
||||
void SaveScreenShot(std::string_view name);
|
||||
|
@ -157,20 +153,11 @@ void SaveScreenShot(std::string_view name);
|
|||
void DisplayMessage(std::string message, int time_in_ms);
|
||||
|
||||
void FrameUpdateOnCPUThread();
|
||||
void OnFrameEnd();
|
||||
|
||||
// Run a function as the CPU thread.
|
||||
//
|
||||
// If called from the Host thread, the CPU thread is paused and the current thread temporarily
|
||||
// becomes the CPU thread while running the function.
|
||||
// If called from the CPU thread, the function will be run directly.
|
||||
//
|
||||
// This should only be called from the CPU thread or the host thread.
|
||||
void RunAsCPUThread(std::function<void()> function);
|
||||
void OnFrameEnd(Core::System& system);
|
||||
|
||||
// Run a function on the CPU thread, asynchronously.
|
||||
// This is only valid to call from the host thread, since it uses PauseAndLock() internally.
|
||||
void RunOnCPUThread(std::function<void()> function, bool wait_for_completion);
|
||||
void RunOnCPUThread(Core::System& system, std::function<void()> function, bool wait_for_completion);
|
||||
|
||||
// for calling back into UI code without introducing a dependency on it in core
|
||||
using StateChangedCallbackFunc = std::function<void(Core::State)>;
|
||||
|
@ -181,7 +168,7 @@ bool RemoveOnStateChangedCallback(int* handle);
|
|||
void CallOnStateChangedCallbacks(Core::State state);
|
||||
|
||||
// Run on the Host thread when the factors change. [NOT THREADSAFE]
|
||||
void UpdateWantDeterminism(bool initial = false);
|
||||
void UpdateWantDeterminism(Core::System& system, bool initial = false);
|
||||
|
||||
// Queue an arbitrary function to asynchronously run once on the Host thread later.
|
||||
// Threadsafe. Can be called by any thread, including the Host itself.
|
||||
|
@ -192,16 +179,16 @@ void UpdateWantDeterminism(bool initial = false);
|
|||
// NOTE: Make sure the jobs check the global state instead of assuming everything is
|
||||
// still the same as when the job was queued.
|
||||
// NOTE: Jobs that are not set to run during stop will be discarded instead.
|
||||
void QueueHostJob(std::function<void()> job, bool run_during_stop = false);
|
||||
void QueueHostJob(std::function<void(Core::System&)> job, bool run_during_stop = false);
|
||||
|
||||
// Should be called periodically by the Host to run pending jobs.
|
||||
// WMUserJobDispatch will be sent when something is added to the queue.
|
||||
void HostDispatchJobs();
|
||||
void HostDispatchJobs(Core::System& system);
|
||||
|
||||
void DoFrameStep();
|
||||
void DoFrameStep(Core::System& system);
|
||||
|
||||
void UpdateInputGate(bool require_focus, bool require_full_focus = false);
|
||||
|
||||
void UpdateTitle();
|
||||
void UpdateTitle(Core::System& system);
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -258,8 +258,8 @@ void DSPJitRegCache::FlushRegs(DSPJitRegCache& cache, bool emit)
|
|||
// free all host regs that are not used for the same guest reg
|
||||
for (size_t i = 0; i < m_regs.size(); i++)
|
||||
{
|
||||
const auto reg = m_regs[i];
|
||||
const auto cached_reg = cache.m_regs[i];
|
||||
const auto& reg = m_regs[i];
|
||||
const auto& cached_reg = cache.m_regs[i];
|
||||
|
||||
if (cached_reg.loc.GetSimpleReg() != reg.loc.GetSimpleReg() && reg.loc.IsSimpleReg())
|
||||
{
|
||||
|
|
|
@ -198,7 +198,7 @@ void BranchWatch::IsolateNotExecuted(const CPUThreadGuard&)
|
|||
|
||||
void BranchWatch::IsolateWasOverwritten(const CPUThreadGuard& guard)
|
||||
{
|
||||
if (Core::GetState() == Core::State::Uninitialized)
|
||||
if (Core::GetState(guard.GetSystem()) == Core::State::Uninitialized)
|
||||
{
|
||||
ASSERT_MSG(CORE, false, "Core is uninitialized.");
|
||||
return;
|
||||
|
@ -246,7 +246,7 @@ void BranchWatch::IsolateWasOverwritten(const CPUThreadGuard& guard)
|
|||
|
||||
void BranchWatch::IsolateNotOverwritten(const CPUThreadGuard& guard)
|
||||
{
|
||||
if (Core::GetState() == Core::State::Uninitialized)
|
||||
if (Core::GetState(guard.GetSystem()) == Core::State::Uninitialized)
|
||||
{
|
||||
ASSERT_MSG(CORE, false, "Core is uninitialized.");
|
||||
return;
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/EnumUtils.h"
|
||||
#include "Core/PowerPC/Gekko.h"
|
||||
|
@ -26,8 +26,7 @@ struct FakeBranchWatchCollectionKey
|
|||
u32 origin_addr;
|
||||
u32 destin_addr;
|
||||
|
||||
// TODO C++20: constexpr w/ std::bit_cast
|
||||
inline operator u64() const { return Common::BitCast<u64>(*this); }
|
||||
constexpr operator u64() const { return std::bit_cast<u64>(*this); }
|
||||
};
|
||||
struct BranchWatchCollectionKey : FakeBranchWatchCollectionKey
|
||||
{
|
||||
|
@ -155,37 +154,37 @@ public:
|
|||
// but also increment the total_hits by N (see dcbx JIT code).
|
||||
static void HitVirtualTrue_fk(BranchWatch* branch_watch, u64 fake_key, u32 inst)
|
||||
{
|
||||
branch_watch->m_collection_vt[{Common::BitCast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
branch_watch->m_collection_vt[{std::bit_cast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
.total_hits += 1;
|
||||
}
|
||||
|
||||
static void HitPhysicalTrue_fk(BranchWatch* branch_watch, u64 fake_key, u32 inst)
|
||||
{
|
||||
branch_watch->m_collection_pt[{Common::BitCast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
branch_watch->m_collection_pt[{std::bit_cast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
.total_hits += 1;
|
||||
}
|
||||
|
||||
static void HitVirtualFalse_fk(BranchWatch* branch_watch, u64 fake_key, u32 inst)
|
||||
{
|
||||
branch_watch->m_collection_vf[{Common::BitCast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
branch_watch->m_collection_vf[{std::bit_cast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
.total_hits += 1;
|
||||
}
|
||||
|
||||
static void HitPhysicalFalse_fk(BranchWatch* branch_watch, u64 fake_key, u32 inst)
|
||||
{
|
||||
branch_watch->m_collection_pf[{Common::BitCast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
branch_watch->m_collection_pf[{std::bit_cast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
.total_hits += 1;
|
||||
}
|
||||
|
||||
static void HitVirtualTrue_fk_n(BranchWatch* branch_watch, u64 fake_key, u32 inst, u32 n)
|
||||
{
|
||||
branch_watch->m_collection_vt[{Common::BitCast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
branch_watch->m_collection_vt[{std::bit_cast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
.total_hits += n;
|
||||
}
|
||||
|
||||
static void HitPhysicalTrue_fk_n(BranchWatch* branch_watch, u64 fake_key, u32 inst, u32 n)
|
||||
{
|
||||
branch_watch->m_collection_pt[{Common::BitCast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
branch_watch->m_collection_pt[{std::bit_cast<FakeBranchWatchCollectionKey>(fake_key), inst}]
|
||||
.total_hits += n;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -103,7 +104,7 @@ public:
|
|||
{
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
virtual std::string GetDescription(u32 /*address*/) const = 0;
|
||||
virtual std::string_view GetDescription(u32 /*address*/) const = 0;
|
||||
virtual void Clear(const CPUThreadGuard& guard) = 0;
|
||||
};
|
||||
} // namespace Core
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/PowerPC/MMU.h"
|
||||
#include "Core/PowerPC/PPCSymbolDB.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
@ -54,9 +55,11 @@ static void WalkTheStack(const Core::CPUThreadGuard& guard,
|
|||
// instead of "pointing ahead"
|
||||
bool GetCallstack(const Core::CPUThreadGuard& guard, std::vector<CallstackEntry>& output)
|
||||
{
|
||||
const auto& ppc_state = guard.GetSystem().GetPPCState();
|
||||
auto& system = guard.GetSystem();
|
||||
auto& power_pc = system.GetPowerPC();
|
||||
const auto& ppc_state = power_pc.GetPPCState();
|
||||
|
||||
if (!Core::IsRunning() || !PowerPC::MMU::HostIsRAMAddress(guard, ppc_state.gpr[1]))
|
||||
if (!Core::IsRunning(system) || !PowerPC::MMU::HostIsRAMAddress(guard, ppc_state.gpr[1]))
|
||||
return false;
|
||||
|
||||
if (LR(ppc_state) == 0)
|
||||
|
@ -68,14 +71,16 @@ bool GetCallstack(const Core::CPUThreadGuard& guard, std::vector<CallstackEntry>
|
|||
return false;
|
||||
}
|
||||
|
||||
auto& ppc_symbol_db = power_pc.GetSymbolDB();
|
||||
|
||||
output.push_back({
|
||||
.Name = fmt::format(" * {} [ LR = {:08x} ]\n", g_symbolDB.GetDescription(LR(ppc_state)),
|
||||
.Name = fmt::format(" * {} [ LR = {:08x} ]\n", ppc_symbol_db.GetDescription(LR(ppc_state)),
|
||||
LR(ppc_state) - 4),
|
||||
.vAddress = LR(ppc_state) - 4,
|
||||
});
|
||||
|
||||
WalkTheStack(guard, [&output](u32 func_addr) {
|
||||
std::string func_desc = g_symbolDB.GetDescription(func_addr);
|
||||
WalkTheStack(guard, [&output, &ppc_symbol_db](u32 func_addr) {
|
||||
std::string_view func_desc = ppc_symbol_db.GetDescription(func_addr);
|
||||
if (func_desc.empty() || func_desc == "Invalid")
|
||||
func_desc = "(unknown)";
|
||||
|
||||
|
@ -91,7 +96,9 @@ bool GetCallstack(const Core::CPUThreadGuard& guard, std::vector<CallstackEntry>
|
|||
void PrintCallstack(const Core::CPUThreadGuard& guard, Common::Log::LogType type,
|
||||
Common::Log::LogLevel level)
|
||||
{
|
||||
const auto& ppc_state = guard.GetSystem().GetPPCState();
|
||||
auto& power_pc = guard.GetSystem().GetPowerPC();
|
||||
const auto& ppc_state = power_pc.GetPPCState();
|
||||
auto& ppc_symbol_db = power_pc.GetSymbolDB();
|
||||
|
||||
GENERIC_LOG_FMT(type, level, "== STACK TRACE - SP = {:08x} ==", ppc_state.gpr[1]);
|
||||
|
||||
|
@ -100,22 +107,25 @@ void PrintCallstack(const Core::CPUThreadGuard& guard, Common::Log::LogType type
|
|||
GENERIC_LOG_FMT(type, level, " LR = 0 - this is bad");
|
||||
}
|
||||
|
||||
if (g_symbolDB.GetDescription(ppc_state.pc) != g_symbolDB.GetDescription(LR(ppc_state)))
|
||||
if (const std::string_view lr_desc = ppc_symbol_db.GetDescription(LR(ppc_state));
|
||||
lr_desc != ppc_symbol_db.GetDescription(ppc_state.pc))
|
||||
{
|
||||
GENERIC_LOG_FMT(type, level, " * {} [ LR = {:08x} ]", g_symbolDB.GetDescription(LR(ppc_state)),
|
||||
LR(ppc_state));
|
||||
GENERIC_LOG_FMT(type, level, " * {} [ LR = {:08x} ]", lr_desc, LR(ppc_state));
|
||||
}
|
||||
|
||||
WalkTheStack(guard, [type, level](u32 func_addr) {
|
||||
std::string func_desc = g_symbolDB.GetDescription(func_addr);
|
||||
WalkTheStack(guard, [type, level, &ppc_symbol_db](u32 func_addr) {
|
||||
std::string_view func_desc = ppc_symbol_db.GetDescription(func_addr);
|
||||
if (func_desc.empty() || func_desc == "Invalid")
|
||||
func_desc = "(unknown)";
|
||||
GENERIC_LOG_FMT(type, level, " * {} [ addr = {:08x} ]", func_desc, func_addr);
|
||||
});
|
||||
}
|
||||
|
||||
void PrintDataBuffer(Common::Log::LogType type, const u8* data, size_t size, std::string_view title)
|
||||
void PrintDataBuffer(const Core::System& system, Common::Log::LogType type, u32 address, u32 size,
|
||||
std::string_view title)
|
||||
{
|
||||
const u8* data = system.GetMemory().GetPointerForRange(address, size);
|
||||
|
||||
GENERIC_LOG_FMT(type, Common::Log::LogLevel::LDEBUG, "{}", title);
|
||||
for (u32 j = 0; j < size;)
|
||||
{
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
}
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Dolphin_Debugger
|
||||
{
|
||||
|
@ -26,6 +27,6 @@ struct CallstackEntry
|
|||
bool GetCallstack(const Core::CPUThreadGuard& guard, std::vector<CallstackEntry>& output);
|
||||
void PrintCallstack(const Core::CPUThreadGuard& guard, Common::Log::LogType type,
|
||||
Common::Log::LogLevel level);
|
||||
void PrintDataBuffer(Common::Log::LogType type, const u8* data, size_t size,
|
||||
void PrintDataBuffer(const Core::System& system, Common::Log::LogType type, u32 address, u32 size,
|
||||
std::string_view title);
|
||||
} // namespace Dolphin_Debugger
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include "Common/GekkoDisassembler.h"
|
||||
#include "Common/StringUtil.h"
|
||||
|
||||
#include "Core/Config/AchievementSettings.h"
|
||||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/OSThread.h"
|
||||
|
@ -31,7 +31,7 @@ void ApplyMemoryPatch(const Core::CPUThreadGuard& guard, Common::Debug::MemoryPa
|
|||
bool store_existing_value)
|
||||
{
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
if (Config::Get(Config::RA_HARDCORE_ENABLED))
|
||||
if (AchievementManager::GetInstance().IsHardcoreModeActive())
|
||||
return;
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
if (patch.value.empty())
|
||||
|
@ -90,7 +90,8 @@ void PPCPatches::UnPatch(std::size_t index)
|
|||
PatchEngine::RemoveMemoryPatch(index);
|
||||
}
|
||||
|
||||
PPCDebugInterface::PPCDebugInterface(Core::System& system) : m_system(system)
|
||||
PPCDebugInterface::PPCDebugInterface(Core::System& system, PPCSymbolDB& ppc_symbol_db)
|
||||
: m_system(system), m_ppc_symbol_db(ppc_symbol_db)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -423,7 +424,7 @@ u32 PPCDebugInterface::GetColor(const Core::CPUThreadGuard* guard, u32 address)
|
|||
if (!PowerPC::MMU::HostIsRAMAddress(*guard, address))
|
||||
return 0xeeeeee;
|
||||
|
||||
Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(address);
|
||||
const Common::Symbol* const symbol = m_ppc_symbol_db.GetSymbolFromAddr(address);
|
||||
if (!symbol)
|
||||
return 0xFFFFFF;
|
||||
if (symbol->type != Common::Symbol::Type::Function)
|
||||
|
@ -441,9 +442,9 @@ u32 PPCDebugInterface::GetColor(const Core::CPUThreadGuard* guard, u32 address)
|
|||
}
|
||||
// =============
|
||||
|
||||
std::string PPCDebugInterface::GetDescription(u32 address) const
|
||||
std::string_view PPCDebugInterface::GetDescription(u32 address) const
|
||||
{
|
||||
return g_symbolDB.GetDescription(address);
|
||||
return m_ppc_symbol_db.GetDescription(address);
|
||||
}
|
||||
|
||||
std::optional<u32>
|
||||
|
|
|
@ -17,6 +17,7 @@ namespace Core
|
|||
class CPUThreadGuard;
|
||||
class System;
|
||||
} // namespace Core
|
||||
class PPCSymbolDB;
|
||||
|
||||
void ApplyMemoryPatch(const Core::CPUThreadGuard&, Common::Debug::MemoryPatch& patch,
|
||||
bool store_existing_value = true);
|
||||
|
@ -36,7 +37,7 @@ private:
|
|||
class PPCDebugInterface final : public Core::DebugInterface
|
||||
{
|
||||
public:
|
||||
explicit PPCDebugInterface(Core::System& system);
|
||||
explicit PPCDebugInterface(Core::System& system, PPCSymbolDB& ppc_symbol_db);
|
||||
~PPCDebugInterface() override;
|
||||
|
||||
// Watches
|
||||
|
@ -101,7 +102,7 @@ public:
|
|||
void Step() override {}
|
||||
void RunToBreakpoint() override;
|
||||
u32 GetColor(const Core::CPUThreadGuard* guard, u32 address) const override;
|
||||
std::string GetDescription(u32 address) const override;
|
||||
std::string_view GetDescription(u32 address) const override;
|
||||
|
||||
std::shared_ptr<Core::NetworkCaptureLogger> NetworkLogger();
|
||||
|
||||
|
@ -112,4 +113,5 @@ private:
|
|||
PPCPatches m_patches;
|
||||
std::shared_ptr<Core::NetworkCaptureLogger> m_network_logger;
|
||||
Core::System& m_system;
|
||||
PPCSymbolDB& m_ppc_symbol_db;
|
||||
};
|
||||
|
|
|
@ -137,7 +137,7 @@ void DolphinAnalytics::ReportGameStart()
|
|||
}
|
||||
|
||||
// Keep in sync with enum class GameQuirk definition.
|
||||
constexpr std::array<const char*, 28> GAME_QUIRKS_NAMES{
|
||||
constexpr std::array<const char*, 32> GAME_QUIRKS_NAMES{
|
||||
"directly-reads-wiimote-input",
|
||||
"uses-DVDLowStopLaser",
|
||||
"uses-DVDLowOffset",
|
||||
|
@ -166,6 +166,10 @@ constexpr std::array<const char*, 28> GAME_QUIRKS_NAMES{
|
|||
"mismatched-gpu-tex-coords-between-cp-and-xf",
|
||||
"mismatched-gpu-matrix-indices-between-cp-and-xf",
|
||||
"reads-bounding-box",
|
||||
"invalid-position-component-format",
|
||||
"invalid-normal-component-format",
|
||||
"invalid-texture-coordinate-component-format",
|
||||
"invalid-color-component-format",
|
||||
};
|
||||
static_assert(GAME_QUIRKS_NAMES.size() == static_cast<u32>(GameQuirk::COUNT),
|
||||
"Game quirks names and enum definition are out of sync.");
|
||||
|
|
|
@ -94,6 +94,15 @@ enum class GameQuirk
|
|||
// only a few read them (from PE_BBOX_LEFT etc.)
|
||||
READS_BOUNDING_BOX,
|
||||
|
||||
// A few games use invalid vertex component formats, but the two known cases (Fifa Street and
|
||||
// Def Jam: Fight for New York, see https://bugs.dolphin-emu.org/issues/12719) only use invalid
|
||||
// normal formats and lighting is disabled in those cases, so it doesn't end up mattering.
|
||||
// It's possible other games use invalid formats, possibly on other vertex components.
|
||||
INVALID_POSITION_COMPONENT_FORMAT,
|
||||
INVALID_NORMAL_COMPONENT_FORMAT,
|
||||
INVALID_TEXTURE_COORDINATE_COMPONENT_FORMAT,
|
||||
INVALID_COLOR_COMPONENT_FORMAT,
|
||||
|
||||
COUNT,
|
||||
};
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ void FifoPlayer::Close()
|
|||
|
||||
bool FifoPlayer::IsPlaying() const
|
||||
{
|
||||
return GetFile() != nullptr && Core::IsRunning();
|
||||
return GetFile() != nullptr && Core::IsRunning(m_system);
|
||||
}
|
||||
|
||||
class FifoPlayer::CPUCore final : public CPUCoreBase
|
||||
|
@ -697,7 +697,7 @@ void FifoPlayer::LoadTextureMemory()
|
|||
{
|
||||
static_assert(static_cast<size_t>(TMEM_SIZE) == static_cast<size_t>(FifoDataFile::TEX_MEM_SIZE),
|
||||
"TMEM_SIZE matches the size of texture memory in FifoDataFile");
|
||||
std::memcpy(texMem, m_File->GetTexMem(), FifoDataFile::TEX_MEM_SIZE);
|
||||
std::memcpy(s_tex_mem.data(), m_File->GetTexMem(), FifoDataFile::TEX_MEM_SIZE);
|
||||
}
|
||||
|
||||
void FifoPlayer::WriteCP(u32 address, u16 value)
|
||||
|
|
|
@ -291,7 +291,7 @@ void FifoRecorder::RecordInitialVideoMemory()
|
|||
|
||||
g_main_cp_state.FillCPMemoryArray(cpmem);
|
||||
|
||||
SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, texMem);
|
||||
SetVideoMemory(bpmem_ptr, cpmem, xfmem_ptr, xfregs_ptr, xfregs_size, s_tex_mem.data());
|
||||
}
|
||||
|
||||
void FifoRecorder::StopRecording()
|
||||
|
|
|
@ -208,9 +208,11 @@ static Installation InstallCodeHandlerLocked(const Core::CPUThreadGuard& guard)
|
|||
|
||||
// Invalidate the icache and any asm codes
|
||||
auto& ppc_state = guard.GetSystem().GetPPCState();
|
||||
auto& memory = guard.GetSystem().GetMemory();
|
||||
auto& jit_interface = guard.GetSystem().GetJitInterface();
|
||||
for (u32 j = 0; j < (INSTALLER_END_ADDRESS - INSTALLER_BASE_ADDRESS); j += 32)
|
||||
{
|
||||
ppc_state.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j);
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, INSTALLER_BASE_ADDRESS + j);
|
||||
}
|
||||
return Installation::Installed;
|
||||
}
|
||||
|
|
|
@ -66,12 +66,14 @@ constexpr std::array<Hook, 23> os_patches{{
|
|||
void Patch(Core::System& system, u32 addr, std::string_view func_name)
|
||||
{
|
||||
auto& ppc_state = system.GetPPCState();
|
||||
auto& memory = system.GetMemory();
|
||||
auto& jit_interface = system.GetJitInterface();
|
||||
for (u32 i = 1; i < os_patches.size(); ++i)
|
||||
{
|
||||
if (os_patches[i].name == func_name)
|
||||
{
|
||||
s_hooked_addresses[addr] = i;
|
||||
ppc_state.iCache.Invalidate(addr);
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, addr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -106,14 +108,18 @@ void PatchFixedFunctions(Core::System& system)
|
|||
|
||||
void PatchFunctions(Core::System& system)
|
||||
{
|
||||
auto& ppc_state = system.GetPPCState();
|
||||
auto& power_pc = system.GetPowerPC();
|
||||
auto& ppc_state = power_pc.GetPPCState();
|
||||
auto& memory = system.GetMemory();
|
||||
auto& jit_interface = system.GetJitInterface();
|
||||
auto& ppc_symbol_db = power_pc.GetSymbolDB();
|
||||
|
||||
// Remove all hooks that aren't fixed address hooks
|
||||
for (auto i = s_hooked_addresses.begin(); i != s_hooked_addresses.end();)
|
||||
{
|
||||
if (os_patches[i->second].flags != HookFlag::Fixed)
|
||||
{
|
||||
ppc_state.iCache.Invalidate(i->first);
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, i->first);
|
||||
i = s_hooked_addresses.erase(i);
|
||||
}
|
||||
else
|
||||
|
@ -128,12 +134,12 @@ void PatchFunctions(Core::System& system)
|
|||
if (os_patches[i].flags == HookFlag::Fixed)
|
||||
continue;
|
||||
|
||||
for (const auto& symbol : g_symbolDB.GetSymbolsFromName(os_patches[i].name))
|
||||
for (const auto& symbol : ppc_symbol_db.GetSymbolsFromName(os_patches[i].name))
|
||||
{
|
||||
for (u32 addr = symbol->address; addr < symbol->address + symbol->size; addr += 4)
|
||||
{
|
||||
s_hooked_addresses[addr] = i;
|
||||
ppc_state.iCache.Invalidate(addr);
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, addr);
|
||||
}
|
||||
INFO_LOG_FMT(OSHLE, "Patching {} {:08x}", os_patches[i].name, symbol->address);
|
||||
}
|
||||
|
@ -178,14 +184,14 @@ u32 GetHookByAddress(u32 address)
|
|||
return (iter != s_hooked_addresses.end()) ? iter->second : 0;
|
||||
}
|
||||
|
||||
u32 GetHookByFunctionAddress(u32 address)
|
||||
u32 GetHookByFunctionAddress(PPCSymbolDB& ppc_symbol_db, u32 address)
|
||||
{
|
||||
const u32 index = GetHookByAddress(address);
|
||||
// Fixed hooks use a fixed address and don't patch the whole function
|
||||
if (index == 0 || os_patches[index].flags == HookFlag::Fixed)
|
||||
return index;
|
||||
|
||||
const auto symbol = g_symbolDB.GetSymbolFromAddr(address);
|
||||
const Common::Symbol* const symbol = ppc_symbol_db.GetSymbolFromAddr(address);
|
||||
return (symbol && symbol->address == address) ? index : 0;
|
||||
}
|
||||
|
||||
|
@ -199,9 +205,10 @@ HookFlag GetHookFlagsByIndex(u32 index)
|
|||
return os_patches[index].flags;
|
||||
}
|
||||
|
||||
TryReplaceFunctionResult TryReplaceFunction(u32 address, PowerPC::CoreMode mode)
|
||||
TryReplaceFunctionResult TryReplaceFunction(PPCSymbolDB& ppc_symbol_db, u32 address,
|
||||
PowerPC::CoreMode mode)
|
||||
{
|
||||
const u32 hook_index = GetHookByFunctionAddress(address);
|
||||
const u32 hook_index = GetHookByFunctionAddress(ppc_symbol_db, address);
|
||||
if (hook_index == 0)
|
||||
return {};
|
||||
|
||||
|
@ -229,7 +236,10 @@ u32 UnPatch(Core::System& system, std::string_view patch_name)
|
|||
if (patch == std::end(os_patches))
|
||||
return 0;
|
||||
|
||||
auto& ppc_state = system.GetPPCState();
|
||||
auto& power_pc = system.GetPowerPC();
|
||||
auto& ppc_state = power_pc.GetPPCState();
|
||||
auto& memory = system.GetMemory();
|
||||
auto& jit_interface = system.GetJitInterface();
|
||||
|
||||
if (patch->flags == HookFlag::Fixed)
|
||||
{
|
||||
|
@ -241,7 +251,7 @@ u32 UnPatch(Core::System& system, std::string_view patch_name)
|
|||
if (i->second == patch_idx)
|
||||
{
|
||||
addr = i->first;
|
||||
ppc_state.iCache.Invalidate(i->first);
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, i->first);
|
||||
i = s_hooked_addresses.erase(i);
|
||||
}
|
||||
else
|
||||
|
@ -252,14 +262,14 @@ u32 UnPatch(Core::System& system, std::string_view patch_name)
|
|||
return addr;
|
||||
}
|
||||
|
||||
const auto& symbols = g_symbolDB.GetSymbolsFromName(patch_name);
|
||||
const auto symbols = power_pc.GetSymbolDB().GetSymbolsFromName(patch_name);
|
||||
if (!symbols.empty())
|
||||
{
|
||||
const auto& symbol = symbols[0];
|
||||
const Common::Symbol* const symbol = symbols.front();
|
||||
for (u32 addr = symbol->address; addr < symbol->address + symbol->size; addr += 4)
|
||||
{
|
||||
s_hooked_addresses.erase(addr);
|
||||
ppc_state.iCache.Invalidate(addr);
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, addr);
|
||||
}
|
||||
return symbol->address;
|
||||
}
|
||||
|
@ -270,6 +280,8 @@ u32 UnPatch(Core::System& system, std::string_view patch_name)
|
|||
u32 UnpatchRange(Core::System& system, u32 start_addr, u32 end_addr)
|
||||
{
|
||||
auto& ppc_state = system.GetPPCState();
|
||||
auto& memory = system.GetMemory();
|
||||
auto& jit_interface = system.GetJitInterface();
|
||||
|
||||
u32 count = 0;
|
||||
|
||||
|
@ -278,7 +290,7 @@ u32 UnpatchRange(Core::System& system, u32 start_addr, u32 end_addr)
|
|||
{
|
||||
INFO_LOG_FMT(OSHLE, "Unpatch HLE hooks [{:08x};{:08x}): {} at {:08x}", start_addr, end_addr,
|
||||
os_patches[i->second].name, i->first);
|
||||
ppc_state.iCache.Invalidate(i->first);
|
||||
ppc_state.iCache.Invalidate(memory, jit_interface, i->first);
|
||||
i = s_hooked_addresses.erase(i);
|
||||
count += 1;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace PowerPC
|
|||
enum class CoreMode;
|
||||
}
|
||||
|
||||
class PPCSymbolDB;
|
||||
|
||||
namespace HLE
|
||||
{
|
||||
using HookFunction = void (*)(const Core::CPUThreadGuard&);
|
||||
|
@ -66,7 +68,7 @@ void ExecuteFromJIT(u32 current_pc, u32 hook_index, Core::System& system);
|
|||
// Returns the HLE hook index of the address
|
||||
u32 GetHookByAddress(u32 address);
|
||||
// Returns the HLE hook index if the address matches the function start
|
||||
u32 GetHookByFunctionAddress(u32 address);
|
||||
u32 GetHookByFunctionAddress(PPCSymbolDB& ppc_symbol_db, u32 address);
|
||||
HookType GetHookTypeByIndex(u32 index);
|
||||
HookFlag GetHookFlagsByIndex(u32 index);
|
||||
|
||||
|
@ -74,6 +76,7 @@ bool IsEnabled(HookFlag flag, PowerPC::CoreMode mode);
|
|||
|
||||
// Performs the backend-independent preliminary checking for whether a function
|
||||
// can be HLEd. If it can be, the information needed for HLEing it is returned.
|
||||
TryReplaceFunctionResult TryReplaceFunction(u32 address, PowerPC::CoreMode mode);
|
||||
TryReplaceFunctionResult TryReplaceFunction(PPCSymbolDB& ppc_symbol_db, u32 address,
|
||||
PowerPC::CoreMode mode);
|
||||
|
||||
} // namespace HLE
|
||||
|
|
|
@ -36,6 +36,7 @@ void GeckoCodeHandlerICacheFlush(const Core::CPUThreadGuard& guard)
|
|||
{
|
||||
auto& system = guard.GetSystem();
|
||||
auto& ppc_state = system.GetPPCState();
|
||||
auto& jit_interface = system.GetJitInterface();
|
||||
|
||||
// Work around the codehandler not properly invalidating the icache, but
|
||||
// only the first few frames.
|
||||
|
@ -54,7 +55,7 @@ void GeckoCodeHandlerICacheFlush(const Core::CPUThreadGuard& guard)
|
|||
}
|
||||
PowerPC::MMU::HostWrite_U32(guard, gch_gameid + 1, Gecko::INSTALLER_BASE_ADDRESS);
|
||||
|
||||
ppc_state.iCache.Reset();
|
||||
ppc_state.iCache.Reset(jit_interface);
|
||||
}
|
||||
|
||||
// Because Dolphin messes around with the CPU state instead of patching the game binary, we
|
||||
|
|
|
@ -303,7 +303,7 @@ std::string GetStringVA(HLEPrintArgs* args, std::string_view string)
|
|||
if (string[i] == '*')
|
||||
{
|
||||
++i;
|
||||
const s32 result_tmp = Common::BitCast<s32>(args->GetU32());
|
||||
const s32 result_tmp = std::bit_cast<s32>(args->GetU32());
|
||||
if (result_tmp >= 0)
|
||||
return static_cast<u32>(result_tmp);
|
||||
|
||||
|
@ -415,7 +415,7 @@ std::string GetStringVA(HLEPrintArgs* args, std::string_view string)
|
|||
}
|
||||
case 'c':
|
||||
{
|
||||
const s32 value = Common::BitCast<s32>(args->GetU32());
|
||||
const s32 value = std::bit_cast<s32>(args->GetU32());
|
||||
if (length_modifier == LengthModifier::l)
|
||||
{
|
||||
// Same problem as with wide strings here.
|
||||
|
@ -443,12 +443,12 @@ std::string GetStringVA(HLEPrintArgs* args, std::string_view string)
|
|||
precision ? fmt::format(".{}", *precision) : "");
|
||||
if (length_modifier == LengthModifier::ll)
|
||||
{
|
||||
const s64 value = Common::BitCast<s64>(args->GetU64());
|
||||
const s64 value = std::bit_cast<s64>(args->GetU64());
|
||||
result += fmt::sprintf(fmt::format("%{}" PRId64, options).c_str(), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 value = Common::BitCast<s32>(args->GetU32());
|
||||
s32 value = std::bit_cast<s32>(args->GetU32());
|
||||
if (length_modifier == LengthModifier::h)
|
||||
value = static_cast<s16>(value);
|
||||
else if (length_modifier == LengthModifier::hh)
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
#include "Core/HW/AddressSpace.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/HW/DSP.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
|
@ -55,7 +55,7 @@ void Accessors::WriteU64(const Core::CPUThreadGuard& guard, u32 address, u64 val
|
|||
|
||||
float Accessors::ReadF32(const Core::CPUThreadGuard& guard, u32 address) const
|
||||
{
|
||||
return Common::BitCast<float>(ReadU32(guard, address));
|
||||
return std::bit_cast<float>(ReadU32(guard, address));
|
||||
}
|
||||
|
||||
Accessors::iterator Accessors::begin() const
|
||||
|
@ -150,14 +150,14 @@ struct EffectiveAddressSpaceAccessors : Accessors
|
|||
return false;
|
||||
}
|
||||
|
||||
u8* page_ptr = memory.GetPointer(*page_physical_address);
|
||||
std::size_t chunk_size = std::min<std::size_t>(0x1000 - offset, needle_size);
|
||||
u8* page_ptr = memory.GetPointerForRange(*page_physical_address + offset, chunk_size);
|
||||
if (page_ptr == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::size_t chunk_size = std::min<std::size_t>(0x1000 - offset, needle_size);
|
||||
if (memcmp(needle_start, page_ptr + offset, chunk_size) != 0)
|
||||
if (memcmp(needle_start, page_ptr, chunk_size) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -430,7 +430,7 @@ void DSPManager::UpdateAudioDMA()
|
|||
// external audio fifo in the emulator, to be mixed with the disc
|
||||
// streaming output.
|
||||
auto& memory = m_system.GetMemory();
|
||||
void* address = memory.GetPointer(m_audio_dma.current_source_address);
|
||||
void* address = memory.GetPointerForRange(m_audio_dma.current_source_address, 32);
|
||||
AudioCommon::SendAIBuffer(m_system, reinterpret_cast<short*>(address), 8);
|
||||
|
||||
if (m_audio_dma.remaining_blocks_count != 0)
|
||||
|
|
|
@ -57,13 +57,15 @@ constexpr u32 SAMPLE_RATE = 48000;
|
|||
|
||||
bool ASndUCode::SwapLeftRight() const
|
||||
{
|
||||
return m_crc == HASH_DESERT_BUS_2011 || m_crc == HASH_DESERT_BUS_2012;
|
||||
return m_crc == HASH_2008 || m_crc == HASH_2009 || m_crc == HASH_2011 || m_crc == HASH_2020 ||
|
||||
m_crc == HASH_2020_PAD;
|
||||
}
|
||||
|
||||
bool ASndUCode::UseNewFlagMasks() const
|
||||
{
|
||||
return m_crc == HASH_2011 || m_crc == HASH_2020 || m_crc == HASH_2020_PAD ||
|
||||
m_crc == HASH_DESERT_BUS_2011 || m_crc == HASH_DESERT_BUS_2012;
|
||||
m_crc == HASH_DESERT_BUS_2011 || m_crc == HASH_DESERT_BUS_2012 || m_crc == HASH_2024 ||
|
||||
m_crc == HASH_2024_PAD;
|
||||
}
|
||||
|
||||
ASndUCode::ASndUCode(DSPHLE* dsphle, u32 crc) : UCodeInterface(dsphle, crc)
|
||||
|
@ -398,11 +400,12 @@ void ASndUCode::DoMixing(u32 return_mail)
|
|||
}
|
||||
// Both paths jmpr $AR3, which is an index into sample_selector
|
||||
|
||||
auto [new_r, new_l] = (this->*sample_function)();
|
||||
auto [new_l, new_r] = (this->*sample_function)();
|
||||
if (SwapLeftRight())
|
||||
{
|
||||
// The Desert Bus versions swapped the left and right input channels so that left
|
||||
// comes first, and then right. Before, right came before left.
|
||||
// Most versions of the ASnd ucode have the right channel come before the left channel.
|
||||
// The Desert Bus and 2024 versions swapped the left and right input channels so that left
|
||||
// comes first, and then right, matching mp3/ogg files.
|
||||
std::swap(new_r, new_l);
|
||||
}
|
||||
// out_samp: "multiply sample x volume" - left is put in $ax0.h, right is put in $ax1.h
|
||||
|
|
|
@ -53,12 +53,23 @@ public:
|
|||
static constexpr u32 HASH_2020_PAD = 0xbad876ef;
|
||||
// Variant used in Desert Bus v1.04 - this is based off of the code in libogc (as it existed in
|
||||
// 2011, even though that code only became used in 2020), but the left and right channels are
|
||||
// swapped. Padded to 0x0620 bytes.
|
||||
// swapped (with the left channel coming before the right channel, which is the the conventional
|
||||
// behavior). Padded to 0x0620 bytes.
|
||||
static constexpr u32 HASH_DESERT_BUS_2011 = 0xfa9c576f;
|
||||
// Variant used in Desert Bus v1.05 - this is the same as the previous version, except 4 junk
|
||||
// instructions were added to the start, which do not change behavior in any way. Padded to 0x0620
|
||||
// bytes.
|
||||
static constexpr u32 HASH_DESERT_BUS_2012 = 0x614dd145;
|
||||
// March 22, 2024 version (0x0606 bytes) - libogc fixed left and right channels being reversed,
|
||||
// which apparently has been the case from the start but was not obvious in earlier testing
|
||||
// because of the oggplayer sample using a mono sound file.
|
||||
// https://github.com/devkitPro/libogc/commit/a0b4b5680944ee7c2ae1b7af63a721623c1a6b69
|
||||
static constexpr u32 HASH_2024 = 0x5dbf8bf1;
|
||||
// March 22, 2024 version (padded to 0x0620 bytes) - same as above, but padded as it's used by
|
||||
// libogc2 and libogc-rice.
|
||||
// https://github.com/extremscorner/libogc2/commit/f3fd10635d4b3fbc6ee03cec335eeb2a2237fd56
|
||||
// https://github.com/extremscorner/libogc-rice/commit/5ebbf8b96d7433bc2af9e882f730e67a5eb20f00
|
||||
static constexpr u32 HASH_2024_PAD = 0x373a950e;
|
||||
|
||||
private:
|
||||
void DMAInVoiceData();
|
||||
|
|
|
@ -190,8 +190,9 @@ void UCodeInterface::PrepareBootUCode(u32 mail)
|
|||
|
||||
if (Config::Get(Config::MAIN_DUMP_UCODE))
|
||||
{
|
||||
DSP::DumpDSPCode(memory.GetPointer(m_next_ucode.iram_mram_addr), m_next_ucode.iram_size,
|
||||
ector_crc);
|
||||
const u8* pointer =
|
||||
memory.GetPointerForRange(m_next_ucode.iram_mram_addr, m_next_ucode.iram_size);
|
||||
DSP::DumpDSPCode(pointer, m_next_ucode.iram_size, ector_crc);
|
||||
}
|
||||
|
||||
DEBUG_LOG_FMT(DSPHLE, "PrepareBootUCode {:#010x}", ector_crc);
|
||||
|
@ -293,6 +294,8 @@ std::unique_ptr<UCodeInterface> UCodeFactory(u32 crc, DSPHLE* dsphle, bool wii)
|
|||
case ASndUCode::HASH_2020_PAD:
|
||||
case ASndUCode::HASH_DESERT_BUS_2011:
|
||||
case ASndUCode::HASH_DESERT_BUS_2012:
|
||||
case ASndUCode::HASH_2024:
|
||||
case ASndUCode::HASH_2024_PAD:
|
||||
INFO_LOG_FMT(DSPHLE, "CRC {:08x}: ASnd chosen (Homebrew)", crc);
|
||||
return std::make_unique<ASndUCode>(dsphle, crc);
|
||||
|
||||
|
|
|
@ -91,30 +91,28 @@ static const std::map<u32, u32> UCODE_FLAGS = {
|
|||
{0x56D36052, SYNC_PER_FRAME | NO_CMD_0D},
|
||||
// The Legend of Zelda: The Wind Waker.
|
||||
{0x86840740, 0},
|
||||
// The Legend of Zelda: Collector's Edition (except Wind Waker).
|
||||
// The Legend of Zelda: Four Swords Adventures.
|
||||
// Mario Kart: Double Dash.
|
||||
// Pikmin 2 GC NTSC.
|
||||
{0x2FCDF1EC, MAKE_DOLBY_LOUDER},
|
||||
// The Legend of Zelda: Twilight Princess / GC.
|
||||
// Donkey Kong Jungle Beat.
|
||||
// Donkey Kong Jungle Beat GC.
|
||||
//
|
||||
// TODO: These do additional filtering at frame rendering time. We don't
|
||||
// implement this yet.
|
||||
{0x6CA33A6D, MAKE_DOLBY_LOUDER},
|
||||
{0x6CA33A6D, MAKE_DOLBY_LOUDER | COMBINED_CMD_0D},
|
||||
// The Legend of Zelda: Twilight Princess / Wii.
|
||||
{0x6C3F6F94, NO_ARAM | MAKE_DOLBY_LOUDER},
|
||||
// Link's Crossbow Training.
|
||||
{0x6C3F6F94, NO_ARAM | MAKE_DOLBY_LOUDER | COMBINED_CMD_0D},
|
||||
// Super Mario Galaxy.
|
||||
// Super Mario Galaxy 2.
|
||||
{0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER},
|
||||
// Donkey Kong Jungle Beat Wii.
|
||||
{0xD643001F, NO_ARAM | MAKE_DOLBY_LOUDER | COMBINED_CMD_0D},
|
||||
// Pikmin 1 New Play Control.
|
||||
{0xB7EB9A9C, NO_ARAM | MAKE_DOLBY_LOUDER | COMBINED_CMD_0D},
|
||||
// Pikmin 2 New Play Control.
|
||||
{0xEAEB38CC, NO_ARAM | MAKE_DOLBY_LOUDER},
|
||||
|
||||
// TODO: Other games that use this UCode (exhaustive list):
|
||||
// * Link's Crossbow Training
|
||||
// * The Legend of Zelda: Collector's Edition
|
||||
// * The Legend of Zelda: Twilight Princess / Wii (type ????, CRC ????)
|
||||
{0xEAEB38CC, NO_ARAM | MAKE_DOLBY_LOUDER | COMBINED_CMD_0D},
|
||||
};
|
||||
|
||||
ZeldaUCode::ZeldaUCode(DSPHLE* dsphle, u32 crc)
|
||||
|
@ -518,6 +516,7 @@ void ZeldaUCode::RunPendingCommands()
|
|||
{
|
||||
// Ignore the two values which are equivalent to arguments passed to
|
||||
// command 0D.
|
||||
// Used by Pikmin 1 Wii.
|
||||
Read32();
|
||||
Read32();
|
||||
}
|
||||
|
@ -559,6 +558,7 @@ void ZeldaUCode::RunPendingCommands()
|
|||
break;
|
||||
|
||||
// Command 0D: TODO: find a name and implement.
|
||||
// Used by Wind Waker.
|
||||
case 0x0D:
|
||||
if (m_flags & NO_CMD_0D)
|
||||
{
|
||||
|
@ -1735,6 +1735,7 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count)
|
|||
|
||||
if (vpb->samples_source_type == VPB::SRC_AFC_HQ_FROM_ARAM)
|
||||
{
|
||||
// 4-bit samples
|
||||
for (size_t i = 0; i < 16; i += 2)
|
||||
{
|
||||
nibbles[i + 0] = *src >> 4;
|
||||
|
@ -1742,14 +1743,11 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count)
|
|||
src++;
|
||||
}
|
||||
for (auto& nibble : nibbles)
|
||||
{
|
||||
if (nibble >= 8)
|
||||
nibble -= 16;
|
||||
nibble <<= 11;
|
||||
}
|
||||
nibble = s16(nibble << 12) >> 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 2-bit samples
|
||||
for (size_t i = 0; i < 16; i += 4)
|
||||
{
|
||||
nibbles[i + 0] = (*src >> 6) & 3;
|
||||
|
@ -1759,11 +1757,7 @@ void ZeldaAudioRenderer::DecodeAFC(VPB* vpb, s16* dst, size_t block_count)
|
|||
src++;
|
||||
}
|
||||
for (auto& nibble : nibbles)
|
||||
{
|
||||
if (nibble >= 2)
|
||||
nibble -= 4;
|
||||
nibble <<= 13;
|
||||
}
|
||||
nibble = s16(nibble << 14) >> 1;
|
||||
}
|
||||
|
||||
s32 yn1 = *vpb->AFCYN1(), yn2 = *vpb->AFCYN2();
|
||||
|
|
|
@ -76,7 +76,7 @@ void CodeLoaded(DSPCore& dsp, u32 addr, size_t size)
|
|||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
auto& memory = system.GetMemory();
|
||||
CodeLoaded(dsp, memory.GetPointer(addr), size);
|
||||
CodeLoaded(dsp, memory.GetPointerForRange(addr, size), size);
|
||||
}
|
||||
|
||||
void CodeLoaded(DSPCore& dsp, const u8* ptr, size_t size)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "Core/AchievementManager.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/SessionSettings.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/DolphinAnalytics.h"
|
||||
#include "Core/HW/AudioInterface.h"
|
||||
|
@ -398,8 +399,7 @@ void DVDInterface::SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
|
|||
}
|
||||
|
||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||
AchievementManager::GetInstance().HashGame(disc.get(),
|
||||
[](AchievementManager::ResponseType r_type) {});
|
||||
AchievementManager::GetInstance().LoadGame("", disc.get());
|
||||
#endif // USE_RETRO_ACHIEVEMENTS
|
||||
|
||||
// Assume that inserting a disc requires having an empty disc before
|
||||
|
@ -419,7 +419,7 @@ bool DVDInterface::IsDiscInside() const
|
|||
|
||||
void DVDInterface::AutoChangeDiscCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||
{
|
||||
system.GetDVDInterface().AutoChangeDisc();
|
||||
system.GetDVDInterface().AutoChangeDisc(Core::CPUThreadGuard{system});
|
||||
}
|
||||
|
||||
void DVDInterface::EjectDiscCallback(Core::System& system, u64 userdata, s64 cyclesLate)
|
||||
|
@ -441,7 +441,7 @@ void DVDInterface::InsertDiscCallback(Core::System& system, u64 userdata, s64 cy
|
|||
}
|
||||
|
||||
// Must only be called on the CPU thread
|
||||
void DVDInterface::EjectDisc(EjectCause cause)
|
||||
void DVDInterface::EjectDisc(const Core::CPUThreadGuard& guard, EjectCause cause)
|
||||
{
|
||||
m_system.GetCoreTiming().ScheduleEvent(0, m_eject_disc);
|
||||
if (cause == EjectCause::User)
|
||||
|
@ -449,7 +449,8 @@ void DVDInterface::EjectDisc(EjectCause cause)
|
|||
}
|
||||
|
||||
// Must only be called on the CPU thread
|
||||
void DVDInterface::ChangeDisc(const std::vector<std::string>& paths)
|
||||
void DVDInterface::ChangeDisc(const Core::CPUThreadGuard& guard,
|
||||
const std::vector<std::string>& paths)
|
||||
{
|
||||
ASSERT_MSG(DISCIO, !paths.empty(), "Trying to insert an empty list of discs");
|
||||
|
||||
|
@ -459,11 +460,11 @@ void DVDInterface::ChangeDisc(const std::vector<std::string>& paths)
|
|||
m_auto_disc_change_index = 0;
|
||||
}
|
||||
|
||||
ChangeDisc(paths[0]);
|
||||
ChangeDisc(guard, paths[0]);
|
||||
}
|
||||
|
||||
// Must only be called on the CPU thread
|
||||
void DVDInterface::ChangeDisc(const std::string& new_path)
|
||||
void DVDInterface::ChangeDisc(const Core::CPUThreadGuard& guard, const std::string& new_path)
|
||||
{
|
||||
if (!m_disc_path_to_insert.empty())
|
||||
{
|
||||
|
@ -471,7 +472,7 @@ void DVDInterface::ChangeDisc(const std::string& new_path)
|
|||
return;
|
||||
}
|
||||
|
||||
EjectDisc(EjectCause::User);
|
||||
EjectDisc(guard, EjectCause::User);
|
||||
|
||||
m_disc_path_to_insert = new_path;
|
||||
m_system.GetCoreTiming().ScheduleEvent(m_system.GetSystemTimers().GetTicksPerSecond(),
|
||||
|
@ -491,13 +492,13 @@ void DVDInterface::ChangeDisc(const std::string& new_path)
|
|||
}
|
||||
|
||||
// Must only be called on the CPU thread
|
||||
bool DVDInterface::AutoChangeDisc()
|
||||
bool DVDInterface::AutoChangeDisc(const Core::CPUThreadGuard& guard)
|
||||
{
|
||||
if (m_auto_disc_change_paths.empty())
|
||||
return false;
|
||||
|
||||
m_auto_disc_change_index = (m_auto_disc_change_index + 1) % m_auto_disc_change_paths.size();
|
||||
ChangeDisc(m_auto_disc_change_paths[m_auto_disc_change_index]);
|
||||
ChangeDisc(guard, m_auto_disc_change_paths[m_auto_disc_change_index]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1096,7 +1097,7 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
|
|||
}
|
||||
else if (force_eject)
|
||||
{
|
||||
EjectDisc(EjectCause::Software);
|
||||
EjectDisc(Core::CPUThreadGuard{m_system}, EjectCause::Software);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@
|
|||
class PointerWrap;
|
||||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
class System;
|
||||
}
|
||||
} // namespace Core
|
||||
namespace CoreTiming
|
||||
{
|
||||
struct EventType;
|
||||
|
@ -140,10 +141,10 @@ public:
|
|||
void SetDisc(std::unique_ptr<DiscIO::VolumeDisc> disc,
|
||||
std::optional<std::vector<std::string>> auto_disc_change_paths);
|
||||
bool IsDiscInside() const;
|
||||
void EjectDisc(EjectCause cause); // Must only be called on the CPU thread
|
||||
void ChangeDisc(const std::vector<std::string>& paths); // Must only be called on the CPU thread
|
||||
void ChangeDisc(const std::string& new_path); // Must only be called on the CPU thread
|
||||
bool AutoChangeDisc(); // Must only be called on the CPU thread
|
||||
void EjectDisc(const Core::CPUThreadGuard& guard, EjectCause cause);
|
||||
void ChangeDisc(const Core::CPUThreadGuard& guard, const std::vector<std::string>& paths);
|
||||
void ChangeDisc(const Core::CPUThreadGuard& guard, const std::string& new_path);
|
||||
bool AutoChangeDisc(const Core::CPUThreadGuard& guard);
|
||||
|
||||
// This function returns true and calls SConfig::SetRunningGameMetadata(Volume&, Partition&)
|
||||
// if both of the following conditions are true:
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
|
||||
#include "Core/HW/EXI/BBA/BuiltIn.h"
|
||||
|
||||
#include <bit>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2ipdef.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
@ -18,8 +21,6 @@
|
|||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
namespace
|
||||
{
|
||||
u64 GetTickCountStd()
|
||||
|
@ -66,6 +67,8 @@ void SetIPIdentification(u8* ptr, std::size_t size, u16 value)
|
|||
}
|
||||
} // namespace
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
bool CEXIETHERNET::BuiltInBBAInterface::Activate()
|
||||
{
|
||||
if (IsActivated())
|
||||
|
@ -87,11 +90,7 @@ bool CEXIETHERNET::BuiltInBBAInterface::Activate()
|
|||
m_router_mac = Common::GenerateMacAddress(Common::MACConsumer::BBA);
|
||||
m_arp_table[m_router_ip] = m_router_mac;
|
||||
|
||||
// clear all ref
|
||||
for (auto& ref : network_ref)
|
||||
{
|
||||
ref.ip = 0;
|
||||
}
|
||||
m_network_ref.Clear();
|
||||
|
||||
m_upnp_httpd.listen(Common::SSDP_PORT, sf::IpAddress(ip));
|
||||
m_upnp_httpd.setBlocking(false);
|
||||
|
@ -109,16 +108,7 @@ void CEXIETHERNET::BuiltInBBAInterface::Deactivate()
|
|||
m_read_thread_shutdown.Set();
|
||||
m_active = false;
|
||||
|
||||
// kill all active socket
|
||||
for (auto& ref : network_ref)
|
||||
{
|
||||
if (ref.ip != 0)
|
||||
{
|
||||
ref.type == IPPROTO_TCP ? ref.tcp_socket.disconnect() : ref.udp_socket.unbind();
|
||||
}
|
||||
ref.ip = 0;
|
||||
}
|
||||
|
||||
m_network_ref.Clear();
|
||||
m_arp_table.clear();
|
||||
m_upnp_httpd.close();
|
||||
|
||||
|
@ -138,6 +128,62 @@ void CEXIETHERNET::BuiltInBBAInterface::WriteToQueue(const std::vector<u8>& data
|
|||
const u8 next_write_index = (m_queue_write + 1) & 15;
|
||||
if (next_write_index != m_queue_read)
|
||||
m_queue_write = next_write_index;
|
||||
else
|
||||
WARN_LOG_FMT(SP1, "BBA queue overrun, data might be lost");
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::BuiltInBBAInterface::WillQueueOverrun() const
|
||||
{
|
||||
return ((m_queue_write + 1) & 15) == m_queue_read;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::BuiltInBBAInterface::PollData(std::size_t* datasize)
|
||||
{
|
||||
for (auto& net_ref : m_network_ref)
|
||||
{
|
||||
if (net_ref.ip == 0)
|
||||
continue;
|
||||
|
||||
// Check for sleeping TCP data
|
||||
if (net_ref.type == IPPROTO_TCP)
|
||||
{
|
||||
for (auto& tcp_buf : net_ref.tcp_buffers)
|
||||
{
|
||||
if (WillQueueOverrun())
|
||||
break;
|
||||
if (!tcp_buf.used || (GetTickCountStd() - tcp_buf.tick) <= 1000)
|
||||
continue;
|
||||
|
||||
// Timed out packet, resend
|
||||
tcp_buf.tick = GetTickCountStd();
|
||||
WriteToQueue(tcp_buf.data);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for connection data
|
||||
if (*datasize == 0)
|
||||
{
|
||||
// Send it to the network buffer if empty
|
||||
const auto socket_data = TryGetDataFromSocket(&net_ref);
|
||||
if (socket_data.has_value())
|
||||
{
|
||||
*datasize = socket_data->size();
|
||||
std::memcpy(m_eth_ref->mRecvBuffer.get(), socket_data->data(), *datasize);
|
||||
}
|
||||
}
|
||||
else if (!WillQueueOverrun())
|
||||
{
|
||||
// Otherwise, enqueue it
|
||||
const auto socket_data = TryGetDataFromSocket(&net_ref);
|
||||
if (socket_data.has_value())
|
||||
WriteToQueue(*socket_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN_LOG_FMT(SP1, "BBA queue might overrun, can't poll more data");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIETHERNET::BuiltInBBAInterface::HandleARP(const Common::ARPPacket& packet)
|
||||
|
@ -199,40 +245,10 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleDHCP(const Common::UDPPacket& pack
|
|||
WriteToQueue(response.Build());
|
||||
}
|
||||
|
||||
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetAvailableSlot(u16 port)
|
||||
{
|
||||
if (port > 0) // existing connection?
|
||||
{
|
||||
for (auto& ref : network_ref)
|
||||
{
|
||||
if (ref.ip != 0 && ref.local == port)
|
||||
return &ref;
|
||||
}
|
||||
}
|
||||
for (auto& ref : network_ref)
|
||||
{
|
||||
if (ref.ip == 0)
|
||||
return &ref;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StackRef* CEXIETHERNET::BuiltInBBAInterface::GetTCPSlot(u16 src_port, u16 dst_port, u32 ip)
|
||||
{
|
||||
for (auto& ref : network_ref)
|
||||
{
|
||||
if (ref.ip == ip && ref.remote == dst_port && ref.local == src_port)
|
||||
{
|
||||
return &ref;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<std::vector<u8>>
|
||||
CEXIETHERNET::BuiltInBBAInterface::TryGetDataFromSocket(StackRef* ref)
|
||||
{
|
||||
size_t datasize = 0; // Set by socket.receive using a non-const reference
|
||||
std::size_t datasize = 0; // Set by socket.receive using a non-const reference
|
||||
unsigned short remote_port;
|
||||
|
||||
switch (ref->type)
|
||||
|
@ -255,6 +271,25 @@ CEXIETHERNET::BuiltInBBAInterface::TryGetDataFromSocket(StackRef* ref)
|
|||
}
|
||||
|
||||
case IPPROTO_TCP:
|
||||
switch (ref->tcp_socket.Connected(ref))
|
||||
{
|
||||
case BbaTcpSocket::ConnectingState::Error:
|
||||
{
|
||||
// Create the resulting RST ACK packet
|
||||
const Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||
ref->ack_num, TCP_FLAG_RST | TCP_FLAG_ACK);
|
||||
WriteToQueue(result.Build());
|
||||
ref->ip = 0;
|
||||
ref->tcp_socket.disconnect();
|
||||
[[fallthrough]];
|
||||
}
|
||||
case BbaTcpSocket::ConnectingState::None:
|
||||
case BbaTcpSocket::ConnectingState::Connecting:
|
||||
return std::nullopt;
|
||||
case BbaTcpSocket::ConnectingState::Connected:
|
||||
break;
|
||||
}
|
||||
|
||||
sf::Socket::Status st = sf::Socket::Status::Done;
|
||||
TcpBuffer* tcp_buffer = nullptr;
|
||||
for (auto& tcp_buf : ref->tcp_buffers)
|
||||
|
@ -275,7 +310,7 @@ CEXIETHERNET::BuiltInBBAInterface::TryGetDataFromSocket(StackRef* ref)
|
|||
if (datasize > 0)
|
||||
{
|
||||
Common::TCPPacket packet(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||
ref->ack_num, TCP_FLAG_ACK);
|
||||
ref->ack_num, TCP_FLAG_ACK | TCP_FLAG_PSH);
|
||||
packet.data = std::vector<u8>(buffer.begin(), buffer.begin() + datasize);
|
||||
|
||||
// build buffer
|
||||
|
@ -307,8 +342,8 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
|
|||
{
|
||||
const auto& [hwdata, ip_header, tcp_header, ip_options, tcp_options, data] = packet;
|
||||
sf::IpAddress target;
|
||||
StackRef* ref = GetTCPSlot(tcp_header.source_port, tcp_header.destination_port,
|
||||
Common::BitCast<u32>(ip_header.destination_addr));
|
||||
StackRef* ref = m_network_ref.GetTCPSlot(tcp_header.source_port, tcp_header.destination_port,
|
||||
std::bit_cast<u32>(ip_header.destination_addr));
|
||||
const u16 flags = ntohs(tcp_header.properties) & 0xfff;
|
||||
if (flags & (TCP_FLAG_FIN | TCP_FLAG_RST))
|
||||
{
|
||||
|
@ -337,7 +372,7 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
|
|||
// new connection
|
||||
if (ref != nullptr)
|
||||
return;
|
||||
ref = GetAvailableSlot(0);
|
||||
ref = m_network_ref.GetAvailableSlot(0);
|
||||
|
||||
ref->delay = GetTickCountStd();
|
||||
ref->local = tcp_header.source_port;
|
||||
|
@ -349,34 +384,19 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
|
|||
ref->type = IPPROTO_TCP;
|
||||
for (auto& tcp_buf : ref->tcp_buffers)
|
||||
tcp_buf.used = false;
|
||||
const u32 destination_ip = Common::BitCast<u32>(ip_header.destination_addr);
|
||||
const u32 destination_ip = std::bit_cast<u32>(ip_header.destination_addr);
|
||||
ref->from.sin_addr.s_addr = destination_ip;
|
||||
ref->from.sin_port = tcp_header.destination_port;
|
||||
ref->to.sin_addr.s_addr = Common::BitCast<u32>(ip_header.source_addr);
|
||||
ref->to.sin_addr.s_addr = std::bit_cast<u32>(ip_header.source_addr);
|
||||
ref->to.sin_port = tcp_header.source_port;
|
||||
ref->bba_mac = m_current_mac;
|
||||
ref->my_mac = ResolveAddress(destination_ip);
|
||||
ref->tcp_socket.setBlocking(false);
|
||||
ref->ready = false;
|
||||
ref->ip = std::bit_cast<u32>(ip_header.destination_addr);
|
||||
|
||||
// reply with a sin_ack
|
||||
Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||
ref->ack_num, TCP_FLAG_SIN | TCP_FLAG_ACK);
|
||||
|
||||
result.tcp_options = {
|
||||
0x02, 0x04, 0x05, 0xb4, // Maximum segment size: 1460 bytes
|
||||
0x01, 0x01, 0x01, 0x01 // NOPs
|
||||
};
|
||||
|
||||
ref->seq_num++;
|
||||
target = sf::IpAddress(ntohl(destination_ip));
|
||||
ref->tcp_socket.Connect(target, ntohs(tcp_header.destination_port), m_current_ip);
|
||||
ref->ready = false;
|
||||
ref->ip = Common::BitCast<u32>(ip_header.destination_addr);
|
||||
|
||||
ref->tcp_buffers[0].data = result.Build();
|
||||
ref->tcp_buffers[0].seq_id = ref->seq_num - 1;
|
||||
ref->tcp_buffers[0].tick = GetTickCountStd() - 900; // delay
|
||||
ref->tcp_buffers[0].used = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -392,7 +412,7 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
|
|||
{
|
||||
// only if contain data
|
||||
if (static_cast<int>(this_seq - ref->ack_num) >= 0 &&
|
||||
data.size() >= static_cast<size_t>(size))
|
||||
data.size() >= static_cast<std::size_t>(size))
|
||||
{
|
||||
ref->tcp_socket.send(data.data(), size);
|
||||
ref->ack_num += size;
|
||||
|
@ -442,7 +462,7 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleTCPFrame(const Common::TCPPacket&
|
|||
// and listen to it. We open it on our side manually.
|
||||
void CEXIETHERNET::BuiltInBBAInterface::InitUDPPort(u16 port)
|
||||
{
|
||||
StackRef* ref = GetAvailableSlot(htons(port));
|
||||
StackRef* ref = m_network_ref.GetAvailableSlot(htons(port));
|
||||
if (ref == nullptr || ref->ip != 0)
|
||||
return;
|
||||
ref->ip = m_router_ip; // change for ip
|
||||
|
@ -470,9 +490,9 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(const Common::UDPPacket&
|
|||
sf::IpAddress target;
|
||||
const u32 destination_addr = ip_header.destination_addr == Common::IP_ADDR_ANY ?
|
||||
m_router_ip : // dns request
|
||||
Common::BitCast<u32>(ip_header.destination_addr);
|
||||
std::bit_cast<u32>(ip_header.destination_addr);
|
||||
|
||||
StackRef* ref = GetAvailableSlot(udp_header.source_port);
|
||||
StackRef* ref = m_network_ref.GetAvailableSlot(udp_header.source_port);
|
||||
if (ref->ip == 0)
|
||||
{
|
||||
ref->ip = destination_addr; // change for ip
|
||||
|
@ -483,7 +503,7 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(const Common::UDPPacket&
|
|||
ref->my_mac = m_router_mac;
|
||||
ref->from.sin_addr.s_addr = destination_addr;
|
||||
ref->from.sin_port = udp_header.destination_port;
|
||||
ref->to.sin_addr.s_addr = Common::BitCast<u32>(ip_header.source_addr);
|
||||
ref->to.sin_addr.s_addr = std::bit_cast<u32>(ip_header.source_addr);
|
||||
ref->to.sin_port = udp_header.source_port;
|
||||
ref->udp_socket.setBlocking(false);
|
||||
if (ref->udp_socket.Bind(ntohs(udp_header.source_port), m_current_ip) != sf::Socket::Done)
|
||||
|
@ -503,7 +523,7 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(const Common::UDPPacket&
|
|||
reply.eth_header.destination = hwdata.source;
|
||||
reply.eth_header.source = hwdata.destination;
|
||||
reply.ip_header.destination_addr = ip_header.source_addr;
|
||||
reply.ip_header.source_addr = Common::BitCast<Common::IPAddress>(destination_addr);
|
||||
reply.ip_header.source_addr = std::bit_cast<Common::IPAddress>(destination_addr);
|
||||
WriteToQueue(reply.Build());
|
||||
}
|
||||
}
|
||||
|
@ -511,13 +531,13 @@ void CEXIETHERNET::BuiltInBBAInterface::HandleUDPFrame(const Common::UDPPacket&
|
|||
if (ntohs(udp_header.destination_port) == 53)
|
||||
target = sf::IpAddress(m_dns_ip.c_str()); // dns server ip
|
||||
else
|
||||
target = sf::IpAddress(ntohl(Common::BitCast<u32>(ip_header.destination_addr)));
|
||||
target = sf::IpAddress(ntohl(std::bit_cast<u32>(ip_header.destination_addr)));
|
||||
ref->udp_socket.send(data.data(), data.size(), target, ntohs(udp_header.destination_port));
|
||||
}
|
||||
|
||||
void CEXIETHERNET::BuiltInBBAInterface::HandleUPnPClient()
|
||||
{
|
||||
StackRef* ref = GetAvailableSlot(0);
|
||||
StackRef* ref = m_network_ref.GetAvailableSlot(0);
|
||||
if (ref == nullptr || m_upnp_httpd.accept(ref->tcp_socket) != sf::Socket::Done)
|
||||
return;
|
||||
|
||||
|
@ -676,14 +696,17 @@ bool CEXIETHERNET::BuiltInBBAInterface::SendFrame(const u8* frame, u32 size)
|
|||
|
||||
void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInBBAInterface* self)
|
||||
{
|
||||
std::size_t datasize = 0;
|
||||
while (!self->m_read_thread_shutdown.IsSet())
|
||||
{
|
||||
// make thread less cpu hungry
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
if (datasize == 0)
|
||||
{
|
||||
// Make thread less CPU hungry
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
if (!self->m_read_enabled.IsSet())
|
||||
continue;
|
||||
size_t datasize = 0;
|
||||
|
||||
u8 wp = self->m_eth_ref->page_ptr(BBA_RWP);
|
||||
const u8 rp = self->m_eth_ref->page_ptr(BBA_RRP);
|
||||
|
@ -711,39 +734,11 @@ void CEXIETHERNET::BuiltInBBAInterface::ReadThreadHandler(CEXIETHERNET::BuiltInB
|
|||
}
|
||||
else
|
||||
{
|
||||
// test connections data
|
||||
for (auto& net_ref : self->network_ref)
|
||||
{
|
||||
if (net_ref.ip == 0)
|
||||
continue;
|
||||
const auto socket_data = self->TryGetDataFromSocket(&net_ref);
|
||||
if (socket_data.has_value())
|
||||
{
|
||||
datasize = socket_data->size();
|
||||
std::memcpy(self->m_eth_ref->mRecvBuffer.get(), socket_data->data(), datasize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
datasize = 0;
|
||||
}
|
||||
|
||||
// test and add any sleeping tcp data
|
||||
for (auto& net_ref : self->network_ref)
|
||||
{
|
||||
if (net_ref.ip == 0 || net_ref.type != IPPROTO_TCP)
|
||||
continue;
|
||||
for (auto& tcp_buf : net_ref.tcp_buffers)
|
||||
{
|
||||
if (!tcp_buf.used || (GetTickCountStd() - tcp_buf.tick) <= 1000)
|
||||
continue;
|
||||
|
||||
tcp_buf.tick = GetTickCountStd();
|
||||
// timmed out packet, resend
|
||||
if (((self->m_queue_write + 1) & 15) != self->m_queue_read)
|
||||
{
|
||||
self->WriteToQueue(tcp_buf.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check network stack references
|
||||
self->PollData(&datasize);
|
||||
|
||||
// Check for new UPnP client
|
||||
self->HandleUPnPClient();
|
||||
|
@ -786,14 +781,7 @@ void CEXIETHERNET::BuiltInBBAInterface::RecvStart()
|
|||
void CEXIETHERNET::BuiltInBBAInterface::RecvStop()
|
||||
{
|
||||
m_read_enabled.Clear();
|
||||
for (auto& net_ref : network_ref)
|
||||
{
|
||||
if (net_ref.ip != 0)
|
||||
{
|
||||
net_ref.type == IPPROTO_TCP ? net_ref.tcp_socket.disconnect() : net_ref.udp_socket.unbind();
|
||||
}
|
||||
net_ref.ip = 0;
|
||||
}
|
||||
m_network_ref.Clear();
|
||||
m_queue_read = 0;
|
||||
m_queue_write = 0;
|
||||
}
|
||||
|
@ -808,6 +796,7 @@ sf::Socket::Status BbaTcpSocket::Connect(const sf::IpAddress& dest, u16 port, u3
|
|||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = 0;
|
||||
::bind(getHandle(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
|
||||
m_connecting_state = ConnectingState::Connecting;
|
||||
return this->connect(dest, port);
|
||||
}
|
||||
|
||||
|
@ -833,6 +822,88 @@ sf::Socket::Status BbaTcpSocket::GetSockName(sockaddr_in* addr) const
|
|||
return sf::Socket::Status::Done;
|
||||
}
|
||||
|
||||
BbaTcpSocket::ConnectingState BbaTcpSocket::Connected(StackRef* ref)
|
||||
{
|
||||
// Called by ReadThreadHandler's TryGetDataFromSocket
|
||||
switch (m_connecting_state)
|
||||
{
|
||||
case ConnectingState::Connecting:
|
||||
{
|
||||
const int fd = getHandle();
|
||||
const s32 nfds = fd + 1;
|
||||
fd_set read_fds;
|
||||
fd_set write_fds;
|
||||
fd_set except_fds;
|
||||
struct timeval t = {0, 0};
|
||||
FD_ZERO(&read_fds);
|
||||
FD_ZERO(&write_fds);
|
||||
FD_ZERO(&except_fds);
|
||||
FD_SET(fd, &write_fds);
|
||||
FD_SET(fd, &except_fds);
|
||||
|
||||
if (select(nfds, &read_fds, &write_fds, &except_fds, &t) < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Failed to get BBA socket connection state: {}",
|
||||
Common::StrNetworkError());
|
||||
break;
|
||||
}
|
||||
|
||||
if (FD_ISSET(fd, &write_fds) == 0 && FD_ISSET(fd, &except_fds) == 0)
|
||||
break;
|
||||
|
||||
s32 error = 0;
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, reinterpret_cast<char*>(&error), &len) != 0)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Failed to get BBA socket error state: {}", Common::StrNetworkError());
|
||||
m_connecting_state = ConnectingState::Error;
|
||||
break;
|
||||
}
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "BBA connect failed (err={}): {}", error,
|
||||
Common::DecodeNetworkError(error));
|
||||
m_connecting_state = ConnectingState::Error;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get peername to ensure the socket is connected
|
||||
sockaddr_in peer;
|
||||
socklen_t peer_len = sizeof(peer);
|
||||
if (getpeername(fd, reinterpret_cast<sockaddr*>(&peer), &peer_len) != 0)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "BBA connect failed to get peername: {}", Common::StrNetworkError());
|
||||
m_connecting_state = ConnectingState::Error;
|
||||
break;
|
||||
}
|
||||
|
||||
// Create the resulting SYN ACK packet
|
||||
m_connecting_state = ConnectingState::Connected;
|
||||
INFO_LOG_FMT(SP1, "BBA connect succeeded");
|
||||
|
||||
Common::TCPPacket result(ref->bba_mac, ref->my_mac, ref->from, ref->to, ref->seq_num,
|
||||
ref->ack_num, TCP_FLAG_SIN | TCP_FLAG_ACK);
|
||||
|
||||
result.tcp_options = {
|
||||
0x02, 0x04, 0x05, 0xb4, // Maximum segment size: 1460 bytes
|
||||
0x01, 0x01, 0x01, 0x01 // NOPs
|
||||
};
|
||||
|
||||
ref->seq_num++;
|
||||
ref->tcp_buffers[0].data = result.Build();
|
||||
ref->tcp_buffers[0].seq_id = ref->seq_num - 1;
|
||||
ref->tcp_buffers[0].tick = GetTickCountStd() - 900; // delay
|
||||
ref->tcp_buffers[0].used = true;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return m_connecting_state;
|
||||
}
|
||||
|
||||
BbaUdpSocket::BbaUdpSocket() = default;
|
||||
|
||||
sf::Socket::Status BbaUdpSocket::Bind(u16 port, u32 net_ip)
|
||||
|
@ -895,7 +966,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;
|
||||
mreq.imr_multiaddr.s_addr = Common::BitCast<u32>(Common::IP_ADDR_SSDP);
|
||||
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),
|
||||
sizeof(mreq)) != 0)
|
||||
|
@ -909,3 +980,45 @@ sf::Socket::Status BbaUdpSocket::Bind(u16 port, u32 net_ip)
|
|||
INFO_LOG_FMT(SP1, "SSDP multicast membership successful");
|
||||
return sf::Socket::Status::Done;
|
||||
}
|
||||
|
||||
StackRef* NetworkRef::GetAvailableSlot(u16 port)
|
||||
{
|
||||
if (port > 0) // existing connection?
|
||||
{
|
||||
for (auto& ref : m_stacks)
|
||||
{
|
||||
if (ref.ip != 0 && ref.local == port)
|
||||
return &ref;
|
||||
}
|
||||
}
|
||||
for (auto& ref : m_stacks)
|
||||
{
|
||||
if (ref.ip == 0)
|
||||
return &ref;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StackRef* NetworkRef::GetTCPSlot(u16 src_port, u16 dst_port, u32 ip)
|
||||
{
|
||||
for (auto& ref : m_stacks)
|
||||
{
|
||||
if (ref.ip == ip && ref.remote == dst_port && ref.local == src_port)
|
||||
{
|
||||
return &ref;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NetworkRef::Clear()
|
||||
{
|
||||
for (auto& ref : m_stacks)
|
||||
{
|
||||
if (ref.ip != 0)
|
||||
{
|
||||
ref.type == IPPROTO_TCP ? ref.tcp_socket.disconnect() : ref.udp_socket.unbind();
|
||||
}
|
||||
ref.ip = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,8 @@ struct TcpBuffer
|
|||
std::vector<u8> data;
|
||||
};
|
||||
|
||||
struct StackRef;
|
||||
|
||||
// Socket helper classes to ensure network interface consistency.
|
||||
//
|
||||
// If the socket isn't bound, the system will pick the interface to use automatically.
|
||||
|
@ -45,6 +47,19 @@ public:
|
|||
sf::Socket::Status Connect(const sf::IpAddress& dest, u16 port, u32 net_ip);
|
||||
sf::Socket::Status GetPeerName(sockaddr_in* addr) const;
|
||||
sf::Socket::Status GetSockName(sockaddr_in* addr) const;
|
||||
|
||||
enum class ConnectingState
|
||||
{
|
||||
None,
|
||||
Connecting,
|
||||
Connected,
|
||||
Error
|
||||
};
|
||||
|
||||
ConnectingState Connected(StackRef* ref);
|
||||
|
||||
private:
|
||||
ConnectingState m_connecting_state = ConnectingState::None;
|
||||
};
|
||||
|
||||
class BbaUdpSocket : public sf::UdpSocket
|
||||
|
@ -57,7 +72,7 @@ public:
|
|||
|
||||
struct StackRef
|
||||
{
|
||||
u32 ip;
|
||||
u32 ip = 0;
|
||||
u16 local;
|
||||
u16 remote;
|
||||
u16 type;
|
||||
|
@ -77,3 +92,26 @@ struct StackRef
|
|||
BbaTcpSocket tcp_socket;
|
||||
u64 poke_time;
|
||||
};
|
||||
|
||||
// Max 10 at same time, I think most gc game had a
|
||||
// limit of 8 in the GC framework
|
||||
using StackRefs = std::array<StackRef, 10>;
|
||||
|
||||
class NetworkRef
|
||||
{
|
||||
public:
|
||||
StackRefs& data() { return m_stacks; }
|
||||
const StackRefs& data() const { return m_stacks; }
|
||||
auto begin() { return m_stacks.begin(); }
|
||||
auto begin() const { return m_stacks.cbegin(); }
|
||||
auto end() { return m_stacks.end(); }
|
||||
auto end() const { return m_stacks.cend(); }
|
||||
|
||||
StackRef* GetAvailableSlot(u16 port);
|
||||
StackRef* GetTCPSlot(u16 src_port, u16 dst_port, u32 ip);
|
||||
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
StackRefs m_stacks;
|
||||
};
|
||||
|
|
75
Source/Core/Core/HW/EXI/BBA/TAPServerBBA.cpp
Normal file
75
Source/Core/Core/HW/EXI/BBA/TAPServerBBA.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2020 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
|
||||
CEXIETHERNET::TAPServerNetworkInterface::TAPServerNetworkInterface(CEXIETHERNET* eth_ref,
|
||||
const std::string& destination)
|
||||
: NetworkInterface(eth_ref),
|
||||
m_tapserver_if(
|
||||
destination,
|
||||
std::bind(&TAPServerNetworkInterface::HandleReceivedFrame, this, std::placeholders::_1),
|
||||
BBA_RECV_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::Activate()
|
||||
{
|
||||
return m_tapserver_if.Activate();
|
||||
}
|
||||
|
||||
void CEXIETHERNET::TAPServerNetworkInterface::Deactivate()
|
||||
{
|
||||
m_tapserver_if.Deactivate();
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::IsActivated()
|
||||
{
|
||||
return m_tapserver_if.IsActivated();
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::RecvInit()
|
||||
{
|
||||
return m_tapserver_if.RecvInit();
|
||||
}
|
||||
|
||||
void CEXIETHERNET::TAPServerNetworkInterface::RecvStart()
|
||||
{
|
||||
m_tapserver_if.RecvStart();
|
||||
}
|
||||
|
||||
void CEXIETHERNET::TAPServerNetworkInterface::RecvStop()
|
||||
{
|
||||
m_tapserver_if.RecvStop();
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::SendFrame(const u8* frame, u32 size)
|
||||
{
|
||||
const bool ret = m_tapserver_if.SendFrame(frame, size);
|
||||
if (ret)
|
||||
m_eth_ref->SendComplete();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CEXIETHERNET::TAPServerNetworkInterface::HandleReceivedFrame(std::string&& data)
|
||||
{
|
||||
if (data.size() > BBA_RECV_SIZE)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received BBA frame of size {}, which is larger than maximum size {}",
|
||||
data.size(), BBA_RECV_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(m_eth_ref->mRecvBuffer.get(), data.data(), data.size());
|
||||
m_eth_ref->mRecvBufferLength = static_cast<u32>(data.size());
|
||||
m_eth_ref->RecvHandlePacket();
|
||||
}
|
||||
|
||||
} // namespace ExpansionInterface
|
365
Source/Core/Core/HW/EXI/BBA/TAPServerConnection.cpp
Normal file
365
Source/Core/Core/HW/EXI/BBA/TAPServerConnection.cpp
Normal file
|
@ -0,0 +1,365 @@
|
|||
// Copyright 2020 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2ipdef.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
using ws_ssize_t = int;
|
||||
#else
|
||||
#define closesocket close
|
||||
using ws_ssize_t = ssize_t;
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#define SEND_FLAGS MSG_NOSIGNAL
|
||||
#else
|
||||
#define SEND_FLAGS 0
|
||||
#endif
|
||||
|
||||
TAPServerConnection::TAPServerConnection(const std::string& destination,
|
||||
std::function<void(std::string&&)> recv_cb,
|
||||
std::size_t max_frame_size)
|
||||
: m_destination(destination), m_recv_cb(recv_cb), m_max_frame_size(max_frame_size)
|
||||
{
|
||||
}
|
||||
|
||||
static int ConnectToDestination(const std::string& destination)
|
||||
{
|
||||
if (destination.empty())
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Cannot connect: destination is empty\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ss_size;
|
||||
sockaddr_storage ss;
|
||||
std::memset(&ss, 0, sizeof(ss));
|
||||
if (destination[0] != '/')
|
||||
{
|
||||
// IP address or hostname
|
||||
const std::size_t colon_offset = destination.find(':');
|
||||
if (colon_offset == std::string::npos)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Destination IP address does not include port\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ss);
|
||||
const sf::IpAddress dest_ip(destination.substr(0, colon_offset));
|
||||
if (dest_ip == sf::IpAddress::None || dest_ip == sf::IpAddress::Any)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Destination IP address is not valid\n");
|
||||
return -1;
|
||||
}
|
||||
sin->sin_addr.s_addr = htonl(dest_ip.toInteger());
|
||||
sin->sin_family = AF_INET;
|
||||
const std::string port_str = destination.substr(colon_offset + 1);
|
||||
const int dest_port = std::atoi(port_str.c_str());
|
||||
if (dest_port < 1 || dest_port > 65535)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Destination port is not valid\n");
|
||||
return -1;
|
||||
}
|
||||
sin->sin_port = htons(dest_port);
|
||||
ss_size = sizeof(*sin);
|
||||
#ifndef _WIN32
|
||||
}
|
||||
else
|
||||
{
|
||||
// UNIX socket
|
||||
sockaddr_un* sun = reinterpret_cast<sockaddr_un*>(&ss);
|
||||
if (destination.size() + 1 > sizeof(sun->sun_path))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Socket path is too long; unable to create tapserver connection\n");
|
||||
return -1;
|
||||
}
|
||||
sun->sun_family = AF_UNIX;
|
||||
std::strcpy(sun->sun_path, destination.c_str());
|
||||
ss_size = sizeof(*sun);
|
||||
#else
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "UNIX sockets are not supported on Windows\n");
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
const int fd = socket(ss.ss_family, SOCK_STREAM, (ss.ss_family == AF_INET) ? IPPROTO_TCP : 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Couldn't create socket; unable to create tapserver connection\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int opt_no_sigpipe = 1;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &opt_no_sigpipe, sizeof(opt_no_sigpipe)) < 0)
|
||||
INFO_LOG_FMT(SP1, "Failed to set SO_NOSIGPIPE on socket\n");
|
||||
#endif
|
||||
|
||||
if (connect(fd, reinterpret_cast<sockaddr*>(&ss), ss_size) == -1)
|
||||
{
|
||||
INFO_LOG_FMT(SP1, "Couldn't connect socket ({}), unable to create tapserver connection\n",
|
||||
Common::StrNetworkError());
|
||||
closesocket(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
bool TAPServerConnection::Activate()
|
||||
{
|
||||
if (IsActivated())
|
||||
return true;
|
||||
|
||||
m_fd = ConnectToDestination(m_destination);
|
||||
if (m_fd < 0)
|
||||
return false;
|
||||
|
||||
return RecvInit();
|
||||
}
|
||||
|
||||
void TAPServerConnection::Deactivate()
|
||||
{
|
||||
m_read_enabled.Clear();
|
||||
m_read_shutdown.Set();
|
||||
if (m_read_thread.joinable())
|
||||
m_read_thread.join();
|
||||
m_read_shutdown.Clear();
|
||||
|
||||
if (m_fd >= 0)
|
||||
closesocket(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
bool TAPServerConnection::IsActivated()
|
||||
{
|
||||
return (m_fd >= 0);
|
||||
}
|
||||
|
||||
bool TAPServerConnection::RecvInit()
|
||||
{
|
||||
m_read_thread = std::thread(&TAPServerConnection::ReadThreadHandler, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TAPServerConnection::RecvStart()
|
||||
{
|
||||
m_read_enabled.Set();
|
||||
}
|
||||
|
||||
void TAPServerConnection::RecvStop()
|
||||
{
|
||||
m_read_enabled.Clear();
|
||||
}
|
||||
|
||||
bool TAPServerConnection::SendAndRemoveAllHDLCFrames(std::string* send_buf)
|
||||
{
|
||||
while (!send_buf->empty())
|
||||
{
|
||||
const std::size_t start_offset = send_buf->find(0x7E);
|
||||
if (start_offset == std::string::npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const std::size_t end_sentinel_offset = send_buf->find(0x7E, start_offset + 1);
|
||||
if (end_sentinel_offset == std::string::npos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
const std::size_t end_offset = end_sentinel_offset + 1;
|
||||
const std::size_t size = end_offset - start_offset;
|
||||
|
||||
const u8 size_bytes[2] = {static_cast<u8>(size), static_cast<u8>(size >> 8)};
|
||||
if (send(m_fd, reinterpret_cast<const char*>(size_bytes), 2, SEND_FLAGS) != 2)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "SendAndRemoveAllHDLCFrames(): could not write size field");
|
||||
return false;
|
||||
}
|
||||
const int written_bytes =
|
||||
send(m_fd, send_buf->data() + start_offset, static_cast<int>(size), SEND_FLAGS);
|
||||
if (u32(written_bytes) != size)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1,
|
||||
"SendAndRemoveAllHDLCFrames(): expected to write {} bytes, instead wrote {}",
|
||||
size, written_bytes);
|
||||
return false;
|
||||
}
|
||||
*send_buf = send_buf->substr(end_offset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TAPServerConnection::SendFrame(const u8* frame, u32 size)
|
||||
{
|
||||
INFO_LOG_FMT(SP1, "SendFrame {}\n{}", size, ArrayToString(frame, size, 0x10));
|
||||
|
||||
// On Windows, the data pointer is of type const char*; on other systems it is
|
||||
// of type const void*. This is the reason for the reinterpret_cast here and
|
||||
// in the other send/recv calls in this file.
|
||||
const u8 size_bytes[2] = {static_cast<u8>(size), static_cast<u8>(size >> 8)};
|
||||
if (send(m_fd, reinterpret_cast<const char*>(size_bytes), 2, SEND_FLAGS) != 2)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "SendFrame(): could not write size field");
|
||||
return false;
|
||||
}
|
||||
const int written_bytes =
|
||||
send(m_fd, reinterpret_cast<const char*>(frame), static_cast<ws_ssize_t>(size), SEND_FLAGS);
|
||||
if (u32(written_bytes) != size)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "SendFrame(): expected to write {} bytes, instead wrote {}", size,
|
||||
written_bytes);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TAPServerConnection::ReadThreadHandler()
|
||||
{
|
||||
enum class ReadState
|
||||
{
|
||||
SIZE,
|
||||
SIZE_HIGH,
|
||||
DATA,
|
||||
SKIP,
|
||||
};
|
||||
ReadState read_state = ReadState::SIZE;
|
||||
|
||||
std::size_t frame_bytes_received = 0;
|
||||
std::size_t frame_bytes_expected = 0;
|
||||
std::string frame_data;
|
||||
|
||||
while (!m_read_shutdown.IsSet())
|
||||
{
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(m_fd, &rfds);
|
||||
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 50000;
|
||||
int select_res = select(m_fd + 1, &rfds, nullptr, nullptr, &timeout);
|
||||
if (select_res < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Can\'t poll tapserver fd: {}", Common::StrNetworkError());
|
||||
continue;
|
||||
}
|
||||
if (select_res == 0)
|
||||
continue;
|
||||
|
||||
// The tapserver protocol is very simple: there is a 16-bit little-endian
|
||||
// size field, followed by that many bytes of packet data
|
||||
switch (read_state)
|
||||
{
|
||||
case ReadState::SIZE:
|
||||
{
|
||||
u8 size_bytes[2];
|
||||
const ws_ssize_t bytes_read = recv(m_fd, reinterpret_cast<char*>(size_bytes), 2, 0);
|
||||
if (bytes_read == 1)
|
||||
{
|
||||
read_state = ReadState::SIZE_HIGH;
|
||||
frame_bytes_expected = size_bytes[0];
|
||||
}
|
||||
else if (bytes_read == 2)
|
||||
{
|
||||
frame_bytes_expected = size_bytes[0] | (size_bytes[1] << 8);
|
||||
frame_data.resize(frame_bytes_expected, '\0');
|
||||
if (frame_bytes_expected > m_max_frame_size)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Packet is too large ({} bytes); dropping it", frame_bytes_expected);
|
||||
read_state = ReadState::SKIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If read is disabled, we still need to actually read the frame in
|
||||
// order to avoid applying backpressure on the remote end, but we
|
||||
// should drop the frame instead of forwarding it to the client.
|
||||
read_state = m_read_enabled.IsSet() ? ReadState::DATA : ReadState::SKIP;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Failed to read size field from destination: {}",
|
||||
Common::StrNetworkError());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ReadState::SIZE_HIGH:
|
||||
{
|
||||
// This handles the annoying case where only one byte of the size field
|
||||
// was available earlier.
|
||||
u8 size_high = 0;
|
||||
const ws_ssize_t bytes_read = recv(m_fd, reinterpret_cast<char*>(&size_high), 1, 0);
|
||||
if (bytes_read != 1)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Failed to read split size field from destination: {}",
|
||||
Common::StrNetworkError());
|
||||
break;
|
||||
}
|
||||
frame_bytes_expected |= (size_high << 8);
|
||||
frame_data.resize(frame_bytes_expected, '\0');
|
||||
if (frame_bytes_expected > m_max_frame_size)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Packet is too large ({} bytes); dropping it", frame_bytes_expected);
|
||||
read_state = ReadState::SKIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
read_state = m_read_enabled.IsSet() ? ReadState::DATA : ReadState::SKIP;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ReadState::DATA:
|
||||
case ReadState::SKIP:
|
||||
{
|
||||
const std::size_t bytes_to_read = frame_data.size() - frame_bytes_received;
|
||||
const ws_ssize_t bytes_read = recv(m_fd, frame_data.data() + frame_bytes_received,
|
||||
static_cast<ws_ssize_t>(bytes_to_read), 0);
|
||||
if (bytes_read <= 0)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Failed to read data from destination: {}", Common::StrNetworkError());
|
||||
break;
|
||||
}
|
||||
frame_bytes_received += bytes_read;
|
||||
if (frame_bytes_received == frame_bytes_expected)
|
||||
{
|
||||
if (read_state == ReadState::DATA)
|
||||
{
|
||||
m_recv_cb(std::move(frame_data));
|
||||
}
|
||||
frame_data.clear();
|
||||
frame_bytes_received = 0;
|
||||
frame_bytes_expected = 0;
|
||||
read_state = ReadState::SIZE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ExpansionInterface
|
48
Source/Core/Core/HW/EXI/BBA/TAPServerConnection.h
Normal file
48
Source/Core/Core/HW/EXI/BBA/TAPServerConnection.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2020 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/SocketContext.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
|
||||
class TAPServerConnection
|
||||
{
|
||||
public:
|
||||
using RecvCallback = std::function<void(std::string&&)>;
|
||||
|
||||
TAPServerConnection(const std::string& destination, RecvCallback recv_cb,
|
||||
std::size_t max_frame_size);
|
||||
|
||||
bool Activate();
|
||||
void Deactivate();
|
||||
bool IsActivated();
|
||||
bool RecvInit();
|
||||
void RecvStart();
|
||||
void RecvStop();
|
||||
bool SendAndRemoveAllHDLCFrames(std::string* send_buf);
|
||||
bool SendFrame(const u8* frame, u32 size);
|
||||
|
||||
private:
|
||||
const std::string m_destination;
|
||||
const RecvCallback m_recv_cb;
|
||||
const std::size_t m_max_frame_size;
|
||||
Common::SocketContext m_socket_context;
|
||||
|
||||
int m_fd = -1;
|
||||
std::thread m_read_thread;
|
||||
Common::Flag m_read_enabled;
|
||||
Common::Flag m_read_shutdown;
|
||||
|
||||
bool StartReadThread();
|
||||
void ReadThreadHandler();
|
||||
};
|
||||
|
||||
} // namespace ExpansionInterface
|
|
@ -1,129 +0,0 @@
|
|||
// Copyright 2020 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
// This interface is only implemented on macOS, since macOS needs a replacement
|
||||
// for TunTap when the kernel extension is no longer supported. This interface
|
||||
// only appears in the menu on macOS, so on other platforms, it does nothing and
|
||||
// refuses to activate.
|
||||
|
||||
constexpr char socket_path[] = "/tmp/dolphin-tap";
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::Activate()
|
||||
{
|
||||
if (IsActivated())
|
||||
return true;
|
||||
|
||||
sockaddr_un sun = {};
|
||||
if (sizeof(socket_path) > sizeof(sun.sun_path))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Socket path is too long, unable to init BBA");
|
||||
return false;
|
||||
}
|
||||
sun.sun_family = AF_UNIX;
|
||||
strcpy(sun.sun_path, socket_path);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Couldn't create socket, unable to init BBA");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connect(fd, reinterpret_cast<sockaddr*>(&sun), sizeof(sun)) == -1)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Couldn't connect socket ({}), unable to init BBA",
|
||||
Common::LastStrerrorString());
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
INFO_LOG_FMT(SP1, "BBA initialized.");
|
||||
return RecvInit();
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::SendFrame(const u8* frame, u32 size)
|
||||
{
|
||||
{
|
||||
const std::string s = ArrayToString(frame, size, 0x10);
|
||||
INFO_LOG_FMT(SP1, "SendFrame {}\n{}", size, s);
|
||||
}
|
||||
|
||||
auto size16 = u16(size);
|
||||
if (write(fd, &size16, 2) != 2)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "SendFrame(): could not write size field");
|
||||
return false;
|
||||
}
|
||||
int written_bytes = write(fd, frame, size);
|
||||
if (u32(written_bytes) != size)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "SendFrame(): expected to write {} bytes, instead wrote {}", size,
|
||||
written_bytes);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_eth_ref->SendComplete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIETHERNET::TAPServerNetworkInterface::ReadThreadHandler()
|
||||
{
|
||||
while (!readThreadShutdown.IsSet())
|
||||
{
|
||||
fd_set rfds;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 50000;
|
||||
if (select(fd + 1, &rfds, nullptr, nullptr, &timeout) <= 0)
|
||||
continue;
|
||||
|
||||
u16 size;
|
||||
if (read(fd, &size, 2) != 2)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Failed to read size field from BBA: {}", Common::LastStrerrorString());
|
||||
}
|
||||
else
|
||||
{
|
||||
int read_bytes = read(fd, m_eth_ref->mRecvBuffer.get(), size);
|
||||
if (read_bytes < 0)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Failed to read packet data from BBA: {}", Common::LastStrerrorString());
|
||||
}
|
||||
else if (readEnabled.IsSet())
|
||||
{
|
||||
std::string data_string = ArrayToString(m_eth_ref->mRecvBuffer.get(), read_bytes, 0x10);
|
||||
INFO_LOG_FMT(SP1, "Read data: {}", data_string);
|
||||
m_eth_ref->mRecvBufferLength = read_bytes;
|
||||
m_eth_ref->RecvHandlePacket();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CEXIETHERNET::TAPServerNetworkInterface::RecvInit()
|
||||
{
|
||||
readThread = std::thread(&CEXIETHERNET::TAPServerNetworkInterface::ReadThreadHandler, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ExpansionInterface
|
|
@ -14,6 +14,7 @@
|
|||
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||
#include "Core/HW/EXI/EXI_DeviceMemoryCard.h"
|
||||
#include "Core/HW/EXI/EXI_DeviceMic.h"
|
||||
#include "Core/HW/EXI/EXI_DeviceModem.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
|
@ -137,11 +138,9 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(Core::System& system, const EXIDevi
|
|||
result = std::make_unique<CEXIETHERNET>(system, BBADeviceType::TAP);
|
||||
break;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
case EXIDeviceType::EthernetTapServer:
|
||||
result = std::make_unique<CEXIETHERNET>(system, BBADeviceType::TAPSERVER);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case EXIDeviceType::EthernetXLink:
|
||||
result = std::make_unique<CEXIETHERNET>(system, BBADeviceType::XLINK);
|
||||
|
@ -151,6 +150,10 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(Core::System& system, const EXIDevi
|
|||
result = std::make_unique<CEXIETHERNET>(system, BBADeviceType::BuiltIn);
|
||||
break;
|
||||
|
||||
case EXIDeviceType::ModemTapServer:
|
||||
result = std::make_unique<CEXIModem>(system, ModemDeviceType::TAPSERVER);
|
||||
break;
|
||||
|
||||
case EXIDeviceType::Gecko:
|
||||
result = std::make_unique<CEXIGecko>(system);
|
||||
break;
|
||||
|
|
|
@ -39,9 +39,9 @@ enum class EXIDeviceType : int
|
|||
MemoryCardFolder,
|
||||
AGP,
|
||||
EthernetXLink,
|
||||
// Only used on Apple devices.
|
||||
EthernetTapServer,
|
||||
EthernetBuiltIn,
|
||||
ModemTapServer,
|
||||
None = 0xFF
|
||||
};
|
||||
|
||||
|
@ -88,7 +88,7 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(Core::System& system, EXIDeviceType
|
|||
|
||||
template <>
|
||||
struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
||||
: EnumFormatter<ExpansionInterface::EXIDeviceType::EthernetBuiltIn>
|
||||
: EnumFormatter<ExpansionInterface::EXIDeviceType::ModemTapServer>
|
||||
{
|
||||
static constexpr array_type names = {
|
||||
_trans("Dummy"),
|
||||
|
@ -105,6 +105,7 @@ struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
|||
_trans("Broadband Adapter (XLink Kai)"),
|
||||
_trans("Broadband Adapter (tapserver)"),
|
||||
_trans("Broadband Adapter (HLE)"),
|
||||
_trans("Modem Adapter (tapserver)"),
|
||||
};
|
||||
|
||||
constexpr formatter() : EnumFormatter(names) {}
|
||||
|
|
|
@ -50,12 +50,11 @@ CEXIETHERNET::CEXIETHERNET(Core::System& system, BBADeviceType type) : IEXIDevic
|
|||
m_network_interface = std::make_unique<TAPNetworkInterface>(this);
|
||||
INFO_LOG_FMT(SP1, "Created TAP physical network interface.");
|
||||
break;
|
||||
#if defined(__APPLE__)
|
||||
case BBADeviceType::TAPSERVER:
|
||||
m_network_interface = std::make_unique<TAPServerNetworkInterface>(this);
|
||||
m_network_interface = std::make_unique<TAPServerNetworkInterface>(
|
||||
this, Config::Get(Config::MAIN_BBA_TAPSERVER_DESTINATION));
|
||||
INFO_LOG_FMT(SP1, "Created tapserver physical network interface.");
|
||||
break;
|
||||
#endif
|
||||
case BBADeviceType::BuiltIn:
|
||||
m_network_interface = std::make_unique<BuiltInBBAInterface>(
|
||||
this, Config::Get(Config::MAIN_BBA_BUILTIN_DNS), Config::Get(Config::MAIN_BBA_BUILTIN_IP));
|
||||
|
@ -232,7 +231,7 @@ void CEXIETHERNET::DMAWrite(u32 addr, u32 size)
|
|||
transfer.address == BBA_WRTXFIFOD)
|
||||
{
|
||||
auto& memory = m_system.GetMemory();
|
||||
DirectFIFOWrite(memory.GetPointer(addr), size);
|
||||
DirectFIFOWrite(memory.GetPointerForRange(addr, size), size);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Network.h"
|
||||
#include "Common/SocketContext.h"
|
||||
#include "Core/HW/EXI/BBA/BuiltIn.h"
|
||||
#include "Core/HW/EXI/BBA/TAPServerConnection.h"
|
||||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
@ -205,9 +207,7 @@ enum class BBADeviceType
|
|||
{
|
||||
TAP,
|
||||
XLINK,
|
||||
#if defined(__APPLE__)
|
||||
TAPSERVER,
|
||||
#endif
|
||||
BuiltIn,
|
||||
};
|
||||
|
||||
|
@ -364,21 +364,25 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
#if defined(__APPLE__)
|
||||
class TAPServerNetworkInterface : public TAPNetworkInterface
|
||||
class TAPServerNetworkInterface : public NetworkInterface
|
||||
{
|
||||
public:
|
||||
explicit TAPServerNetworkInterface(CEXIETHERNET* eth_ref) : TAPNetworkInterface(eth_ref) {}
|
||||
TAPServerNetworkInterface(CEXIETHERNET* eth_ref, const std::string& destination);
|
||||
|
||||
public:
|
||||
bool Activate() override;
|
||||
void Deactivate() override;
|
||||
bool IsActivated() override;
|
||||
bool SendFrame(const u8* frame, u32 size) override;
|
||||
bool RecvInit() override;
|
||||
void RecvStart() override;
|
||||
void RecvStop() override;
|
||||
|
||||
private:
|
||||
void ReadThreadHandler();
|
||||
TAPServerConnection m_tapserver_if;
|
||||
|
||||
void HandleReceivedFrame(std::string&& data);
|
||||
};
|
||||
#endif
|
||||
|
||||
class XLinkNetworkInterface : public NetworkInterface
|
||||
{
|
||||
|
@ -452,16 +456,15 @@ private:
|
|||
sf::TcpListener m_upnp_httpd;
|
||||
#if defined(WIN32) || defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
||||
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__)
|
||||
std::array<StackRef, 10> network_ref{}; // max 10 at same time, i think most gc game had a
|
||||
// limit of 8 in the gc framework
|
||||
NetworkRef m_network_ref;
|
||||
std::thread m_read_thread;
|
||||
Common::Flag m_read_enabled;
|
||||
Common::Flag m_read_thread_shutdown;
|
||||
static void ReadThreadHandler(BuiltInBBAInterface* self);
|
||||
#endif
|
||||
void WriteToQueue(const std::vector<u8>& data);
|
||||
StackRef* GetAvailableSlot(u16 port);
|
||||
StackRef* GetTCPSlot(u16 src_port, u16 dst_port, u32 ip);
|
||||
bool WillQueueOverrun() const;
|
||||
void PollData(std::size_t* datasize);
|
||||
std::optional<std::vector<u8>> TryGetDataFromSocket(StackRef* ref);
|
||||
|
||||
void HandleARP(const Common::ARPPacket& packet);
|
||||
|
|
|
@ -296,10 +296,6 @@ void CEXIIPL::TransferByte(u8& data)
|
|||
DEBUG_LOG_FMT(EXPANSIONINTERFACE, "IPL-DEV data {} {:08x} {:02x}",
|
||||
m_command.is_write() ? "write" : "read", address, data);
|
||||
|
||||
#define IN_RANGE(x) (address >= x##_BASE && address < x##_BASE + x##_SIZE)
|
||||
#define DEV_ADDR(x) (address - x##_BASE)
|
||||
#define DEV_ADDR_CURSOR(x) (DEV_ADDR(x) + m_cursor++)
|
||||
|
||||
auto UartFifoAccess = [&]() {
|
||||
if (m_command.is_write())
|
||||
{
|
||||
|
@ -323,7 +319,9 @@ void CEXIIPL::TransferByte(u8& data)
|
|||
{
|
||||
if (!m_command.is_write())
|
||||
{
|
||||
u32 dev_addr = DEV_ADDR_CURSOR(ROM);
|
||||
u32 dev_addr = address - ROM_BASE + m_cursor++;
|
||||
// TODO: Is this address wrapping correct? Needs a hardware test
|
||||
dev_addr %= ROM_SIZE;
|
||||
// Technically we should descramble here iff descrambling logic is enabled.
|
||||
// At the moment, we pre-decrypt the whole thing and
|
||||
// ignore the "enabled" bit - see CEXIIPL::CEXIIPL
|
||||
|
@ -346,18 +344,20 @@ void CEXIIPL::TransferByte(u8& data)
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (IN_RANGE(SRAM))
|
||||
else if (address >= SRAM_BASE && address < SRAM_BASE + SRAM_SIZE)
|
||||
{
|
||||
auto& sram = m_system.GetSRAM();
|
||||
u32 dev_addr = DEV_ADDR_CURSOR(SRAM);
|
||||
u32 dev_addr = address - SRAM_BASE + m_cursor++;
|
||||
// TODO: Is this address wrapping correct? Needs a hardware test
|
||||
dev_addr %= SRAM_SIZE;
|
||||
if (m_command.is_write())
|
||||
sram[dev_addr] = data;
|
||||
else
|
||||
data = sram[dev_addr];
|
||||
}
|
||||
else if (IN_RANGE(UART))
|
||||
else if (address >= UART_BASE && address < UART_BASE + UART_SIZE)
|
||||
{
|
||||
switch (DEV_ADDR(UART))
|
||||
switch (address - UART_BASE)
|
||||
{
|
||||
case 0:
|
||||
// Seems to be 16byte fifo
|
||||
|
@ -371,16 +371,17 @@ void CEXIIPL::TransferByte(u8& data)
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (IN_RANGE(WII_RTC) && DEV_ADDR(WII_RTC) == 0x20)
|
||||
else if (address >= WII_RTC_BASE && address < WII_RTC_BASE + WII_RTC_SIZE &&
|
||||
address - WII_RTC_BASE == 0x20)
|
||||
{
|
||||
if (m_command.is_write())
|
||||
g_rtc_flags.m_hex = data;
|
||||
else
|
||||
data = g_rtc_flags.m_hex;
|
||||
}
|
||||
else if (IN_RANGE(EUART))
|
||||
else if (address >= EUART_BASE && address < EUART_BASE + EUART_SIZE)
|
||||
{
|
||||
switch (DEV_ADDR(EUART))
|
||||
switch (address - EUART_BASE)
|
||||
{
|
||||
case 0:
|
||||
// Writes 0xf2 then 0xf3 on EUART init. Just need to return non-zero
|
||||
|
@ -395,10 +396,6 @@ void CEXIIPL::TransferByte(u8& data)
|
|||
{
|
||||
NOTICE_LOG_FMT(EXPANSIONINTERFACE, "IPL-DEV Accessing unknown device");
|
||||
}
|
||||
|
||||
#undef DEV_ADDR_CURSOR
|
||||
#undef DEV_ADDR
|
||||
#undef IN_RANGE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -525,7 +525,7 @@ void CEXIMemoryCard::DoState(PointerWrap& p)
|
|||
void CEXIMemoryCard::DMARead(u32 addr, u32 size)
|
||||
{
|
||||
auto& memory = m_system.GetMemory();
|
||||
m_memory_card->Read(m_address, size, memory.GetPointer(addr));
|
||||
m_memory_card->Read(m_address, size, memory.GetPointerForRange(addr, size));
|
||||
|
||||
if ((m_address + size) % Memcard::BLOCK_SIZE == 0)
|
||||
{
|
||||
|
@ -543,7 +543,7 @@ void CEXIMemoryCard::DMARead(u32 addr, u32 size)
|
|||
void CEXIMemoryCard::DMAWrite(u32 addr, u32 size)
|
||||
{
|
||||
auto& memory = m_system.GetMemory();
|
||||
m_memory_card->Write(m_address, size, memory.GetPointer(addr));
|
||||
m_memory_card->Write(m_address, size, memory.GetPointerForRange(addr, size));
|
||||
|
||||
if (((m_address + size) % Memcard::BLOCK_SIZE) == 0)
|
||||
{
|
||||
|
|
398
Source/Core/Core/HW/EXI/EXI_DeviceModem.cpp
Normal file
398
Source/Core/Core/HW/EXI/EXI_DeviceModem.cpp
Normal file
|
@ -0,0 +1,398 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/EXI/EXI_DeviceModem.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/Network.h"
|
||||
#include "Common/StringUtil.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/HW/EXI/EXI.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
|
||||
CEXIModem::CEXIModem(Core::System& system, ModemDeviceType type) : IEXIDevice(system)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ModemDeviceType::TAPSERVER:
|
||||
m_network_interface = std::make_unique<TAPServerNetworkInterface>(
|
||||
this, Config::Get(Config::MAIN_MODEM_TAPSERVER_DESTINATION));
|
||||
INFO_LOG_FMT(SP1, "Created tapserver physical network interface.");
|
||||
break;
|
||||
}
|
||||
|
||||
m_regs[Register::DEVICE_TYPE] = 0x02;
|
||||
m_regs[Register::INTERRUPT_MASK] = 0x02;
|
||||
}
|
||||
|
||||
CEXIModem::~CEXIModem()
|
||||
{
|
||||
m_network_interface->RecvStop();
|
||||
m_network_interface->Deactivate();
|
||||
}
|
||||
|
||||
bool CEXIModem::IsPresent() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void CEXIModem::SetCS(int cs)
|
||||
{
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
|
||||
bool CEXIModem::IsInterruptSet()
|
||||
{
|
||||
return !!(m_regs[Register::INTERRUPT_MASK] & m_regs[Register::PENDING_INTERRUPT_MASK]);
|
||||
}
|
||||
|
||||
void CEXIModem::ImmWrite(u32 data, u32 size)
|
||||
{
|
||||
if (m_transfer_descriptor == INVALID_TRANSFER_DESCRIPTOR)
|
||||
{
|
||||
m_transfer_descriptor = data;
|
||||
if (m_transfer_descriptor == 0x00008000)
|
||||
{ // Reset
|
||||
m_network_interface->RecvStop();
|
||||
m_network_interface->Deactivate();
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
}
|
||||
else if (!IsWriteTransfer(m_transfer_descriptor))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI IMM write {:x} ({} bytes) after read command {:x}", data, size,
|
||||
m_transfer_descriptor);
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
else if (IsModemTransfer(m_transfer_descriptor))
|
||||
{ // Write AT command buffer or packet send buffer
|
||||
const u32 be_data = htonl(data);
|
||||
HandleWriteModemTransfer(&be_data, size);
|
||||
}
|
||||
else
|
||||
{ // Write device register
|
||||
u8 reg_num = static_cast<uint8_t>((m_transfer_descriptor >> 24) & 0x1F);
|
||||
bool should_update_interrupts = false;
|
||||
for (; size && reg_num < m_regs.size(); size--)
|
||||
{
|
||||
should_update_interrupts |=
|
||||
((reg_num == Register::INTERRUPT_MASK) || (reg_num == Register::PENDING_INTERRUPT_MASK));
|
||||
m_regs[reg_num++] = (data >> 24);
|
||||
data <<= 8;
|
||||
}
|
||||
if (should_update_interrupts)
|
||||
{
|
||||
m_system.GetExpansionInterface().ScheduleUpdateInterrupts(CoreTiming::FromThread::CPU, 0);
|
||||
}
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIModem::DMAWrite(u32 addr, u32 size)
|
||||
{
|
||||
if (m_transfer_descriptor == INVALID_TRANSFER_DESCRIPTOR)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI DMA write {:x} ({} bytes) after read command {:x}", addr, size,
|
||||
m_transfer_descriptor);
|
||||
}
|
||||
else if (!IsWriteTransfer(m_transfer_descriptor))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI DMA write {:x} ({} bytes) after read command {:x}", addr, size,
|
||||
m_transfer_descriptor);
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
else if (!IsModemTransfer(m_transfer_descriptor))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI DMA write {:x} ({} bytes) to registers {:x}", addr, size,
|
||||
m_transfer_descriptor);
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& memory = m_system.GetMemory();
|
||||
HandleWriteModemTransfer(memory.GetPointerForRange(addr, size), size);
|
||||
}
|
||||
}
|
||||
|
||||
u32 CEXIModem::ImmRead(u32 size)
|
||||
{
|
||||
if (m_transfer_descriptor == INVALID_TRANSFER_DESCRIPTOR)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI IMM read ({} bytes) with no pending transfer", size);
|
||||
return 0;
|
||||
}
|
||||
else if (IsWriteTransfer(m_transfer_descriptor))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI IMM read ({} bytes) after write command {:x}", size,
|
||||
m_transfer_descriptor);
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
return 0;
|
||||
}
|
||||
else if (IsModemTransfer(m_transfer_descriptor))
|
||||
{
|
||||
u32 be_data = 0;
|
||||
HandleReadModemTransfer(&be_data, size);
|
||||
return ntohl(be_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read device register
|
||||
const u8 reg_num = static_cast<uint8_t>((m_transfer_descriptor >> 24) & 0x1F);
|
||||
if (reg_num == 0)
|
||||
{
|
||||
return 0x02020000; // Device ID (modem)
|
||||
}
|
||||
u32 ret = 0;
|
||||
for (u8 z = 0; z < size; z++)
|
||||
{
|
||||
if (reg_num + z >= m_regs.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
ret |= (m_regs[reg_num + z] << ((3 - z) * 8));
|
||||
}
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIModem::DMARead(u32 addr, u32 size)
|
||||
{
|
||||
if (m_transfer_descriptor == INVALID_TRANSFER_DESCRIPTOR)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI DMA read {:x} ({} bytes) with no pending transfer", addr,
|
||||
size);
|
||||
}
|
||||
else if (IsWriteTransfer(m_transfer_descriptor))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI DMA read {:x} ({} bytes) after write command {:x}", addr, size,
|
||||
m_transfer_descriptor);
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
else if (!IsModemTransfer(m_transfer_descriptor))
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Received EXI DMA read {:x} ({} bytes) to registers {:x}", addr, size,
|
||||
m_transfer_descriptor);
|
||||
m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& memory = m_system.GetMemory();
|
||||
HandleReadModemTransfer(memory.GetPointerForRange(addr, size), size);
|
||||
}
|
||||
}
|
||||
|
||||
void CEXIModem::HandleReadModemTransfer(void* data, u32 size)
|
||||
{
|
||||
const u16 bytes_requested = GetModemTransferSize(m_transfer_descriptor);
|
||||
if (size > bytes_requested)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "More bytes requested ({}) than originally requested for transfer {:x}",
|
||||
size, m_transfer_descriptor);
|
||||
size = bytes_requested;
|
||||
}
|
||||
const u16 bytes_requested_after_read = bytes_requested - size;
|
||||
|
||||
if ((m_transfer_descriptor & 0x0F000000) == 0x03000000)
|
||||
{
|
||||
// AT command buffer
|
||||
const std::size_t bytes_to_copy = std::min<std::size_t>(size, m_at_reply_data.size());
|
||||
std::memcpy(data, m_at_reply_data.data(), bytes_to_copy);
|
||||
m_at_reply_data = m_at_reply_data.substr(bytes_to_copy);
|
||||
m_regs[Register::AT_REPLY_SIZE] = static_cast<u8>(m_at_reply_data.size());
|
||||
SetInterruptFlag(Interrupt::AT_REPLY_DATA_AVAILABLE, !m_at_reply_data.empty(), true);
|
||||
}
|
||||
else if ((m_transfer_descriptor & 0x0F000000) == 0x08000000)
|
||||
{
|
||||
// Packet receive buffer
|
||||
std::lock_guard<std::mutex> g(m_receive_buffer_lock);
|
||||
const std::size_t bytes_to_copy = std::min<std::size_t>(size, m_receive_buffer.size());
|
||||
std::memcpy(data, m_receive_buffer.data(), bytes_to_copy);
|
||||
m_receive_buffer = m_receive_buffer.substr(bytes_to_copy);
|
||||
OnReceiveBufferSizeChangedLocked(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Invalid modem read transfer type {:x}", m_transfer_descriptor);
|
||||
}
|
||||
|
||||
m_transfer_descriptor =
|
||||
(bytes_requested_after_read == 0) ?
|
||||
INVALID_TRANSFER_DESCRIPTOR :
|
||||
SetModemTransferSize(m_transfer_descriptor, bytes_requested_after_read);
|
||||
}
|
||||
|
||||
void CEXIModem::HandleWriteModemTransfer(const void* data, u32 size)
|
||||
{
|
||||
const u16 bytes_expected = GetModemTransferSize(m_transfer_descriptor);
|
||||
if (size > bytes_expected)
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "More bytes received ({}) than expected for transfer {:x}", size,
|
||||
m_transfer_descriptor);
|
||||
return;
|
||||
}
|
||||
const u16 bytes_expected_after_write = bytes_expected - size;
|
||||
|
||||
if ((m_transfer_descriptor & 0x0F000000) == 0x03000000)
|
||||
{ // AT command buffer
|
||||
m_at_command_data.append(reinterpret_cast<const char*>(data), size);
|
||||
RunAllPendingATCommands();
|
||||
m_regs[Register::AT_COMMAND_SIZE] = static_cast<u8>(m_at_command_data.size());
|
||||
}
|
||||
else if ((m_transfer_descriptor & 0x0F000000) == 0x08000000)
|
||||
{ // Packet send buffer
|
||||
m_send_buffer.append(reinterpret_cast<const char*>(data), size);
|
||||
// A more accurate implementation would only set this interrupt if the send
|
||||
// FIFO has enough space; however, we can clear the send FIFO "instantly"
|
||||
// from the emulated program's perspective, so we always tell it the send
|
||||
// FIFO is empty.
|
||||
SetInterruptFlag(Interrupt::SEND_BUFFER_BELOW_THRESHOLD, true, true);
|
||||
m_network_interface->SendAndRemoveAllHDLCFrames(&m_send_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ERROR_LOG_FMT(SP1, "Invalid modem write transfer type {:x}", m_transfer_descriptor);
|
||||
}
|
||||
|
||||
m_transfer_descriptor =
|
||||
(bytes_expected_after_write == 0) ?
|
||||
INVALID_TRANSFER_DESCRIPTOR :
|
||||
SetModemTransferSize(m_transfer_descriptor, bytes_expected_after_write);
|
||||
}
|
||||
|
||||
void CEXIModem::DoState(PointerWrap& p)
|
||||
{
|
||||
// There isn't really any state to save. The registers depend on the state of
|
||||
// the external connection, which Dolphin doesn't have control over. What
|
||||
// should happen when the user saves a state during an online session and
|
||||
// loads it later? The remote server presumably doesn't support point-in-time
|
||||
// snapshots and reloading thereof.
|
||||
}
|
||||
|
||||
u16 CEXIModem::GetTxThreshold() const
|
||||
{
|
||||
return (m_regs[Register::TX_THRESHOLD_HIGH] << 8) | m_regs[Register::TX_THRESHOLD_LOW];
|
||||
}
|
||||
|
||||
u16 CEXIModem::GetRxThreshold() const
|
||||
{
|
||||
return (m_regs[Register::RX_THRESHOLD_HIGH] << 8) | m_regs[Register::RX_THRESHOLD_LOW];
|
||||
}
|
||||
|
||||
void CEXIModem::SetInterruptFlag(u8 what, bool enabled, bool from_cpu)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
m_regs[Register::PENDING_INTERRUPT_MASK] |= what;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_regs[Register::PENDING_INTERRUPT_MASK] &= (~what);
|
||||
}
|
||||
m_system.GetExpansionInterface().ScheduleUpdateInterrupts(
|
||||
from_cpu ? CoreTiming::FromThread::CPU : CoreTiming::FromThread::NON_CPU, 0);
|
||||
}
|
||||
|
||||
void CEXIModem::OnReceiveBufferSizeChangedLocked(bool from_cpu)
|
||||
{
|
||||
// The caller is expected to hold m_receive_buffer_lock when calling this.
|
||||
const u16 bytes_available =
|
||||
static_cast<u16>(std::min<std::size_t>(m_receive_buffer.size(), 0x200));
|
||||
m_regs[Register::BYTES_AVAILABLE_HIGH] = (bytes_available >> 8) & 0xFF;
|
||||
m_regs[Register::BYTES_AVAILABLE_LOW] = bytes_available & 0xFF;
|
||||
SetInterruptFlag(Interrupt::RECEIVE_BUFFER_ABOVE_THRESHOLD,
|
||||
m_receive_buffer.size() >= GetRxThreshold(), from_cpu);
|
||||
// TODO: There is a second interrupt here, which the GameCube modem library
|
||||
// expects to be used when large frames are received. However, the correct
|
||||
// semantics for this interrupt aren't obvious. Reverse-engineering some games
|
||||
// that use the modem adapter makes it look like this interrupt should trigger
|
||||
// when there's any data at all in the receive buffer, but implementing the
|
||||
// interrupt this way causes them to crash. Further research is needed.
|
||||
// SetInterruptFlag(Interrupt::RECEIVE_BUFFER_NOT_EMPTY, !m_receive_buffer.empty(), from_cpu);
|
||||
}
|
||||
|
||||
void CEXIModem::SendComplete()
|
||||
{
|
||||
// See comment in HandleWriteModemTransfer about why this is always true.
|
||||
SetInterruptFlag(Interrupt::SEND_BUFFER_BELOW_THRESHOLD, true, true);
|
||||
}
|
||||
|
||||
void CEXIModem::AddToReceiveBuffer(std::string&& data)
|
||||
{
|
||||
std::lock_guard<std::mutex> g(m_receive_buffer_lock);
|
||||
if (m_receive_buffer.empty())
|
||||
{
|
||||
m_receive_buffer = std::move(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_receive_buffer += data;
|
||||
}
|
||||
OnReceiveBufferSizeChangedLocked(false);
|
||||
}
|
||||
|
||||
void CEXIModem::AddATReply(const std::string& data)
|
||||
{
|
||||
m_at_reply_data += data;
|
||||
m_regs[Register::AT_REPLY_SIZE] = static_cast<u8>(m_at_reply_data.size());
|
||||
SetInterruptFlag(Interrupt::AT_REPLY_DATA_AVAILABLE, !m_at_reply_data.empty(), true);
|
||||
}
|
||||
|
||||
void CEXIModem::RunAllPendingATCommands()
|
||||
{
|
||||
for (std::size_t newline_pos = m_at_command_data.find_first_of("\r\n");
|
||||
newline_pos != std::string::npos; newline_pos = m_at_command_data.find_first_of("\r\n"))
|
||||
{
|
||||
std::string command = m_at_command_data.substr(0, newline_pos);
|
||||
m_at_command_data = m_at_command_data.substr(newline_pos + 1);
|
||||
|
||||
INFO_LOG_FMT(SP1, "Received AT command: {}", command);
|
||||
|
||||
if (command.substr(0, 3) == "ATZ" || command == "ATH0")
|
||||
{
|
||||
// Reset (ATZ) or hang up (ATH0)
|
||||
m_network_interface->RecvStop();
|
||||
m_network_interface->Deactivate();
|
||||
AddATReply("OK\r");
|
||||
}
|
||||
else if (command.substr(0, 3) == "ATD")
|
||||
{
|
||||
// Dial
|
||||
if (m_network_interface->Activate())
|
||||
{
|
||||
m_network_interface->RecvStart();
|
||||
AddATReply("OK\rCONNECT 115200\r"); // Maximum baud rate
|
||||
}
|
||||
else
|
||||
{
|
||||
AddATReply("OK\rNO ANSWER\r");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// PSO sends several other AT commands during modem setup, but in our
|
||||
// implementation we don't actually have to do anything in response to
|
||||
// them, so we just pretend we did.
|
||||
AddATReply("OK\r");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ExpansionInterface
|
172
Source/Core/Core/HW/EXI/EXI_DeviceModem.h
Normal file
172
Source/Core/Core/HW/EXI/EXI_DeviceModem.h
Normal file
|
@ -0,0 +1,172 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/Flag.h"
|
||||
#include "Common/Network.h"
|
||||
#include "Core/HW/EXI/BBA/TAPServerConnection.h"
|
||||
#include "Core/HW/EXI/EXI_Device.h"
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
|
||||
static constexpr std::size_t MODEM_RECV_SIZE = 0x800;
|
||||
|
||||
enum
|
||||
{
|
||||
EXI_DEVTYPE_MODEM = 0x02020000,
|
||||
};
|
||||
|
||||
enum class ModemDeviceType
|
||||
{
|
||||
TAPSERVER,
|
||||
};
|
||||
|
||||
class CEXIModem : public IEXIDevice
|
||||
{
|
||||
public:
|
||||
CEXIModem(Core::System& system, ModemDeviceType type);
|
||||
virtual ~CEXIModem();
|
||||
void SetCS(int cs) override;
|
||||
bool IsPresent() const override;
|
||||
bool IsInterruptSet() override;
|
||||
void ImmWrite(u32 data, u32 size) override;
|
||||
u32 ImmRead(u32 size) override;
|
||||
void DMAWrite(u32 addr, u32 size) override;
|
||||
void DMARead(u32 addr, u32 size) override;
|
||||
void DoState(PointerWrap& p) override;
|
||||
|
||||
private:
|
||||
// Note: The names in these enums are based on reverse-engineering of PSO and
|
||||
// not on any documentation of the GC modem or its chipset. If official
|
||||
// documentation is found, any names in there probably will not match these
|
||||
// names.
|
||||
|
||||
enum Interrupt
|
||||
{
|
||||
// Used for Register::INTERRUPT_MASK and Register::PENDING_INTERRUPT_MASK
|
||||
AT_REPLY_DATA_AVAILABLE = 0x02,
|
||||
SEND_BUFFER_BELOW_THRESHOLD = 0x10,
|
||||
RECEIVE_BUFFER_ABOVE_THRESHOLD = 0x20,
|
||||
RECEIVE_BUFFER_NOT_EMPTY = 0x40,
|
||||
};
|
||||
|
||||
enum Register
|
||||
{
|
||||
DEVICE_TYPE = 0x00,
|
||||
INTERRUPT_MASK = 0x01,
|
||||
PENDING_INTERRUPT_MASK = 0x02,
|
||||
UNKNOWN_03 = 0x03,
|
||||
AT_COMMAND_SIZE = 0x04,
|
||||
AT_REPLY_SIZE = 0x05,
|
||||
UNKNOWN_06 = 0x06,
|
||||
UNKNOWN_07 = 0x07,
|
||||
UNKNOWN_08 = 0x08,
|
||||
BYTES_SENT_HIGH = 0x09,
|
||||
BYTES_SENT_LOW = 0x0A,
|
||||
BYTES_AVAILABLE_HIGH = 0x0B,
|
||||
BYTES_AVAILABLE_LOW = 0x0C,
|
||||
ESR = 0x0D,
|
||||
TX_THRESHOLD_HIGH = 0x0E,
|
||||
TX_THRESHOLD_LOW = 0x0F,
|
||||
RX_THRESHOLD_HIGH = 0x10,
|
||||
RX_THRESHOLD_LOW = 0x11,
|
||||
STATUS = 0x12,
|
||||
FWT = 0x13,
|
||||
};
|
||||
|
||||
u16 GetTxThreshold() const;
|
||||
u16 GetRxThreshold() const;
|
||||
void SetInterruptFlag(u8 what, bool enabled, bool from_cpu);
|
||||
void HandleReadModemTransfer(void* data, u32 size);
|
||||
void HandleWriteModemTransfer(const void* data, u32 size);
|
||||
void OnReceiveBufferSizeChangedLocked(bool from_cpu);
|
||||
void SendComplete();
|
||||
void AddToReceiveBuffer(std::string&& data);
|
||||
void RunAllPendingATCommands();
|
||||
void AddATReply(const std::string& data);
|
||||
|
||||
static inline bool TransferIsResetCommand(u32 transfer_descriptor)
|
||||
{
|
||||
return transfer_descriptor == 0x80000000;
|
||||
}
|
||||
|
||||
static inline bool IsWriteTransfer(u32 transfer_descriptor)
|
||||
{
|
||||
return transfer_descriptor & 0x40000000;
|
||||
}
|
||||
|
||||
static inline bool IsModemTransfer(u32 transfer_descriptor)
|
||||
{
|
||||
return transfer_descriptor & 0x20000000;
|
||||
}
|
||||
|
||||
static inline u16 GetModemTransferSize(u32 transfer_descriptor)
|
||||
{
|
||||
return (transfer_descriptor >> 8) & 0xFFFF;
|
||||
}
|
||||
|
||||
static inline u32 SetModemTransferSize(u32 transfer_descriptor, u16 new_size)
|
||||
{
|
||||
return (transfer_descriptor & 0xFF000000) | (new_size << 8);
|
||||
}
|
||||
|
||||
class NetworkInterface
|
||||
{
|
||||
protected:
|
||||
CEXIModem* m_modem_ref = nullptr;
|
||||
explicit NetworkInterface(CEXIModem* modem_ref) : m_modem_ref{modem_ref} {}
|
||||
|
||||
public:
|
||||
virtual bool Activate() { return false; }
|
||||
virtual void Deactivate() {}
|
||||
virtual bool IsActivated() { return false; }
|
||||
virtual bool SendAndRemoveAllHDLCFrames(std::string*) { return false; }
|
||||
virtual bool RecvInit() { return false; }
|
||||
virtual void RecvStart() {}
|
||||
virtual void RecvStop() {}
|
||||
|
||||
virtual ~NetworkInterface() = default;
|
||||
};
|
||||
|
||||
class TAPServerNetworkInterface : public NetworkInterface
|
||||
{
|
||||
public:
|
||||
TAPServerNetworkInterface(CEXIModem* modem_ref, const std::string& destination);
|
||||
|
||||
public:
|
||||
virtual bool Activate() override;
|
||||
virtual void Deactivate() override;
|
||||
virtual bool IsActivated() override;
|
||||
virtual bool SendAndRemoveAllHDLCFrames(std::string* send_buffer) override;
|
||||
virtual bool RecvInit() override;
|
||||
virtual void RecvStart() override;
|
||||
virtual void RecvStop() override;
|
||||
|
||||
private:
|
||||
TAPServerConnection m_tapserver_if;
|
||||
|
||||
void HandleReceivedFrame(std::string&& data);
|
||||
};
|
||||
|
||||
std::unique_ptr<NetworkInterface> m_network_interface;
|
||||
|
||||
static constexpr u32 INVALID_TRANSFER_DESCRIPTOR = 0xFFFFFFFF;
|
||||
|
||||
u32 m_transfer_descriptor = INVALID_TRANSFER_DESCRIPTOR;
|
||||
|
||||
std::string m_at_command_data;
|
||||
std::string m_at_reply_data;
|
||||
std::string m_send_buffer;
|
||||
std::mutex m_receive_buffer_lock;
|
||||
std::string m_receive_buffer;
|
||||
std::array<u8, 0x20> m_regs{};
|
||||
};
|
||||
} // namespace ExpansionInterface
|
65
Source/Core/Core/HW/EXI/Modem/TAPServerModem.cpp
Normal file
65
Source/Core/Core/HW/EXI/Modem/TAPServerModem.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2024 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/EXI/EXI_DeviceModem.h"
|
||||
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
namespace ExpansionInterface
|
||||
{
|
||||
|
||||
CEXIModem::TAPServerNetworkInterface::TAPServerNetworkInterface(CEXIModem* modem_ref,
|
||||
const std::string& destination)
|
||||
: NetworkInterface(modem_ref),
|
||||
m_tapserver_if(
|
||||
destination,
|
||||
std::bind(&TAPServerNetworkInterface::HandleReceivedFrame, this, std::placeholders::_1),
|
||||
MODEM_RECV_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
bool CEXIModem::TAPServerNetworkInterface::Activate()
|
||||
{
|
||||
return m_tapserver_if.Activate();
|
||||
}
|
||||
|
||||
void CEXIModem::TAPServerNetworkInterface::Deactivate()
|
||||
{
|
||||
m_tapserver_if.Deactivate();
|
||||
}
|
||||
|
||||
bool CEXIModem::TAPServerNetworkInterface::IsActivated()
|
||||
{
|
||||
return m_tapserver_if.IsActivated();
|
||||
}
|
||||
|
||||
bool CEXIModem::TAPServerNetworkInterface::SendAndRemoveAllHDLCFrames(std::string* send_buffer)
|
||||
{
|
||||
const std::size_t orig_size = send_buffer->size();
|
||||
const bool send_succeeded = m_tapserver_if.SendAndRemoveAllHDLCFrames(send_buffer);
|
||||
if (send_succeeded && (send_buffer->size() < orig_size))
|
||||
m_modem_ref->SendComplete();
|
||||
return send_succeeded;
|
||||
}
|
||||
|
||||
bool CEXIModem::TAPServerNetworkInterface::RecvInit()
|
||||
{
|
||||
return m_tapserver_if.RecvInit();
|
||||
}
|
||||
|
||||
void CEXIModem::TAPServerNetworkInterface::RecvStart()
|
||||
{
|
||||
m_tapserver_if.RecvStart();
|
||||
}
|
||||
|
||||
void CEXIModem::TAPServerNetworkInterface::RecvStop()
|
||||
{
|
||||
m_tapserver_if.RecvStop();
|
||||
}
|
||||
|
||||
void CEXIModem::TAPServerNetworkInterface::HandleReceivedFrame(std::string&& data)
|
||||
{
|
||||
m_modem_ref->AddToReceiveBuffer(std::move(data));
|
||||
}
|
||||
|
||||
} // namespace ExpansionInterface
|
|
@ -90,24 +90,18 @@ void GPFifoManager::UpdateGatherPipe()
|
|||
|
||||
size_t pipe_count = GetGatherPipeCount();
|
||||
size_t processed;
|
||||
u8* cur_mem = memory.GetPointer(processor_interface.m_fifo_cpu_write_pointer);
|
||||
for (processed = 0; pipe_count >= GATHER_PIPE_SIZE; processed += GATHER_PIPE_SIZE)
|
||||
{
|
||||
// copy the GatherPipe
|
||||
memcpy(cur_mem, m_gather_pipe + processed, GATHER_PIPE_SIZE);
|
||||
memory.CopyToEmu(processor_interface.m_fifo_cpu_write_pointer, m_gather_pipe + processed,
|
||||
GATHER_PIPE_SIZE);
|
||||
pipe_count -= GATHER_PIPE_SIZE;
|
||||
|
||||
// increase the CPUWritePointer
|
||||
if (processor_interface.m_fifo_cpu_write_pointer == processor_interface.m_fifo_cpu_end)
|
||||
{
|
||||
processor_interface.m_fifo_cpu_write_pointer = processor_interface.m_fifo_cpu_base;
|
||||
cur_mem = memory.GetPointer(processor_interface.m_fifo_cpu_write_pointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_mem += GATHER_PIPE_SIZE;
|
||||
processor_interface.m_fifo_cpu_write_pointer += GATHER_PIPE_SIZE;
|
||||
}
|
||||
|
||||
system.GetCommandProcessor().GatherPipeBursted();
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ void Init(Core::System& system, const Sram* override_sram)
|
|||
system.GetCoreTiming().Init();
|
||||
system.GetSystemTimers().PreInit();
|
||||
|
||||
State::Init();
|
||||
State::Init(system);
|
||||
|
||||
// Init the whole Hardware
|
||||
system.GetAudioInterface().Init();
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/HW/GPFifo.h"
|
||||
#include "Core/HW/MMIOHandlers.h"
|
||||
|
@ -88,7 +88,7 @@ inline u16* LowPart(u32* ptr)
|
|||
inline u16* LowPart(std::atomic<u32>* ptr)
|
||||
{
|
||||
static_assert(std::atomic<u32>::is_always_lock_free && sizeof(std::atomic<u32>) == sizeof(u32));
|
||||
return LowPart(Common::BitCast<u32*>(ptr));
|
||||
return LowPart(std::bit_cast<u32*>(ptr));
|
||||
}
|
||||
inline u16* HighPart(u32* ptr)
|
||||
{
|
||||
|
@ -97,7 +97,7 @@ inline u16* HighPart(u32* ptr)
|
|||
inline u16* HighPart(std::atomic<u32>* ptr)
|
||||
{
|
||||
static_assert(std::atomic<u32>::is_always_lock_free && sizeof(std::atomic<u32>) == sizeof(u32));
|
||||
return HighPart(Common::BitCast<u32*>(ptr));
|
||||
return HighPart(std::bit_cast<u32*>(ptr));
|
||||
}
|
||||
} // namespace Utils
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <tuple>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
|
@ -400,22 +401,23 @@ void MemoryManager::Clear()
|
|||
|
||||
u8* MemoryManager::GetPointerForRange(u32 address, size_t size) const
|
||||
{
|
||||
// Make sure we don't have a range spanning 2 separate banks
|
||||
if (size >= GetExRamSizeReal())
|
||||
std::span<u8> span = GetSpanForAddress(address);
|
||||
|
||||
if (span.data() == nullptr)
|
||||
{
|
||||
// The address isn't in a valid memory region.
|
||||
// A panic alert has already been raised by GetPointer, so let's not raise another one.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (span.size() < size)
|
||||
{
|
||||
// The start address is in a valid region, but the end address is beyond the end of that region.
|
||||
PanicAlertFmt("Oversized range in GetPointerForRange. {:x} bytes at {:#010x}", size, address);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check that the beginning and end of the range are valid
|
||||
u8* pointer = GetPointer(address);
|
||||
if (!pointer || !GetPointer(address + u32(size) - 1))
|
||||
{
|
||||
// A panic alert has already been raised by GetPointer
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return pointer;
|
||||
return span.data();
|
||||
}
|
||||
|
||||
void MemoryManager::CopyFromEmu(void* data, u32 address, size_t size) const
|
||||
|
@ -462,39 +464,52 @@ void MemoryManager::Memset(u32 address, u8 value, size_t size)
|
|||
|
||||
std::string MemoryManager::GetString(u32 em_address, size_t size)
|
||||
{
|
||||
const char* ptr = reinterpret_cast<const char*>(GetPointer(em_address));
|
||||
if (ptr == nullptr)
|
||||
return "";
|
||||
std::string result;
|
||||
|
||||
if (size == 0) // Null terminated string.
|
||||
{
|
||||
return std::string(ptr);
|
||||
while (true)
|
||||
{
|
||||
const u8 value = Read_U8(em_address);
|
||||
if (value == 0)
|
||||
break;
|
||||
|
||||
result.push_back(value);
|
||||
++em_address;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else // Fixed size string, potentially null terminated or null padded.
|
||||
{
|
||||
size_t length = strnlen(ptr, size);
|
||||
return std::string(ptr, length);
|
||||
result.resize(size);
|
||||
CopyFromEmu(result.data(), em_address, size);
|
||||
size_t length = strnlen(result.data(), size);
|
||||
result.resize(length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
u8* MemoryManager::GetPointer(u32 address) const
|
||||
std::span<u8> MemoryManager::GetSpanForAddress(u32 address) const
|
||||
{
|
||||
// TODO: Should we be masking off more bits here? Can all devices access
|
||||
// EXRAM?
|
||||
address &= 0x3FFFFFFF;
|
||||
if (address < GetRamSizeReal())
|
||||
return m_ram + address;
|
||||
return std::span(m_ram + address, GetRamSizeReal() - address);
|
||||
|
||||
if (m_exram)
|
||||
{
|
||||
if ((address >> 28) == 0x1 && (address & 0x0fffffff) < GetExRamSizeReal())
|
||||
return m_exram + (address & GetExRamMask());
|
||||
{
|
||||
return std::span(m_exram + (address & GetExRamMask()),
|
||||
GetExRamSizeReal() - (address & GetExRamMask()));
|
||||
}
|
||||
}
|
||||
|
||||
auto& ppc_state = m_system.GetPPCState();
|
||||
PanicAlertFmt("Unknown Pointer {:#010x} PC {:#010x} LR {:#010x}", address, ppc_state.pc,
|
||||
LR(ppc_state));
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
u8 MemoryManager::Read_U8(u32 address) const
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -105,8 +106,16 @@ public:
|
|||
// Routines to access physically addressed memory, designed for use by
|
||||
// emulated hardware outside the CPU. Use "Device_" prefix.
|
||||
std::string GetString(u32 em_address, size_t size = 0);
|
||||
u8* GetPointer(u32 address) const;
|
||||
|
||||
// If the specified guest address is within a valid memory region, returns a span starting at the
|
||||
// host address corresponding to the specified address and ending where the memory region ends.
|
||||
// Otherwise, returns a 0-length span starting at nullptr.
|
||||
std::span<u8> GetSpanForAddress(u32 address) const;
|
||||
|
||||
// If the specified range is within a single valid memory region, returns a pointer to the start
|
||||
// of the corresponding range in host memory. Otherwise, returns nullptr.
|
||||
u8* GetPointerForRange(u32 address, size_t size) const;
|
||||
|
||||
void CopyFromEmu(void* data, u32 address, size_t size) const;
|
||||
void CopyToEmu(u32 address, const void* data, size_t size);
|
||||
void Memset(u32 address, u8 value, size_t size);
|
||||
|
|
|
@ -256,7 +256,7 @@ void ProcessorInterfaceManager::IOSNotifyPowerButtonCallback(Core::System& syste
|
|||
|
||||
void ProcessorInterfaceManager::ResetButton_Tap()
|
||||
{
|
||||
if (!Core::IsRunning())
|
||||
if (!Core::IsRunning(m_system))
|
||||
return;
|
||||
|
||||
auto& core_timing = m_system.GetCoreTiming();
|
||||
|
@ -277,7 +277,7 @@ void ProcessorInterfaceManager::ResetButton_Tap_FromUser()
|
|||
|
||||
void ProcessorInterfaceManager::PowerButton_Tap()
|
||||
{
|
||||
if (!Core::IsRunning())
|
||||
if (!Core::IsRunning(m_system))
|
||||
return;
|
||||
|
||||
auto& core_timing = m_system.GetCoreTiming();
|
||||
|
|
|
@ -841,7 +841,7 @@ void VideoInterfaceManager::EndField(FieldType field, u64 ticks)
|
|||
|
||||
g_perf_metrics.CountVBlank();
|
||||
VIEndFieldEvent::Trigger();
|
||||
Core::OnFrameEnd();
|
||||
Core::OnFrameEnd(m_system);
|
||||
}
|
||||
|
||||
// Purpose: Send VI interrupt when triggered
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue