/** @brief カメラを用いた描画空間の一部を切り取って描画するサンプルを表示する。 */ void CameraObject2D_Basic() { // Altseedを初期化する。 asd::Engine::Initialize(asd::ToAString("CameraObject2D_Basic").c_str(), 640, 480, asd::EngineOption()); // 画像を読み込む。 auto tex0 = asd::Engine::GetGraphics()->CreateTexture2D(asd::ToAString("Data/Texture/Picture1.png").c_str()); // テクスチャを描画するオブジェクトを設定する。 auto obj0 = std::make_shared<asd::TextureObject2D>(); obj0->SetTexture(tex0); obj0->SetPosition(asd::Vector2DF(10, 10)); obj0->SetScale(asd::Vector2DF(0.7f, 0.7f)); asd::Engine::AddObject2D(obj0); //画面全体を写すカメラを設定する。(オブジェクトをそのまま描画する。) auto entityCamera = std::make_shared<asd::CameraObject2D>(); entityCamera->SetSrc(asd::RectI(0, 0, 640, 480)); entityCamera->SetDst(asd::RectI(0, 0, 640, 480)); asd::Engine::AddObject2D(entityCamera); //テクスチャの左上から縦横150ピクセルを切り取って描画するカメラを設定する。 auto camera = std::make_shared<asd::CameraObject2D>(); camera->SetSrc(asd::RectI(10, 10, 150, 150)); camera->SetDst(asd::RectI(450, 10, 150, 150)); asd::Engine::AddObject2D(camera); // Altseedのウインドウが閉じられていないか確認する。 while (asd::Engine::DoEvents()) { // Altseedを更新する。 asd::Engine::Update(); } // Altseedを終了する。 asd::Engine::Terminate(); }
void CNpc::Update(float dt) { CRole::Update(dt); Pos pos; pos.x = m_pHge->Random_Float(-200,200) + m_pos.x; pos.y = m_pHge->Random_Float(/*-200,200*/0,0) + m_pos.y; static float time = 0; time += dt; if(time > 2.0f) { time = 0; SetDst(pos); } }
void GLFragmentDecompilerThread::Task() { mem32_ptr_t data(m_addr); m_size = 0; m_location = 0; m_loop_count = 0; m_code_level = 1; while(true) { for (auto finded = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); finded != m_end_offsets.end(); finded = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) { m_end_offsets.erase(finded); m_code_level--; AddCode("}"); m_loop_count--; } for (auto finded = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); finded != m_else_offsets.end(); finded = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) { m_else_offsets.erase(finded); m_code_level--; AddCode("}"); AddCode("else"); AddCode("{"); m_code_level++; } dst.HEX = GetData(data[0]); src0.HEX = GetData(data[1]); src1.HEX = GetData(data[2]); src2.HEX = GetData(data[3]); m_offset = 4 * 4; const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6); switch(opcode) { case 0x00: break; //NOP case 0x01: SetDst("$0"); break; //MOV case 0x02: SetDst("($0 * $1)"); break; //MUL case 0x03: SetDst("($0 + $1)"); break; //ADD case 0x04: SetDst("($0 * $1 + $2)"); break; //MAD case 0x05: SetDst("vec2(dot($0.xyz, $1.xyz), 0).xxxx"); break; // DP3 case 0x06: SetDst("vec2(dot($0, $1), 0).xxxx"); break; // DP4 case 0x07: SetDst("vec2(distance($0, $1), 0).xxxx"); break; // DST case 0x08: SetDst("min($0, $1)"); break; // MIN case 0x09: SetDst("max($0, $1)"); break; // MAX case 0x0a: SetDst("vec4(lessThan($0, $1))"); break; // SLT case 0x0b: SetDst("vec4(greaterThanEqual($0, $1))"); break; // SGE case 0x0c: SetDst("vec4(lessThanEqual($0, $1))"); break; // SLE case 0x0d: SetDst("vec4(greaterThan($0, $1))"); break; // SGT case 0x0e: SetDst("vec4(notEqual($0, $1))"); break; // SNE case 0x0f: SetDst("vec4(equal($0, $1))"); break; // SEQ case 0x10: SetDst("fract($0)"); break; // FRC case 0x11: SetDst("floor($0)"); break; // FLR case 0x12: SetDst("discard", false); break; // KIL (kill fragment) //case 0x13: break; // PK4 (pack four signed 8-bit values) //case 0x14: break; // UP4 (unpack four signed 8-bit values) case 0x15: SetDst("dFdx($0)"); break; // DDX case 0x16: SetDst("dFdy($0)"); break; // DDY case 0x17: SetDst("texture($t, $0.xy)"); break; // TEX (texture lookup) //case 0x18: break; // TXP (projective texture lookup) //case 0x19: break; // TXD (texture lookup with derivatives) case 0x1a: SetDst("(1 / $0)"); break; // RCP case 0x1b: SetDst("inversesqrt(abs($0))"); break; // RSQ case 0x1c: SetDst("exp2($0)"); break; // EX2 case 0x1d: SetDst("log2($0)"); break; // LG2 case 0x1e: SetDst("vec4(1.0, $0.x, ($0.x > 0 ? exp2($0.w * log2($0.y)) : 0.0), 1.0)"); break; // LIT (compute light coefficients) case 0x1f: SetDst("($0 * ($1 - $2) + $2)"); break; // LRP (linear interpolation) case 0x20: SetDst("vec4(equal($0, vec4(1.0)))"); break; // STR (set on true) case 0x21: SetDst("vec4(equal($0, vec4(0.0)))"); break; // SFL (set on false) case 0x22: SetDst("cos($0)"); break; // COS case 0x23: SetDst("sin($0)"); break; // SIN //case 0x24: break; // PK2 (pack two 16-bit floats) //case 0x25: break; // UP2 (unpack two 16-bit floats) case 0x26: SetDst("pow($0, $1)"); break; // POW //case 0x27: break; // PKB //case 0x28: break; // UPB //case 0x29: break; // PK16 //case 0x2a: break; // UP16 //case 0x2b: break; // BEM //case 0x2c: break; // PKG //case 0x2d: break; // UPG case 0x2e: SetDst("($0.x * $1.x + $0.y * $1.y + $2.x)"); break; // DP2A (2-component dot product and add) //case 0x2f: break; // TXL (texture lookup with LOD) //case 0x30: break; //case 0x31: break; // TXB (texture lookup with bias) //case 0x33: break; // TEXBEM //case 0x34: break; // TXPBEM //case 0x35: break; // BEMLUM case 0x36: SetDst("($0 - 2.0 * $1 * dot($0, $1))"); break; // RFL (reflection vector) //case 0x37: break; // TIMESWTEX case 0x38: SetDst("vec2(dot($0.xy, $1.xy)).xxxx"); break; // DP2 case 0x39: SetDst("normalize($0.xyz)"); break; // NRM case 0x3a: SetDst("($0 / $1)"); break; // DIV case 0x3b: SetDst("($0 / sqrt($1))"); break; // DIVSQ case 0x3c: SetDst("vec4(1.0, $0.y, ($0.y > 0 ? pow(2.0, $0.w) : 0.0), 1.0)"); break; // LIF case 0x3d: break; // FENCT case 0x3e: break; // FENCB case 0x40: SetDst("break"); break; //BRK //case 0x41: break; //CAL case 0x42: AddCode("if($cond)"); //IF m_else_offsets.push_back(src1.else_offset << 2); m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; break; case 0x43: //LOOP AddCode(fmt::Format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) //LOOP", m_loop_count, src1.rep2, m_loop_count, src1.rep1, m_loop_count, src1.rep3)); m_loop_count++; m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; break; case 0x44: //REP AddCode(fmt::Format("if($cond) for(int i%u = %u; i%u < %u; i%u += %u) //REP", m_loop_count, src1.rep2, m_loop_count, src1.rep1, m_loop_count, src1.rep3)); m_loop_count++; m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; break; //case 0x45: SetDst("return"); break; //RET default: ConLog.Error("Unknown fp opcode 0x%x (inst %d)", opcode, m_size / (4 * 4)); //Emu.Pause(); break; } m_size += m_offset; if(dst.end) break; data.Skip(m_offset); } m_shader = BuildCode(); main.clear(); m_parr.params.clear(); }
/** @brief カメラを用いて描画空間の一部を虫眼鏡のような表示で描画するサンプル。 */ void CameraObject2D_Magnify() { // Altseedを初期化する。 asd::Engine::Initialize(asd::ToAString("CameraObject2D_Magnify").c_str(), 640, 480, asd::EngineOption()); // 画像を読み込み、画像描画オブジェクトを設定する。 { auto tex0 = asd::Engine::GetGraphics()->CreateTexture2D(asd::ToAString("Data/Texture/Sample1.png").c_str()); auto obj0 = std::make_shared<asd::TextureObject2D>(); obj0->SetTexture(tex0); obj0->SetCenterPosition(asd::Vector2DF(256, 256)); obj0->SetPosition(asd::Vector2DF(320, 240)); obj0->SetScale(asd::Vector2DF(0.5f, 0.5f)); asd::Engine::AddObject2D(obj0); } //一つ目の画面全体を写すカメラ。(オブジェクトをそのまま描画する。) { auto entityCamera = std::make_shared<asd::CameraObject2D>(); entityCamera->SetSrc(asd::RectI(0, 0, 640, 480)); entityCamera->SetDst(asd::RectI(0, 0, 640, 480)); asd::Engine::AddObject2D(entityCamera); } //二つ目のマウスポインタの周辺を拡大して表示するカメラ。 auto camera2 = std::make_shared<asd::CameraObject2D>(); asd::Engine::AddObject2D(camera2); //フレーム用画像を読み込む auto frame = std::make_shared<asd::TextureObject2D>(); { auto tex = asd::Engine::GetGraphics()->CreateTexture2D(asd::ToAString("Data/Texture/Frame.png").c_str()); frame->SetTexture(tex); frame->SetCenterPosition(asd::Vector2DF(55.0f, 55.0f)); asd::Engine::AddObject2D(frame); } // Altseedのウインドウが閉じられていないか確認する。 while (asd::Engine::DoEvents()) { //マウスポインタの位置を取得する。 auto pos = asd::Engine::GetMouse()->GetPosition(); //拡大用カメラの描画元を指定する。 camera2->SetSrc(asd::RectI((int)(pos.X) - 25, (int)(pos.Y) - 25, 50, 50)); //ポインタを中心に100x100の拡大画像を表示する。 camera2->SetDst(asd::RectI((int)(pos.X) - 50, (int)(pos.Y) - 50, 100, 100)); //フレーム画像の描画中心をマウスポインタの位置に合わせる。 frame->SetPosition(pos); // Altseedを更新する。 asd::Engine::Update(); } // Altseedの終了処理をする。 asd::Engine::Terminate(); }
/** @brief カメラ付きでマップを表示する。 */ void MapObject2D_Camera() { // aceを初期化する asd::Engine::Initialize(asd::ToAString("MapObject2D_Camera").c_str(), 640, 480, asd::EngineOption()); //カメラを設定する。 auto camera = std::make_shared<asd::CameraObject2D>(); camera->SetSrc(asd::RectI(0, 0, 640, 480)); camera->SetDst(asd::RectI(0, 0, 640, 480)); { //マップオブジェクトを生成する。 auto mapObject = std::make_shared<asd::MapObject2D>(); auto texture = asd::Engine::GetGraphics()->CreateTexture2D(asd::ToAString("Data/Texture/Sample2.png").c_str()); //マップオブジェクトに50*50=2500個のチップを登録する。 for (int i = 0; i < 50; ++i) { for (int j = 0; j < 50; ++j) { //チップを生成する。 auto chip = std::make_shared<asd::Chip2D>(); //チップにテクスチャを設定する。 chip->SetTexture(texture); //チップの描画先を指定する。 chip->SetPosition(asd::Vector2DF(i * 40 - 1000, j * 40 - 1000)); //マップオブジェクトにチップを追加する。 mapObject->AddChip(chip); } } //レイヤーにマップオブジェクトを追加する。 asd::Engine::AddObject2D(mapObject); } //レイヤーにカメラオブジェクトを追加する。 asd::Engine::AddObject2D(camera); // aceが進行可能かチェックする。 while (asd::Engine::DoEvents()) { //カメラを移動させる auto pos = camera->GetSrc(); pos.X += 1; pos.Y += 1; pos.X %= 1000; pos.Y %= 1000; camera->SetSrc(pos); // aceを更新する。 asd::Engine::Update(); } // aceを終了する。 asd::Engine::Terminate(); }
std::string FragmentProgramDecompiler::Decompile() { auto data = vm::ps3::ptr<u32>::make(m_addr); m_size = 0; m_location = 0; m_loop_count = 0; m_code_level = 1; enum { FORCE_NONE, FORCE_SCT, FORCE_SCB, }; int forced_unit = FORCE_NONE; while (true) { for (auto finded = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); finded != m_end_offsets.end(); finded = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) { m_end_offsets.erase(finded); m_code_level--; AddCode("}"); m_loop_count--; } for (auto finded = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); finded != m_else_offsets.end(); finded = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) { m_else_offsets.erase(finded); m_code_level--; AddCode("}"); AddCode("else"); AddCode("{"); m_code_level++; } dst.HEX = GetData(data[0]); src0.HEX = GetData(data[1]); src1.HEX = GetData(data[2]); src2.HEX = GetData(data[3]); m_offset = 4 * sizeof(u32); const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6); auto SIP = [&]() { switch (opcode) { case RSX_FP_OPCODE_BRK: SetDst("break"); break; case RSX_FP_OPCODE_CAL: LOG_ERROR(RSX, "Unimplemented SIP instruction: CAL"); break; case RSX_FP_OPCODE_FENCT: forced_unit = FORCE_SCT; break; case RSX_FP_OPCODE_FENCB: forced_unit = FORCE_SCB; break; case RSX_FP_OPCODE_IFE: AddCode("if($cond)"); if (src2.end_offset != src1.else_offset) m_else_offsets.push_back(src1.else_offset << 2); m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; break; case RSX_FP_OPCODE_LOOP: if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) { AddCode(fmt::format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //LOOP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); } else { AddCode(fmt::format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) //LOOP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); m_loop_count++; m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; } break; case RSX_FP_OPCODE_REP: if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) { AddCode(fmt::format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //REP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); } else { AddCode(fmt::format("if($cond) for(int i%u = %u; i%u < %u; i%u += %u) //REP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); m_loop_count++; m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; } break; case RSX_FP_OPCODE_RET: SetDst("return"); break; default: return false; } return true; }; switch (opcode) { case RSX_FP_OPCODE_NOP: break; case RSX_FP_OPCODE_KIL: SetDst("discard", false); break; default: if (forced_unit == FORCE_NONE) { if (SIP()) break; if (handle_sct(opcode)) break; if (handle_tex_srb(opcode)) break; if (handle_scb(opcode)) break; } else if (forced_unit == FORCE_SCT) { forced_unit = FORCE_NONE; if (handle_sct(opcode)) break; } else if (forced_unit == FORCE_SCB) { forced_unit = FORCE_NONE; if (handle_scb(opcode)) break; } LOG_ERROR(RSX, "Unknown/illegal instruction: 0x%x (forced unit %d)", opcode, forced_unit); break; } m_size += m_offset; if (dst.end) break; assert(m_offset % sizeof(u32) == 0); data += m_offset / sizeof(u32); } // flush m_code_level m_code_level = 1; std::string m_shader = BuildCode(); main.clear(); // m_parr.params.clear(); return m_shader; }
bool FragmentProgramDecompiler::handle_tex_srb(u32 opcode) { switch (opcode) { case RSX_FP_OPCODE_DDX: SetDst(getFunction(FUNCTION::FUNCTION_DFDX)); return true; case RSX_FP_OPCODE_DDY: SetDst(getFunction(FUNCTION::FUNCTION_DFDY)); return true; case RSX_FP_OPCODE_NRM: SetDst("normalize($0)"); return true; case RSX_FP_OPCODE_BEM: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: BEM"); return true; case RSX_FP_OPCODE_TEX: if (dst.tex_num >= m_texture_dimensions.size()) { SetDst(getFunction(FUNCTION::FUNCTION_TEXTURE_SAMPLE)); return true; } switch (m_texture_dimensions[dst.tex_num]) { case texture_dimension::texture_dimension_2d: SetDst(getFunction(FUNCTION::FUNCTION_TEXTURE_SAMPLE)); return true; case texture_dimension::texture_dimension_cubemap: SetDst(getFunction(FUNCTION::FUNCTION_TEXTURE_CUBE_SAMPLE)); return true; } return false; case RSX_FP_OPCODE_TEXBEM: SetDst("texture($t, $0.xy, $1.x)"); return true; case RSX_FP_OPCODE_TXP: if (dst.tex_num >= m_texture_dimensions.size()) { SetDst(getFunction(FUNCTION::FUNCTION_TEXTURE_SAMPLE_PROJ)); return true; } switch (m_texture_dimensions[dst.tex_num]) { case texture_dimension::texture_dimension_2d: SetDst(getFunction(FUNCTION::FUNCTION_TEXTURE_SAMPLE_PROJ)); return true; case texture_dimension::texture_dimension_cubemap: SetDst(getFunction(FUNCTION::FUNCTION_TEXTURE_CUBE_SAMPLE_PROJ)); return true; } return false; case RSX_FP_OPCODE_TXPBEM: SetDst("textureProj($t, $0.xyz, $1.x)"); return true; case RSX_FP_OPCODE_TXD: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: TXD"); return true; case RSX_FP_OPCODE_TXB: SetDst("texture($t, $0.xy, $1.x)"); return true; case RSX_FP_OPCODE_TXL: SetDst("textureLod($t, $0.xy, $1.x)"); return true; case RSX_FP_OPCODE_UP2: SetDst("unpackSnorm2x16($0)"); return true; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_UP4: SetDst("unpackSnorm4x8($0)"); return true; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_UP16: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UP16"); return true; case RSX_FP_OPCODE_UPB: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UPB"); return true; case RSX_FP_OPCODE_UPG: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UPG"); return true; } return false; };
bool FragmentProgramDecompiler::handle_scb(u32 opcode) { switch (opcode) { case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); return true; case RSX_FP_OPCODE_COS: SetDst("cos($0.xxxx)"); return true; case RSX_FP_OPCODE_DIVSQ: SetDst("($0 / sqrt($1).xxxx)"); return true; case RSX_FP_OPCODE_DP2: SetDst(getFunction(FUNCTION::FUNCTION_DP2)); return true; case RSX_FP_OPCODE_DP3: SetDst(getFunction(FUNCTION::FUNCTION_DP3)); return true; case RSX_FP_OPCODE_DP4: SetDst(getFunction(FUNCTION::FUNCTION_DP4)); return true; case RSX_FP_OPCODE_DP2A: SetDst(getFunction(FUNCTION::FUNCTION_DP2A)); return true; case RSX_FP_OPCODE_DST: SetDst("vec4(distance($0, $1))"); return true; case RSX_FP_OPCODE_REFL: LOG_ERROR(RSX, "Unimplemented SCB instruction: REFL"); return true; // TODO: Is this in the right category? case RSX_FP_OPCODE_EX2: SetDst("exp2($0.xxxx)"); return true; case RSX_FP_OPCODE_FLR: SetDst("floor($0)"); return true; case RSX_FP_OPCODE_FRC: SetDst(getFunction(FUNCTION::FUNCTION_FRACT)); return true; case RSX_FP_OPCODE_LIT: SetDst(getFloatTypeName(4) + "(1.0, $0.x, ($0.x > 0.0 ? exp($0.w * log2($0.y)) : 0.0), 1.0)"); return true; case RSX_FP_OPCODE_LIF: SetDst(getFloatTypeName(4) + "(1.0, $0.y, ($0.y > 0 ? pow(2.0, $0.w) : 0.0), 1.0)"); return true; case RSX_FP_OPCODE_LRP: LOG_ERROR(RSX, "Unimplemented SCB instruction: LRP"); return true; // TODO: Is this in the right category? case RSX_FP_OPCODE_LG2: SetDst("log2($0.xxxx)"); return true; case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); return true; case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); return true; case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); return true; case RSX_FP_OPCODE_MOV: SetDst("$0"); return true; case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); return true; case RSX_FP_OPCODE_PK2: SetDst("packSnorm2x16($0)"); return true; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_PK4: SetDst("packSnorm4x8($0)"); return true; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_PK16: LOG_ERROR(RSX, "Unimplemented SCB instruction: PK16"); return true; case RSX_FP_OPCODE_PKB: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKB"); return true; case RSX_FP_OPCODE_PKG: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKG"); return true; case RSX_FP_OPCODE_SEQ: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SFL: SetDst(getFunction(FUNCTION::FUNCTION_SFL)); return true; case RSX_FP_OPCODE_SGE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SGT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SIN: SetDst("sin($0.xxxx)"); return true; case RSX_FP_OPCODE_SLE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SLT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SNE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_STR: SetDst(getFunction(FUNCTION::FUNCTION_STR)); return true; } return false; }
bool FragmentProgramDecompiler::handle_sct(u32 opcode) { switch (opcode) { case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); return true; case RSX_FP_OPCODE_DIV: SetDst("($0 / $1)"); return true; case RSX_FP_OPCODE_DIVSQ: SetDst("($0 / sqrt($1).xxxx)"); return true; case RSX_FP_OPCODE_DP2: SetDst(getFunction(FUNCTION::FUNCTION_DP2)); return true; case RSX_FP_OPCODE_DP3: SetDst(getFunction(FUNCTION::FUNCTION_DP3)); return true; case RSX_FP_OPCODE_DP4: SetDst(getFunction(FUNCTION::FUNCTION_DP4)); return true; case RSX_FP_OPCODE_DP2A: SetDst(getFunction(FUNCTION::FUNCTION_DP2A)); return true; case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); return true; case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); return true; case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); return true; case RSX_FP_OPCODE_MOV: SetDst("$0"); return true; case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); return true; case RSX_FP_OPCODE_RCP: SetDst("1.0 / $0"); return true; case RSX_FP_OPCODE_RSQ: SetDst("1.f / sqrt($0)"); return true; case RSX_FP_OPCODE_SEQ: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SFL: SetDst(getFunction(FUNCTION::FUNCTION_SFL)); return true; case RSX_FP_OPCODE_SGE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SGT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SLE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SLT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SNE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_STR: SetDst(getFunction(FUNCTION::FUNCTION_STR)); return true; } return false; }
void GLFragmentDecompilerThread::Task() { auto data = vm::ptr<u32>::make(m_addr); m_size = 0; m_location = 0; m_loop_count = 0; m_code_level = 1; enum { FORCE_NONE, FORCE_SCT, FORCE_SCB, }; int forced_unit = FORCE_NONE; while (true) { for (auto found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); found != m_end_offsets.end(); found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) { m_end_offsets.erase(found); m_code_level--; AddCode("}"); m_loop_count--; } for (auto found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); found != m_else_offsets.end(); found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) { m_else_offsets.erase(found); m_code_level--; AddCode("}"); AddCode("else"); AddCode("{"); m_code_level++; } dst.HEX = GetData(data[0]); src0.HEX = GetData(data[1]); src1.HEX = GetData(data[2]); src2.HEX = GetData(data[3]); m_offset = 4 * sizeof(u32); const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6); auto SCT = [&]() { switch (opcode) { case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); break; case RSX_FP_OPCODE_DIV: SetDst("($0 / $1)"); break; case RSX_FP_OPCODE_DIVSQ: SetDst("($0 / sqrt($1))"); break; case RSX_FP_OPCODE_DP2: SetDst("vec4(dot($0.xy, $1.xy))"); break; case RSX_FP_OPCODE_DP3: SetDst("vec4(dot($0.xyz, $1.xyz))"); break; case RSX_FP_OPCODE_DP4: SetDst("vec4(dot($0, $1))"); break; case RSX_FP_OPCODE_DP2A: SetDst("vec4($0.x * $1.x + $0.y * $1.y + $2.x)"); break; case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); break; case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); break; case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); break; case RSX_FP_OPCODE_MOV: SetDst("$0"); break; case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); break; case RSX_FP_OPCODE_RCP: SetDst("1 / $0"); break; case RSX_FP_OPCODE_RSQ: SetDst("inversesqrt(abs($0))"); break; case RSX_FP_OPCODE_SEQ: SetDst("vec4(equal($0, $1))"); break; case RSX_FP_OPCODE_SFL: SetDst("vec4(0.0)"); break; case RSX_FP_OPCODE_SGE: SetDst("vec4(greaterThanEqual($0, $1))"); break; case RSX_FP_OPCODE_SGT: SetDst("vec4(greaterThan($0, $1))"); break; case RSX_FP_OPCODE_SLE: SetDst("vec4(lessThanEqual($0, $1))"); break; case RSX_FP_OPCODE_SLT: SetDst("vec4(lessThan($0, $1))"); break; case RSX_FP_OPCODE_SNE: SetDst("vec4(notEqual($0, $1))"); break; case RSX_FP_OPCODE_STR: SetDst("vec4(1.0)"); break; default: return false; } return true; }; auto SCB = [&]() { switch (opcode) { case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); break; case RSX_FP_OPCODE_COS: SetDst("cos($0)"); break; case RSX_FP_OPCODE_DP2: SetDst("vec4(dot($0.xy, $1.xy))"); break; case RSX_FP_OPCODE_DP3: SetDst("vec4(dot($0.xyz, $1.xyz))"); break; case RSX_FP_OPCODE_DP4: SetDst("vec4(dot($0, $1))"); break; case RSX_FP_OPCODE_DP2A: SetDst("vec4($0.x * $1.x + $0.y * $1.y + $2.x)"); break; case RSX_FP_OPCODE_DST: SetDst("vec4(distance($0, $1))"); break; case RSX_FP_OPCODE_REFL: LOG_ERROR(RSX, "Unimplemented SCB instruction: REFL"); break; // TODO: Is this in the right category? case RSX_FP_OPCODE_EX2: SetDst("exp2($0)"); break; case RSX_FP_OPCODE_FLR: SetDst("floor($0)"); break; case RSX_FP_OPCODE_FRC: SetDst("fract($0)"); break; case RSX_FP_OPCODE_LIT: SetDst("vec4(1.0, $0.x, ($0.x > 0.0 ? exp($0.w * log2($0.y)) : 0.0), 1.0)"); break; case RSX_FP_OPCODE_LIF: SetDst("vec4(1.0, $0.y, ($0.y > 0 ? pow(2.0, $0.w) : 0.0), 1.0)"); break; case RSX_FP_OPCODE_LRP: LOG_ERROR(RSX, "Unimplemented SCB instruction: LRP"); break; // TODO: Is this in the right category? case RSX_FP_OPCODE_LG2: SetDst("log2($0)"); break; case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); break; case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); break; case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); break; case RSX_FP_OPCODE_MOV: SetDst("$0"); break; case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); break; case RSX_FP_OPCODE_PK2: SetDst("packSnorm2x16($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_PK4: SetDst("packSnorm4x8($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_PK16: LOG_ERROR(RSX, "Unimplemented SCB instruction: PK16"); break; case RSX_FP_OPCODE_PKB: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKB"); break; case RSX_FP_OPCODE_PKG: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKG"); break; case RSX_FP_OPCODE_SEQ: SetDst("vec4(equal($0, $1))"); break; case RSX_FP_OPCODE_SFL: SetDst("vec4(0.0)"); break; case RSX_FP_OPCODE_SGE: SetDst("vec4(greaterThanEqual($0, $1))"); break; case RSX_FP_OPCODE_SGT: SetDst("vec4(greaterThan($0, $1))"); break; case RSX_FP_OPCODE_SIN: SetDst("sin($0)"); break; case RSX_FP_OPCODE_SLE: SetDst("vec4(lessThanEqual($0, $1))"); break; case RSX_FP_OPCODE_SLT: SetDst("vec4(lessThan($0, $1))"); break; case RSX_FP_OPCODE_SNE: SetDst("vec4(notEqual($0, $1))"); break; case RSX_FP_OPCODE_STR: SetDst("vec4(1.0)"); break; default: return false; } return true; }; auto TEX_SRB = [&]() { switch (opcode) { case RSX_FP_OPCODE_DDX: SetDst("dFdx($0)"); break; case RSX_FP_OPCODE_DDY: SetDst("dFdy($0)"); break; case RSX_FP_OPCODE_NRM: SetDst("normalize($0)"); break; case RSX_FP_OPCODE_BEM: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: BEM"); break; case RSX_FP_OPCODE_TEX: SetDst("texture($t, $0.xy)"); break; case RSX_FP_OPCODE_TEXBEM: SetDst("texture($t, $0.xy, $1.x)"); break; case RSX_FP_OPCODE_TXP: SetDst("textureProj($t, $0.xyz, $1.x)"); break; //TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478) and The Simpsons Arcade Game (NPUB30563)) case RSX_FP_OPCODE_TXPBEM: SetDst("textureProj($t, $0.xyz, $1.x)"); break; case RSX_FP_OPCODE_TXD: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: TXD"); break; case RSX_FP_OPCODE_TXB: SetDst("texture($t, $0.xy, $1.x)"); break; case RSX_FP_OPCODE_TXL: SetDst("textureLod($t, $0.xy, $1.x)"); break; case RSX_FP_OPCODE_UP2: SetDst("unpackSnorm2x16($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_UP4: SetDst("unpackSnorm4x8($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_UP16: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UP16"); break; case RSX_FP_OPCODE_UPB: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UPB"); break; case RSX_FP_OPCODE_UPG: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UPG"); break; default: return false; } return true; }; auto SIP = [&]() { switch (opcode) { case RSX_FP_OPCODE_BRK: SetDst("break"); break; case RSX_FP_OPCODE_CAL: LOG_ERROR(RSX, "Unimplemented SIP instruction: CAL"); break; case RSX_FP_OPCODE_FENCT: forced_unit = FORCE_SCT; break; case RSX_FP_OPCODE_FENCB: forced_unit = FORCE_SCB; break; case RSX_FP_OPCODE_IFE: AddCode("if($cond)"); m_else_offsets.push_back(src1.else_offset << 2); m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; break; case RSX_FP_OPCODE_LOOP: if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) { AddCode(fmt::Format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //LOOP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); } else { AddCode(fmt::Format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) //LOOP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); m_loop_count++; m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; } break; case RSX_FP_OPCODE_REP: if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) { AddCode(fmt::Format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //REP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); } else { AddCode(fmt::Format("if($cond) for(int i%u = %u; i%u < %u; i%u += %u) //REP", m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); m_loop_count++; m_end_offsets.push_back(src2.end_offset << 2); AddCode("{"); m_code_level++; } break; case RSX_FP_OPCODE_RET: SetDst("return"); break; default: return false; } return true; }; switch (opcode) { case RSX_FP_OPCODE_NOP: break; case RSX_FP_OPCODE_KIL: SetDst("discard", false); break; default: if (forced_unit == FORCE_NONE) { if (SIP()) break; if (SCT()) break; if (TEX_SRB()) break; if (SCB()) break; } else if (forced_unit == FORCE_SCT) { forced_unit = FORCE_NONE; if (SCT()) break; } else if (forced_unit == FORCE_SCB) { forced_unit = FORCE_NONE; if (SCB()) break; } LOG_ERROR(RSX, "Unknown/illegal instruction: 0x%x (forced unit %d)", opcode, forced_unit); break; } m_size += m_offset; if (dst.end) break; assert(m_offset % sizeof(u32) == 0); data += m_offset / sizeof(u32); } // flush m_code_level m_code_level = 1; m_shader = BuildCode(); main.clear(); m_parr.params.clear(); }
bool FragmentProgramDecompiler::handle_scb(u32 opcode) { switch (opcode) { case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); return true; case RSX_FP_OPCODE_COS: SetDst("cos($0.xxxx)"); return true; case RSX_FP_OPCODE_DIV: SetDst("($0 / $1.xxxx)"); return true; // Note: DIVSQ is not IEEE compliant. sqrt(0, 0) is 0 (Super Puzzle Fighter II Turbo HD Remix). // sqrt(x, 0) might be equal to some big value (in absolute) whose sign is sign(x) but it has to be proven. case RSX_FP_OPCODE_DIVSQ: SetDst("divsq_legacy($0, sqrt($1).xxxx)"); return true; case RSX_FP_OPCODE_DP2: SetDst(getFunction(FUNCTION::FUNCTION_DP2)); return true; case RSX_FP_OPCODE_DP3: SetDst(getFunction(FUNCTION::FUNCTION_DP3)); return true; case RSX_FP_OPCODE_DP4: SetDst(getFunction(FUNCTION::FUNCTION_DP4)); return true; case RSX_FP_OPCODE_DP2A: SetDst(getFunction(FUNCTION::FUNCTION_DP2A)); return true; case RSX_FP_OPCODE_DST: SetDst("vec4(distance($0, $1))"); return true; case RSX_FP_OPCODE_REFL: LOG_ERROR(RSX, "Unimplemented SCB instruction: REFL"); return true; // TODO: Is this in the right category? case RSX_FP_OPCODE_EX2: SetDst("exp2($0.xxxx)"); return true; case RSX_FP_OPCODE_FLR: SetDst("floor($0)"); return true; case RSX_FP_OPCODE_FRC: SetDst(getFunction(FUNCTION::FUNCTION_FRACT)); return true; case RSX_FP_OPCODE_LIT: SetDst(getFloatTypeName(4) + "(1.0, $0.x, ($0.x > 0.0 ? exp($0.w * log2($0.y)) : 0.0), 1.0)"); return true; case RSX_FP_OPCODE_LIF: SetDst(getFloatTypeName(4) + "(1.0, $0.y, ($0.y > 0 ? pow(2.0, $0.w) : 0.0), 1.0)"); return true; case RSX_FP_OPCODE_LRP: LOG_ERROR(RSX, "Unimplemented SCB instruction: LRP"); return true; // TODO: Is this in the right category? case RSX_FP_OPCODE_LG2: SetDst("log2($0.xxxx)"); return true; case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); return true; case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); return true; case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); return true; case RSX_FP_OPCODE_MOV: SetDst("$0"); return true; case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); return true; case RSX_FP_OPCODE_PK2: SetDst("packSnorm2x16($0)"); return true; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_PK4: SetDst("packSnorm4x8($0)"); return true; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) case RSX_FP_OPCODE_PK16: LOG_ERROR(RSX, "Unimplemented SCB instruction: PK16"); return true; case RSX_FP_OPCODE_PKB: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKB"); return true; case RSX_FP_OPCODE_PKG: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKG"); return true; case RSX_FP_OPCODE_SEQ: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SFL: SetDst(getFunction(FUNCTION::FUNCTION_SFL)); return true; case RSX_FP_OPCODE_SGE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SGT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SIN: SetDst("sin($0.xxxx)"); return true; case RSX_FP_OPCODE_SLE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SLT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SNE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_STR: SetDst(getFunction(FUNCTION::FUNCTION_STR)); return true; } return false; }
bool FragmentProgramDecompiler::handle_sct(u32 opcode) { switch (opcode) { case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); return true; case RSX_FP_OPCODE_DIV: SetDst("($0 / $1.xxxx)"); return true; // Note: DIVSQ is not IEEE compliant. divsq(0, 0) is 0 (Super Puzzle Fighter II Turbo HD Remix). // sqrt(x, 0) might be equal to some big value (in absolute) whose sign is sign(x) but it has to be proven. case RSX_FP_OPCODE_DIVSQ: SetDst("divsq_legacy($0, $1)"); return true; case RSX_FP_OPCODE_DP2: SetDst(getFunction(FUNCTION::FUNCTION_DP2)); return true; case RSX_FP_OPCODE_DP3: SetDst(getFunction(FUNCTION::FUNCTION_DP3)); return true; case RSX_FP_OPCODE_DP4: SetDst(getFunction(FUNCTION::FUNCTION_DP4)); return true; case RSX_FP_OPCODE_DP2A: SetDst(getFunction(FUNCTION::FUNCTION_DP2A)); return true; case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); return true; case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); return true; case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); return true; case RSX_FP_OPCODE_MOV: SetDst("$0"); return true; case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); return true; // Note: It's higly likely that RCP is not IEEE compliant but a game that uses rcp(0) has to be found case RSX_FP_OPCODE_RCP: SetDst("rcp_legacy($0)"); return true; // Note: RSQ is not IEEE compliant. rsq(0) is some big number (Silent Hill 3 HD) // It is not know what happens if 0 is negative. case RSX_FP_OPCODE_RSQ: SetDst("rsq_legacy($0)"); return true; case RSX_FP_OPCODE_SEQ: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SFL: SetDst(getFunction(FUNCTION::FUNCTION_SFL)); return true; case RSX_FP_OPCODE_SGE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SGT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SLE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SLT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_SNE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); return true; case RSX_FP_OPCODE_STR: SetDst(getFunction(FUNCTION::FUNCTION_STR)); return true; } return false; }