s32 cellMusicInitialize2(s32 mode, s32 spuPriority, vm::ptr<CellMusic2Callback> func, vm::ptr<void> userData) { cellMusic.Todo("cellMusicInitialize2(mode=%d, spuPriority=%d, func=*0x%x, userData=*0x%x)", mode, spuPriority, func, userData); if (mode != CELL_MUSIC2_PLAYER_MODE_NORMAL) { cellMusic.Todo("Unknown player mode: 0x%x", mode); return CELL_MUSIC2_ERROR_PARAM; } named_thread_t(WRAP_EXPR("CellMusicInit"), [=]() { const auto music = fxm::make_always<music2_t>(); music->func = func; music->userData = userData; Emu.GetCallbackManager().Register([=](CPUThread& CPU) -> s32 { vm::var<u32> ret(CPU); *ret = CELL_OK; func(static_cast<PPUThread&>(CPU), CELL_MUSIC2_EVENT_INITIALIZE_RESULT, ret, userData); return CELL_OK; }); }).detach(); return CELL_OK; }
void shared_mutex_t::lock() { if (!try_lock()) { std::unique_lock<std::mutex> lock(m_mutex); m_wwcv.wait(lock, WRAP_EXPR(m_info.atomic_op([](ownership_info_t& info) -> bool { if (info.waiting_writers < UINT16_MAX) { info.waiting_writers++; return true; } return false; }))); m_wcv.wait(lock, WRAP_EXPR(m_info.atomic_op([](ownership_info_t& info) -> bool { if (!info.writers) { info.writers++; return true; } return false; }))); m_wcv.wait(lock, WRAP_EXPR(m_info.load().readers == 0)); const auto info = m_info.atomic_op([](ownership_info_t& info) { if (!info.waiting_writers--) { throw EXCEPTION("Invalid value"); } }); if (info.waiting_writers == UINT16_MAX) { m_wwcv.notify_one(); } } }
SPUThread::SPUThread(const std::string& name, u32 index) : CPUThread(CPU_THREAD_SPU, name, WRAP_EXPR(fmt::format("SPU[0x%x] Thread (%s)[0x%05x]", m_id, m_name.c_str(), pc))) , index(index) , offset(vm::alloc(0x40000, vm::main)) { if (!offset) { throw EXCEPTION("Failed to allocate SPU local storage"); } }
s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func) { cellFs.Warning("cellFsAioWrite(aio=*0x%x, id=*0x%x, func=*0x%x)", aio, id, func); // TODO: detect mount point and send AIO request to the AIO thread of this mount point const s32 xid = (*id = ++g_fs_aio_id); named_thread_t(WRAP_EXPR("FS AIO Write Thread"), [=]{ fsAio(aio, true, xid, func); }).detach(); return CELL_OK; }
u32 SPUThread::get_ch_value(u32 ch) { if (Ini.HLELogging.GetValue()) { LOG_NOTICE(SPU, "get_ch_value(ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???"); } auto read_channel = [this](spu_channel_t& channel) -> u32 { std::unique_lock<std::mutex> lock(mutex, std::defer_lock); while (true) { bool result; u32 value; std::tie(result, value) = channel.try_pop(); if (result) { return value; } CHECK_EMU_STATUS; if (is_stopped()) throw CPUThreadStop{}; if (!lock) { lock.lock(); continue; } cv.wait(lock); } }; switch (ch) { //case SPU_RdSRR0: // value = SRR0; // break; case SPU_RdInMbox: { std::unique_lock<std::mutex> lock(mutex, std::defer_lock); while (true) { bool result; u32 value; u32 count; std::tie(result, value, count) = ch_in_mbox.try_pop(); if (result) { if (count + 1 == 4 /* SPU_IN_MBOX_THRESHOLD */) // TODO: check this { int_ctrl[2].set(SPU_INT2_STAT_SPU_MAILBOX_THRESHOLD_INT); } return value; } CHECK_EMU_STATUS; if (is_stopped()) throw CPUThreadStop{}; if (!lock) { lock.lock(); continue; } cv.wait(lock); } } case MFC_RdTagStat: { return read_channel(ch_tag_stat); } case MFC_RdTagMask: { return ch_tag_mask; } case SPU_RdSigNotify1: { return read_channel(ch_snr1); } case SPU_RdSigNotify2: { return read_channel(ch_snr2); } case MFC_RdAtomicStat: { return read_channel(ch_atomic_stat); } case MFC_RdListStallStat: { return read_channel(ch_stall_stat); } case SPU_RdDec: { return ch_dec_value - (u32)(get_timebased_time() - ch_dec_start_timestamp); } case SPU_RdEventMask: { return ch_event_mask.load(); } case SPU_RdEventStat: { std::unique_lock<std::mutex> lock(mutex, std::defer_lock); // start waiting or return immediately if (u32 res = get_events(true)) { return res; } if (ch_event_mask.load() & SPU_EVENT_LR) { // register waiter if polling reservation status is required vm::wait_op(*this, last_raddr, 128, WRAP_EXPR(get_events(true) || is_stopped())); } else { lock.lock(); // simple waiting loop otherwise while (!get_events(true) && !is_stopped()) { CHECK_EMU_STATUS; cv.wait(lock); } } ch_event_stat &= ~SPU_EVENT_WAITING; if (is_stopped()) throw CPUThreadStop{}; return get_events(); } case SPU_RdMachStat: { // HACK: "Not isolated" status // Return SPU Interrupt status in LSB return (ch_event_stat.load() & SPU_EVENT_INTR_ENABLED) != 0; } } throw EXCEPTION("Unknown/illegal channel (ch=%d [%s])", ch, ch < 128 ? spu_ch_name[ch] : "???"); }
s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam) { cellSysutil.Warning("cellMsgDialogOpen2(type=0x%x, msgString=*0x%x, callback=*0x%x, userData=*0x%x, extParam=*0x%x)", type, msgString, callback, userData, extParam); if (!msgString || strlen(msgString.get_ptr()) >= 0x200 || type & -0x33f8) { return CELL_MSGDIALOG_ERROR_PARAM; } switch (type & CELL_MSGDIALOG_TYPE_BUTTON_TYPE) { case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_NONE: { if (type & CELL_MSGDIALOG_TYPE_DEFAULT_CURSOR) { return CELL_MSGDIALOG_ERROR_PARAM; } switch (type & CELL_MSGDIALOG_TYPE_PROGRESSBAR) { case CELL_MSGDIALOG_TYPE_PROGRESSBAR_NONE: break; case CELL_MSGDIALOG_TYPE_PROGRESSBAR_SINGLE: break; case CELL_MSGDIALOG_TYPE_PROGRESSBAR_DOUBLE: break; default: return CELL_MSGDIALOG_ERROR_PARAM; } break; } case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_YESNO: { switch (type & CELL_MSGDIALOG_TYPE_DEFAULT_CURSOR) { case CELL_MSGDIALOG_TYPE_DEFAULT_CURSOR_YES: break; case CELL_MSGDIALOG_TYPE_DEFAULT_CURSOR_NO: break; default: return CELL_MSGDIALOG_ERROR_PARAM; } if (type & CELL_MSGDIALOG_TYPE_PROGRESSBAR) { return CELL_MSGDIALOG_ERROR_PARAM; } break; } case CELL_MSGDIALOG_TYPE_BUTTON_TYPE_OK: { if (type & CELL_MSGDIALOG_TYPE_DEFAULT_CURSOR) { return CELL_MSGDIALOG_ERROR_PARAM; } if (type & CELL_MSGDIALOG_TYPE_PROGRESSBAR) { return CELL_MSGDIALOG_ERROR_PARAM; } break; } default: return CELL_MSGDIALOG_ERROR_PARAM; } MsgDialogState old = msgDialogNone; if (!g_msg_dialog->state.compare_exchange_strong(old, msgDialogInit)) { return CELL_SYSUTIL_ERROR_BUSY; } g_msg_dialog->wait_until = get_system_time() + 31536000000000ull; // some big value switch (type & CELL_MSGDIALOG_TYPE_PROGRESSBAR) { case CELL_MSGDIALOG_TYPE_PROGRESSBAR_DOUBLE: g_msg_dialog->progress_bar_count = 2; break; case CELL_MSGDIALOG_TYPE_PROGRESSBAR_SINGLE: g_msg_dialog->progress_bar_count = 1; break; default: g_msg_dialog->progress_bar_count = 0; break; } switch (type & CELL_MSGDIALOG_TYPE_SE_MUTE) // TODO { case CELL_MSGDIALOG_TYPE_SE_MUTE_OFF: break; case CELL_MSGDIALOG_TYPE_SE_MUTE_ON: break; } std::string msg = msgString.get_ptr(); switch (type & CELL_MSGDIALOG_TYPE_SE_TYPE) { case CELL_MSGDIALOG_TYPE_SE_TYPE_NORMAL: cellSysutil.Warning("%s", msg); break; case CELL_MSGDIALOG_TYPE_SE_TYPE_ERROR: cellSysutil.Error("%s", msg); break; } g_msg_dialog->status = CELL_MSGDIALOG_BUTTON_NONE; CallAfter([type, msg]() { if (Emu.IsStopped()) { g_msg_dialog->state.exchange(msgDialogNone); return; } g_msg_dialog->Create(type, msg); g_msg_dialog->state.exchange(msgDialogOpen); }); while (g_msg_dialog->state == msgDialogInit) { if (Emu.IsStopped()) { if (g_msg_dialog->state != msgDialogNone) { break; } CHECK_EMU_STATUS; } std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack } thread_t(WRAP_EXPR("MsgDialog Thread"), [=]() { while (g_msg_dialog->state == msgDialogOpen || (s64)(get_system_time() - g_msg_dialog->wait_until) < 0) { if (Emu.IsStopped()) { g_msg_dialog->state = msgDialogAbort; break; } std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack } if (callback && g_msg_dialog->state != msgDialogAbort) { const s32 status = g_msg_dialog->status; Emu.GetCallbackManager().Register([=](CPUThread& CPU) -> s32 { callback(static_cast<PPUThread&>(CPU), status, userData); return CELL_OK; }); } CallAfter([]() { g_msg_dialog->Destroy(); g_msg_dialog->state = msgDialogNone; }); }).detach(); return CELL_OK; }
ARMv7Thread::ARMv7Thread(const std::string& name) : CPUThread(CPU_THREAD_ARMv7, name, WRAP_EXPR(fmt::format("ARMv7[0x%x] Thread (%s)[0x%08x]", m_id, m_name.c_str(), PC))) , ARMv7Context({}) { }
PPUThread::PPUThread(const std::string& name) : CPUThread(CPU_THREAD_PPU, name, WRAP_EXPR(fmt::format("PPU[0x%x] Thread (%s)[0x%08x]", m_id, m_name.c_str(), PC))) { InitRotateMask(); }
void MainFrame::InstallPkg(wxCommandEvent& WXUNUSED(event)) { const bool was_running = Emu.Pause(); wxFileDialog ctrl(this, L"Select PKG", wxEmptyString, wxEmptyString, "PKG files (*.pkg)|*.pkg|All files (*.*)|*.*", wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (ctrl.ShowModal() == wxID_CANCEL) { if (was_running) Emu.Resume(); return; } Emu.Stop(); Emu.GetVFS().Init("/"); std::string local_path; Emu.GetVFS().GetDevice("/dev_hdd0/game/", local_path); // Open PKG file fs::file pkg_f(ctrl.GetPath().ToStdString()); // Open file mapping (test) fs::file_ptr pkg_ptr(pkg_f); if (!pkg_f || !pkg_ptr) { LOG_ERROR(LOADER, "PKG: Failed to open %s", ctrl.GetPath().ToStdString()); return; } // Append title ID to the path local_path += '/'; local_path += { pkg_ptr + 55, 9 }; if (!fs::create_dir(local_path)) { if (fs::is_dir(local_path)) { if (wxMessageDialog(this, "Another installation found. Do you want to overwrite it?", "PKG Decrypter / Installer", wxYES_NO | wxCENTRE).ShowModal() != wxID_YES) { LOG_ERROR(LOADER, "PKG: Cancelled installation to existing directory %s", local_path); return; } } else { LOG_ERROR(LOADER, "PKG: Could not create the installation directory %s", local_path); return; } } wxProgressDialog pdlg("PKG Decrypter / Installer", "Please wait, unpacking...", 1000, this, wxPD_AUTO_HIDE | wxPD_APP_MODAL); volatile f64 progress = 0.0; // Run PKG unpacking asynchronously auto result = std::async(std::launch::async, WRAP_EXPR(pkg_install(pkg_f, local_path + "/", progress))); // Wait for the completion while (result.wait_for(15ms) != std::future_status::ready) { // Update progress window pdlg.Update(progress * pdlg.GetRange()); // Update main frame Update(); wxGetApp().ProcessPendingEvents(); } pdlg.Close(); if (result.get()) { LOG_SUCCESS(LOADER, "PKG: Package successfully installed in %s", local_path); // Refresh game list m_game_viewer->Refresh(); } }