void GLVertexDecompilerThread::Task() { m_parr.params.clear(); m_instr_count = 0; for (int i = 0; i < m_max_instr_count; ++i) m_instructions[i].reset(); for (u32 i = 0; m_instr_count < m_max_instr_count; m_instr_count++) { m_cur_instr = &m_instructions[m_instr_count]; d0.HEX = m_data[i++]; d1.HEX = m_data[i++]; d2.HEX = m_data[i++]; d3.HEX = m_data[i++]; src[0].src0l = d2.src0l; src[0].src0h = d1.src0h; src[1].src1 = d2.src1; src[2].src2l = d3.src2l; src[2].src2h = d2.src2h; if(!d1.sca_opcode && !d1.vec_opcode) { m_body.push_back("//nop"); } switch(d1.sca_opcode) { case 0x00: break; // NOP case 0x01: SetDSTSca("$s"); break; // MOV case 0x02: SetDSTSca("(1.0 / $s)"); break; // RCP case 0x03: SetDSTSca("clamp(1.0 / $s, 5.42101e-20, 1.884467e19)"); break; // RCC case 0x04: SetDSTSca("inversesqrt(abs($s))"); break; // RSQ case 0x05: SetDSTSca("exp($s)"); break; // EXP case 0x06: SetDSTSca("log($s)"); break; // LOG case 0x07: SetDSTSca("vec4(1.0, $s.x, ($s.x > 0 ? exp2($s.w * log2($s.y)) : 0.0), 1.0)"); break; // LIT //case 0x08: break; // BRA case 0x09: // BRI : works differently (BRI o[1].x(TR) L0;) //AddCode("$ifcond { $f(); return; }"); if (GetAddr() > m_instr_count) { AddCode("if(!$cond)"); AddCode("{"); m_cur_instr->open_scopes++; m_instructions[GetAddr()].put_close_scopes++; } else { AddCode("} while ($cond);"); m_cur_instr->close_scopes++; m_instructions[GetAddr()].do_count++; } break; //case 0x0a: AddCode("$ifcond $f(); //CAL"); break; // CAL : works same as BRI case 0x0b: AddCode("$ifcond $f(); //CLI"); break; // CLI : works same as BRI case 0x0c: AddCode("$ifcond return;"); break; // RET : works like BRI but shorter (RET o[1].x(TR);) case 0x0d: SetDSTSca("log2($s)"); break; // LG2 case 0x0e: SetDSTSca("exp2($s)"); break; // EX2 case 0x0f: SetDSTSca("sin($s)"); break; // SIN case 0x10: SetDSTSca("cos($s)"); break; // COS //case 0x11: break; // BRB : works differently (BRB o[1].x !b0, L0;) //case 0x12: break; // CLB : works same as BRB //case 0x13: break; // PSH : works differently (PSH o[1].x A0;) //case 0x14: break; // POP : works differently (POP o[1].x;) default: m_body.push_back(fmt::Format("//Unknown vp sca_opcode 0x%x", fmt::by_value(d1.sca_opcode))); ConLog.Error("Unknown vp sca_opcode 0x%x", fmt::by_value(d1.sca_opcode)); Emu.Pause(); break; } switch(d1.vec_opcode) { case 0x00: break; //NOP case 0x01: SetDSTVec("$0"); break; //MOV case 0x02: SetDSTVec("($0 * $1)"); break; //MUL case 0x03: SetDSTVec("($0 + $2)"); break; //ADD case 0x04: SetDSTVec("($0 * $1 + $2)"); break; //MAD case 0x05: SetDSTVec("vec2(dot($0.xyz, $1.xyz), 0.0).xxxx"); break; //DP3 case 0x06: SetDSTVec("vec2(dot(vec4($0.xyz, 1.0), $1), 0.0).xxxx"); break; //DPH case 0x07: SetDSTVec("vec2(dot($0, $1), 0.0).xxxx"); break; //DP4 case 0x08: SetDSTVec("vec2(distance($0, $1), 0.0).xxxx"); break; //DST case 0x09: SetDSTVec("min($0, $1)"); break; //MIN case 0x0a: SetDSTVec("max($0, $1)"); break; //MAX case 0x0b: SetDSTVec("vec4(lessThan($0, $1))"); break; //SLT case 0x0c: SetDSTVec("vec4(greaterThanEqual($0, $1))"); break; //SGE case 0x0d: AddCode("$ifcond $a = ivec4($0)$am;"); break; //ARL case 0x0e: SetDSTVec("fract($0)"); break; //FRC case 0x0f: SetDSTVec("floor($0)"); break; //FLR case 0x10: SetDSTVec("vec4(equal($0, $1))"); break; //SEQ case 0x11: SetDSTVec("vec4(equal($0, vec4(0.0)))"); break; //SFL case 0x12: SetDSTVec("vec4(greaterThan($0, $1))"); break; //SGT case 0x13: SetDSTVec("vec4(lessThanEqual($0, $1))"); break; //SLE case 0x14: SetDSTVec("vec4(notEqual($0, $1))"); break; //SNE case 0x15: SetDSTVec("vec4(equal($0, vec4(1.0)))"); break; //STR case 0x16: SetDSTVec("sign($0)"); break; //SSG default: m_body.push_back(fmt::Format("//Unknown vp opcode 0x%x", fmt::by_value(d1.vec_opcode))); ConLog.Error("Unknown vp opcode 0x%x", fmt::by_value(d1.vec_opcode)); Emu.Pause(); break; } if(d3.end) { m_instr_count++; if(i < m_data.size()) ConLog.Error("Program end before buffer end."); break; } } m_shader = BuildCode(); m_body.clear(); if (m_funcs.size() > 2) { m_funcs.erase(m_funcs.begin()+2, m_funcs.end()); } }
std::string VertexProgramDecompiler::Decompile() { for (unsigned i = 0; i < PF_PARAM_COUNT; i++) m_parr.params[i].clear(); m_instr_count = 0; for (int i = 0; i < m_max_instr_count; ++i) { m_instructions[i].reset(); } bool is_has_BRA = false; for (u32 i = 1; m_instr_count < m_max_instr_count; m_instr_count++) { m_cur_instr = &m_instructions[m_instr_count]; if (is_has_BRA) { d3.HEX = m_data[i]; i += 4; } else { d1.HEX = m_data[i++]; switch (d1.sca_opcode) { case 0x08: //BRA LOG_ERROR(RSX, "BRA found. Please report to RPCS3 team."); is_has_BRA = true; m_jump_lvls.clear(); d3.HEX = m_data[++i]; i += 4; break; case 0x09: //BRI d2.HEX = m_data[i++]; d3.HEX = m_data[i]; i += 2; m_jump_lvls.emplace(GetAddr()); break; default: d3.HEX = m_data[++i]; i += 2; break; } } if (d3.end) { m_instr_count++; if (i < m_data.size()) { LOG_ERROR(RSX, "Program end before buffer end."); } break; } } uint jump_position = 0; if (is_has_BRA || !m_jump_lvls.empty()) { m_cur_instr = &m_instructions[0]; AddCode("int jump_position = 0;"); AddCode("while (true)"); AddCode("{"); m_cur_instr->open_scopes++; AddCode(fmt::format("if (jump_position <= %u)", jump_position++)); AddCode("{"); m_cur_instr->open_scopes++; } for (u32 i = 0; i < m_instr_count; ++i) { m_cur_instr = &m_instructions[i]; d0.HEX = m_data[i * 4 + 0]; d1.HEX = m_data[i * 4 + 1]; d2.HEX = m_data[i * 4 + 2]; d3.HEX = m_data[i * 4 + 3]; src[0].src0l = d2.src0l; src[0].src0h = d1.src0h; src[1].src1 = d2.src1; src[2].src2l = d3.src2l; src[2].src2h = d2.src2h; if (i && (is_has_BRA || std::find(m_jump_lvls.begin(), m_jump_lvls.end(), i) != m_jump_lvls.end())) { m_cur_instr->close_scopes++; AddCode("}"); AddCode(""); AddCode(fmt::format("if (jump_position <= %u)", jump_position++)); AddCode("{"); m_cur_instr->open_scopes++; } if (!d1.sca_opcode && !d1.vec_opcode) { AddCode("//nop"); } switch (d1.sca_opcode) { case RSX_SCA_OPCODE_NOP: break; case RSX_SCA_OPCODE_MOV: SetDSTSca("$s"); break; case RSX_SCA_OPCODE_RCP: SetDSTSca("(1.0 / $s)"); break; case RSX_SCA_OPCODE_RCC: SetDSTSca("clamp(1.0 / $s, 5.42101e-20, 1.884467e19)"); break; case RSX_SCA_OPCODE_RSQ: SetDSTSca("(1.f / sqrt($s))"); break; case RSX_SCA_OPCODE_EXP: SetDSTSca("exp($s)"); break; case RSX_SCA_OPCODE_LOG: SetDSTSca("log($s)"); break; case RSX_SCA_OPCODE_LIT: SetDSTSca(getFloatTypeName(4) + "(1.0, $s.x, ($s.x > 0.0 ? exp($s.w * log2($s.y)) : 0.0), 1.0)"); break; case RSX_SCA_OPCODE_BRA: { AddCode("$if ($cond)"); AddCode("{"); m_cur_instr->open_scopes++; AddCode("jump_position = $a$am;"); AddCode("continue;"); m_cur_instr->close_scopes++; AddCode("}"); } break; case RSX_SCA_OPCODE_BRI: // works differently (BRI o[1].x(TR) L0;) { u32 jump_position = 1; if (is_has_BRA) { jump_position = GetAddr(); } else { u32 addr = GetAddr(); for (auto pos : m_jump_lvls) { if (addr == pos) break; ++jump_position; } } AddCode("$ifcond "); AddCode("{"); m_cur_instr->open_scopes++; AddCode(fmt::format("jump_position = %u;", jump_position)); AddCode("continue;"); m_cur_instr->close_scopes++; AddCode("}"); } break; case RSX_SCA_OPCODE_CAL: // works same as BRI AddCode("$ifcond $f(); //CAL"); break; case RSX_SCA_OPCODE_CLI: // works same as BRI AddCode("$ifcond $f(); //CLI"); break; case RSX_SCA_OPCODE_RET: // works like BRI but shorter (RET o[1].x(TR);) AddCode("$ifcond return;"); break; case RSX_SCA_OPCODE_LG2: SetDSTSca("log2($s)"); break; case RSX_SCA_OPCODE_EX2: SetDSTSca("exp2($s)"); break; case RSX_SCA_OPCODE_SIN: SetDSTSca("sin($s)"); break; case RSX_SCA_OPCODE_COS: SetDSTSca("cos($s)"); break; case RSX_SCA_OPCODE_BRB: // works differently (BRB o[1].x !b0, L0;) LOG_ERROR(RSX, "Unimplemented sca_opcode BRB"); break; case RSX_SCA_OPCODE_CLB: break; // works same as BRB LOG_ERROR(RSX, "Unimplemented sca_opcode CLB"); break; case RSX_SCA_OPCODE_PSH: break; // works differently (PSH o[1].x A0;) LOG_ERROR(RSX, "Unimplemented sca_opcode PSH"); break; case RSX_SCA_OPCODE_POP: break; // works differently (POP o[1].x;) LOG_ERROR(RSX, "Unimplemented sca_opcode POP"); break; default: AddCode(fmt::format("//Unknown vp sca_opcode 0x%x", u32{ d1.sca_opcode })); LOG_ERROR(RSX, "Unknown vp sca_opcode 0x%x", u32{ d1.sca_opcode }); Emu.Pause(); break; } switch (d1.vec_opcode) { case RSX_VEC_OPCODE_NOP: break; case RSX_VEC_OPCODE_MOV: SetDSTVec("$0"); break; case RSX_VEC_OPCODE_MUL: SetDSTVec("($0 * $1)"); break; case RSX_VEC_OPCODE_ADD: SetDSTVec("($0 + $2)"); break; case RSX_VEC_OPCODE_MAD: SetDSTVec("($0 * $1 + $2)"); break; case RSX_VEC_OPCODE_DP3: SetDSTVec(getFunction(FUNCTION::FUNCTION_DP3)); break; case RSX_VEC_OPCODE_DPH: SetDSTVec(getFunction(FUNCTION::FUNCTION_DPH)); break; case RSX_VEC_OPCODE_DP4: SetDSTVec(getFunction(FUNCTION::FUNCTION_DP4)); break; case RSX_VEC_OPCODE_DST: SetDSTVec("vec4(distance($0, $1))"); break; case RSX_VEC_OPCODE_MIN: SetDSTVec("min($0, $1)"); break; case RSX_VEC_OPCODE_MAX: SetDSTVec("max($0, $1)"); break; case RSX_VEC_OPCODE_SLT: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); break; case RSX_VEC_OPCODE_SGE: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$0", "$1") + ")"); break; // Note: It looks like ARL opcode ignore input/output swizzle mask (SH3) case RSX_VEC_OPCODE_ARL: AddCode("$ifcond $awm = " + getIntTypeName(4) + "($0);"); break; case RSX_VEC_OPCODE_FRC: SetDSTVec(getFunction(FUNCTION::FUNCTION_FRACT)); break; case RSX_VEC_OPCODE_FLR: SetDSTVec("floor($0)"); break; case RSX_VEC_OPCODE_SEQ: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); break; case RSX_VEC_OPCODE_SFL: SetDSTVec(getFunction(FUNCTION::FUNCTION_SFL)); break; case RSX_VEC_OPCODE_SGT: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); break; case RSX_VEC_OPCODE_SLE: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); break; case RSX_VEC_OPCODE_SNE: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); break; case RSX_VEC_OPCODE_STR: SetDSTVec(getFunction(FUNCTION::FUNCTION_STR)); break; case RSX_VEC_OPCODE_SSG: SetDSTVec("sign($0)"); break; case RSX_VEC_OPCODE_TXL: SetDSTVec("texture($t, $0.xy)"); break; default: AddCode(fmt::format("//Unknown vp opcode 0x%x", u32{ d1.vec_opcode })); LOG_ERROR(RSX, "Unknown vp opcode 0x%x", u32{ d1.vec_opcode }); Emu.Pause(); break; } } if (is_has_BRA || !m_jump_lvls.empty()) { m_cur_instr = &m_instructions[m_instr_count - 1]; m_cur_instr->close_scopes++; AddCode("}"); AddCode("break;"); m_cur_instr->close_scopes++; AddCode("}"); } std::string result = BuildCode(); m_jump_lvls.clear(); m_body.clear(); if (m_funcs.size() > 2) { m_funcs.erase(m_funcs.begin() + 2, m_funcs.end()); } return result; }
void GLVertexDecompilerThread::Task() { m_parr.params.clear(); m_instr_count = 0; for (int i = 0; i < m_max_instr_count; ++i) { m_instructions[i].reset(); } bool is_has_BRA = false; for (u32 i = 1; m_instr_count < m_max_instr_count; m_instr_count++) { m_cur_instr = &m_instructions[m_instr_count]; if (is_has_BRA) { d3.HEX = m_data[i]; i += 4; } else { d1.HEX = m_data[i++]; switch (d1.sca_opcode) { case 0x08: //BRA LOG_WARNING(RSX, "BRA found. Please report to RPCS3 team."); is_has_BRA = true; m_jump_lvls.clear(); d3.HEX = m_data[++i]; i += 4; break; case 0x09: //BRI d2.HEX = m_data[i++]; d3.HEX = m_data[i]; i += 2; m_jump_lvls.emplace(GetAddr()); break; default: d3.HEX = m_data[++i]; i += 2; break; } } if (d3.end) { m_instr_count++; if (i < m_data.size()) { LOG_ERROR(RSX, "Program end before buffer end."); } break; } } uint jump_position = 0; if (is_has_BRA || !m_jump_lvls.empty()) { m_cur_instr = &m_instructions[0]; AddCode("int jump_position = 0;"); AddCode("while (true)"); AddCode("{"); m_cur_instr->open_scopes++; AddCode(fmt::Format("if (jump_position <= %u)", jump_position++)); AddCode("{"); m_cur_instr->open_scopes++; } for (u32 i = 0; i < m_instr_count; ++i) { m_cur_instr = &m_instructions[i]; d0.HEX = m_data[i * 4 + 0]; d1.HEX = m_data[i * 4 + 1]; d2.HEX = m_data[i * 4 + 2]; d3.HEX = m_data[i * 4 + 3]; src[0].src0l = d2.src0l; src[0].src0h = d1.src0h; src[1].src1 = d2.src1; src[2].src2l = d3.src2l; src[2].src2h = d2.src2h; if (i && (is_has_BRA || std::find(m_jump_lvls.begin(), m_jump_lvls.end(), i) != m_jump_lvls.end())) { m_cur_instr->close_scopes++; AddCode("}"); AddCode(""); AddCode(fmt::Format("if (jump_position <= %u)", jump_position++)); AddCode("{"); m_cur_instr->open_scopes++; } if (!d1.sca_opcode && !d1.vec_opcode) { AddCode("//nop"); } switch (d1.sca_opcode) { case RSX_SCA_OPCODE_NOP: break; case RSX_SCA_OPCODE_MOV: SetDSTSca("$s"); break; case RSX_SCA_OPCODE_RCP: SetDSTSca("(1.0 / $s)"); break; case RSX_SCA_OPCODE_RCC: SetDSTSca("clamp(1.0 / $s, 5.42101e-20, 1.884467e19)"); break; case RSX_SCA_OPCODE_RSQ: SetDSTSca("inversesqrt(abs($s))"); break; case RSX_SCA_OPCODE_EXP: SetDSTSca("exp($s)"); break; case RSX_SCA_OPCODE_LOG: SetDSTSca("log($s)"); break; case RSX_SCA_OPCODE_LIT: SetDSTSca("vec4(1.0, $s.x, ($s.x > 0.0 ? exp($s.w * log2($s.y)) : 0.0), 1.0)"); break; case RSX_SCA_OPCODE_BRA: { AddCode("$if ($cond)"); AddCode("{"); m_cur_instr->open_scopes++; AddCode("jump_position = $a$am;"); AddCode("continue;"); m_cur_instr->close_scopes++; AddCode("}"); } break; /* This triggers opengl driver lost connection error code 7 case RSX_SCA_OPCODE_BRI: // works differently (BRI o[1].x(TR) L0;) { uint jump_position; if (is_has_BRA) { jump_position = GetAddr(); } else { int addr = GetAddr(); jump_position = 0; for (auto pos : m_jump_lvls) { if (addr == pos) break; ++jump_position; } } AddCode("$ifcond "); AddCode("{"); m_cur_instr->open_scopes++; AddCode(fmt::Format("jump_position = %u;", jump_position)); AddCode("continue;"); m_cur_instr->close_scopes++; AddCode("}"); } break; */ case RSX_SCA_OPCODE_CAL: // works same as BRI AddCode("$ifcond $f(); //CAL"); break; case RSX_SCA_OPCODE_CLI: // works same as BRI AddCode("$ifcond $f(); //CLI"); break; case RSX_SCA_OPCODE_RET: // works like BRI but shorter (RET o[1].x(TR);) AddCode("$ifcond return;"); break; case RSX_SCA_OPCODE_LG2: SetDSTSca("log2($s)"); break; case RSX_SCA_OPCODE_EX2: SetDSTSca("exp2($s)"); break; case RSX_SCA_OPCODE_SIN: SetDSTSca("sin($s)"); break; case RSX_SCA_OPCODE_COS: SetDSTSca("cos($s)"); break; case RSX_SCA_OPCODE_BRB: // works differently (BRB o[1].x !b0, L0;) LOG_ERROR(RSX, "Unimplemented sca_opcode BRB"); break; case RSX_SCA_OPCODE_CLB: break; // works same as BRB LOG_ERROR(RSX, "Unimplemented sca_opcode CLB"); break; case RSX_SCA_OPCODE_PSH: break; // works differently (PSH o[1].x A0;) LOG_ERROR(RSX, "Unimplemented sca_opcode PSH"); break; case RSX_SCA_OPCODE_POP: break; // works differently (POP o[1].x;) LOG_ERROR(RSX, "Unimplemented sca_opcode POP"); break; default: AddCode(fmt::Format("//Unknown vp sca_opcode 0x%x", fmt::by_value(d1.sca_opcode))); LOG_ERROR(RSX, "Unknown vp sca_opcode 0x%x", fmt::by_value(d1.sca_opcode)); Emu.Pause(); break; } switch (d1.vec_opcode) { case RSX_VEC_OPCODE_NOP: break; case RSX_VEC_OPCODE_MOV: SetDSTVec("$0"); break; case RSX_VEC_OPCODE_MUL: SetDSTVec("($0 * $1)"); break; case RSX_VEC_OPCODE_ADD: SetDSTVec("($0 + $2)"); break; case RSX_VEC_OPCODE_MAD: SetDSTVec("($0 * $1 + $2)"); break; case RSX_VEC_OPCODE_DP3: SetDSTVec("vec4(dot($0.xyz, $1.xyz))"); break; case RSX_VEC_OPCODE_DPH: SetDSTVec("vec4(dot(vec4($0.xyz, 1.0), $1))"); break; case RSX_VEC_OPCODE_DP4: SetDSTVec("vec4(dot($0, $1))"); break; case RSX_VEC_OPCODE_DST: SetDSTVec("vec4(distance($0, $1))"); break; case RSX_VEC_OPCODE_MIN: SetDSTVec("min($0, $1)"); break; case RSX_VEC_OPCODE_MAX: SetDSTVec("max($0, $1)"); break; case RSX_VEC_OPCODE_SLT: SetDSTVec("vec4(lessThan($0, $1))"); break; case RSX_VEC_OPCODE_SGE: SetDSTVec("vec4(greaterThanEqual($0, $1))"); break; case RSX_VEC_OPCODE_ARL: AddCode("$ifcond $a = ivec4($0)$am;"); break; case RSX_VEC_OPCODE_FRC: SetDSTVec("fract($0)"); break; case RSX_VEC_OPCODE_FLR: SetDSTVec("floor($0)"); break; case RSX_VEC_OPCODE_SEQ: SetDSTVec("vec4(equal($0, $1))"); break; case RSX_VEC_OPCODE_SFL: SetDSTVec("vec4(equal($0, vec4(0.0)))"); break; case RSX_VEC_OPCODE_SGT: SetDSTVec("vec4(greaterThan($0, $1))"); break; case RSX_VEC_OPCODE_SLE: SetDSTVec("vec4(lessThanEqual($0, $1))"); break; case RSX_VEC_OPCODE_SNE: SetDSTVec("vec4(notEqual($0, $1))"); break; case RSX_VEC_OPCODE_STR: SetDSTVec("vec4(equal($0, vec4(1.0)))"); break; case RSX_VEC_OPCODE_SSG: SetDSTVec("sign($0)"); break; case RSX_VEC_OPCODE_TEX: SetDSTVec("texture($t, $0.xy)"); break; default: AddCode(fmt::Format("//Unknown vp opcode 0x%x", fmt::by_value(d1.vec_opcode))); LOG_ERROR(RSX, "Unknown vp opcode 0x%x", fmt::by_value(d1.vec_opcode)); Emu.Pause(); break; } } if (is_has_BRA || !m_jump_lvls.empty()) { m_cur_instr = &m_instructions[m_instr_count - 1]; m_cur_instr->close_scopes++; AddCode("}"); AddCode("break;"); m_cur_instr->close_scopes++; AddCode("}"); } m_shader = BuildCode(); m_jump_lvls.clear(); m_body.clear(); if (m_funcs.size() > 2) { m_funcs.erase(m_funcs.begin() + 2, m_funcs.end()); } }