EndConditionalBlock.cs 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. using Ryujinx.HLE.Exceptions;
  2. using Ryujinx.HLE.HOS.Tamper.Conditions;
  3. using Ryujinx.HLE.HOS.Tamper.Operations;
  4. using System.Collections.Generic;
  5. namespace Ryujinx.HLE.HOS.Tamper.CodeEmitters
  6. {
  7. /// <summary>
  8. /// Code type 2 marks the end of a conditional block (started by Code Type 1, Code Type 8 or Code Type C0).
  9. /// </summary>
  10. class EndConditionalBlock
  11. {
  12. const int TerminationTypeIndex = 1;
  13. private const byte End = 0; // True end of the conditional.
  14. private const byte Else = 1; // End of the 'then' block and beginning of 'else' block.
  15. public static void Emit(byte[] instruction, CompilationContext context)
  16. {
  17. Emit(instruction, context, null);
  18. }
  19. private static void Emit(byte[] instruction, CompilationContext context, IEnumerable<IOperation> operationsElse)
  20. {
  21. // 2X000000
  22. // X: End type (0 = End, 1 = Else).
  23. byte terminationType = instruction[TerminationTypeIndex];
  24. switch (terminationType)
  25. {
  26. case End:
  27. break;
  28. case Else:
  29. // Start a new operation block with the 'else' instruction to signal that there is the 'then' block just above it.
  30. context.BlockStack.Push(new OperationBlock(instruction));
  31. return;
  32. default:
  33. throw new TamperCompilationException($"Unknown conditional termination type {terminationType}");
  34. }
  35. // Use the conditional begin instruction stored in the stack.
  36. var upperInstruction = context.CurrentBlock.BaseInstruction;
  37. CodeType codeType = InstructionHelper.GetCodeType(upperInstruction);
  38. // Pop the current block of operations from the stack so control instructions
  39. // for the conditional can be emitted in the upper block.
  40. IEnumerable<IOperation> operations = context.CurrentOperations;
  41. context.BlockStack.Pop();
  42. // If the else operations are already set, then the upper block must not be another end.
  43. if (operationsElse != null && codeType == CodeType.EndConditionalBlock)
  44. {
  45. throw new TamperCompilationException($"Expected an upper 'if' conditional instead of 'end conditional'");
  46. }
  47. ICondition condition;
  48. switch (codeType)
  49. {
  50. case CodeType.BeginMemoryConditionalBlock:
  51. condition = MemoryConditional.Emit(upperInstruction, context);
  52. break;
  53. case CodeType.BeginKeypressConditionalBlock:
  54. condition = KeyPressConditional.Emit(upperInstruction, context);
  55. break;
  56. case CodeType.BeginRegisterConditionalBlock:
  57. condition = RegisterConditional.Emit(upperInstruction, context);
  58. break;
  59. case CodeType.EndConditionalBlock:
  60. terminationType = upperInstruction[TerminationTypeIndex];
  61. // If there is an end instruction above then it must be an else.
  62. if (terminationType != Else)
  63. {
  64. throw new TamperCompilationException($"Expected an upper 'else' conditional instead of {terminationType}");
  65. }
  66. // Re-run the Emit with the else operations set.
  67. Emit(instruction, context, operations);
  68. return;
  69. default:
  70. throw new TamperCompilationException($"Conditional end does not match code type {codeType} in Atmosphere cheat");
  71. }
  72. // Create a conditional block with the current operations and nest it in the upper
  73. // block of the stack.
  74. IfBlock block = new IfBlock(condition, operations, operationsElse);
  75. context.CurrentOperations.Add(block);
  76. }
  77. }
  78. }