mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-28 19:58:53 +00:00
Merge #15
This commit is contained in:
parent
7d8fe0f105
commit
871da4e307
275 changed files with 33002 additions and 27474 deletions
|
@ -31,8 +31,6 @@ add_library(common
|
|||
Crypto/ec.h
|
||||
Crypto/SHA1.cpp
|
||||
Crypto/SHA1.h
|
||||
Debug/CodeTrace.cpp
|
||||
Debug/CodeTrace.h
|
||||
Debug/MemoryPatches.cpp
|
||||
Debug/MemoryPatches.h
|
||||
Debug/Threads.h
|
||||
|
@ -155,7 +153,7 @@ PUBLIC
|
|||
PRIVATE
|
||||
${CURL_LIBRARIES}
|
||||
FatFs
|
||||
${ICONV_LIBRARIES}
|
||||
Iconv::Iconv
|
||||
${spng_target}
|
||||
${VTUNE_LIBRARIES}
|
||||
)
|
||||
|
|
|
@ -1,396 +0,0 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/Debug/CodeTrace.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <regex>
|
||||
|
||||
#include "Common/Event.h"
|
||||
#include "Core/Core.h"
|
||||
#include "Core/Debugger/PPCDebugInterface.h"
|
||||
#include "Core/HW/CPU.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
bool IsInstructionLoadStore(std::string_view ins)
|
||||
{
|
||||
return (ins.starts_with('l') && !ins.starts_with("li")) || ins.starts_with("st") ||
|
||||
ins.starts_with("psq_l") || ins.starts_with("psq_s");
|
||||
}
|
||||
|
||||
u32 GetMemoryTargetSize(std::string_view instr)
|
||||
{
|
||||
// Word-size operations are taken as the default, check the others.
|
||||
auto op = instr.substr(0, 4);
|
||||
|
||||
constexpr char BYTE_TAG = 'b';
|
||||
constexpr char HALF_TAG = 'h';
|
||||
constexpr char DOUBLE_WORD_TAG = 'd';
|
||||
constexpr char PAIRED_TAG = 'p';
|
||||
|
||||
// Actual range is 0 to size - 1;
|
||||
if (op.find(BYTE_TAG) != std::string::npos)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if (op.find(HALF_TAG) != std::string::npos)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if (op.find(DOUBLE_WORD_TAG) != std::string::npos ||
|
||||
op.find(PAIRED_TAG) != std::string::npos)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
bool CompareMemoryTargetToTracked(const std::string& instr, const u32 mem_target,
|
||||
const std::set<u32>& mem_tracked)
|
||||
{
|
||||
// This function is hit often and should be optimized.
|
||||
auto it_lower = std::lower_bound(mem_tracked.begin(), mem_tracked.end(), mem_target);
|
||||
|
||||
if (it_lower == mem_tracked.end())
|
||||
return false;
|
||||
else if (*it_lower == mem_target)
|
||||
return true;
|
||||
|
||||
// If the base value doesn't hit, still need to check if longer values overlap.
|
||||
return *it_lower < mem_target + GetMemoryTargetSize(instr);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void CodeTrace::SetRegTracked(const std::string& reg)
|
||||
{
|
||||
m_reg_autotrack.push_back(reg);
|
||||
}
|
||||
|
||||
InstructionAttributes CodeTrace::GetInstructionAttributes(const TraceOutput& instruction) const
|
||||
{
|
||||
auto& system = Core::System::GetInstance();
|
||||
|
||||
// Slower process of breaking down saved instruction. Only used when stepping through code if a
|
||||
// decision has to be made, otherwise used afterwards on a log file.
|
||||
InstructionAttributes tmp_attributes;
|
||||
tmp_attributes.instruction = instruction.instruction;
|
||||
tmp_attributes.address = system.GetPPCState().pc;
|
||||
std::string instr = instruction.instruction;
|
||||
std::smatch match;
|
||||
|
||||
// Convert sp, rtoc, and ps to r1, r2, and F#. ps is handled like a float operation.
|
||||
static const std::regex replace_sp("(\\W)sp");
|
||||
instr = std::regex_replace(instr, replace_sp, "$1r1");
|
||||
static const std::regex replace_rtoc("rtoc");
|
||||
instr = std::regex_replace(instr, replace_rtoc, "r2");
|
||||
static const std::regex replace_ps("(\\W)p(\\d+)");
|
||||
instr = std::regex_replace(instr, replace_ps, "$1f$2");
|
||||
|
||||
// Pull all register numbers out and store them. Limited to Reg0 if ps operation, as ps get
|
||||
// too complicated to track easily.
|
||||
// ex: add r4, r5, r6 -> r4 = Reg0, r5 = Reg1, r6 = Reg2. Reg0 is always the target register.
|
||||
static const std::regex regis(
|
||||
"\\W([rfp]\\d+)[^r^f]*(?:([rf]\\d+))?[^r^f\\d]*(?:([rf]\\d+))?[^r^f\\d]*(?:([rf]\\d+))?",
|
||||
std::regex::optimize);
|
||||
|
||||
if (std::regex_search(instr, match, regis))
|
||||
{
|
||||
tmp_attributes.reg0 = match.str(1);
|
||||
if (match[2].matched)
|
||||
tmp_attributes.reg1 = match.str(2);
|
||||
if (match[3].matched)
|
||||
tmp_attributes.reg2 = match.str(3);
|
||||
if (match[4].matched)
|
||||
tmp_attributes.reg3 = match.str(4);
|
||||
|
||||
if (instruction.memory_target)
|
||||
{
|
||||
tmp_attributes.memory_target = instruction.memory_target;
|
||||
tmp_attributes.memory_target_size = GetMemoryTargetSize(instr);
|
||||
|
||||
if (instr.starts_with("st") || instr.starts_with("psq_s"))
|
||||
tmp_attributes.is_store = true;
|
||||
else
|
||||
tmp_attributes.is_load = true;
|
||||
}
|
||||
}
|
||||
|
||||
return tmp_attributes;
|
||||
}
|
||||
|
||||
TraceOutput CodeTrace::SaveCurrentInstruction(const Core::CPUThreadGuard& guard) const
|
||||
{
|
||||
auto& system = guard.GetSystem();
|
||||
auto& power_pc = system.GetPowerPC();
|
||||
auto& ppc_state = power_pc.GetPPCState();
|
||||
auto& debug_interface = power_pc.GetDebugInterface();
|
||||
|
||||
// Quickly save instruction and memory target for fast logging.
|
||||
TraceOutput output;
|
||||
const std::string instr = debug_interface.Disassemble(&guard, ppc_state.pc);
|
||||
output.instruction = instr;
|
||||
output.address = ppc_state.pc;
|
||||
|
||||
if (IsInstructionLoadStore(output.instruction))
|
||||
output.memory_target = debug_interface.GetMemoryAddressFromInstruction(instr);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
AutoStepResults CodeTrace::AutoStepping(const Core::CPUThreadGuard& guard, bool continue_previous,
|
||||
AutoStop stop_on)
|
||||
{
|
||||
AutoStepResults results;
|
||||
|
||||
if (m_recording)
|
||||
return results;
|
||||
|
||||
TraceOutput pc_instr = SaveCurrentInstruction(guard);
|
||||
const InstructionAttributes instr = GetInstructionAttributes(pc_instr);
|
||||
|
||||
// Not an instruction we should start autostepping from (ie branches).
|
||||
if (instr.reg0.empty() && !continue_previous)
|
||||
return results;
|
||||
|
||||
m_recording = true;
|
||||
|
||||
// Once autostep stops, it can be told to continue running without resetting the tracked
|
||||
// registers and memory.
|
||||
if (!continue_previous)
|
||||
{
|
||||
m_reg_autotrack.clear();
|
||||
m_mem_autotrack.clear();
|
||||
m_reg_autotrack.push_back(instr.reg0);
|
||||
|
||||
// It wouldn't necessarily be wrong to also record the memory of a load operation, as the
|
||||
// value exists there too. May or may not be desirable depending on task. Leaving it out.
|
||||
if (instr.is_store)
|
||||
{
|
||||
const u32 size = GetMemoryTargetSize(instr.instruction);
|
||||
for (u32 i = 0; i < size; i++)
|
||||
m_mem_autotrack.insert(instr.memory_target.value() + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Count is important for feedback on how much work was done.
|
||||
|
||||
HitType hit = HitType::SKIP;
|
||||
HitType stop_condition = HitType::SAVELOAD;
|
||||
|
||||
// Could use bit flags, but I organized it to have decreasing levels of verbosity, so the
|
||||
// less-than comparison ignores what is needed for the current usage.
|
||||
if (stop_on == AutoStop::Always)
|
||||
stop_condition = HitType::SAVELOAD;
|
||||
else if (stop_on == AutoStop::Used)
|
||||
stop_condition = HitType::PASSIVE;
|
||||
else if (stop_on == AutoStop::Changed)
|
||||
stop_condition = HitType::ACTIVE;
|
||||
|
||||
auto& power_pc = guard.GetSystem().GetPowerPC();
|
||||
power_pc.GetBreakPoints().ClearAllTemporary();
|
||||
using clock = std::chrono::steady_clock;
|
||||
clock::time_point timeout = clock::now() + std::chrono::seconds(4);
|
||||
|
||||
PowerPC::CoreMode old_mode = power_pc.GetMode();
|
||||
power_pc.SetMode(PowerPC::CoreMode::Interpreter);
|
||||
|
||||
do
|
||||
{
|
||||
power_pc.SingleStep();
|
||||
|
||||
pc_instr = SaveCurrentInstruction(guard);
|
||||
hit = TraceLogic(pc_instr);
|
||||
results.count += 1;
|
||||
} while (clock::now() < timeout && hit < stop_condition &&
|
||||
!(m_reg_autotrack.empty() && m_mem_autotrack.empty()));
|
||||
|
||||
// Report the timeout to the caller.
|
||||
if (clock::now() >= timeout)
|
||||
results.timed_out = true;
|
||||
|
||||
power_pc.SetMode(old_mode);
|
||||
m_recording = false;
|
||||
|
||||
results.reg_tracked = m_reg_autotrack;
|
||||
results.mem_tracked = m_mem_autotrack;
|
||||
|
||||
// Doesn't currently need to report the hit type to the caller. Denoting when the reg and mem
|
||||
// trackers are both empty is important, as it means our target was overwritten and can no longer
|
||||
// be tracked. Different actions can be taken on a timeout vs empty trackers, so they are reported
|
||||
// individually.
|
||||
return results;
|
||||
}
|
||||
|
||||
HitType CodeTrace::TraceLogic(const TraceOutput& current_instr, bool first_hit)
|
||||
{
|
||||
// Tracks the original value that is in the targeted register or memory through loads, stores,
|
||||
// register moves, and value changes. Also finds when it is used. ps operations are not fully
|
||||
// supported. -ux memory instructions may need special cases.
|
||||
// Should not be called if reg and mem tracked are empty.
|
||||
|
||||
// Using a std::set because it can easily insert the memory range being accessed without
|
||||
// causing duplicates, and quickly erases all members of the memory range without caring if the
|
||||
// element actually exists.
|
||||
|
||||
bool mem_hit = false;
|
||||
if (current_instr.memory_target && !m_mem_autotrack.empty())
|
||||
{
|
||||
mem_hit = CompareMemoryTargetToTracked(current_instr.instruction, *current_instr.memory_target,
|
||||
m_mem_autotrack);
|
||||
}
|
||||
|
||||
// Optimization for tracking a memory target when no registers are being tracked.
|
||||
if (m_reg_autotrack.empty() && !mem_hit)
|
||||
return HitType::SKIP;
|
||||
|
||||
// Break instruction down into parts to be analyzed.
|
||||
const InstructionAttributes instr = GetInstructionAttributes(current_instr);
|
||||
|
||||
// Not an instruction we care about (branches).
|
||||
if (instr.reg0.empty())
|
||||
return HitType::SKIP;
|
||||
|
||||
// The reg_itr will be used later for erasing.
|
||||
auto reg_itr = std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(), instr.reg0);
|
||||
const bool match_reg123 =
|
||||
(!instr.reg1.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
|
||||
instr.reg1) != m_reg_autotrack.end()) ||
|
||||
(!instr.reg2.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
|
||||
instr.reg2) != m_reg_autotrack.end()) ||
|
||||
(!instr.reg3.empty() && std::find(m_reg_autotrack.begin(), m_reg_autotrack.end(),
|
||||
instr.reg3) != m_reg_autotrack.end());
|
||||
const bool match_reg0 = reg_itr != m_reg_autotrack.end();
|
||||
|
||||
if (!match_reg0 && !match_reg123 && !mem_hit)
|
||||
return HitType::SKIP;
|
||||
|
||||
// Checks if the intstruction is a type that needs special handling.
|
||||
const auto CompareInstruction = [](std::string_view instruction, const auto& type_compare) {
|
||||
return std::any_of(type_compare.begin(), type_compare.end(),
|
||||
[&instruction](std::string_view s) { return instruction.starts_with(s); });
|
||||
};
|
||||
|
||||
// Exclusions from updating tracking logic. mt operations are too complex and specialized.
|
||||
// Combiner used later.
|
||||
static const std::array<std::string_view, 3> exclude{"dc", "ic", "mt"};
|
||||
static const std::array<std::string_view, 2> compare{"c", "fc"};
|
||||
|
||||
// rlwimi, at least, can preserve parts of the target register. Not sure if rldimi can too or if
|
||||
// there are any others like this.
|
||||
static const std::array<std::string_view, 1> combiner{"rlwimi"};
|
||||
|
||||
static const std::array<std::string_view, 2> mover{"mr", "fmr"};
|
||||
|
||||
// Link register for when r0 gets overwritten
|
||||
if (instr.instruction.starts_with("mflr") && match_reg0)
|
||||
{
|
||||
m_reg_autotrack.erase(reg_itr);
|
||||
return HitType::OVERWRITE;
|
||||
}
|
||||
if (instr.instruction.starts_with("mtlr") && match_reg0)
|
||||
{
|
||||
// LR is not something tracked
|
||||
return HitType::MOVED;
|
||||
}
|
||||
|
||||
if (CompareInstruction(instr.instruction, exclude))
|
||||
return HitType::SKIP;
|
||||
else if (CompareInstruction(instr.instruction, compare))
|
||||
return HitType::PASSIVE;
|
||||
else if (match_reg123 && !match_reg0 && (instr.is_store || instr.is_load))
|
||||
return HitType::POINTER;
|
||||
|
||||
// Update tracking logic. At this point a memory or register hit happened.
|
||||
// Save/Load
|
||||
if (instr.memory_target)
|
||||
{
|
||||
if (mem_hit)
|
||||
{
|
||||
// If hit a tracked memory. Load -> Add register to tracked. Store -> Remove tracked memory
|
||||
// if overwritten.
|
||||
|
||||
if (instr.is_load && !match_reg0)
|
||||
{
|
||||
m_reg_autotrack.push_back(instr.reg0);
|
||||
return HitType::SAVELOAD;
|
||||
}
|
||||
else if (instr.is_store && !match_reg0)
|
||||
{
|
||||
// On First Hit it wouldn't necessarily be wrong to track the register, which contains the
|
||||
// same value. A matter of preference.
|
||||
if (first_hit)
|
||||
return HitType::SAVELOAD;
|
||||
|
||||
for (u32 i = 0; i < instr.memory_target_size; i++)
|
||||
m_mem_autotrack.erase(*instr.memory_target + i);
|
||||
|
||||
return HitType::OVERWRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If reg0 and store/load are both already tracked, do nothing.
|
||||
return HitType::SAVELOAD;
|
||||
}
|
||||
}
|
||||
else if (instr.is_store && match_reg0)
|
||||
{
|
||||
// If store to untracked memory, then track memory.
|
||||
for (u32 i = 0; i < instr.memory_target_size; i++)
|
||||
m_mem_autotrack.insert(*instr.memory_target + i);
|
||||
|
||||
return HitType::SAVELOAD;
|
||||
}
|
||||
else if (instr.is_load && match_reg0)
|
||||
{
|
||||
// Not wrong to track load memory_target here. Preference.
|
||||
if (first_hit)
|
||||
return HitType::SAVELOAD;
|
||||
|
||||
// If untracked load is overwriting tracked register, then remove register
|
||||
m_reg_autotrack.erase(reg_itr);
|
||||
return HitType::OVERWRITE;
|
||||
}
|
||||
}
|
||||
else if (!match_reg0 && !match_reg123)
|
||||
{
|
||||
// Skip if no matches. Happens most often.
|
||||
return HitType::SKIP;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If tracked register data is being stored in a new register, save new register.
|
||||
if (match_reg123 && !match_reg0)
|
||||
{
|
||||
m_reg_autotrack.push_back(instr.reg0);
|
||||
|
||||
// This should include any instruction that can reach this point and is not ACTIVE. Can only
|
||||
// think of mr at this time.
|
||||
if (CompareInstruction(instr.instruction, mover))
|
||||
return HitType::MOVED;
|
||||
|
||||
return HitType::ACTIVE;
|
||||
}
|
||||
// If tracked register is overwritten, stop tracking.
|
||||
else if (match_reg0 && !match_reg123)
|
||||
{
|
||||
if (CompareInstruction(instr.instruction, combiner) || first_hit)
|
||||
return HitType::UPDATED;
|
||||
|
||||
m_reg_autotrack.erase(reg_itr);
|
||||
return HitType::OVERWRITE;
|
||||
}
|
||||
else if (match_reg0 && match_reg123)
|
||||
{
|
||||
// Or moved
|
||||
return HitType::UPDATED;
|
||||
}
|
||||
}
|
||||
|
||||
// Should not reach this
|
||||
return HitType::SKIP;
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class CPUThreadGuard;
|
||||
}
|
||||
|
||||
struct InstructionAttributes
|
||||
{
|
||||
u32 address = 0;
|
||||
std::string instruction = "";
|
||||
std::string reg0 = "";
|
||||
std::string reg1 = "";
|
||||
std::string reg2 = "";
|
||||
std::string reg3 = "";
|
||||
std::optional<u32> memory_target = std::nullopt;
|
||||
u32 memory_target_size = 4;
|
||||
bool is_store = false;
|
||||
bool is_load = false;
|
||||
};
|
||||
|
||||
struct TraceOutput
|
||||
{
|
||||
u32 address = 0;
|
||||
std::optional<u32> memory_target = std::nullopt;
|
||||
std::string instruction;
|
||||
};
|
||||
|
||||
struct AutoStepResults
|
||||
{
|
||||
std::vector<std::string> reg_tracked;
|
||||
std::set<u32> mem_tracked;
|
||||
u32 count = 0;
|
||||
bool timed_out = false;
|
||||
bool trackers_empty = false;
|
||||
};
|
||||
|
||||
enum class HitType : u32
|
||||
{
|
||||
SKIP = (1 << 0), // Not a hit
|
||||
OVERWRITE = (1 << 1), // Tracked value gets overwritten by untracked. Typically skipped.
|
||||
MOVED = (1 << 2), // Target duplicated to another register, unchanged.
|
||||
SAVELOAD = (1 << 3), // Target saved or loaded. Priority over Pointer.
|
||||
POINTER = (1 << 4), // Target used as pointer/offset for save or load
|
||||
PASSIVE = (1 << 5), // Conditional, etc, but not pointer. Unchanged
|
||||
ACTIVE = (1 << 6), // Math, etc. Changed.
|
||||
UPDATED = (1 << 7), // Masked or math without changing register.
|
||||
};
|
||||
|
||||
class CodeTrace
|
||||
{
|
||||
public:
|
||||
enum class AutoStop
|
||||
{
|
||||
Always,
|
||||
Used,
|
||||
Changed
|
||||
};
|
||||
|
||||
void SetRegTracked(const std::string& reg);
|
||||
AutoStepResults AutoStepping(const Core::CPUThreadGuard& guard, bool continue_previous = false,
|
||||
AutoStop stop_on = AutoStop::Always);
|
||||
|
||||
private:
|
||||
InstructionAttributes GetInstructionAttributes(const TraceOutput& line) const;
|
||||
TraceOutput SaveCurrentInstruction(const Core::CPUThreadGuard& guard) const;
|
||||
HitType TraceLogic(const TraceOutput& current_instr, bool first_hit = false);
|
||||
|
||||
bool m_recording = false;
|
||||
std::vector<std::string> m_reg_autotrack;
|
||||
std::set<u32> m_mem_autotrack;
|
||||
};
|
|
@ -30,6 +30,7 @@ public:
|
|||
void SetCookies(const std::string& cookies);
|
||||
void UseIPv4();
|
||||
void FollowRedirects(long max);
|
||||
s32 GetLastResponseCode();
|
||||
Response Fetch(const std::string& url, Method method, const Headers& headers, const u8* payload,
|
||||
size_t size, AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only);
|
||||
|
||||
|
@ -76,6 +77,11 @@ std::string HttpRequest::EscapeComponent(const std::string& string)
|
|||
return m_impl->EscapeComponent(string);
|
||||
}
|
||||
|
||||
s32 HttpRequest::GetLastResponseCode() const
|
||||
{
|
||||
return m_impl->GetLastResponseCode();
|
||||
}
|
||||
|
||||
HttpRequest::Response HttpRequest::Get(const std::string& url, const Headers& headers,
|
||||
AllowedReturnCodes codes)
|
||||
{
|
||||
|
@ -144,6 +150,13 @@ bool HttpRequest::Impl::IsValid() const
|
|||
return m_curl != nullptr;
|
||||
}
|
||||
|
||||
s32 HttpRequest::Impl::GetLastResponseCode()
|
||||
{
|
||||
s32 response_code{};
|
||||
curl_easy_getinfo(m_curl.get(), CURLINFO_RESPONSE_CODE, &response_code);
|
||||
return response_code;
|
||||
}
|
||||
|
||||
void HttpRequest::Impl::SetCookies(const std::string& cookies)
|
||||
{
|
||||
curl_easy_setopt(m_curl.get(), CURLOPT_COOKIE, cookies.c_str());
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
void SetCookies(const std::string& cookies);
|
||||
void UseIPv4();
|
||||
void FollowRedirects(long max = 1);
|
||||
s32 GetLastResponseCode() const;
|
||||
std::string EscapeComponent(const std::string& string);
|
||||
Response Get(const std::string& url, const Headers& headers = {},
|
||||
AllowedReturnCodes codes = AllowedReturnCodes::Ok_Only);
|
||||
|
|
|
@ -36,7 +36,7 @@ static op_agent_t s_agent = nullptr;
|
|||
|
||||
static File::IOFile s_perf_map_file;
|
||||
|
||||
namespace JitRegister
|
||||
namespace Common::JitRegister
|
||||
{
|
||||
static bool s_is_enabled = false;
|
||||
|
||||
|
@ -108,4 +108,4 @@ void Register(const void* base_address, u32 code_size, const std::string& symbol
|
|||
const auto entry = fmt::format("{} {:x} {}\n", fmt::ptr(base_address), code_size, symbol_name);
|
||||
s_perf_map_file.WriteBytes(entry.data(), entry.size());
|
||||
}
|
||||
} // namespace JitRegister
|
||||
} // namespace Common::JitRegister
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace JitRegister
|
||||
namespace Common::JitRegister
|
||||
{
|
||||
void Init(const std::string& perf_dir);
|
||||
void Shutdown();
|
||||
|
@ -30,4 +30,4 @@ inline void Register(const void* start, const void* end, fmt::format_string<Args
|
|||
u32 code_size = (u32)((const char*)end - (const char*)start);
|
||||
Register(start, code_size, fmt::format(format, std::forward<Args>(args)...));
|
||||
}
|
||||
} // namespace JitRegister
|
||||
} // namespace Common::JitRegister
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
@ -34,8 +35,10 @@ public:
|
|||
/// CreateView() and ReleaseView(). Used to make a mappable region for emulated memory.
|
||||
///
|
||||
/// @param size The amount of bytes that should be allocated in this region.
|
||||
/// @param base_name A base name for the shared memory region, if applicable for this platform.
|
||||
/// Will be extended with the process ID.
|
||||
///
|
||||
void GrabSHMSegment(size_t size);
|
||||
void GrabSHMSegment(size_t size, std::string_view base_name);
|
||||
|
||||
///
|
||||
/// Release the memory segment previously allocated with GrabSHMSegment().
|
||||
|
@ -113,13 +116,9 @@ private:
|
|||
void* m_address_VirtualAlloc2 = nullptr;
|
||||
void* m_address_MapViewOfFile3 = nullptr;
|
||||
#else
|
||||
#ifdef ANDROID
|
||||
int fd;
|
||||
#else
|
||||
int m_shm_fd;
|
||||
void* m_reserved_region;
|
||||
std::size_t m_reserved_region_size;
|
||||
#endif
|
||||
int m_shm_fd = 0;
|
||||
void* m_reserved_region = nullptr;
|
||||
std::size_t m_reserved_region_size = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/ashmem.h>
|
||||
|
@ -62,26 +64,36 @@ static int AshmemCreateFileMapping(const char* name, size_t size)
|
|||
MemArena::MemArena() = default;
|
||||
MemArena::~MemArena() = default;
|
||||
|
||||
void MemArena::GrabSHMSegment(size_t size)
|
||||
void MemArena::GrabSHMSegment(size_t size, std::string_view base_name)
|
||||
{
|
||||
fd = AshmemCreateFileMapping(("dolphin-emu." + std::to_string(getpid())).c_str(), size);
|
||||
if (fd < 0)
|
||||
const std::string name = fmt::format("{}.{}", base_name, getpid());
|
||||
m_shm_fd = AshmemCreateFileMapping(name.c_str(), size);
|
||||
if (m_shm_fd < 0)
|
||||
NOTICE_LOG_FMT(MEMMAP, "Ashmem allocation failed");
|
||||
}
|
||||
|
||||
void MemArena::ReleaseSHMSegment()
|
||||
{
|
||||
close(fd);
|
||||
close(m_shm_fd);
|
||||
}
|
||||
|
||||
void* MemArena::CreateView(s64 offset, size_t size)
|
||||
{
|
||||
return MapInMemoryRegion(offset, size, nullptr);
|
||||
void* retval = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_shm_fd, offset);
|
||||
if (retval == MAP_FAILED)
|
||||
{
|
||||
NOTICE_LOG_FMT(MEMMAP, "mmap failed");
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
void MemArena::ReleaseView(void* view, size_t size)
|
||||
{
|
||||
UnmapFromMemoryRegion(view, size);
|
||||
munmap(view, size);
|
||||
}
|
||||
|
||||
u8* MemArena::ReserveMemoryRegion(size_t memory_size)
|
||||
|
@ -96,19 +108,23 @@ u8* MemArena::ReserveMemoryRegion(size_t memory_size)
|
|||
PanicAlertFmt("Failed to map enough memory space: {}", LastStrerrorString());
|
||||
return nullptr;
|
||||
}
|
||||
munmap(base, memory_size);
|
||||
m_reserved_region = base;
|
||||
m_reserved_region_size = memory_size;
|
||||
return static_cast<u8*>(base);
|
||||
}
|
||||
|
||||
void MemArena::ReleaseMemoryRegion()
|
||||
{
|
||||
if (m_reserved_region)
|
||||
{
|
||||
munmap(m_reserved_region, m_reserved_region_size);
|
||||
m_reserved_region = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void* MemArena::MapInMemoryRegion(s64 offset, size_t size, void* base)
|
||||
{
|
||||
void* retval = mmap(base, size, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset);
|
||||
|
||||
void* retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, m_shm_fd, offset);
|
||||
if (retval == MAP_FAILED)
|
||||
{
|
||||
NOTICE_LOG_FMT(MEMMAP, "mmap failed");
|
||||
|
@ -122,6 +138,8 @@ void* MemArena::MapInMemoryRegion(s64 offset, size_t size, void* base)
|
|||
|
||||
void MemArena::UnmapFromMemoryRegion(void* view, size_t size)
|
||||
{
|
||||
munmap(view, size);
|
||||
void* retval = mmap(view, size, PROT_NONE, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
|
||||
if (retval == MAP_FAILED)
|
||||
NOTICE_LOG_FMT(MEMMAP, "mmap failed");
|
||||
}
|
||||
} // namespace Common
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
@ -25,9 +27,9 @@ namespace Common
|
|||
MemArena::MemArena() = default;
|
||||
MemArena::~MemArena() = default;
|
||||
|
||||
void MemArena::GrabSHMSegment(size_t size)
|
||||
void MemArena::GrabSHMSegment(size_t size, std::string_view base_name)
|
||||
{
|
||||
const std::string file_name = "/dolphin-emu." + std::to_string(getpid());
|
||||
const std::string file_name = fmt::format("/{}.{}", base_name, getpid());
|
||||
m_shm_fd = shm_open(file_name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
|
||||
if (m_shm_fd == -1)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
|
@ -97,11 +99,22 @@ MemArena::~MemArena()
|
|||
ReleaseSHMSegment();
|
||||
}
|
||||
|
||||
void MemArena::GrabSHMSegment(size_t size)
|
||||
static DWORD GetHighDWORD(u64 value)
|
||||
{
|
||||
const std::string name = "dolphin-emu." + std::to_string(GetCurrentProcessId());
|
||||
m_memory_handle = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
|
||||
static_cast<DWORD>(size), UTF8ToTStr(name).c_str());
|
||||
return static_cast<DWORD>(value >> 32);
|
||||
}
|
||||
|
||||
static DWORD GetLowDWORD(u64 value)
|
||||
{
|
||||
return static_cast<DWORD>(value);
|
||||
}
|
||||
|
||||
void MemArena::GrabSHMSegment(size_t size, std::string_view base_name)
|
||||
{
|
||||
const std::string name = fmt::format("{}.{}", base_name, GetCurrentProcessId());
|
||||
m_memory_handle =
|
||||
CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, GetHighDWORD(size),
|
||||
GetLowDWORD(size), UTF8ToTStr(name).c_str());
|
||||
}
|
||||
|
||||
void MemArena::ReleaseSHMSegment()
|
||||
|
@ -114,8 +127,9 @@ void MemArena::ReleaseSHMSegment()
|
|||
|
||||
void* MemArena::CreateView(s64 offset, size_t size)
|
||||
{
|
||||
return MapViewOfFileEx(m_memory_handle, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size,
|
||||
nullptr);
|
||||
const u64 off = static_cast<u64>(offset);
|
||||
return MapViewOfFileEx(m_memory_handle, FILE_MAP_ALL_ACCESS, GetHighDWORD(off), GetLowDWORD(off),
|
||||
size, nullptr);
|
||||
}
|
||||
|
||||
void MemArena::ReleaseView(void* view, size_t size)
|
||||
|
|
|
@ -77,7 +77,7 @@ std::string HexDump(const u8* data, size_t size)
|
|||
if (row_start + i < size)
|
||||
{
|
||||
char c = static_cast<char>(data[row_start + i]);
|
||||
out += IsPrintableCharacter(c) ? c : '.';
|
||||
out += Common::IsPrintableCharacter(c) ? c : '.';
|
||||
}
|
||||
}
|
||||
out += "\n";
|
||||
|
@ -547,7 +547,7 @@ std::string CodeTo(const char* tocode, const char* fromcode, std::basic_string_v
|
|||
while (src_bytes != 0)
|
||||
{
|
||||
size_t const iconv_result =
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#if defined(__NetBSD__)
|
||||
iconv(conv_desc, reinterpret_cast<const char**>(&src_buffer), &src_bytes, &dst_buffer,
|
||||
&dst_bytes);
|
||||
#else
|
||||
|
@ -656,6 +656,8 @@ std::string PathToString(const std::filesystem::path& path)
|
|||
#endif
|
||||
}
|
||||
|
||||
namespace Common
|
||||
{
|
||||
#ifdef _WIN32
|
||||
std::vector<std::string> CommandLineToUtf8Argv(const wchar_t* command_line)
|
||||
{
|
||||
|
@ -693,8 +695,6 @@ std::string GetEscapedHtml(std::string html)
|
|||
return html;
|
||||
}
|
||||
|
||||
namespace Common
|
||||
{
|
||||
void ToLower(std::string* str)
|
||||
{
|
||||
std::transform(str->begin(), str->end(), str->begin(), [](char c) { return Common::ToLower(c); });
|
||||
|
|
|
@ -211,6 +211,34 @@ inline std::string UTF8ToTStr(std::string_view str)
|
|||
std::filesystem::path StringToPath(std::string_view path);
|
||||
std::string PathToString(const std::filesystem::path& path);
|
||||
|
||||
namespace Common
|
||||
{
|
||||
/// Returns whether a character is printable, i.e. whether 0x20 <= c <= 0x7e is true.
|
||||
/// Use this instead of calling std::isprint directly to ensure
|
||||
/// the C locale is being used and to avoid possibly undefined behaviour.
|
||||
inline bool IsPrintableCharacter(char c)
|
||||
{
|
||||
return std::isprint(c, std::locale::classic());
|
||||
}
|
||||
|
||||
/// Returns whether a character is a letter, i.e. whether 'a' <= c <= 'z' || 'A' <= c <= 'Z'
|
||||
/// is true. Use this instead of calling std::isalpha directly to ensure
|
||||
/// the C locale is being used and to avoid possibly undefined behaviour.
|
||||
inline bool IsAlpha(char c)
|
||||
{
|
||||
return std::isalpha(c, std::locale::classic());
|
||||
}
|
||||
|
||||
inline char ToLower(char ch)
|
||||
{
|
||||
return std::tolower(ch, std::locale::classic());
|
||||
}
|
||||
|
||||
inline char ToUpper(char ch)
|
||||
{
|
||||
return std::toupper(ch, std::locale::classic());
|
||||
}
|
||||
|
||||
// Thousand separator. Turns 12345678 into 12,345,678
|
||||
template <typename I>
|
||||
std::string ThousandSeparate(I value, int spaces = 0)
|
||||
|
@ -230,38 +258,12 @@ std::string ThousandSeparate(I value, int spaces = 0)
|
|||
#endif
|
||||
}
|
||||
|
||||
/// Returns whether a character is printable, i.e. whether 0x20 <= c <= 0x7e is true.
|
||||
/// Use this instead of calling std::isprint directly to ensure
|
||||
/// the C locale is being used and to avoid possibly undefined behaviour.
|
||||
inline bool IsPrintableCharacter(char c)
|
||||
{
|
||||
return std::isprint(c, std::locale::classic());
|
||||
}
|
||||
|
||||
/// Returns whether a character is a letter, i.e. whether 'a' <= c <= 'z' || 'A' <= c <= 'Z'
|
||||
/// is true. Use this instead of calling std::isalpha directly to ensure
|
||||
/// the C locale is being used and to avoid possibly undefined behaviour.
|
||||
inline bool IsAlpha(char c)
|
||||
{
|
||||
return std::isalpha(c, std::locale::classic());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
std::vector<std::string> CommandLineToUtf8Argv(const wchar_t* command_line);
|
||||
#endif
|
||||
|
||||
std::string GetEscapedHtml(std::string html);
|
||||
|
||||
namespace Common
|
||||
{
|
||||
inline char ToLower(char ch)
|
||||
{
|
||||
return std::tolower(ch, std::locale::classic());
|
||||
}
|
||||
inline char ToUpper(char ch)
|
||||
{
|
||||
return std::toupper(ch, std::locale::classic());
|
||||
}
|
||||
void ToLower(std::string* str);
|
||||
void ToUpper(std::string* str);
|
||||
bool CaseInsensitiveEquals(std::string_view a, std::string_view b);
|
||||
|
|
|
@ -37,6 +37,9 @@ struct Symbol
|
|||
Data,
|
||||
};
|
||||
|
||||
Symbol() = default;
|
||||
explicit Symbol(const std::string& name) { Rename(name); }
|
||||
|
||||
void Rename(const std::string& symbol_name);
|
||||
|
||||
std::string name;
|
||||
|
|
|
@ -200,8 +200,8 @@ std::tuple<void*, size_t> GetCurrentThreadStack()
|
|||
stack_t stack;
|
||||
pthread_stackseg_np(self, &stack);
|
||||
|
||||
stack_addr = reinterpret_cast<u8*>(stack->ss_sp) - stack->ss_size;
|
||||
stack_size = stack->ss_size;
|
||||
stack_addr = reinterpret_cast<u8*>(stack.ss_sp) - stack.ss_size;
|
||||
stack_size = stack.ss_size;
|
||||
#else
|
||||
pthread_attr_t attr;
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include "Common/Random.h"
|
||||
#include "Core/NetPlayProto.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
TraversalClient::TraversalClient(ENetHost* netHost, const std::string& server, const u16 port)
|
||||
: m_NetHost(netHost), m_Server(server), m_port(port)
|
||||
{
|
||||
|
@ -304,7 +306,7 @@ int ENET_CALLBACK TraversalClient::InterceptCallback(ENetHost* host, ENetEvent*
|
|||
}
|
||||
|
||||
std::unique_ptr<TraversalClient> g_TraversalClient;
|
||||
Common::ENet::ENetHostPtr g_MainNetHost;
|
||||
ENet::ENetHostPtr g_MainNetHost;
|
||||
|
||||
// The settings at the previous TraversalClient reset - notably, we
|
||||
// need to know not just what port it's on, but whether it was
|
||||
|
@ -348,3 +350,4 @@ void ReleaseTraversalClient()
|
|||
g_TraversalClient.reset();
|
||||
g_MainNetHost.reset();
|
||||
}
|
||||
} // namespace Common
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "Common/Thread.h"
|
||||
#include "Common/TraversalProto.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
class TraversalClientClient
|
||||
{
|
||||
public:
|
||||
|
@ -90,9 +92,12 @@ private:
|
|||
u16 m_port;
|
||||
u32 m_PingTime = 0;
|
||||
};
|
||||
|
||||
extern std::unique_ptr<TraversalClient> g_TraversalClient;
|
||||
// the NetHost connected to the TraversalClient.
|
||||
extern Common::ENet::ENetHostPtr g_MainNetHost;
|
||||
extern ENet::ENetHostPtr g_MainNetHost;
|
||||
|
||||
// Create g_TraversalClient and g_MainNetHost if necessary.
|
||||
bool EnsureTraversalClient(const std::string& server, u16 server_port, u16 listen_port = 0);
|
||||
void ReleaseTraversalClient();
|
||||
} // namespace Common
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include <cstddef>
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
constexpr size_t NETPLAY_CODE_SIZE = 8;
|
||||
using TraversalHostId = std::array<char, NETPLAY_CODE_SIZE>;
|
||||
using TraversalRequestId = u64;
|
||||
|
@ -92,3 +94,4 @@ struct TraversalPacket
|
|||
};
|
||||
};
|
||||
#pragma pack(pop)
|
||||
} // namespace Common
|
||||
|
|
|
@ -31,8 +31,8 @@ static u64 currentTime;
|
|||
|
||||
struct OutgoingPacketInfo
|
||||
{
|
||||
TraversalPacket packet;
|
||||
TraversalRequestId misc;
|
||||
Common::TraversalPacket packet;
|
||||
Common::TraversalRequestId misc;
|
||||
sockaddr_in6 dest;
|
||||
int tries;
|
||||
u64 sendTime;
|
||||
|
@ -104,9 +104,9 @@ V* EvictSet(std::unordered_map<K, EvictEntry<V>>& map, const K& key)
|
|||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<TraversalHostId>
|
||||
struct hash<Common::TraversalHostId>
|
||||
{
|
||||
size_t operator()(const TraversalHostId& id) const
|
||||
size_t operator()(const Common::TraversalHostId& id) const noexcept
|
||||
{
|
||||
auto p = (u32*)id.data();
|
||||
return p[0] ^ ((p[1] << 13) | (p[1] >> 19));
|
||||
|
@ -114,11 +114,15 @@ struct hash<TraversalHostId>
|
|||
};
|
||||
} // namespace std
|
||||
|
||||
static int sock;
|
||||
static std::unordered_map<TraversalRequestId, OutgoingPacketInfo> outgoingPackets;
|
||||
static std::unordered_map<TraversalHostId, EvictEntry<TraversalInetAddress>> connectedClients;
|
||||
using ConnectedClients =
|
||||
std::unordered_map<Common::TraversalHostId, EvictEntry<Common::TraversalInetAddress>>;
|
||||
using OutgoingPackets = std::unordered_map<Common::TraversalRequestId, OutgoingPacketInfo>;
|
||||
|
||||
static TraversalInetAddress MakeInetAddress(const sockaddr_in6& addr)
|
||||
static int sock;
|
||||
static OutgoingPackets outgoingPackets;
|
||||
static ConnectedClients connectedClients;
|
||||
|
||||
static Common::TraversalInetAddress MakeInetAddress(const sockaddr_in6& addr)
|
||||
{
|
||||
if (addr.sin6_family != AF_INET6)
|
||||
{
|
||||
|
@ -126,7 +130,7 @@ static TraversalInetAddress MakeInetAddress(const sockaddr_in6& addr)
|
|||
exit(1);
|
||||
}
|
||||
u32* words = (u32*)addr.sin6_addr.s6_addr;
|
||||
TraversalInetAddress result = {0};
|
||||
Common::TraversalInetAddress result = {};
|
||||
if (words[0] == 0 && words[1] == 0 && words[2] == 0xffff0000)
|
||||
{
|
||||
result.isIPV6 = false;
|
||||
|
@ -141,7 +145,7 @@ static TraversalInetAddress MakeInetAddress(const sockaddr_in6& addr)
|
|||
return result;
|
||||
}
|
||||
|
||||
static sockaddr_in6 MakeSinAddr(const TraversalInetAddress& addr)
|
||||
static sockaddr_in6 MakeSinAddr(const Common::TraversalInetAddress& addr)
|
||||
{
|
||||
sockaddr_in6 result;
|
||||
#ifdef SIN6_LEN
|
||||
|
@ -166,7 +170,7 @@ static sockaddr_in6 MakeSinAddr(const TraversalInetAddress& addr)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void GetRandomHostId(TraversalHostId* hostId)
|
||||
static void GetRandomHostId(Common::TraversalHostId* hostId)
|
||||
{
|
||||
char buf[9];
|
||||
const u32 num = Common::Random::GenerateValue<u32>();
|
||||
|
@ -185,7 +189,7 @@ static const char* SenderName(sockaddr_in6* addr)
|
|||
static void TrySend(const void* buffer, size_t size, sockaddr_in6* addr)
|
||||
{
|
||||
#if DEBUG
|
||||
const auto* packet = static_cast<const TraversalPacket*>(buffer);
|
||||
const auto* packet = static_cast<const Common::TraversalPacket*>(buffer);
|
||||
printf("-> %d %llu %s\n", static_cast<int>(packet->type),
|
||||
static_cast<long long>(packet->requestId), SenderName(addr));
|
||||
#endif
|
||||
|
@ -195,16 +199,17 @@ static void TrySend(const void* buffer, size_t size, sockaddr_in6* addr)
|
|||
}
|
||||
}
|
||||
|
||||
static TraversalPacket* AllocPacket(const sockaddr_in6& dest, TraversalRequestId misc = 0)
|
||||
static Common::TraversalPacket* AllocPacket(const sockaddr_in6& dest,
|
||||
Common::TraversalRequestId misc = 0)
|
||||
{
|
||||
TraversalRequestId requestId;
|
||||
Common::TraversalRequestId requestId{};
|
||||
Common::Random::Generate(&requestId, sizeof(requestId));
|
||||
OutgoingPacketInfo* info = &outgoingPackets[requestId];
|
||||
info->dest = dest;
|
||||
info->misc = misc;
|
||||
info->tries = 0;
|
||||
info->sendTime = currentTime;
|
||||
TraversalPacket* result = &info->packet;
|
||||
Common::TraversalPacket* result = &info->packet;
|
||||
memset(result, 0, sizeof(*result));
|
||||
result->requestId = requestId;
|
||||
return result;
|
||||
|
@ -219,7 +224,7 @@ static void SendPacket(OutgoingPacketInfo* info)
|
|||
|
||||
static void ResendPackets()
|
||||
{
|
||||
std::vector<std::pair<TraversalInetAddress, TraversalRequestId>> todoFailures;
|
||||
std::vector<std::pair<Common::TraversalInetAddress, Common::TraversalRequestId>> todoFailures;
|
||||
todoFailures.clear();
|
||||
for (auto it = outgoingPackets.begin(); it != outgoingPackets.end();)
|
||||
{
|
||||
|
@ -228,7 +233,7 @@ static void ResendPackets()
|
|||
{
|
||||
if (info->tries >= NUMBER_OF_TRIES)
|
||||
{
|
||||
if (info->packet.type == TraversalPacketType::PleaseSendPacket)
|
||||
if (info->packet.type == Common::TraversalPacketType::PleaseSendPacket)
|
||||
{
|
||||
todoFailures.push_back(std::make_pair(info->packet.pleaseSendPacket.address, info->misc));
|
||||
}
|
||||
|
@ -245,14 +250,14 @@ static void ResendPackets()
|
|||
|
||||
for (const auto& p : todoFailures)
|
||||
{
|
||||
TraversalPacket* fail = AllocPacket(MakeSinAddr(p.first));
|
||||
fail->type = TraversalPacketType::ConnectFailed;
|
||||
Common::TraversalPacket* fail = AllocPacket(MakeSinAddr(p.first));
|
||||
fail->type = Common::TraversalPacketType::ConnectFailed;
|
||||
fail->connectFailed.requestId = p.second;
|
||||
fail->connectFailed.reason = TraversalConnectFailedReason::ClientDidntRespond;
|
||||
fail->connectFailed.reason = Common::TraversalConnectFailedReason::ClientDidntRespond;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandlePacket(TraversalPacket* packet, sockaddr_in6* addr)
|
||||
static void HandlePacket(Common::TraversalPacket* packet, sockaddr_in6* addr)
|
||||
{
|
||||
#if DEBUG
|
||||
printf("<- %d %llu %s\n", static_cast<int>(packet->type),
|
||||
|
@ -261,7 +266,7 @@ static void HandlePacket(TraversalPacket* packet, sockaddr_in6* addr)
|
|||
bool packetOk = true;
|
||||
switch (packet->type)
|
||||
{
|
||||
case TraversalPacketType::Ack:
|
||||
case Common::TraversalPacketType::Ack:
|
||||
{
|
||||
auto it = outgoingPackets.find(packet->requestId);
|
||||
if (it == outgoingPackets.end())
|
||||
|
@ -269,42 +274,42 @@ static void HandlePacket(TraversalPacket* packet, sockaddr_in6* addr)
|
|||
|
||||
OutgoingPacketInfo* info = &it->second;
|
||||
|
||||
if (info->packet.type == TraversalPacketType::PleaseSendPacket)
|
||||
if (info->packet.type == Common::TraversalPacketType::PleaseSendPacket)
|
||||
{
|
||||
TraversalPacket* ready = AllocPacket(MakeSinAddr(info->packet.pleaseSendPacket.address));
|
||||
auto* ready = AllocPacket(MakeSinAddr(info->packet.pleaseSendPacket.address));
|
||||
if (packet->ack.ok)
|
||||
{
|
||||
ready->type = TraversalPacketType::ConnectReady;
|
||||
ready->type = Common::TraversalPacketType::ConnectReady;
|
||||
ready->connectReady.requestId = info->misc;
|
||||
ready->connectReady.address = MakeInetAddress(info->dest);
|
||||
}
|
||||
else
|
||||
{
|
||||
ready->type = TraversalPacketType::ConnectFailed;
|
||||
ready->type = Common::TraversalPacketType::ConnectFailed;
|
||||
ready->connectFailed.requestId = info->misc;
|
||||
ready->connectFailed.reason = TraversalConnectFailedReason::ClientFailure;
|
||||
ready->connectFailed.reason = Common::TraversalConnectFailedReason::ClientFailure;
|
||||
}
|
||||
}
|
||||
|
||||
outgoingPackets.erase(it);
|
||||
break;
|
||||
}
|
||||
case TraversalPacketType::Ping:
|
||||
case Common::TraversalPacketType::Ping:
|
||||
{
|
||||
auto r = EvictFind(connectedClients, packet->ping.hostId, true);
|
||||
packetOk = r.found;
|
||||
break;
|
||||
}
|
||||
case TraversalPacketType::HelloFromClient:
|
||||
case Common::TraversalPacketType::HelloFromClient:
|
||||
{
|
||||
u8 ok = packet->helloFromClient.protoVersion <= TraversalProtoVersion;
|
||||
TraversalPacket* reply = AllocPacket(*addr);
|
||||
reply->type = TraversalPacketType::HelloFromServer;
|
||||
u8 ok = packet->helloFromClient.protoVersion <= Common::TraversalProtoVersion;
|
||||
Common::TraversalPacket* reply = AllocPacket(*addr);
|
||||
reply->type = Common::TraversalPacketType::HelloFromServer;
|
||||
reply->helloFromServer.ok = ok;
|
||||
if (ok)
|
||||
{
|
||||
TraversalHostId hostId;
|
||||
TraversalInetAddress* iaddr;
|
||||
Common::TraversalHostId hostId{};
|
||||
Common::TraversalInetAddress* iaddr{};
|
||||
// not that there is any significant change of
|
||||
// duplication, but...
|
||||
GetRandomHostId(&hostId);
|
||||
|
@ -325,21 +330,21 @@ static void HandlePacket(TraversalPacket* packet, sockaddr_in6* addr)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case TraversalPacketType::ConnectPlease:
|
||||
case Common::TraversalPacketType::ConnectPlease:
|
||||
{
|
||||
TraversalHostId& hostId = packet->connectPlease.hostId;
|
||||
Common::TraversalHostId& hostId = packet->connectPlease.hostId;
|
||||
auto r = EvictFind(connectedClients, hostId);
|
||||
if (!r.found)
|
||||
{
|
||||
TraversalPacket* reply = AllocPacket(*addr);
|
||||
reply->type = TraversalPacketType::ConnectFailed;
|
||||
Common::TraversalPacket* reply = AllocPacket(*addr);
|
||||
reply->type = Common::TraversalPacketType::ConnectFailed;
|
||||
reply->connectFailed.requestId = packet->requestId;
|
||||
reply->connectFailed.reason = TraversalConnectFailedReason::NoSuchClient;
|
||||
reply->connectFailed.reason = Common::TraversalConnectFailedReason::NoSuchClient;
|
||||
}
|
||||
else
|
||||
{
|
||||
TraversalPacket* please = AllocPacket(MakeSinAddr(*r.value), packet->requestId);
|
||||
please->type = TraversalPacketType::PleaseSendPacket;
|
||||
Common::TraversalPacket* please = AllocPacket(MakeSinAddr(*r.value), packet->requestId);
|
||||
please->type = Common::TraversalPacketType::PleaseSendPacket;
|
||||
please->pleaseSendPacket.address = MakeInetAddress(*addr);
|
||||
}
|
||||
break;
|
||||
|
@ -349,10 +354,10 @@ static void HandlePacket(TraversalPacket* packet, sockaddr_in6* addr)
|
|||
SenderName(addr));
|
||||
break;
|
||||
}
|
||||
if (packet->type != TraversalPacketType::Ack)
|
||||
if (packet->type != Common::TraversalPacketType::Ack)
|
||||
{
|
||||
TraversalPacket ack = {};
|
||||
ack.type = TraversalPacketType::Ack;
|
||||
Common::TraversalPacket ack = {};
|
||||
ack.type = Common::TraversalPacketType::Ack;
|
||||
ack.requestId = packet->requestId;
|
||||
ack.ack.ok = packetOk;
|
||||
TrySend(&ack, sizeof(ack), addr);
|
||||
|
@ -411,7 +416,7 @@ int main()
|
|||
{
|
||||
sockaddr_in6 raddr;
|
||||
socklen_t addrLen = sizeof(raddr);
|
||||
TraversalPacket packet;
|
||||
Common::TraversalPacket packet{};
|
||||
// note: switch to recvmmsg (yes, mmsg) if this becomes
|
||||
// expensive
|
||||
rv = recvfrom(sock, &packet, sizeof(packet), 0, (sockaddr*)&raddr, &addrLen);
|
||||
|
|
|
@ -19,5 +19,5 @@ OSMinimumVersionWin11=10.0.22000.0
|
|||
// VCToolsVersion=14.33.31629
|
||||
// We're really looking for "14.32.31332.0" (because that's what will appear in the registry once
|
||||
// installed), NOT the other values!
|
||||
VCToolsVersion=14.34.31931.0
|
||||
VCToolsVersion=${VC_TOOLS_VERSION}
|
||||
VCToolsUpdateURL=https://aka.ms/vs/17/release/vc_redist.x64.exe
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue