Merge pull request #11025 from AdmiralCurtiss/hle-printf
HLE_OS: Manually handle printfs from emulated software to prevent emulated software from crashing Dolphin with an invalid printf formatting string.
This commit is contained in:
commit
f19651e49b
|
@ -3,9 +3,15 @@
|
||||||
|
|
||||||
#include "Core/HLE/HLE_OS.h"
|
#include "Core/HLE/HLE_OS.h"
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <fmt/printf.h>
|
||||||
|
|
||||||
|
#include "Common/BitUtils.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
|
@ -204,82 +210,365 @@ void HLE_LogVFPrint(const Core::CPUThreadGuard& guard)
|
||||||
HLE_LogFPrint(guard, ParameterType::VariableArgumentList);
|
HLE_LogFPrint(guard, ParameterType::VariableArgumentList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class HLEPrintArgsVAList final : public HLEPrintArgs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HLEPrintArgsVAList(const Core::CPUThreadGuard& guard, HLE::SystemVABI::VAList* va_list)
|
||||||
|
: m_guard(guard), m_va_list(va_list)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetU32() override { return m_va_list->GetArgT<u32>(); }
|
||||||
|
u64 GetU64() override { return m_va_list->GetArgT<u64>(); }
|
||||||
|
double GetF64() override { return m_va_list->GetArgT<double>(); }
|
||||||
|
std::string GetString(std::optional<u32> max_length) override
|
||||||
|
{
|
||||||
|
return max_length == 0u ? std::string() :
|
||||||
|
PowerPC::MMU::HostGetString(m_guard, m_va_list->GetArgT<u32>(),
|
||||||
|
max_length.value_or(0u));
|
||||||
|
}
|
||||||
|
std::u16string GetU16String(std::optional<u32> max_length) override
|
||||||
|
{
|
||||||
|
return max_length == 0u ? std::u16string() :
|
||||||
|
PowerPC::MMU::HostGetU16String(m_guard, m_va_list->GetArgT<u32>(),
|
||||||
|
max_length.value_or(0u));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Core::CPUThreadGuard& m_guard;
|
||||||
|
HLE::SystemVABI::VAList* m_va_list;
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
std::string GetStringVA(Core::System& system, const Core::CPUThreadGuard& guard, u32 str_reg,
|
std::string GetStringVA(Core::System& system, const Core::CPUThreadGuard& guard, u32 str_reg,
|
||||||
ParameterType parameter_type)
|
ParameterType parameter_type)
|
||||||
{
|
{
|
||||||
auto& ppc_state = system.GetPPCState();
|
auto& ppc_state = system.GetPPCState();
|
||||||
|
|
||||||
std::string ArgumentBuffer;
|
|
||||||
std::string result;
|
|
||||||
std::string string = PowerPC::MMU::HostGetString(guard, ppc_state.gpr[str_reg]);
|
std::string string = PowerPC::MMU::HostGetString(guard, ppc_state.gpr[str_reg]);
|
||||||
auto ap =
|
std::unique_ptr<HLE::SystemVABI::VAList> ap =
|
||||||
parameter_type == ParameterType::VariableArgumentList ?
|
parameter_type == ParameterType::VariableArgumentList ?
|
||||||
std::make_unique<HLE::SystemVABI::VAListStruct>(system, guard,
|
std::make_unique<HLE::SystemVABI::VAListStruct>(guard, ppc_state.gpr[str_reg + 1]) :
|
||||||
ppc_state.gpr[str_reg + 1]) :
|
std::make_unique<HLE::SystemVABI::VAList>(guard, ppc_state.gpr[1] + 0x8, str_reg + 1);
|
||||||
std::make_unique<HLE::SystemVABI::VAList>(system, ppc_state.gpr[1] + 0x8, str_reg + 1);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < string.size(); i++)
|
HLEPrintArgsVAList args(guard, ap.get());
|
||||||
|
return GetStringVA(&args, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetStringVA(HLEPrintArgs* args, std::string_view string)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
for (size_t i = 0; i < string.size(); ++i)
|
||||||
{
|
{
|
||||||
if (string[i] == '%')
|
if (string[i] != '%')
|
||||||
{
|
|
||||||
ArgumentBuffer = '%';
|
|
||||||
i++;
|
|
||||||
if (string[i] == '%')
|
|
||||||
{
|
|
||||||
result += '%';
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (i < string.size() &&
|
|
||||||
(string[i] < 'A' || string[i] > 'z' || string[i] == 'l' || string[i] == '-'))
|
|
||||||
{
|
|
||||||
ArgumentBuffer += string[i++];
|
|
||||||
}
|
|
||||||
if (i >= string.size())
|
|
||||||
break;
|
|
||||||
|
|
||||||
ArgumentBuffer += string[i];
|
|
||||||
|
|
||||||
switch (string[i])
|
|
||||||
{
|
|
||||||
case 's':
|
|
||||||
result +=
|
|
||||||
StringFromFormat(ArgumentBuffer.c_str(),
|
|
||||||
PowerPC::MMU::HostGetString(guard, ap->GetArgT<u32>(guard)).c_str());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'a':
|
|
||||||
case 'A':
|
|
||||||
case 'e':
|
|
||||||
case 'E':
|
|
||||||
case 'f':
|
|
||||||
case 'F':
|
|
||||||
case 'g':
|
|
||||||
case 'G':
|
|
||||||
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<double>(guard));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'p':
|
|
||||||
// Override, so 64bit Dolphin prints 32bit pointers, since the ppc is 32bit :)
|
|
||||||
result += StringFromFormat("%x", ap->GetArgT<u32>(guard));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'n':
|
|
||||||
// %n doesn't output anything, so the result variable is untouched
|
|
||||||
// the actual PPC function will take care of the memory write
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (string[i - 1] == 'l' && string[i - 2] == 'l')
|
|
||||||
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u64>(guard));
|
|
||||||
else
|
|
||||||
result += StringFromFormat(ArgumentBuffer.c_str(), ap->GetArgT<u32>(guard));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
result += string[i];
|
result += string[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t formatting_start_position = i;
|
||||||
|
++i;
|
||||||
|
if (i < string.size() && string[i] == '%')
|
||||||
|
{
|
||||||
|
result += '%';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool left_justified_flag = false;
|
||||||
|
bool sign_prepended_flag = false;
|
||||||
|
bool space_prepended_flag = false;
|
||||||
|
bool alternative_form_flag = false;
|
||||||
|
bool padding_zeroes_flag = false;
|
||||||
|
while (i < string.size())
|
||||||
|
{
|
||||||
|
if (string[i] == '-')
|
||||||
|
left_justified_flag = true;
|
||||||
|
else if (string[i] == '+')
|
||||||
|
sign_prepended_flag = true;
|
||||||
|
else if (string[i] == ' ')
|
||||||
|
space_prepended_flag = true;
|
||||||
|
else if (string[i] == '#')
|
||||||
|
alternative_form_flag = true;
|
||||||
|
else if (string[i] == '0')
|
||||||
|
padding_zeroes_flag = true;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto take_field_or_precision = [&](bool* left_justified_flag_ptr) -> std::optional<u32> {
|
||||||
|
if (i >= string.size())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (string[i] == '*')
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
const s32 result = Common::BitCast<s32>(args->GetU32());
|
||||||
|
if (result >= 0)
|
||||||
|
return static_cast<u32>(result);
|
||||||
|
|
||||||
|
if (left_justified_flag_ptr)
|
||||||
|
{
|
||||||
|
// field width; this results in positive field width and left_justified flag set
|
||||||
|
*left_justified_flag_ptr = true;
|
||||||
|
return static_cast<u32>(-static_cast<s64>(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
// precision; this is ignored
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t start = i;
|
||||||
|
while (i < string.size() && string[i] >= '0' && string[i] <= '9')
|
||||||
|
++i;
|
||||||
|
if (start != i)
|
||||||
|
{
|
||||||
|
while (start < i && string[start] == '0')
|
||||||
|
++start;
|
||||||
|
if (start == i)
|
||||||
|
return 0;
|
||||||
|
u32 result = 0;
|
||||||
|
const std::string tmp(string, start, i - start);
|
||||||
|
if (TryParse(tmp, &result, 10))
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::optional<u32> field_width = take_field_or_precision(&left_justified_flag);
|
||||||
|
std::optional<u32> precision = std::nullopt;
|
||||||
|
if (i < string.size() && string[i] == '.')
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
precision = take_field_or_precision(nullptr).value_or(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LengthModifier
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
hh,
|
||||||
|
h,
|
||||||
|
l,
|
||||||
|
ll,
|
||||||
|
L,
|
||||||
|
};
|
||||||
|
auto length_modifier = LengthModifier::None;
|
||||||
|
|
||||||
|
if (i < string.size() && (string[i] == 'h' || string[i] == 'l' || string[i] == 'L'))
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
if (i < string.size() && string[i - 1] == 'h' && string[i] == 'h')
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
length_modifier = LengthModifier::hh;
|
||||||
|
}
|
||||||
|
else if (i < string.size() && string[i - 1] == 'l' && string[i] == 'l')
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
length_modifier = LengthModifier::ll;
|
||||||
|
}
|
||||||
|
else if (string[i - 1] == 'h')
|
||||||
|
{
|
||||||
|
length_modifier = LengthModifier::h;
|
||||||
|
}
|
||||||
|
else if (string[i - 1] == 'l')
|
||||||
|
{
|
||||||
|
length_modifier = LengthModifier::l;
|
||||||
|
}
|
||||||
|
else if (string[i - 1] == 'L')
|
||||||
|
{
|
||||||
|
length_modifier = LengthModifier::L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= string.size())
|
||||||
|
{
|
||||||
|
// not a valid formatting string, print the formatting string as-is
|
||||||
|
result += string.substr(formatting_start_position);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char format_specifier = string[i];
|
||||||
|
switch (format_specifier)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
{
|
||||||
|
if (length_modifier == LengthModifier::l)
|
||||||
|
{
|
||||||
|
// This is a bit of a mess... wchar_t could be 16 bits or 32 bits per character depending
|
||||||
|
// on the software. Retail software seems usually to use 16 bits and homebrew 32 bits, but
|
||||||
|
// that's really just a guess. Ideally we can figure out a way to autodetect this, but if
|
||||||
|
// not we should probably just expose a setting for it in the debugger somewhere. For now
|
||||||
|
// we just assume 16 bits.
|
||||||
|
fmt::format_to(
|
||||||
|
std::back_inserter(result), fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||||
|
UTF8ToSHIFTJIS(UTF16ToUTF8(args->GetU16String(precision))), field_width.value_or(0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fmt::format_to(std::back_inserter(result),
|
||||||
|
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||||
|
args->GetString(precision), field_width.value_or(0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c':
|
||||||
|
{
|
||||||
|
const s32 value = Common::BitCast<s32>(args->GetU32());
|
||||||
|
if (length_modifier == LengthModifier::l)
|
||||||
|
{
|
||||||
|
// Same problem as with wide strings here.
|
||||||
|
const char16_t wide_char = static_cast<char16_t>(value);
|
||||||
|
fmt::format_to(std::back_inserter(result),
|
||||||
|
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||||
|
UTF8ToSHIFTJIS(UTF16ToUTF8(std::u16string_view(&wide_char, 1))),
|
||||||
|
field_width.value_or(0));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fmt::format_to(std::back_inserter(result),
|
||||||
|
fmt::runtime(left_justified_flag ? "{0:<{1}}" : "{0:>{1}}"),
|
||||||
|
static_cast<char>(value), field_width.value_or(0));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
case 'i':
|
||||||
|
{
|
||||||
|
const auto options = fmt::format(
|
||||||
|
"{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||||
|
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
|
||||||
|
field_width ? fmt::format("{}", *field_width) : "",
|
||||||
|
precision ? fmt::format(".{}", *precision) : "");
|
||||||
|
if (length_modifier == LengthModifier::ll)
|
||||||
|
{
|
||||||
|
const s64 value = Common::BitCast<s64>(args->GetU64());
|
||||||
|
result += fmt::sprintf(fmt::format("%{}" PRId64, options).c_str(), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s32 value = Common::BitCast<s32>(args->GetU32());
|
||||||
|
if (length_modifier == LengthModifier::h)
|
||||||
|
value = static_cast<s16>(value);
|
||||||
|
else if (length_modifier == LengthModifier::hh)
|
||||||
|
value = static_cast<s8>(value);
|
||||||
|
result += fmt::sprintf(fmt::format("%{}" PRId32, options).c_str(), value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'o':
|
||||||
|
{
|
||||||
|
const auto options = fmt::format(
|
||||||
|
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||||
|
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
|
||||||
|
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
|
||||||
|
precision ? fmt::format(".{}", *precision) : "");
|
||||||
|
if (length_modifier == LengthModifier::ll)
|
||||||
|
{
|
||||||
|
const u64 value = args->GetU64();
|
||||||
|
result += fmt::sprintf(fmt::format("%{}" PRIo64, options).c_str(), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 value = args->GetU32();
|
||||||
|
if (length_modifier == LengthModifier::h)
|
||||||
|
value = static_cast<u16>(value);
|
||||||
|
else if (length_modifier == LengthModifier::hh)
|
||||||
|
value = static_cast<u8>(value);
|
||||||
|
result += fmt::sprintf(fmt::format("%{}" PRIo32, options).c_str(), value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
{
|
||||||
|
const auto options = fmt::format(
|
||||||
|
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||||
|
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
|
||||||
|
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
|
||||||
|
precision ? fmt::format(".{}", *precision) : "");
|
||||||
|
if (length_modifier == LengthModifier::ll)
|
||||||
|
{
|
||||||
|
const u64 value = args->GetU64();
|
||||||
|
result += fmt::sprintf(
|
||||||
|
fmt::format("%{}{}", options, format_specifier == 'x' ? PRIx64 : PRIX64).c_str(),
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 value = args->GetU32();
|
||||||
|
if (length_modifier == LengthModifier::h)
|
||||||
|
value = static_cast<u16>(value);
|
||||||
|
else if (length_modifier == LengthModifier::hh)
|
||||||
|
value = static_cast<u8>(value);
|
||||||
|
result += fmt::sprintf(
|
||||||
|
fmt::format("%{}{}", options, format_specifier == 'x' ? PRIx32 : PRIX32).c_str(),
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u':
|
||||||
|
{
|
||||||
|
const auto options = fmt::format(
|
||||||
|
"{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||||
|
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
|
||||||
|
field_width ? fmt::format("{}", *field_width) : "",
|
||||||
|
precision ? fmt::format(".{}", *precision) : "");
|
||||||
|
if (length_modifier == LengthModifier::ll)
|
||||||
|
{
|
||||||
|
const u64 value = args->GetU64();
|
||||||
|
result += fmt::sprintf(fmt::format("%{}" PRIu64, options).c_str(), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 value = args->GetU32();
|
||||||
|
if (length_modifier == LengthModifier::h)
|
||||||
|
value = static_cast<u16>(value);
|
||||||
|
else if (length_modifier == LengthModifier::hh)
|
||||||
|
value = static_cast<u8>(value);
|
||||||
|
result += fmt::sprintf(fmt::format("%{}" PRIu32, options).c_str(), value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
{
|
||||||
|
const auto options = fmt::format(
|
||||||
|
"{}{}{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||||
|
space_prepended_flag ? " " : "", alternative_form_flag ? "#" : "",
|
||||||
|
padding_zeroes_flag ? "0" : "", field_width ? fmt::format("{}", *field_width) : "",
|
||||||
|
precision ? fmt::format(".{}", *precision) : "");
|
||||||
|
double value = args->GetF64();
|
||||||
|
result += fmt::sprintf(fmt::format("%{}{}", options, format_specifier).c_str(), value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'n':
|
||||||
|
// %n doesn't output anything, so the result variable is untouched
|
||||||
|
// the actual PPC function will take care of the memory write
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
{
|
||||||
|
const auto options =
|
||||||
|
fmt::format("{}{}{}{}{}", left_justified_flag ? "-" : "", sign_prepended_flag ? "+" : "",
|
||||||
|
space_prepended_flag ? " " : "", padding_zeroes_flag ? "0" : "",
|
||||||
|
field_width ? fmt::format("{}", *field_width) : "");
|
||||||
|
const u32 value = args->GetU32();
|
||||||
|
result += fmt::sprintf(fmt::format("%{}" PRIx32, options).c_str(), value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// invalid conversion specifier, print the formatting string as-is
|
||||||
|
result += string.substr(formatting_start_position, formatting_start_position - i + 1);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
class CPUThreadGuard;
|
class CPUThreadGuard;
|
||||||
|
@ -10,6 +15,18 @@ class CPUThreadGuard;
|
||||||
|
|
||||||
namespace HLE_OS
|
namespace HLE_OS
|
||||||
{
|
{
|
||||||
|
class HLEPrintArgs
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual u32 GetU32() = 0;
|
||||||
|
virtual u64 GetU64() = 0;
|
||||||
|
virtual double GetF64() = 0;
|
||||||
|
virtual std::string GetString(std::optional<u32> max_length) = 0;
|
||||||
|
virtual std::u16string GetU16String(std::optional<u32> max_length) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string GetStringVA(HLEPrintArgs* args, std::string_view string);
|
||||||
|
|
||||||
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard);
|
void HLE_GeneralDebugPrint(const Core::CPUThreadGuard& guard);
|
||||||
void HLE_GeneralDebugVPrint(const Core::CPUThreadGuard& guard);
|
void HLE_GeneralDebugVPrint(const Core::CPUThreadGuard& guard);
|
||||||
void HLE_write_console(const Core::CPUThreadGuard& guard);
|
void HLE_write_console(const Core::CPUThreadGuard& guard);
|
||||||
|
|
|
@ -2,29 +2,29 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "Core/HLE/HLE_VarArgs.h"
|
#include "Core/HLE/HLE_VarArgs.h"
|
||||||
|
#include "Core/Core.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
|
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
|
|
||||||
HLE::SystemVABI::VAList::~VAList() = default;
|
HLE::SystemVABI::VAList::~VAList() = default;
|
||||||
|
|
||||||
u32 HLE::SystemVABI::VAList::GetGPR(const Core::CPUThreadGuard&, u32 gpr) const
|
u32 HLE::SystemVABI::VAList::GetGPR(u32 gpr) const
|
||||||
{
|
{
|
||||||
return m_system.GetPPCState().gpr[gpr];
|
return m_guard.GetSystem().GetPPCState().gpr[gpr];
|
||||||
}
|
}
|
||||||
|
|
||||||
double HLE::SystemVABI::VAList::GetFPR(const Core::CPUThreadGuard&, u32 fpr) const
|
double HLE::SystemVABI::VAList::GetFPR(u32 fpr) const
|
||||||
{
|
{
|
||||||
return m_system.GetPPCState().ps[fpr].PS0AsDouble();
|
return m_guard.GetSystem().GetPPCState().ps[fpr].PS0AsDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
HLE::SystemVABI::VAListStruct::VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard,
|
HLE::SystemVABI::VAListStruct::VAListStruct(const Core::CPUThreadGuard& guard, u32 address)
|
||||||
u32 address)
|
: VAList(guard, 0), m_va_list{PowerPC::MMU::HostRead_U8(guard, address),
|
||||||
: VAList(system, 0), m_va_list{PowerPC::MMU::HostRead_U8(guard, address),
|
PowerPC::MMU::HostRead_U8(guard, address + 1),
|
||||||
PowerPC::MMU::HostRead_U8(guard, address + 1),
|
PowerPC::MMU::HostRead_U32(guard, address + 4),
|
||||||
PowerPC::MMU::HostRead_U32(guard, address + 4),
|
PowerPC::MMU::HostRead_U32(guard, address + 8)},
|
||||||
PowerPC::MMU::HostRead_U32(guard, address + 8)},
|
m_address(address), m_has_fpr_area(guard.GetSystem().GetPPCState().cr.GetBit(6) == 1)
|
||||||
m_address(address), m_has_fpr_area(system.GetPPCState().cr.GetBit(6) == 1)
|
|
||||||
{
|
{
|
||||||
m_stack = m_va_list.overflow_arg_area;
|
m_stack = m_va_list.overflow_arg_area;
|
||||||
m_gpr += m_va_list.gpr;
|
m_gpr += m_va_list.gpr;
|
||||||
|
@ -41,7 +41,7 @@ u32 HLE::SystemVABI::VAListStruct::GetFPRArea() const
|
||||||
return GetGPRArea() + 4 * 8;
|
return GetGPRArea() + 4 * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const
|
u32 HLE::SystemVABI::VAListStruct::GetGPR(u32 gpr) const
|
||||||
{
|
{
|
||||||
if (gpr < 3 || gpr > 10)
|
if (gpr < 3 || gpr > 10)
|
||||||
{
|
{
|
||||||
|
@ -49,10 +49,10 @@ u32 HLE::SystemVABI::VAListStruct::GetGPR(const Core::CPUThreadGuard& guard, u32
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const u32 gpr_address = Common::AlignUp(GetGPRArea() + 4 * (gpr - 3), 4);
|
const u32 gpr_address = Common::AlignUp(GetGPRArea() + 4 * (gpr - 3), 4);
|
||||||
return PowerPC::MMU::HostRead_U32(guard, gpr_address);
|
return PowerPC::MMU::HostRead_U32(m_guard, gpr_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const
|
double HLE::SystemVABI::VAListStruct::GetFPR(u32 fpr) const
|
||||||
{
|
{
|
||||||
if (!m_has_fpr_area || fpr < 1 || fpr > 8)
|
if (!m_has_fpr_area || fpr < 1 || fpr > 8)
|
||||||
{
|
{
|
||||||
|
@ -60,5 +60,5 @@ double HLE::SystemVABI::VAListStruct::GetFPR(const Core::CPUThreadGuard& guard,
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
const u32 fpr_address = Common::AlignUp(GetFPRArea() + 8 * (fpr - 1), 8);
|
const u32 fpr_address = Common::AlignUp(GetFPRArea() + 8 * (fpr - 1), 8);
|
||||||
return PowerPC::MMU::HostRead_F64(guard, fpr_address);
|
return PowerPC::MMU::HostRead_F64(m_guard, fpr_address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "Common/Align.h"
|
#include "Common/Align.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
#include "Core/PowerPC/MMU.h"
|
#include "Core/PowerPC/MMU.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Core
|
namespace Core
|
||||||
{
|
{
|
||||||
class CPUThreadGuard;
|
class CPUThreadGuard;
|
||||||
|
@ -38,9 +38,9 @@ constexpr bool IS_ARG_REAL = std::is_floating_point<T>();
|
||||||
class VAList
|
class VAList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VAList(Core::System& system, u32 stack, u32 gpr = 3, u32 fpr = 1, u32 gpr_max = 10,
|
explicit VAList(const Core::CPUThreadGuard& guard, u32 stack, u32 gpr = 3, u32 fpr = 1,
|
||||||
u32 fpr_max = 8)
|
u32 gpr_max = 10, u32 fpr_max = 8)
|
||||||
: m_system(system), m_gpr(gpr), m_fpr(fpr), m_gpr_max(gpr_max), m_fpr_max(fpr_max),
|
: m_guard(guard), m_gpr(gpr), m_fpr(fpr), m_gpr_max(gpr_max), m_fpr_max(fpr_max),
|
||||||
m_stack(stack)
|
m_stack(stack)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -48,14 +48,14 @@ public:
|
||||||
|
|
||||||
// 0 - arg_ARGPOINTER
|
// 0 - arg_ARGPOINTER
|
||||||
template <typename T, typename std::enable_if_t<IS_ARG_POINTER<T>>* = nullptr>
|
template <typename T, typename std::enable_if_t<IS_ARG_POINTER<T>>* = nullptr>
|
||||||
T GetArg(const Core::CPUThreadGuard& guard)
|
T GetArg()
|
||||||
{
|
{
|
||||||
T obj;
|
T obj;
|
||||||
u32 addr = GetArg<u32>(guard);
|
u32 addr = GetArg<u32>();
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(T); i += 1, addr += 1)
|
for (size_t i = 0; i < sizeof(T); i += 1, addr += 1)
|
||||||
{
|
{
|
||||||
reinterpret_cast<u8*>(&obj)[i] = PowerPC::MMU::HostRead_U8(guard, addr);
|
reinterpret_cast<u8*>(&obj)[i] = PowerPC::MMU::HostRead_U8(m_guard, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
|
@ -63,20 +63,20 @@ public:
|
||||||
|
|
||||||
// 1 - arg_WORD
|
// 1 - arg_WORD
|
||||||
template <typename T, typename std::enable_if_t<IS_WORD<T>>* = nullptr>
|
template <typename T, typename std::enable_if_t<IS_WORD<T>>* = nullptr>
|
||||||
T GetArg(const Core::CPUThreadGuard& guard)
|
T GetArg()
|
||||||
{
|
{
|
||||||
static_assert(!std::is_pointer<T>(), "VAList doesn't support pointers");
|
static_assert(!std::is_pointer<T>(), "VAList doesn't support pointers");
|
||||||
u64 value;
|
u64 value;
|
||||||
|
|
||||||
if (m_gpr <= m_gpr_max)
|
if (m_gpr <= m_gpr_max)
|
||||||
{
|
{
|
||||||
value = GetGPR(guard, m_gpr);
|
value = GetGPR(m_gpr);
|
||||||
m_gpr += 1;
|
m_gpr += 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_stack = Common::AlignUp(m_stack, 4);
|
m_stack = Common::AlignUp(m_stack, 4);
|
||||||
value = PowerPC::MMU::HostRead_U32(guard, m_stack);
|
value = PowerPC::MMU::HostRead_U32(m_guard, m_stack);
|
||||||
m_stack += 4;
|
m_stack += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ public:
|
||||||
|
|
||||||
// 2 - arg_DOUBLEWORD
|
// 2 - arg_DOUBLEWORD
|
||||||
template <typename T, typename std::enable_if_t<IS_DOUBLE_WORD<T>>* = nullptr>
|
template <typename T, typename std::enable_if_t<IS_DOUBLE_WORD<T>>* = nullptr>
|
||||||
T GetArg(const Core::CPUThreadGuard& guard)
|
T GetArg()
|
||||||
{
|
{
|
||||||
u64 value;
|
u64 value;
|
||||||
|
|
||||||
|
@ -93,13 +93,13 @@ public:
|
||||||
m_gpr += 1;
|
m_gpr += 1;
|
||||||
if (m_gpr < m_gpr_max)
|
if (m_gpr < m_gpr_max)
|
||||||
{
|
{
|
||||||
value = static_cast<u64>(GetGPR(guard, m_gpr)) << 32 | GetGPR(guard, m_gpr + 1);
|
value = static_cast<u64>(GetGPR(m_gpr)) << 32 | GetGPR(m_gpr + 1);
|
||||||
m_gpr += 2;
|
m_gpr += 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_stack = Common::AlignUp(m_stack, 8);
|
m_stack = Common::AlignUp(m_stack, 8);
|
||||||
value = PowerPC::MMU::HostRead_U64(guard, m_stack);
|
value = PowerPC::MMU::HostRead_U64(m_guard, m_stack);
|
||||||
m_stack += 8;
|
m_stack += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,19 +108,19 @@ public:
|
||||||
|
|
||||||
// 3 - arg_ARGREAL
|
// 3 - arg_ARGREAL
|
||||||
template <typename T, typename std::enable_if_t<IS_ARG_REAL<T>>* = nullptr>
|
template <typename T, typename std::enable_if_t<IS_ARG_REAL<T>>* = nullptr>
|
||||||
T GetArg(const Core::CPUThreadGuard& guard)
|
T GetArg()
|
||||||
{
|
{
|
||||||
double value;
|
double value;
|
||||||
|
|
||||||
if (m_fpr <= m_fpr_max)
|
if (m_fpr <= m_fpr_max)
|
||||||
{
|
{
|
||||||
value = GetFPR(guard, m_fpr);
|
value = GetFPR(m_fpr);
|
||||||
m_fpr += 1;
|
m_fpr += 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_stack = Common::AlignUp(m_stack, 8);
|
m_stack = Common::AlignUp(m_stack, 8);
|
||||||
value = PowerPC::MMU::HostRead_F64(guard, m_stack);
|
value = PowerPC::MMU::HostRead_F64(m_guard, m_stack);
|
||||||
m_stack += 8;
|
m_stack += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,13 +129,13 @@ public:
|
||||||
|
|
||||||
// Helper
|
// Helper
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T GetArgT(const Core::CPUThreadGuard& guard)
|
T GetArgT()
|
||||||
{
|
{
|
||||||
return static_cast<T>(GetArg<T>(guard));
|
return static_cast<T>(GetArg<T>());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Core::System& m_system;
|
const Core::CPUThreadGuard& m_guard;
|
||||||
u32 m_gpr = 3;
|
u32 m_gpr = 3;
|
||||||
u32 m_fpr = 1;
|
u32 m_fpr = 1;
|
||||||
const u32 m_gpr_max = 10;
|
const u32 m_gpr_max = 10;
|
||||||
|
@ -143,8 +143,8 @@ protected:
|
||||||
u32 m_stack;
|
u32 m_stack;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const;
|
virtual u32 GetGPR(u32 gpr) const;
|
||||||
virtual double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const;
|
virtual double GetFPR(u32 fpr) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// See System V ABI (SVR4) for more details
|
// See System V ABI (SVR4) for more details
|
||||||
|
@ -156,7 +156,7 @@ private:
|
||||||
class VAListStruct : public VAList
|
class VAListStruct : public VAList
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VAListStruct(Core::System& system, const Core::CPUThreadGuard& guard, u32 address);
|
explicit VAListStruct(const Core::CPUThreadGuard& guard, u32 address);
|
||||||
~VAListStruct() = default;
|
~VAListStruct() = default;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -174,8 +174,8 @@ private:
|
||||||
u32 GetGPRArea() const;
|
u32 GetGPRArea() const;
|
||||||
u32 GetFPRArea() const;
|
u32 GetFPRArea() const;
|
||||||
|
|
||||||
u32 GetGPR(const Core::CPUThreadGuard& guard, u32 gpr) const override;
|
u32 GetGPR(u32 gpr) const override;
|
||||||
double GetFPR(const Core::CPUThreadGuard& guard, u32 fpr) const override;
|
double GetFPR(u32 fpr) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HLE::SystemVABI
|
} // namespace HLE::SystemVABI
|
||||||
|
|
|
@ -867,6 +867,22 @@ std::string MMU::HostGetString(const Core::CPUThreadGuard& guard, u32 address, s
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::u16string MMU::HostGetU16String(const Core::CPUThreadGuard& guard, u32 address, size_t size)
|
||||||
|
{
|
||||||
|
std::u16string s;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!HostIsRAMAddress(guard, address) || !HostIsRAMAddress(guard, address + 1))
|
||||||
|
break;
|
||||||
|
const u16 res = HostRead_U16(guard, address);
|
||||||
|
if (!res)
|
||||||
|
break;
|
||||||
|
s += static_cast<char16_t>(res);
|
||||||
|
address += 2;
|
||||||
|
} while (size == 0 || s.length() < size);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ReadResult<std::string>> MMU::HostTryReadString(const Core::CPUThreadGuard& guard,
|
std::optional<ReadResult<std::string>> MMU::HostTryReadString(const Core::CPUThreadGuard& guard,
|
||||||
u32 address, size_t size,
|
u32 address, size_t size,
|
||||||
RequestedAddressSpace space)
|
RequestedAddressSpace space)
|
||||||
|
|
|
@ -132,6 +132,8 @@ public:
|
||||||
static double HostRead_F64(const Core::CPUThreadGuard& guard, u32 address);
|
static double HostRead_F64(const Core::CPUThreadGuard& guard, u32 address);
|
||||||
static u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, u32 address);
|
static u32 HostRead_Instruction(const Core::CPUThreadGuard& guard, u32 address);
|
||||||
static std::string HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size = 0);
|
static std::string HostGetString(const Core::CPUThreadGuard& guard, u32 address, size_t size = 0);
|
||||||
|
static std::u16string HostGetU16String(const Core::CPUThreadGuard& guard, u32 address,
|
||||||
|
size_t size = 0);
|
||||||
|
|
||||||
// Try to read a value from emulated memory at the given address in the given memory space.
|
// Try to read a value from emulated memory at the given address in the given memory space.
|
||||||
// If the read succeeds, the returned value will be present and the ReadResult contains the read
|
// If the read succeeds, the returned value will be present and the ReadResult contains the read
|
||||||
|
|
Loading…
Reference in New Issue