Merge branch 'master' into master

This commit is contained in:
Elad Ashkenazi 2023-07-24 08:41:20 +03:00 committed by GitHub
commit 45023d298b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 455 additions and 219 deletions

View File

@ -1637,7 +1637,7 @@ bool handle_access_violation(u32 addr, bool is_writing, ucontext_t* context) noe
if (!g_tls_access_violation_recovered)
{
vm_log.notice("\n%s", dump_useful_thread_info());
vm_log.error("Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
vm_log.error("[%s] Access violation %s location 0x%x (%s)", is_writing ? "writing" : "reading", cpu->get_name(), addr, (is_writing && vm::check_addr(addr)) ? "read-only memory" : "unmapped memory");
}
// TODO:

View File

@ -791,6 +791,19 @@ bool cpu_thread::check_state() noexcept
return store;
}
if (s_tls_thread_slot == umax)
{
if (cpu_flag::wait - state)
{
// Force wait flag (must be set during ownership of s_cpu_lock), this makes the atomic op fail as a side effect
state += cpu_flag::wait;
store = true;
}
// Restore thread in the suspend list
cpu_counter::add(this);
}
if (flags & cpu_flag::wait)
{
flags -= cpu_flag::wait;
@ -845,12 +858,6 @@ bool cpu_thread::check_state() noexcept
if (escape)
{
if (s_tls_thread_slot == umax && !retval)
{
// Restore thread in the suspend list
cpu_counter::add(this);
}
if (cpu_can_stop && state0 & cpu_flag::pending)
{
// Execute pending work
@ -999,7 +1006,7 @@ cpu_thread& cpu_thread::operator=(thread_state)
{
if (u32 resv = atomic_storage<u32>::load(thread->raddr))
{
vm::reservation_notifier(resv).notify_one();
vm::reservation_notifier(resv).notify_all(-128);
}
}
}

View File

@ -335,17 +335,7 @@ public:
[[maybe_unused]] const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), cellGem);
ar(attribute, vc_attribute, status_flags, enable_pitch_correction, inertial_counter, controllers
, connected_controllers, update_started, camera_frame, memory_ptr);
if (version == 1 && !ar.is_writing())
{
u32 ts = ar;
start_timestamp_us = ts;
}
else
{
ar(start_timestamp_us);
}
, connected_controllers, update_started, camera_frame, memory_ptr, start_timestamp_us);
}
gem_config_data(utils::serial& ar)

View File

@ -78,7 +78,7 @@ void ppu_module::validate(u32 reloc)
if (size && size != funcs[index].size)
{
if (size + 4 != funcs[index].size || *ensure(get_ptr<u32>(addr + size)) != ppu_instructions::NOP())
if (size + 4 != funcs[index].size || get_ref<u32>(addr + size) != ppu_instructions::NOP())
{
ppu_validator.error("%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", path, found, funcs[index].size, addr, size);
}
@ -733,7 +733,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
// Register TOC from entry point
if (entry && !lib_toc)
{
lib_toc = *ensure(get_ptr<u32>(entry)) ? *ensure(get_ptr<u32>(entry + 4)) : *ensure(get_ptr<u32>(entry + 20));
lib_toc = get_ref<u32>(entry) ? get_ref<u32>(entry + 4) : get_ref<u32>(entry + 20);
}
// Secondary attempt
@ -1425,7 +1425,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
for (vm::cptr<u32> _ptr = vm::cast(block.first); _ptr.addr() < block.first + block.second;)
{
const u32 iaddr = _ptr.addr();
const ppu_opcode_t op{*ensure(get_ptr<u32>(_ptr++))};
const ppu_opcode_t op{get_ref<u32>(_ptr++)};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (type == ppu_itype::B || type == ppu_itype::BC)
@ -1499,7 +1499,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
for (vm::cptr<u32> _ptr = vm::cast(start); _ptr.addr() < next;)
{
const u32 addr = _ptr.addr();
const ppu_opcode_t op{*ensure(get_ptr<u32>(_ptr++))};
const ppu_opcode_t op{get_ref<u32>(_ptr++)};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (type == ppu_itype::UNK)
@ -1641,7 +1641,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
continue;
}
const u32 target = *ensure(get_ptr<u32>(rel.addr));
const u32 target = get_ref<u32>(rel.addr);
if (target % 4 || target < start || target >= end)
{
@ -1718,7 +1718,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
for (; i_pos < lim; i_pos += 4)
{
const u32 opc = *ensure(get_ptr<u32>(i_pos));
const u32 opc = get_ref<u32>(i_pos);
switch (auto type = s_ppu_itype.decode(opc))
{

View File

@ -6,6 +6,7 @@
#include <deque>
#include "util/types.hpp"
#include "util/endian.hpp"
#include "util/asm.hpp"
#include "util/to_endian.hpp"
#include "Utilities/bit_set.h"
@ -127,7 +128,7 @@ struct ppu_module
const u32 seg_size = seg.size;
const u32 seg_addr = seg.addr;
if (seg_size >= std::max<usz>(size_bytes, 1) && addr <= seg_addr + seg_size - size_bytes)
if (seg_size >= std::max<usz>(size_bytes, 1) && addr <= utils::align<u32>(seg_addr + seg_size, 0x10000) - size_bytes)
{
return reinterpret_cast<to_be_t<T>*>(static_cast<u8*>(seg.ptr) + (addr - seg_addr));
}
@ -148,6 +149,40 @@ struct ppu_module
constexpr usz size_element = std::is_void_v<T> ? 0 : sizeof(std::conditional_t<std::is_void_v<T>, char, T>);
return get_ptr<T>(addr.addr(), u32{size_element});
}
template <typename T>
to_be_t<T>& get_ref(u32 addr,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
constexpr usz size_element = std::is_void_v<T> ? 0 : sizeof(std::conditional_t<std::is_void_v<T>, char, T>);
if (auto ptr = get_ptr<T>(addr, u32{size_element}))
{
return *ptr;
}
fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", addr, src_loc{line, col, file, func});
return *std::add_pointer_t<to_be_t<T>>{};
}
template <typename T, typename U> requires requires (const U& obj) { +obj.size() * 0; }
to_be_t<T>& get_ref(U&& addr,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
constexpr usz size_element = std::is_void_v<T> ? 0 : sizeof(std::conditional_t<std::is_void_v<T>, char, T>);
if (auto ptr = get_ptr<T>(addr.addr(), u32{size_element}))
{
return *ptr;
}
fmt::throw_exception("get_ref(): Failure! (addr=0x%x)%s", addr.addr(), src_loc{line, col, file, func});
return *std::add_pointer_t<to_be_t<T>>{};
}
};
struct main_ppu_module : public ppu_module

View File

@ -2030,21 +2030,7 @@ void ppu_thread::serialize_common(utils::serial& ar)
{
[[maybe_unused]] const s32 version = GET_OR_USE_SERIALIZATION_VERSION(ar.is_writing(), ppu);
ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj);
if (ar.is_writing())
{
ar(prio.load().all);
}
else if (version < 2)
{
prio.raw().all = 0;
prio.raw().prio = ar.operator s32();
}
else
{
ar(prio.raw().all);
}
ar(gpr, fpr, cr, fpscr.bits, lr, ctr, vrsave, cia, xer, sat, nj, prio.raw().all);
ar(optional_savestate_state, vr);

View File

@ -259,10 +259,7 @@ lv2_socket::lv2_socket(utils::serial& ar, lv2_socket_type _type)
const s32 version = GET_SERIALIZATION_VERSION(lv2_net);
if (version >= 2)
{
ar(so_rcvtimeo, so_sendtimeo);
}
ar(so_rcvtimeo, so_sendtimeo);
lv2_id = idm::last_id();

View File

@ -312,11 +312,7 @@ std::shared_ptr<void> lv2_prx::load(utils::serial& ar)
{
std::basic_string<bool> loaded_flags, external_flags;
if (version >= 4)
{
ar(loaded_flags);
ar(external_flags);
}
ar(loaded_flags, external_flags);
fs::file file{path.substr(0, path.size() - (offset ? fmt::format("_x%x", offset).size() : 0))};
@ -328,21 +324,11 @@ std::shared_ptr<void> lv2_prx::load(utils::serial& ar)
prx->m_loaded_flags = std::move(loaded_flags);
prx->m_external_loaded_flags = std::move(external_flags);
if (version == 2 && (state == PRX_STATE_STARTED || state == PRX_STATE_STARTING))
{
prx->load_exports();
}
if (version >= 4 && state <= PRX_STATE_STARTED)
if (state <= PRX_STATE_STARTED)
{
prx->restore_exports();
}
if (version == 1)
{
prx->load_exports();
}
ensure(prx);
}
else

View File

@ -215,14 +215,7 @@ lv2_spu_group::lv2_spu_group(utils::serial& ar) noexcept
{
std::common_type_t<decltype(lv2_spu_group::prio)> prio{};
if (GET_SERIALIZATION_VERSION(spu) < 3)
{
prio.prio = ar.operator s32();
}
else
{
ar(prio.all);
}
ar(prio.all);
return prio;
}())
@ -387,10 +380,7 @@ struct spu_limits_t
spu_limits_t(utils::serial& ar) noexcept
{
if (GET_SERIALIZATION_VERSION(spu) >= 2)
{
ar(max_raw, max_spu);
}
ar(max_raw, max_spu);
}
void save(utils::serial& ar)
@ -1407,7 +1397,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value)
if (prev_resv && prev_resv != resv)
{
// Batch reservation notifications if possible
vm::reservation_notifier(prev_resv).notify_all();
vm::reservation_notifier(prev_resv).notify_all(-128);
}
prev_resv = resv;
@ -1417,7 +1407,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread& ppu, u32 id, s32 value)
if (prev_resv)
{
vm::reservation_notifier(prev_resv).notify_all();
vm::reservation_notifier(prev_resv).notify_all(-128);
}
group->exit_status = value;

View File

@ -1,5 +1,4 @@
// v406 USIO emulator
// Responses may be specific to Taiko no Tatsujin
#include "stdafx.h"
#include "usio.h"
@ -18,18 +17,21 @@ void fmt_class_string<usio_btn>::format(std::string& out, u64 arg)
{
case usio_btn::test: return "Test";
case usio_btn::coin: return "Coin";
case usio_btn::enter: return "Enter";
case usio_btn::service: return "Service";
case usio_btn::enter: return "Enter/Start";
case usio_btn::up: return "Up";
case usio_btn::down: return "Down";
case usio_btn::service: return "Service";
case usio_btn::strong_hit_side_left: return "Strong Hit Side Left";
case usio_btn::strong_hit_side_right: return "Strong Hit Side Right";
case usio_btn::strong_hit_center_left: return "Strong Hit Center Left";
case usio_btn::strong_hit_center_right: return "Strong Hit Center Right";
case usio_btn::small_hit_side_left: return "Small Hit Side Left";
case usio_btn::small_hit_side_right: return "Small Hit Side Right";
case usio_btn::small_hit_center_left: return "Small Hit Center Left";
case usio_btn::small_hit_center_right: return "Small Hit Center Right";
case usio_btn::left: return "Left";
case usio_btn::right: return "Right";
case usio_btn::taiko_hit_side_left: return "Taiko Hit Side Left";
case usio_btn::taiko_hit_side_right: return "Taiko Hit Side Right";
case usio_btn::taiko_hit_center_left: return "Taiko Hit Center Left";
case usio_btn::taiko_hit_center_right: return "Taiko Hit Center Right";
case usio_btn::tekken_button1: return "Tekken Button 1";
case usio_btn::tekken_button2: return "Tekken Button 2";
case usio_btn::tekken_button3: return "Tekken Button 3";
case usio_btn::tekken_button4: return "Tekken Button 4";
case usio_btn::tekken_button5: return "Tekken Button 5";
case usio_btn::count: return "Count";
}
@ -47,11 +49,11 @@ struct usio_memory
void init()
{
backup_memory.clear();
backup_memory.resize(chip_size * chip_count);
backup_memory.resize(page_size * page_count);
}
static constexpr usz chip_size = 0x10000;
static constexpr usz chip_count = 0x10;
static constexpr usz page_size = 0x10000;
static constexpr usz page_count = 0x10;
};
usb_device_usio::usb_device_usio(const std::array<u8, 7>& location)
@ -179,15 +181,14 @@ void usb_device_usio::save_backup()
usio_backup_file.trunc(file_size);
}
void usb_device_usio::translate_input()
void usb_device_usio::translate_input_taiko()
{
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
std::vector<u8> input_buf = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0x60, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr le_t<u16> c_small_hit = 0x4D0;
constexpr le_t<u16> c_big_hit = 0x1800;
le_t<u16> test_keys = 0x0000;
std::vector<u8> input_buf(96);
constexpr le_t<u16> c_hit = 0x1800;
le_t<u16> digital_input = 0;
auto translate_from_pad = [&](usz pad_number, usz player)
{
@ -202,7 +203,7 @@ void usb_device_usio::translate_input()
return;
}
const usz offset = (player * 8ULL);
const usz offset = player * 8ULL;
const auto& cfg = ::at32(g_cfg_usio.players, pad_number);
cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed)
@ -221,89 +222,160 @@ void usb_device_usio::translate_input()
coin_counter++;
coin_key_pressed = pressed;
break;
case usio_btn::service:
if (player == 0 && pressed)
digital_input |= 0x4000;
break;
case usio_btn::enter:
if (player == 0 && pressed)
test_keys |= 0x200; // Enter
digital_input |= 0x200;
break;
case usio_btn::up:
if (player == 0 && pressed)
test_keys |= 0x2000; // Up
digital_input |= 0x2000;
break;
case usio_btn::down:
if (player == 0 && pressed)
test_keys |= 0x1000; // Down
digital_input |= 0x1000;
break;
case usio_btn::service:
if (player == 0 && pressed)
test_keys |= 0x4000; // Service
break;
case usio_btn::strong_hit_side_left:
// Strong hit side left
case usio_btn::taiko_hit_side_left:
if (pressed)
std::memcpy(input_buf.data() + 32 + offset, &c_big_hit, sizeof(u16));
std::memcpy(input_buf.data() + 32 + offset, &c_hit, sizeof(u16));
break;
case usio_btn::strong_hit_center_right:
// Strong hit center right
case usio_btn::taiko_hit_center_right:
if (pressed)
std::memcpy(input_buf.data() + 36 + offset, &c_big_hit, sizeof(u16));
std::memcpy(input_buf.data() + 36 + offset, &c_hit, sizeof(u16));
break;
case usio_btn::strong_hit_side_right:
// Strong hit side right
case usio_btn::taiko_hit_side_right:
if (pressed)
std::memcpy(input_buf.data() + 38 + offset, &c_big_hit, sizeof(u16));
std::memcpy(input_buf.data() + 38 + offset, &c_hit, sizeof(u16));
break;
case usio_btn::strong_hit_center_left:
// Strong hit center left
case usio_btn::taiko_hit_center_left:
if (pressed)
std::memcpy(input_buf.data() + 34 + offset, &c_big_hit, sizeof(u16));
std::memcpy(input_buf.data() + 34 + offset, &c_hit, sizeof(u16));
break;
case usio_btn::small_hit_center_left:
// Small hit center left
if (pressed)
std::memcpy(input_buf.data() + 34 + offset, &c_small_hit, sizeof(u16));
break;
case usio_btn::small_hit_center_right:
// Small hit center right
if (pressed)
std::memcpy(input_buf.data() + 36 + offset, &c_small_hit, sizeof(u16));
break;
case usio_btn::small_hit_side_left:
// Small hit side left
if (pressed)
std::memcpy(input_buf.data() + 32 + offset, &c_small_hit, sizeof(u16));
break;
case usio_btn::small_hit_side_right:
// Small hit side right
if (pressed)
std::memcpy(input_buf.data() + 38 + offset, &c_small_hit, sizeof(u16));
break;
case usio_btn::count:
default:
break;
}
});
};
for (usz i = 0; i < g_cfg_usio.players.size(); i++)
{
translate_from_pad(i, i);
}
test_keys |= test_on ? 0x80 : 0x00;
std::memcpy(input_buf.data(), &test_keys, sizeof(u16));
digital_input |= test_on ? 0x80 : 0x00;
std::memcpy(input_buf.data(), &digital_input, sizeof(u16));
std::memcpy(input_buf.data() + 16, &coin_counter, sizeof(u16));
response = std::move(input_buf);
}
void usb_device_usio::translate_input_tekken()
{
std::lock_guard lock(pad::g_pad_mutex);
const auto handler = pad::get_current_handler();
std::vector<u8> input_buf(256);
le_t<u64> digital_input = 0;
auto translate_from_pad = [&](usz pad_number, usz player)
{
if (!is_input_allowed())
{
return;
}
const auto& pad = ::at32(handler->GetPads(), pad_number);
if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
{
return;
}
const usz shift = player * 24ULL;
const auto& cfg = ::at32(g_cfg_usio.players, pad_number);
cfg->handle_input(pad, false, [&](usio_btn btn, u16 /*value*/, bool pressed)
{
switch (btn)
{
case usio_btn::test:
if (player != 0)
break;
if (pressed && !test_key_pressed) // Solve the need to hold the Test key
test_on = !test_on;
test_key_pressed = pressed;
break;
case usio_btn::coin:
if (player != 0)
break;
if (pressed && !coin_key_pressed) // Ensure only one coin is inserted each time the Coin key is pressed
coin_counter++;
coin_key_pressed = pressed;
break;
case usio_btn::service:
if (player == 0 && pressed)
digital_input |= 0x4000;
break;
case usio_btn::enter:
if (pressed)
digital_input |= 0x800000ULL << shift;
break;
case usio_btn::up:
if (pressed)
digital_input |= 0x200000ULL << shift;
break;
case usio_btn::down:
if ( pressed)
digital_input |= 0x100000ULL << shift;
break;
case usio_btn::left:
if (pressed)
digital_input |= 0x80000ULL << shift;
break;
case usio_btn::right:
if (pressed)
digital_input |= 0x40000ULL << shift;
break;
case usio_btn::tekken_button1:
if (pressed)
digital_input |= 0x20000ULL << shift;
break;
case usio_btn::tekken_button2:
if (pressed)
digital_input |= 0x10000ULL << shift;
break;
case usio_btn::tekken_button3:
if (pressed)
digital_input |= 0x40000000ULL << shift;
break;
case usio_btn::tekken_button4:
if (pressed)
digital_input |= 0x20000000ULL << shift;
break;
case usio_btn::tekken_button5:
if (pressed)
digital_input |= 0x80000000ULL << shift;
break;
default:
break;
}
});
};
for (usz i = 0; i < g_cfg_usio.players.size(); i++)
translate_from_pad(i, i);
digital_input |= test_on ? 0x80 : 0x00;
std::memcpy(input_buf.data() + 128, &digital_input, sizeof(u64));
std::memcpy(input_buf.data() + 128 + 16, &coin_counter, sizeof(u16));
input_buf[2] = 0b00010000; // DIP Switches, 8 in total
response = std::move(input_buf);
}
void usb_device_usio::usio_write(u8 channel, u16 reg, std::vector<u8>& data)
{
auto write_memory = [&](std::vector<u8>& memory)
{
auto size = memory.size();
memory = std::move(data);
memory.resize(size);
};
const auto get_u16 = [&](std::string_view usio_func) -> u16
{
if (data.size() != 2)
@ -351,27 +423,27 @@ void usb_device_usio::usio_write(u8 channel, u16 reg, std::vector<u8>& data)
}
default:
{
//usio_log.error("Unhandled channel 0 register write: 0x%04X", reg);
usio_log.trace("Unhandled channel 0 register write(reg: 0x%04X, size: 0x%04X, data: %s)", reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size()));
break;
}
}
}
else if (channel >= 2)
{
const u8 chip = channel - 2;
usio_log.trace("Usio write of sram(chip: %d, addr: 0x%04X)", chip, reg);
const u8 page = channel - 2;
usio_log.trace("Usio write of sram(page: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s)", page, reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size()));
auto& memory = g_fxo->get<usio_memory>().backup_memory;
const usz addr_end = reg + data.size();
if (data.size() > 0 && chip < usio_memory::chip_count && addr_end <= usio_memory::chip_size)
std::memcpy(&memory[usio_memory::chip_size * chip + reg], data.data(), data.size());
if (data.size() > 0 && page < usio_memory::page_count && addr_end <= usio_memory::page_size)
std::memcpy(&memory[usio_memory::page_size * page + reg], data.data(), data.size());
else
usio_log.error("Usio sram invalid write operation(chip: %d, addr: 0x%04X, size: %x)", chip, reg, data.size());
usio_log.error("Usio sram invalid write operation(page: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s)", page, reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size()));
}
else
{
// Channel 1 is the endpoint for firmware update.
// We are not using any firmware since this is emulation.
usio_log.warning("Unsupported write operation(channel: 0x%02X, addr: 0x%04X)", channel, reg);
usio_log.trace("Unsupported write operation(channel: 0x%02X, addr: 0x%04X, size: 0x%04X, data: %s)", channel, reg, data.size(), fmt::buf_to_hexstring(data.data(), data.size()));
}
}
@ -401,50 +473,56 @@ void usb_device_usio::usio_read(u8 channel, u16 reg, u16 size)
// No data returned
break;
}
case 0x1000:
{
// Often called, gets input from usio for Tekken
translate_input_tekken();
break;
}
case 0x1080:
{
// Often called, gets input from usio
translate_input();
// Often called, gets input from usio for Taiko
translate_input_taiko();
break;
}
case 0x1800:
{
// Firmware
// "NBGI.;USIO01;Ver1.00;JPN,Multipurpose with PPG."
response = {0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
response = {0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x75, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
break;
}
case 0x1880:
{
// Seems to contain a few extra bytes of info in addition to the firmware string
// Firmware
// "NBGI2;USIO01;Ver1.00;JPN,Multipurpose with PPG."
response = {0x4E, 0x42, 0x47, 0x49, 0x32, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x08, 0xE2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// "NBGI.;USIO01;Ver1.00;JPN,Multipurpose with PPG."
response = {0x4E, 0x42, 0x47, 0x49, 0x2E, 0x3B, 0x55, 0x53, 0x49, 0x4F, 0x30, 0x31, 0x3B, 0x56, 0x65, 0x72, 0x31, 0x2E, 0x30, 0x30, 0x3B, 0x4A, 0x50, 0x4E, 0x2C, 0x4D, 0x75, 0x6C, 0x74, 0x69, 0x70, 0x75, 0x72, 0x70, 0x6F, 0x73, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x50, 0x50, 0x47, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x30, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x02, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x75, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
break;
}
default:
{
usio_log.error("Unhandled channel 0 register read: 0x%04X", reg);
usio_log.trace("Unhandled channel 0 register read(reg: 0x%04X, size: 0x%04X)", reg, size);
break;
}
}
}
else if (channel >= 2)
{
const u8 chip = channel - 2;
usio_log.trace("Usio read of sram(chip: %d, addr: 0x%04X)", chip, reg);
const u8 page = channel - 2;
usio_log.trace("Usio read of sram(page: 0x%02X, addr: 0x%04X, size: 0x%04X)", page, reg, size);
auto& memory = g_fxo->get<usio_memory>().backup_memory;
const usz addr_end = reg + size;
if (size > 0 && chip < usio_memory::chip_count && addr_end <= usio_memory::chip_size)
response.insert(response.end(), memory.begin() + (usio_memory::chip_size * chip + reg), memory.begin() + (usio_memory::chip_size * chip + addr_end));
if (size > 0 && page < usio_memory::page_count && addr_end <= usio_memory::page_size)
response.insert(response.end(), memory.begin() + (usio_memory::page_size * page + reg), memory.begin() + (usio_memory::page_size * page + addr_end));
else
usio_log.error("Usio sram invalid read operation(chip: %d, addr: 0x%04X, size: %x)", chip, reg, size);
usio_log.error("Usio sram invalid read operation(page: 0x%02X, addr: 0x%04X, size: 0x%04X)", page, reg, size);
}
else
{
// Channel 1 is the endpoint for firmware update.
// We are not using any firmware since this is emulation.
usio_log.warning("Unsupported read operation(channel: 0x%02X, addr: 0x%04X)", channel, reg);
usio_log.trace("Unsupported read operation(channel: 0x%02X, addr: 0x%04X, size: 0x%04X)", channel, reg, size);
}
response.resize(size); // Always resize the response vector to the given size

View File

@ -15,7 +15,8 @@ public:
private:
void load_backup();
void save_backup();
void translate_input();
void translate_input_taiko();
void translate_input_tekken();
void usio_write(u8 channel, u16 reg, std::vector<u8>& data);
void usio_read(u8 channel, u16 reg, u16 size);

View File

@ -8,18 +8,21 @@ enum class usio_btn
{
test,
coin,
service,
enter,
up,
down,
service,
strong_hit_side_left,
strong_hit_side_right,
strong_hit_center_left,
strong_hit_center_right,
small_hit_side_left,
small_hit_side_right,
small_hit_center_left,
small_hit_center_right,
left,
right,
taiko_hit_side_left,
taiko_hit_side_right,
taiko_hit_center_left,
taiko_hit_center_right,
tekken_button1,
tekken_button2,
tekken_button3,
tekken_button4,
tekken_button5,
count
};
@ -29,19 +32,22 @@ struct cfg_usio final : public emulated_pad_config<usio_btn>
cfg_usio(node* owner, const std::string& name) : emulated_pad_config(owner, name) {}
cfg_pad_btn<usio_btn> test{ this, "Test", usio_btn::test, pad_button::select };
cfg_pad_btn<usio_btn> coin{ this, "Coin", usio_btn::coin, pad_button::dpad_left };
cfg_pad_btn<usio_btn> enter{ this, "Enter", usio_btn::enter, pad_button::start };
cfg_pad_btn<usio_btn> coin{ this, "Coin", usio_btn::coin, pad_button::L3 };
cfg_pad_btn<usio_btn> service{this, "Service", usio_btn::service, pad_button::R3};
cfg_pad_btn<usio_btn> enter{ this, "Enter/Start", usio_btn::enter, pad_button::start };
cfg_pad_btn<usio_btn> up{ this, "Up", usio_btn::up, pad_button::dpad_up };
cfg_pad_btn<usio_btn> down{ this, "Down", usio_btn::down, pad_button::dpad_down };
cfg_pad_btn<usio_btn> service{ this, "Service", usio_btn::service, pad_button::dpad_right };
cfg_pad_btn<usio_btn> strong_hit_side_left{ this, "Strong Hit Side Left", usio_btn::strong_hit_side_left, pad_button::square };
cfg_pad_btn<usio_btn> strong_hit_side_right{ this, "Strong Hit Side Right", usio_btn::strong_hit_side_right, pad_button::circle };
cfg_pad_btn<usio_btn> strong_hit_center_left{ this, "Strong Hit Center Left", usio_btn::strong_hit_center_left, pad_button::triangle };
cfg_pad_btn<usio_btn> strong_hit_center_right{ this, "Strong Hit Center Right", usio_btn::strong_hit_center_right, pad_button::cross };
cfg_pad_btn<usio_btn> small_hit_side_left{ this, "Small Hit Side Left", usio_btn::small_hit_side_left, pad_button::L2 };
cfg_pad_btn<usio_btn> small_hit_side_right{ this, "Small Hit Side Right", usio_btn::small_hit_side_right, pad_button::R2 };
cfg_pad_btn<usio_btn> small_hit_center_left{ this, "Small Hit Center Left", usio_btn::small_hit_center_left, pad_button::L1 };
cfg_pad_btn<usio_btn> small_hit_center_right{ this, "Small Hit Center Right", usio_btn::small_hit_center_right, pad_button::R1 };
cfg_pad_btn<usio_btn> left{this, "Left", usio_btn::left, pad_button::dpad_left};
cfg_pad_btn<usio_btn> right{this, "Right", usio_btn::right, pad_button::dpad_right};
cfg_pad_btn<usio_btn> taiko_hit_side_left{ this, "Taiko Hit Side Left", usio_btn::taiko_hit_side_left, pad_button::square };
cfg_pad_btn<usio_btn> taiko_hit_side_right{ this, "Taiko Hit Side Right", usio_btn::taiko_hit_side_right, pad_button::circle };
cfg_pad_btn<usio_btn> taiko_hit_center_left{ this, "Taiko Hit Center Left", usio_btn::taiko_hit_center_left, pad_button::triangle };
cfg_pad_btn<usio_btn> taiko_hit_center_right{ this, "Taiko Hit Center Right", usio_btn::taiko_hit_center_right, pad_button::cross };
cfg_pad_btn<usio_btn> tekken_button1{this, "Tekken Button 1", usio_btn::tekken_button1, pad_button::square};
cfg_pad_btn<usio_btn> tekken_button2{this, "Tekken Button 2", usio_btn::tekken_button2, pad_button::triangle};
cfg_pad_btn<usio_btn> tekken_button3{this, "Tekken Button 3", usio_btn::tekken_button3, pad_button::cross};
cfg_pad_btn<usio_btn> tekken_button4{this, "Tekken Button 4", usio_btn::tekken_button4, pad_button::circle};
cfg_pad_btn<usio_btn> tekken_button5{this, "Tekken Button 5", usio_btn::tekken_button5, pad_button::R1};
};
struct cfg_usios final : public emulated_pads_config<cfg_usio, 2>

View File

@ -506,13 +506,10 @@ namespace rsx
ar(u32{0});
}
}
else if (version > 1)
else if (u32 count = ar)
{
if (u32 count = ar)
{
restore_fifo_count = count;
ar(restore_fifo_cmd);
}
restore_fifo_count = count;
ar(restore_fifo_cmd);
}
}

View File

@ -82,7 +82,7 @@ extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const
extern bool ppu_load_rel_exec(const ppu_rel_object&);
extern bool is_savestate_version_compatible(const std::vector<std::pair<u16, u16>>& data, bool is_boot_check);
extern std::vector<std::pair<u16, u16>> read_used_savestate_versions();
std::string get_savestate_path(std::string_view title_id, std::string_view boot_path);
std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id);
extern void send_close_home_menu_cmds();
@ -492,7 +492,7 @@ void Emulator::Init()
make_path_verbose(fs::get_cache_dir() + "shaderlog/", false);
make_path_verbose(fs::get_cache_dir() + "spu_progs/", false);
make_path_verbose(fs::get_cache_dir() + "/savestates/", false);
make_path_verbose(fs::get_parent_dir(get_savestate_file("NO_ID", "/NO_FILE", -1, -1)), false);
make_path_verbose(fs::get_config_dir() + "captures/", false);
make_path_verbose(fs::get_config_dir() + "sounds/", false);
make_path_verbose(patch_engine::get_patches_path(), false);
@ -2180,7 +2180,7 @@ void Emulator::FixGuestTime()
// Mark a known savestate location and the one we try to boot (in case we boot a moved/copied savestate)
if (g_cfg.savestate.suspend_emu)
{
for (std::string old_path : std::initializer_list<std::string>{m_ar ? m_path_old : "", m_title_id.empty() ? "" : get_savestate_path(m_title_id, m_path_old)})
for (std::string old_path : std::initializer_list<std::string>{m_ar ? m_path_old : "", m_title_id.empty() ? "" : get_savestate_file(m_title_id, m_path_old, 0, 0)})
{
if (old_path.empty())
{
@ -2841,7 +2841,12 @@ void Emulator::Kill(bool allow_autoexit, bool savestate)
if (savestate)
{
const std::string path = get_savestate_path(m_title_id, m_path);
const std::string path = get_savestate_file(m_title_id, m_path, 0, 0);
if (!fs::create_path(fs::get_parent_dir(path)))
{
sys_log.error("Failed to create savestate directory! (path='%s', %s)", fs::get_parent_dir(path), fs::g_tls_error);
}
fs::pending_file file(path);

View File

@ -35,20 +35,20 @@ static std::array<serial_ver_t, 23> s_serial_versions;
return ::s_serial_versions[identifier].current_version;\
}
SERIALIZATION_VER(global_version, 0, 12) // For stuff not listed here
SERIALIZATION_VER(ppu, 1, 1, 2 /*thread sleep queue order*/)
SERIALIZATION_VER(spu, 2, 1, 2 /*spu_limits_t ctor*/, 3 /*thread sleep queue order*/)
SERIALIZATION_VER(global_version, 0, 13) // For stuff not listed here
SERIALIZATION_VER(ppu, 1, 1)
SERIALIZATION_VER(spu, 2, 1)
SERIALIZATION_VER(lv2_sync, 3, 1)
SERIALIZATION_VER(lv2_vm, 4, 1)
SERIALIZATION_VER(lv2_net, 5, 1, 2/*RECV/SEND timeout*/)
SERIALIZATION_VER(lv2_net, 5, 1)
SERIALIZATION_VER(lv2_fs, 6, 1)
SERIALIZATION_VER(lv2_prx_overlay, 7, 1, 2/*PRX dynamic exports*/, 4/*Conditionally Loaded Local Exports*/)
SERIALIZATION_VER(lv2_prx_overlay, 7, 1)
SERIALIZATION_VER(lv2_memory, 8, 1)
SERIALIZATION_VER(lv2_config, 9, 1)
namespace rsx
{
SERIALIZATION_VER(rsx, 10, 1, 2)
SERIALIZATION_VER(rsx, 10, 1)
}
namespace np
@ -65,7 +65,7 @@ SERIALIZATION_VER(sceNp, 11)
SERIALIZATION_VER(cellVdec, 12, 1)
SERIALIZATION_VER(cellAudio, 13, 1)
SERIALIZATION_VER(cellCamera, 14, 1)
SERIALIZATION_VER(cellGem, 15, 1, 2/*frame_timestamp u32->u64*/)
SERIALIZATION_VER(cellGem, 15, 1)
SERIALIZATION_VER(sceNpTrophy, 16, 1)
SERIALIZATION_VER(cellMusic, 17, 1)
SERIALIZATION_VER(cellVoice, 18, 1)
@ -146,9 +146,25 @@ bool is_savestate_version_compatible(const std::vector<std::pair<u16, u16>>& dat
return ok;
}
std::string get_savestate_path(std::string_view title_id, std::string_view boot_path)
std::string get_savestate_file(std::string_view title_id, std::string_view boot_path, s64 abs_id, s64 rel_id)
{
return fs::get_cache_dir() + "/savestates/" + std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id} + ".SAVESTAT";
const std::string title = std::string{title_id.empty() ? boot_path.substr(boot_path.find_last_of(fs::delim) + 1) : title_id};
if (abs_id == -1 && rel_id == -1)
{
// Return directory
return fs::get_cache_dir() + "/savestates/" + title + "/";
}
ensure(rel_id < 0 || abs_id >= 0, "Unimplemented!");
const std::string save_id = fmt::format("%d", abs_id);
// Make sure that savestate file with higher IDs are placed at the bottom of "by name" file ordering in directory view by adding a single character prefix which tells the ID length
// While not needing to keep a 59 chars long suffix at all times for this purpose
const char prefix = ::at32("0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"sv, save_id.size());
return fs::get_cache_dir() + "/savestates/" + title + "/" + title + '_' + prefix + '_' + save_id + ".SAVESTAT";
}
bool is_savestate_compatible(const fs::file& file)

View File

@ -50,6 +50,8 @@ LOG_CHANNEL(sys_log, "SYS");
extern atomic_t<bool> g_system_progress_canceled;
std::string get_savestate_file(std::string_view title_id, std::string_view boot_pat, s64 abs_id, s64 rel_id);
inline std::string sstr(const QString& _in) { return _in.toStdString(); }
game_list_frame::game_list_frame(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget* parent)
@ -1010,7 +1012,12 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
// Make Actions
QMenu menu;
const bool is_current_running_game = (Emu.IsRunning() || Emu.IsPaused()) && current_game.serial == Emu.GetTitleID();
static const auto is_game_running = [](const std::string& serial)
{
return Emu.GetStatus(false) != system_state::stopped && serial == Emu.GetTitleID();
};
const bool is_current_running_game = is_game_running(current_game.serial);
QAction* boot = new QAction(gameinfo->hasCustomConfig
? (is_current_running_game
@ -1073,7 +1080,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
extern bool is_savestate_compatible(const fs::file& file);
if (const std::string sstate = fs::get_cache_dir() + "/savestates/" + current_game.serial + ".SAVESTAT"; is_savestate_compatible(fs::file(sstate)))
if (const std::string sstate = get_savestate_file(current_game.serial, current_game.path, 0, 0); is_savestate_compatible(fs::file(sstate)))
{
QAction* boot_state = menu.addAction(is_current_running_game
? tr("&Reboot with savestate")
@ -1119,6 +1126,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
menu.addSeparator();
QMenu* remove_menu = menu.addMenu(tr("&Remove"));
QAction* remove_game = remove_menu->addAction(tr("&Remove %1").arg(gameinfo->localized_category));
remove_game->setEnabled(!is_current_running_game);
if (gameinfo->hasCustomConfig)
{
QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration"));
@ -1141,27 +1149,63 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
}
});
}
if (fs::is_dir(cache_base_dir))
const bool has_cache_dir = fs::is_dir(cache_base_dir);
if (has_cache_dir)
{
remove_menu->addSeparator();
QAction* remove_shaders_cache = remove_menu->addAction(tr("&Remove Shaders Cache"));
remove_shaders_cache->setEnabled(!is_current_running_game);
connect(remove_shaders_cache, &QAction::triggered, [this, cache_base_dir]()
{
RemoveShadersCache(cache_base_dir, true);
});
QAction* remove_ppu_cache = remove_menu->addAction(tr("&Remove PPU Cache"));
remove_ppu_cache->setEnabled(!is_current_running_game);
connect(remove_ppu_cache, &QAction::triggered, [this, cache_base_dir]()
{
RemovePPUCache(cache_base_dir, true);
});
QAction* remove_spu_cache = remove_menu->addAction(tr("&Remove SPU Cache"));
remove_spu_cache->setEnabled(!is_current_running_game);
connect(remove_spu_cache, &QAction::triggered, [this, cache_base_dir]()
{
RemoveSPUCache(cache_base_dir, true);
});
QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches"));
connect(remove_all_caches, &QAction::triggered, [this, cache_base_dir]()
}
bool has_hdd1_cache = false;
const std::string hdd1 = rpcs3::utils::get_hdd1_dir() + "/caches/";
for (const auto& entry : fs::dir(hdd1))
{
if (entry.is_directory && entry.name.starts_with(current_game.serial))
{
has_hdd1_cache = true;
break;
}
}
if (has_hdd1_cache)
{
QAction* remove_hdd1_cache = remove_menu->addAction(tr("&Remove HDD1 Cache"));
remove_hdd1_cache->setEnabled(!is_current_running_game);
connect(remove_hdd1_cache, &QAction::triggered, [this, hdd1, serial = current_game.serial]()
{
RemoveHDD1Cache(hdd1, serial, true);
});
}
if (has_cache_dir || has_hdd1_cache)
{
QAction* remove_all_caches = remove_menu->addAction(tr("&Remove All Caches"));
remove_all_caches->setEnabled(!is_current_running_game);
connect(remove_all_caches, &QAction::triggered, [this, current_game, cache_base_dir, hdd1]()
{
if (is_game_running(current_game.serial))
return;
if (QMessageBox::question(this, tr("Confirm Removal"), tr("Remove all caches?")) != QMessageBox::Yes)
return;
@ -1169,8 +1213,11 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
game_list_log.success("Removed cache directory: '%s'", cache_base_dir);
else
game_list_log.error("Could not remove cache directory: '%s' (%s)", cache_base_dir, fs::g_tls_error);
RemoveHDD1Cache(hdd1, current_game.serial);
});
}
menu.addSeparator();
QAction* open_game_folder = menu.addAction(tr("&Open Install Folder"));
if (gameinfo->hasCustomConfig)
@ -1404,6 +1451,12 @@ void game_list_frame::ShowContextMenu(const QPoint &pos)
return;
}
if (is_game_running(current_game.serial))
{
QMessageBox::critical(this, tr("Cannot Remove Game"), tr("The PS3 application is still running, it cannot be removed!"));
return;
}
QString size_information;
if (current_game.size_on_disk != umax)
@ -1812,6 +1865,48 @@ bool game_list_frame::RemoveSPUCache(const std::string& base_dir, bool is_intera
return success;
}
void game_list_frame::RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive)
{
if (!fs::is_dir(base_dir))
return;
if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove HDD1 cache?")) != QMessageBox::Yes)
return;
u32 dirs_removed = 0;
u32 dirs_total = 0;
const QString q_base_dir = qstr(base_dir);
const QStringList filter{ qstr(title_id + "_*") };
QDirIterator dir_iter(q_base_dir, filter, QDir::Dirs | QDir::NoDotAndDotDot);
while (dir_iter.hasNext())
{
const QString filepath = dir_iter.next();
if (fs::remove_all(filepath.toStdString()))
{
++dirs_removed;
game_list_log.notice("Removed HDD1 cache directory: %s", filepath);
}
else
{
game_list_log.warning("Could not remove HDD1 cache directory: %s", filepath);
}
++dirs_total;
}
const bool success = dirs_removed == dirs_total;
if (success)
game_list_log.success("Removed HDD1 cache in %s (%s)", base_dir, title_id);
else
game_list_log.fatal("Only %d/%d HDD1 cache directories could be removed in %s (%s)", dirs_removed, dirs_total, base_dir, title_id);
}
void game_list_frame::BatchCreatePPUCaches()
{
const std::string vsh_path = g_cfg_vfs.get_dev_flash() + "vsh/module/";
@ -1903,6 +1998,11 @@ void game_list_frame::BatchCreatePPUCaches()
void game_list_frame::BatchRemovePPUCaches()
{
if (Emu.GetStatus(false) != system_state::stopped)
{
return;
}
std::set<std::string> serials;
for (const auto& game : m_game_data)
{
@ -1944,6 +2044,11 @@ void game_list_frame::BatchRemovePPUCaches()
void game_list_frame::BatchRemoveSPUCaches()
{
if (Emu.GetStatus(false) != system_state::stopped)
{
return;
}
std::set<std::string> serials;
for (const auto& game : m_game_data)
{
@ -2075,6 +2180,11 @@ void game_list_frame::BatchRemoveCustomPadConfigurations()
void game_list_frame::BatchRemoveShaderCaches()
{
if (Emu.GetStatus(false) != system_state::stopped)
{
return;
}
std::set<std::string> serials;
for (const auto& game : m_game_data)
{

View File

@ -107,6 +107,7 @@ private:
bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false);
bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false);
bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false);
void RemoveHDD1Cache(const std::string& base_dir, const std::string& title_id, bool is_interactive = false);
static bool CreatePPUCache(const std::string& path, const std::string& serial = {});
static bool CreatePPUCache(const game_info& game);

View File

@ -1,5 +1,6 @@
#include "gl_gs_frame.h"
#include "Emu/System.h"
#include "Emu/system_config.h"
#include <QOpenGLContext>
@ -31,7 +32,11 @@ draw_context_t gl_gs_frame::make_context()
{
auto surface = new QOffscreenSurface();
surface->setFormat(m_format);
surface->create();
// Workaround for the Qt warning: "Attempting to create QWindow-based QOffscreenSurface outside the gui thread. Expect failures."
Emu.BlockingCallFromMainThread([&]()
{
surface->create();
});
// Share resources with the first created context
context->handle->setShareContext(m_primary_context->handle);
@ -117,5 +122,8 @@ void gl_gs_frame::flip(draw_context_t context, bool skip_frame)
const auto gl_ctx = static_cast<GLContext*>(context);
gl_ctx->handle->swapBuffers(gl_ctx->surface);
if (auto window = dynamic_cast<QWindow*>(gl_ctx->surface); window && window->isExposed())
{
gl_ctx->handle->swapBuffers(gl_ctx->surface);
}
}

View File

@ -9,6 +9,8 @@
#include <QCoreApplication>
#include <QMessageBox>
#include <thread>
LOG_CHANNEL(cfg_log, "CFG");
namespace gui
@ -188,6 +190,12 @@ void gui_settings::ShowInfoBox(const QString& title, const QString& text, const
bool gui_settings::GetBootConfirmation(QWidget* parent, const gui_save& gui_save_entry)
{
while (Emu.GetStatus(false) == system_state::stopping)
{
QCoreApplication::processEvents();
std::this_thread::sleep_for(16ms);
}
if (!Emu.IsStopped())
{
QString title = tr("Close Running Game?");

View File

@ -182,15 +182,19 @@ bool main_window::Init([[maybe_unused]] bool with_cli_boot)
connect(m_thumb_stop, &QWinThumbnailToolButton::clicked, this, []()
{
gui_log.notice("User clicked stop button on thumbnail toolbar");
gui_log.notice("User clicked the stop button on thumbnail toolbar");
Emu.GracefulShutdown(false, true);
});
connect(m_thumb_restart, &QWinThumbnailToolButton::clicked, this, []()
{
gui_log.notice("User clicked restart button on thumbnail toolbar");
gui_log.notice("User clicked the restart button on thumbnail toolbar");
Emu.Restart();
});
connect(m_thumb_playPause, &QWinThumbnailToolButton::clicked, this, &main_window::OnPlayOrPause);
connect(m_thumb_playPause, &QWinThumbnailToolButton::clicked, this, [this]()
{
gui_log.notice("User clicked the playPause button on thumbnail toolbar");
OnPlayOrPause();
});
#endif
// RPCS3 Updater
@ -1909,6 +1913,12 @@ void main_window::OnEmuStop()
m_thumb_restart->setEnabled(true);
#endif
}
ui->batchRemovePPUCachesAct->setEnabled(true);
ui->batchRemoveSPUCachesAct->setEnabled(true);
ui->batchRemoveShaderCachesAct->setEnabled(true);
ui->removeDiskCacheAct->setEnabled(true);
ui->actionManage_Users->setEnabled(true);
ui->confCamerasAct->setEnabled(true);
@ -1951,6 +1961,11 @@ void main_window::OnEmuReady() const
ui->actionManage_Users->setEnabled(false);
ui->confCamerasAct->setEnabled(false);
ui->batchRemovePPUCachesAct->setEnabled(false);
ui->batchRemoveSPUCachesAct->setEnabled(false);
ui->batchRemoveShaderCachesAct->setEnabled(false);
ui->removeDiskCacheAct->setEnabled(false);
}
void main_window::EnableMenus(bool enabled) const

View File

@ -52,7 +52,7 @@ namespace utils
#ifdef MAP_NORESERVE
constexpr int c_map_noreserve = MAP_NORESERVE;
#else
constexpr int c_map_noreserve = 0;
[[maybe_unused]] constexpr int c_map_noreserve = 0;
#endif
#ifdef MADV_FREE
@ -66,7 +66,7 @@ namespace utils
#ifdef MADV_HUGEPAGE
constexpr int c_madv_hugepage = MADV_HUGEPAGE;
#else
constexpr int c_madv_hugepage = 0;
[[maybe_unused]] constexpr int c_madv_hugepage = 0;
#endif
#if defined(MADV_DONTDUMP) && defined(MADV_DODUMP)
@ -76,8 +76,8 @@ namespace utils
constexpr int c_madv_no_dump = MADV_NOCORE;
constexpr int c_madv_dump = MADV_CORE;
#else
constexpr int c_madv_no_dump = 0;
constexpr int c_madv_dump = 0;
[[maybe_unused]] constexpr int c_madv_no_dump = 0;
[[maybe_unused]] constexpr int c_madv_dump = 0;
#endif
#if defined(MFD_HUGETLB) && defined(MFD_HUGE_2MB)