diff --git a/rpcs3/Emu/Cell/SPURecompiler.cpp b/rpcs3/Emu/Cell/SPURecompiler.cpp index fd213b1247..0ef9903be1 100644 --- a/rpcs3/Emu/Cell/SPURecompiler.cpp +++ b/rpcs3/Emu/Cell/SPURecompiler.cpp @@ -565,6 +565,21 @@ extern void utilize_spu_data_segment(u32 vaddr, const void* ls_data_vaddr, u32 s g_fxo->get().precompile_funcs.push(std::move(obj)); } +// For SPU cache validity check +static u16 calculate_crc16(const uchar* data, usz length) +{ + u16 crc = umax; + + while (length--) + { + u8 x = (crc >> 8) ^ *data++; + x ^= (x >> 4); + crc = static_cast((crc << 8) ^ (x << 12) ^ (x << 5) ^ x); + } + + return crc; +} + std::deque spu_cache::get() { std::deque result; @@ -579,20 +594,29 @@ std::deque spu_cache::get() // TODO: signal truncated or otherwise broken file while (true) { - be_t size; - be_t addr; + struct block_info_t + { + be_t crc; + be_t size; + be_t addr; + } block_info{}; + + if (!m_file.read(block_info)) + { + break; + } + + const u32 crc = block_info.crc; + const u32 size = block_info.size; + const u32 addr = block_info.addr; + + if (utils::add_saturate(addr, size * 4) > SPU_LS_SIZE) + { + break; + } + std::vector func; - if (!m_file.read(size) || !m_file.read(addr)) - { - break; - } - - if (utils::add_saturate(addr, utils::mul_saturate(size, 4)) > SPU_LS_SIZE) - { - break; - } - if (!m_file.read(func, size)) { break; @@ -604,6 +628,13 @@ std::deque spu_cache::get() continue; } + // CRC check is optional to be compatible with old format + if (crc && std::max(calculate_crc16(reinterpret_cast(func.data()), size * 4), 1) != crc) + { + // Invalid, but continue anyway + continue; + } + spu_program res; res.entry_point = addr; res.lower_bound = addr; @@ -624,6 +655,9 @@ void spu_cache::add(const spu_program& func) be_t size = ::size32(func.data); be_t addr = func.entry_point; + // Add CRC (forced non-zero) + size |= std::max(calculate_crc16(reinterpret_cast(func.data.data()), size * 4), 1) << 16; + const fs::iovec_clone gather[3] { {&size, sizeof(size)},