Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions core/libs/SceTypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,11 +688,13 @@ struct AvPlayerVideoEx {
bool video_full_range_flag;
std::uint8_t reserved1[5];
double frame_rate;
std::uint8_t reserved2[24];
std::uint32_t colour_primaries;
std::uint32_t transfer_characteristics;
std::uint8_t reserved2[16];
};
static_assert(sizeof(AvPlayerVideoEx) == 80);
static_assert(offsetof(AvPlayerVideoEx, crop_left_offset) == 20 && offsetof(AvPlayerVideoEx, pitch) == 36);
static_assert(offsetof(AvPlayerVideoEx, frame_rate) == 48);
static_assert(offsetof(AvPlayerVideoEx, crop_left_offset) == 20 && offsetof(AvPlayerVideoEx, pitch) == 36 && offsetof(AvPlayerVideoEx, frame_rate) == 48);
static_assert(offsetof(AvPlayerVideoEx, colour_primaries) == 56 && offsetof(AvPlayerVideoEx, transfer_characteristics) == 60);

struct AvPlayerTimedTextEx {
std::uint8_t language_code[4];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ alignas(256) constexpr std::array<std::uint32_t, 64> Caller{
0x7e020280u, 0xf4080400u, 0xfa000000u, 0x8f148402u, 0xbf8cc07fu, 0xf4280208u, 0x28000000u, 0xbf8cc07fu,
0xbe8e0308u, 0xbe8f0309u, 0xbf068103u, 0xbe8e210eu, 0x34040082u, 0xe0701000u, 0x80010102u, 0xbf810000u,
0xbf9f0000u, 0x30306c73u, 0x00000040u, 0x00000008u};
alignas(256) constexpr std::array<std::uint32_t, 64> HoistedCaller{
0x7e020280u, 0xf4080400u, 0xfa000000u, 0xbf8cc07fu, 0xbf068203u, 0xbf850001u, 0x7e020280u, 0x8f148402u,
0xf4280208u, 0x28000000u, 0xbf8cc07fu, 0xbe8e0308u, 0xbe8f0309u, 0xbf068103u, 0xbe8e210eu, 0x34040082u,
0xe0701000u, 0x80010102u, 0xbf810000u, 0xbf9f0000u, 0x30306c73u, 0x00000040u, 0x00000008u};
alignas(256) constexpr std::array<std::uint32_t, 3> SelectSixteen{0x85159091u, 0x7e020215u, 0xbe80200eu};
alignas(256) constexpr std::array<std::uint32_t, 3> SelectThirtyTwo{0x8515a0a1u, 0x7e020215u, 0xbe80200eu};
alignas(16) std::array<std::uint32_t, 8> Table{};
Expand All @@ -49,7 +53,7 @@ std::array<std::uint32_t, 8> UserData(std::uint32_t index, bool scc) {
static_cast<std::uint32_t>(output), static_cast<std::uint32_t>((output >> 32u) & 0xffffu), static_cast<std::uint32_t>(sizeof(Output)), 0x31016facu};
}

std::vector<std::uint32_t> Commands(const std::array<std::uint32_t, 8>& data, bool wave32) {
std::vector<std::uint32_t> Commands(const std::array<std::uint32_t, 8>& data, bool wave32, const std::uint32_t* caller) {
std::vector<std::uint32_t> words;
const auto registers = [&](std::uint32_t first, std::span<const std::uint32_t> values) {
words.push_back(0xc0007600u | (static_cast<std::uint32_t>(values.size()) << 16u));
Expand All @@ -58,7 +62,7 @@ std::vector<std::uint32_t> Commands(const std::array<std::uint32_t, 8>& data, bo
};
const std::array<std::uint32_t, 3> shape{Threads, 1, 1};
registers(0x207, shape);
const auto address = reinterpret_cast<std::uintptr_t>(Caller.data());
const auto address = reinterpret_cast<std::uintptr_t>(caller);
const std::array<std::uint32_t, 2> program{static_cast<std::uint32_t>(address >> 8u), static_cast<std::uint32_t>(address >> 40u)};
registers(0x20c, program);
const std::array<std::uint32_t, 1> resource{static_cast<std::uint32_t>(data.size()) << 1u};
Expand Down Expand Up @@ -105,9 +109,9 @@ class StderrCapture {
int saved = -1;
};

std::string Dispatch(std::uint32_t index, bool scc, bool wave32) {
std::string Dispatch(std::uint32_t index, bool scc, bool wave32, const std::uint32_t* caller = Caller.data()) {
Output.fill(Untouched);
const auto words = Commands(UserData(index, scc), wave32);
const auto words = Commands(UserData(index, scc), wave32, caller);
Packet packet{const_cast<std::uint32_t*>(words.data()), static_cast<std::uint32_t>(words.size()), 0, {}};
StderrCapture log;
std::string failure;
Expand All @@ -121,8 +125,8 @@ std::string Dispatch(std::uint32_t index, bool scc, bool wave32) {
return log.Finish() + failure;
}

void Check(std::uint32_t index, bool scc, bool wave32, std::uint32_t expected, const std::string& what) {
const auto log = Dispatch(index, scc, wave32);
void Check(std::uint32_t index, bool scc, bool wave32, std::uint32_t expected, const std::string& what, const std::uint32_t* caller = Caller.data()) {
const auto log = Dispatch(index, scc, wave32, caller);
Require(log.find("no linked callee") == std::string::npos, what + ": a linked callee was reported missing: " + log);
for (std::uint32_t lane = 0; lane < Threads; ++lane) {
Require(Output[lane] == expected, what + (wave32 ? " (wave32)" : " (wave64)") + ": lane " + std::to_string(lane) + " is " + std::to_string(Output[lane]) + ", expected " + std::to_string(expected));
Expand All @@ -143,6 +147,8 @@ int main() {
Check(0u, true, wave32, 17u, "the first record's callee with SCC 1");
Check(1u, false, wave32, 32u, "the second record's callee with SCC 0");
Check(1u, true, wave32, 33u, "the second record's callee with SCC 1");
Check(0u, false, wave32, 16u, "the first record's callee through a descriptor loaded before a branch", HoistedCaller.data());
Check(1u, true, wave32, 33u, "the second record's callee through a descriptor loaded before a branch", HoistedCaller.data());
const auto log = Dispatch(2u, false, wave32);
Require(log.find("called address 0x0 at program counter") != std::string::npos && log.find("which no linked callee of that call matches") != std::string::npos,
std::string("a record past the table did not report an unlinked call") + (wave32 ? " (wave32)" : " (wave64)") + ": " + log);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ int main() {
const auto written = Count(texels, SurfaceBytes, 0xff);
const auto cleared = Count(texels, SurfaceBytes, 0x00);
char message[320];
std::snprintf(message, sizeof(message), "the sampled read stored %zu written and %zu cleared bytes of %zu, expected %u written", written, cleared, SurfaceBytes, Threads * 4u + SampleTexelBytes);
std::snprintf(message, sizeof(message), "the sampled read stored %zu written and %zu cleared bytes of %zu, expected %zu written", written, cleared, SurfaceBytes, Threads * 4u + SampleTexelBytes);
Require(written == Threads * 4u + SampleTexelBytes && cleared == SurfaceBytes - written, message);
for (std::uint32_t index = 0; index < Threads * 4u; ++index) {
std::snprintf(message, sizeof(message), "component %u of thread %u is 0x%08x, expected the drawn 0x3f800000 the staged clear does not cover", index % 4u, index / 4u, outputData[index]);
Expand Down
4 changes: 3 additions & 1 deletion core/libs/prx/libSceAvPlayer/src/Source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -577,8 +577,10 @@ class FfmpegSource final : public ISource {
picture.luma_bit_depth = 8;
picture.chroma_bit_depth = 8;
picture.video_full_range_flag = parameters->color_range == AVCOL_RANGE_JPEG;
const auto rate = av_guess_frame_rate(format, const_cast<AVStream*>(&stream), nullptr);
const AVRational rate = stream.avg_frame_rate.num > 0 && stream.avg_frame_rate.den > 0 ? stream.avg_frame_rate : stream.r_frame_rate;
picture.frame_rate = rate.num > 0 && rate.den > 0 ? av_q2d(rate) : 0.0;
picture.colour_primaries = static_cast<std::uint32_t>(parameters->color_primaries);
picture.transfer_characteristics = static_cast<std::uint32_t>(parameters->color_trc);
} else {
auto& sound = info.details.audio;
sound.channel_count = basic.details.audio.channel_count;
Expand Down
15 changes: 15 additions & 0 deletions core/libs/prx/libSceCommonDialog/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
add_library(libSceCommonDialog SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libSceCommonDialog PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceCommonDialog)

if(BUILD_TESTING)
add_test_executable(guest_common_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestCommonDialog.cpp)
target_include_directories(guest_common_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_common_dialog_tests PRIVATE libSceCommonDialog libc)
configure_windows_unwind(guest_common_dialog_tests)
add_test(NAME guest_common_dialog COMMAND guest_common_dialog_tests)
if(WIN32)
set_tests_properties(guest_common_dialog PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
26 changes: 23 additions & 3 deletions core/libs/prx/libSceIme/Export.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <atomic>
#include <algorithm>
#include <cstdint>
#include <cstddef>
Expand Down Expand Up @@ -67,6 +68,7 @@ std::mutex g_keyboardMutex;
std::set<int32_t> g_openKeyboards;
std::mutex g_imeMutex;
std::optional<Session> g_session;
std::atomic<ImeHostTextSink> g_textSink{nullptr};

void PanelSize(const Param& param, uint32_t* width, uint32_t* height) {
const uint32_t scale = (param.option & OptionUseOver2K) != 0 ? 2 : 1;
Expand Down Expand Up @@ -284,13 +286,31 @@ int ValidateOpen(const Param* param, const ExtendedParam* extended) {
extern "C" {

void ImePublishInput_nid_postfix(const ImeHostInput& input) {
std::lock_guard lock(g_imeMutex);
if (g_session) Apply(*g_session, input);
{
std::lock_guard lock(g_imeMutex);
if (g_session) {
Apply(*g_session, input);
return;
}
}
const ImeHostTextSink sink = g_textSink.load();
if (sink == nullptr) return;
std::u16string units;
if (input.kind == ImeHostInputKind::Text) {
std::size_t size = 0;
while (size < sizeof(input.text) && input.text[size] != '\0') ++size;
units = DecodeUtf8(input.text, size);
}
sink(input.kind, units.data(), units.size());
}

bool ImeCapturesKeyboard_nid_postfix() {
std::lock_guard lock(g_imeMutex);
return g_session.has_value();
return g_session.has_value() || g_textSink.load() != nullptr;
}

void ImeSetHostTextSink_nid_no_patch(ImeHostTextSink sink) {
g_textSink = sink;
}

int APS5_VABI sceImeOpen_nid_postfix(const Param* param, const ExtendedParam* extended) {
Expand Down
5 changes: 5 additions & 0 deletions core/libs/prx/libSceIme/include/ImeInput.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef CORE_LIBS_PRX_LIBSCEIME_INCLUDE_IMEINPUT_HPP
#define CORE_LIBS_PRX_LIBSCEIME_INCLUDE_IMEINPUT_HPP

#include <cstddef>
#include <cstdint>

enum class ImeHostInputKind : std::uint32_t {
Expand All @@ -23,4 +24,8 @@ struct ImeHostInput {
extern "C" void ImePublishInput_nid_postfix(const ImeHostInput& input);
extern "C" bool ImeCapturesKeyboard_nid_postfix();

using ImeHostTextSink = void (*)(ImeHostInputKind kind, const char16_t* text, std::size_t length);

extern "C" void ImeSetHostTextSink_nid_no_patch(ImeHostTextSink sink);

#endif
17 changes: 17 additions & 0 deletions core/libs/prx/libSceImeDialog/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
add_library(libSceImeDialog SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp)
target_include_directories(libSceImeDialog PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(libSceImeDialog PRIVATE libSceIme)
configure_windows_unwind(libSceImeDialog)

if(BUILD_TESTING)
add_test_executable(guest_ime_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestImeDialog.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../libSceVideoOut/src/ImeTextInput.cpp)
target_include_directories(guest_ime_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_SDL2_INCLUDE_DIR})
target_link_libraries(guest_ime_dialog_tests PRIVATE libSceImeDialog libSceIme libc)
configure_windows_unwind(guest_ime_dialog_tests)
add_test(NAME guest_ime_dialog COMMAND guest_ime_dialog_tests)
if(WIN32)
set_tests_properties(guest_ime_dialog PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceImeDialog>")
endif()
endif()
153 changes: 117 additions & 36 deletions core/libs/prx/libSceImeDialog/Export.cpp
Original file line number Diff line number Diff line change
@@ -1,31 +1,117 @@
#include <atomic>
#include <algorithm>
#include <cstdint>
#include <cstddef>
#include <cstdio>
#include <mutex>
#include <string>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
#include "prx/libSceIme/include/ImeInput.hpp"

namespace {

constexpr int StatusNone = 0;
constexpr int StatusRunning = 1;
constexpr int StatusFinished = 2;
constexpr std::uint32_t EndStatusOk = 0;
constexpr std::uint32_t EndStatusUserCanceled = 1;
constexpr std::uint32_t EndStatusAborted = 2;
constexpr std::uint32_t MaxTextLength = 2048;
constexpr int ErrorBusy = static_cast<int>(0x80bc0001u);
constexpr int ErrorNotOpened = static_cast<int>(0x80bc0002u);
constexpr int ErrorInvalidMaxTextLength = static_cast<int>(0x80bc0016u);
constexpr int ErrorInvalidInputTextBuffer = static_cast<int>(0x80bc0017u);
constexpr int ErrorInvalidAddress = static_cast<int>(0x80bc0031u);

std::atomic<int> status{StatusNone};
std::atomic<int> polls{0};
std::atomic<std::uint32_t> endStatus{EndStatusOk};
struct Dialog {
int status = StatusNone;
std::uint32_t endStatus = EndStatusOk;
char16_t* buffer = nullptr;
std::uint32_t maxLength = 0;
std::u16string text;
std::size_t caret = 0;
};

std::mutex g_mutex;
Dialog g_dialog;

bool IsHighSurrogate(char16_t unit) { return unit >= 0xd800 && unit <= 0xdbff; }
bool IsLowSurrogate(char16_t unit) { return unit >= 0xdc00 && unit <= 0xdfff; }

std::size_t UnitsBefore(const Dialog& dialog) {
if (dialog.caret == 0) return 0;
return dialog.caret >= 2 && IsLowSurrogate(dialog.text[dialog.caret - 1]) && IsHighSurrogate(dialog.text[dialog.caret - 2]) ? 2 : 1;
}

std::size_t UnitsAfter(const Dialog& dialog) {
if (dialog.caret >= dialog.text.size()) return 0;
return dialog.caret + 1 < dialog.text.size() && IsHighSurrogate(dialog.text[dialog.caret]) && IsLowSurrogate(dialog.text[dialog.caret + 1]) ? 2 : 1;
}

void Finish(std::uint32_t endStatus) {
g_dialog.endStatus = endStatus;
g_dialog.status = StatusFinished;
ImeSetHostTextSink_nid_no_patch(nullptr);
}

void Insert(Dialog& dialog, const char16_t* units, std::size_t length) {
std::u16string accepted;
for (std::size_t i = 0; i < length; ++i) {
if (units[i] != u'\n' && units[i] != u'\r') accepted.push_back(units[i]);
}
std::size_t count = std::min<std::size_t>(accepted.size(), dialog.maxLength - dialog.text.size());
if (count > 0 && count < accepted.size() && IsHighSurrogate(accepted[count - 1])) --count;
dialog.text.insert(dialog.caret, accepted, 0, count);
dialog.caret += count;
}

void Receive(ImeHostInputKind kind, const char16_t* units, std::size_t length) {
std::lock_guard lock(g_mutex);
Dialog& dialog = g_dialog;
if (dialog.status != StatusRunning) return;
switch (kind) {
case ImeHostInputKind::Text:
Insert(dialog, units, length);
return;
case ImeHostInputKind::Backspace: {
const std::size_t count = UnitsBefore(dialog);
dialog.caret -= count;
dialog.text.erase(dialog.caret, count);
return;
}
case ImeHostInputKind::Delete:
dialog.text.erase(dialog.caret, UnitsAfter(dialog));
return;
case ImeHostInputKind::Left:
dialog.caret -= UnitsBefore(dialog);
return;
case ImeHostInputKind::Right:
dialog.caret += UnitsAfter(dialog);
return;
case ImeHostInputKind::Home:
dialog.caret = 0;
return;
case ImeHostInputKind::End:
dialog.caret = dialog.text.size();
return;
case ImeHostInputKind::Enter:
std::copy(dialog.text.begin(), dialog.text.end(), dialog.buffer);
dialog.buffer[dialog.text.size()] = u'\0';
Finish(EndStatusOk);
return;
case ImeHostInputKind::Escape:
Finish(EndStatusUserCanceled);
return;
}
}

}

extern "C" {

int APS5_VABI sceImeDialogAbort(void) {
if (status.load() != StatusRunning) return ErrorNotOpened;
endStatus = EndStatusAborted;
status = StatusFinished;
std::lock_guard lock(g_mutex);
if (g_dialog.status != StatusRunning) return ErrorNotOpened;
Finish(EndStatusAborted);
return 0;
}

Expand All @@ -50,47 +136,42 @@ int APS5_VABI sceImeDialogGetPanelSizeExtended(const Param* param, const Extende
}

int APS5_VABI sceImeDialogGetResult(Result* result) {
std::fprintf(stderr, "[ime] GetResult (status %d)\n", status.load());
if (result == nullptr || status.load() != StatusFinished) return ErrorNotOpened;
std::lock_guard lock(g_mutex);
if (result == nullptr || g_dialog.status != StatusFinished) return ErrorNotOpened;
*result = Result{};
result->endstatus = endStatus.load();
result->endstatus = g_dialog.endStatus;
return 0;
}

int APS5_VABI sceImeDialogGetStatus(void) {
if (status.load() == StatusRunning && ++polls > 1) status = StatusFinished;
static std::atomic<int> traced{0};
const int count = traced.fetch_add(1);
if (count < 8 || count % 2000 == 0) std::fprintf(stderr, "[ime] GetStatus #%d -> %d\n", count, status.load());
return status.load();
std::lock_guard lock(g_mutex);
return g_dialog.status;
}

int APS5_VABI sceImeDialogInit(const Param* param, const ExtendedParam* extended) {
(void)extended;
static std::atomic<int> reported{0};
bool filled = false;
if (param != nullptr && param->input_text_buffer != nullptr && param->max_text_length != 0 && param->input_text_buffer[0] == u'\0') {
static constexpr char16_t defaultText[] = u"Slayer";
std::uint32_t length = 0;
while (defaultText[length] != u'\0' && length < param->max_text_length) {
param->input_text_buffer[length] = defaultText[length];
++length;
}
param->input_text_buffer[length] = u'\0';
filled = true;
}
std::fprintf(stderr, "[ime] Init #%d\n", reported.load());
if (reported.fetch_add(1) == 0) std::fprintf(stderr, "[ime] dialog requested (max text length %u): finishing at once with %s\n", param != nullptr ? param->max_text_length : 0u, filled ? "a default text" : "the text unchanged");
polls = 0;
endStatus = EndStatusOk;
status = StatusRunning;
std::lock_guard lock(g_mutex);
if (g_dialog.status != StatusNone) return ErrorBusy;
if (param == nullptr) return ErrorInvalidAddress;
if (param->max_text_length == 0 || param->max_text_length > MaxTextLength) return ErrorInvalidMaxTextLength;
if (param->input_text_buffer == nullptr) return ErrorInvalidInputTextBuffer;
g_dialog = Dialog{};
g_dialog.buffer = param->input_text_buffer;
g_dialog.maxLength = param->max_text_length;
std::size_t length = 0;
while (length < param->max_text_length && param->input_text_buffer[length] != u'\0') ++length;
g_dialog.text.assign(param->input_text_buffer, length);
g_dialog.caret = length;
g_dialog.status = StatusRunning;
ImeSetHostTextSink_nid_no_patch(&Receive);
return 0;
}

int APS5_VABI sceImeDialogTerm(void) {
std::fprintf(stderr, "[ime] Term (status %d)\n", status.load());
if (status.load() == StatusNone) return ErrorNotOpened;
status = StatusNone;
std::lock_guard lock(g_mutex);
if (g_dialog.status == StatusNone) return ErrorNotOpened;
ImeSetHostTextSink_nid_no_patch(nullptr);
g_dialog = Dialog{};
return 0;
}

Expand Down
Loading