mikage-dev/source/processes/cecd.cpp
2024-03-08 10:54:13 +01:00

138 lines
4.5 KiB
C++

#include "processes/dlp.hpp"
#include "fake_process.hpp"
namespace HLE {
namespace OS {
struct FakeCECD {
FakeCECD(FakeThread&);
// Applies to cecd:u and cecd:s. cecd:ndm only accepts a single connection
static constexpr auto session_limit = 4;
spdlog::logger& logger;
HandleTable::Entry<Event> info_event;
HandleTable::Entry<Event> state_change_event;
};
static auto UserCommandHandler(FakeThread& thread, FakeCECD& context, const Platform::IPC::CommandHeader& header) {
switch (header.command_id) {
case 0x1: // OpenRawFile
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x1, 2, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
// A non-zero value needs to be returned here, since Home Menu fails to
// allocate some internal buffers otherwise
thread.WriteTLS(0x88, 1);
break;
case 0x2: // ReadRawFile
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x1, 2, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
thread.WriteTLS(0x88, 1);
break;
case 0x5: // WriteRawFile
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x5, 1, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
break;
case 0x8: // Delete
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x8, 1, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
break;
case 0x9:
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x9, 1, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
break;
case 0xc: // RunCommandAlt
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0xc, 1, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
break;
case 0xe: // GetCecStateAbbreviated
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0xe, 2, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
thread.WriteTLS(0x88, 1); // idle state
break;
case 0xf: // GetCecInfoEventHandle
{
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0xf, 1, 2).raw);
thread.WriteTLS(0x84, RESULT_OK);
thread.WriteTLS(0x88, IPC::TranslationDescriptor::MakeHandles(1).raw);
thread.WriteTLS(0x8c, context.info_event.first.value);
break;
}
case 0x10: // GetChangeStateEventHandle
{
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x10, 1, 2).raw);
thread.WriteTLS(0x84, RESULT_OK);
thread.WriteTLS(0x88, IPC::TranslationDescriptor::MakeHandles(1).raw);
thread.WriteTLS(0x8c, context.info_event.first.value);
break;
}
case 0x11: // OpenAndWrite
{
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x11, 1, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
break;
}
case 0x12: // OpenAndRead
{
thread.WriteTLS(0x80, IPC::CommandHeader::Make(0x12, 2 + 2, 0).raw);
thread.WriteTLS(0x84, RESULT_OK);
thread.WriteTLS(0x88, 1); // Number of bytes read (Home Menu fails to allocate a buffer used later on if we return 0 here)
thread.WriteTLS(0x8c, 0);
thread.WriteTLS(0x90, 0);
break;
}
default:
throw std::runtime_error(fmt::format("CECD: Unknown cecd:u command with header {:#x}", header.raw));
}
return ServiceHelper::SendReply;
}
FakeCECD::FakeCECD(FakeThread& thread)
: logger(*thread.GetLogger()) {
// TODO: Should these be OneShot or Sticky events?
OS::Result result;
std::tie(result, info_event) = thread.CallSVC(&OS::SVCCreateEvent, ResetType::OneShot);
if (result != RESULT_OK) {
throw std::runtime_error("Failed to create CECD info event");
}
std::tie(result, state_change_event) = thread.CallSVC(&OS::SVCCreateEvent, ResetType::OneShot);
if (result != RESULT_OK) {
throw std::runtime_error("Failed to create CECD state change event");
}
ServiceHelper service;
service.Append(ServiceUtil::SetupService(thread, "cecd:u", session_limit));
service.Append(ServiceUtil::SetupService(thread, "cecd:s", session_limit));
auto InvokeCommandHandler = [this](FakeThread& thread, uint32_t /* index of signalled handle */) {
Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) };
return UserCommandHandler(thread, *this, header);
};
service.Run(thread, std::move(InvokeCommandHandler));
}
template<> std::shared_ptr<WrappedFakeProcess> CreateFakeProcessViaContext<FakeCECD>(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name) {
return WrappedFakeProcess::CreateWithContext<FakeCECD>(os, setup, pid, name);
}
} // namespace OS
} // namespace HLE