#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 info_event; HandleTable::Entry 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 CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name) { return WrappedFakeProcess::CreateWithContext(os, setup, pid, name); } } // namespace OS } // namespace HLE