#include "stdafx.h" #include "util/sysinfo.hpp" #include "util/v128.hpp" #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/System.h" #include "Emu/system_utils.hpp" #include "Emu/IdManager.h" #include "Utilities/Thread.h" #include "sys_game.h" LOG_CHANNEL(sys_game); struct board_storage { public: bool read(u8* buffer) { if (!buffer) return false; const auto data = storage.load(); memcpy(buffer, &data, size); return true; } bool write(u8* buffer) { if (!buffer) return false; storage.store(read_from_ptr>(buffer)); written = true; return true; } board_storage() { memset(&storage.raw(), -1, size); if (fs::file file; file.open(file_path, fs::read)) file.read(&storage.raw(), std::min(file.size(), size)); } board_storage(const board_storage&) = delete; board_storage& operator =(const board_storage&) = delete; ~board_storage() { if (written) { if (fs::file file; file.open(file_path, fs::create + fs::write + fs::lock)) { file.write(&storage.raw(), size); file.trunc(size); } } } private: atomic_be_t storage; bool written = false; const std::string file_path = rpcs3::utils::get_hdd1_dir() + "/caches/board_storage.bin"; static constexpr u64 size = sizeof(v128); }; struct watchdog_t { struct alignas(8) control_t { bool needs_restart = false; bool active = false; char pad[sizeof(u32) - sizeof(bool) * 2]{}; u32 timeout = 0; }; atomic_t control; void operator()() { u64 start_time = get_system_time(); u64 old_time = start_time; u64 current_time = old_time; constexpr u64 sleep_time = 50'000; while (thread_ctrl::state() != thread_state::aborting) { if (Emu.GetStatus(false) == system_state::paused) { start_time += current_time - old_time; old_time = current_time; thread_ctrl::wait_for(sleep_time); current_time = get_system_time(); continue; } old_time = std::exchange(current_time, get_system_time()); const auto old = control.fetch_op([&](control_t& data) { if (data.needs_restart) { data.needs_restart = false; return true; } return false; }).first; if (old.active && old.needs_restart) { start_time = current_time; old_time = current_time; continue; } if (old.active && current_time - start_time >= old.timeout) { sys_game.success("Watchdog timeout! Restarting the game..."); Emu.CallFromMainThread([]() { Emu.Restart(false); }); return; } thread_ctrl::wait_for(sleep_time); } } static constexpr auto thread_name = "LV2 Watchdog Thread"sv; }; void abort_lv2_watchdog() { if (auto thr = g_fxo->try_get>()) { sys_game.notice("Aborting %s...", thr->thread_name); *thr = thread_state::aborting; } } error_code _sys_game_watchdog_start(u32 timeout) { sys_game.trace("sys_game_watchdog_start(timeout=%d)", timeout); // According to disassembly timeout *= 1'000'000; timeout &= -64; if (!g_fxo->get>().control.fetch_op([&](watchdog_t::control_t& data) { if (data.active) { return false; } data.needs_restart = true; data.active = true; data.timeout = timeout; return true; }).second) { return CELL_EABORT; } return CELL_OK; } error_code _sys_game_watchdog_stop() { sys_game.trace("sys_game_watchdog_stop()"); g_fxo->get>().control.fetch_op([](watchdog_t::control_t& data) { if (!data.active) { return false; } data.active = false; return true; }); return CELL_OK; } error_code _sys_game_watchdog_clear() { sys_game.trace("sys_game_watchdog_clear()"); g_fxo->get>().control.fetch_op([](watchdog_t::control_t& data) { if (!data.active || data.needs_restart) { return false; } data.needs_restart = true; return true; }); return CELL_OK; } u64 _sys_game_get_system_sw_version() { return stof(utils::get_firmware_version()) * 10000; } error_code _sys_game_board_storage_read(vm::ptr buffer, vm::ptr status) { sys_game.trace("sys_game_board_storage_read(buffer=*0x%x, status=*0x%x)", buffer, status); if (!buffer || !status) { return CELL_EFAULT; } *status = g_fxo->get().read(buffer.get_ptr()) ? 0x00 : 0xFF; return CELL_OK; } error_code _sys_game_board_storage_write(vm::ptr buffer, vm::ptr status) { sys_game.trace("sys_game_board_storage_write(buffer=*0x%x, status=*0x%x)", buffer, status); if (!buffer || !status) { return CELL_EFAULT; } *status = g_fxo->get().write(buffer.get_ptr()) ? 0x00 : 0xFF; return CELL_OK; } error_code _sys_game_get_rtc_status(vm::ptr status) { sys_game.trace("sys_game_get_rtc_status(status=*0x%x)", status); if (!status) { return CELL_EFAULT; } *status = 0; return CELL_OK; }