Expand Up
@@ -3,9 +3,15 @@
#include " Core/HLE/HLE_OS.h"
#include < cinttypes>
#include < memory>
#include < string>
#include < string_view>
#include < fmt/format.h>
#include < fmt/printf.h>
#include " Common/BitUtils.h"
#include " Common/CommonTypes.h"
#include " Common/Logging/Log.h"
#include " Common/MsgHandler.h"
Expand Down
Expand Up
@@ -204,82 +210,365 @@ void HLE_LogVFPrint(const Core::CPUThreadGuard& guard)
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,
ParameterType parameter_type)
{
auto & ppc_state = system .GetPPCState ();
std::string ArgumentBuffer;
std::string result;
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 ?
std::make_unique<HLE::SystemVABI::VAListStruct>(system , guard,
ppc_state.gpr [str_reg + 1 ]) :
std::make_unique<HLE::SystemVABI::VAList>(system , ppc_state.gpr [1 ] + 0x8 , str_reg + 1 );
std::make_unique<HLE::SystemVABI::VAListStruct>(guard, ppc_state.gpr [str_reg + 1 ]) :
std::make_unique<HLE::SystemVABI::VAList>(guard, ppc_state.gpr [1 ] + 0x8 , str_reg + 1 );
HLEPrintArgsVAList args (guard, ap.get ());
return GetStringVA (&args, string);
}
for (size_t i = 0 ; i < string.size (); i++)
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] != ' %' )
{
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 ())
{
ArgumentBuffer = ' %' ;
i++;
if (string[i] == ' %' )
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] == ' *' )
{
result += ' %' ;
continue ;
++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;
}
while (i < string.size () &&
(string[i] < ' A' || string[i] > ' z' || string[i] == ' l' || string[i] == ' -' ))
size_t start = i;
while (i < string.size () && string[i] >= ' 0' && string[i] <= ' 9' )
++i;
if (start != i)
{
ArgumentBuffer += string[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;
}
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 ;
return std::nullopt;
};
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 ;
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 );
}
case ' p' :
// Override, so 64bit Dolphin prints 32bit pointers, since the ppc is 32bit :)
result += StringFromFormat (" %x" , ap->GetArgT <u32>(guard));
break ;
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;
}
}
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 ;
if (i >= string.size ())
{
// not a valid formatting string, print the formatting string as-is
result += string.substr (formatting_start_position);
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 ;
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 ;
}
else
case ' c ' :
{
result += string[i];
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 ;
}
}
Expand Down