ryujinx/Ryujinx.HLE/HOS/Tamper/AtmosphereCompiler.cs
Caian Benedicto 0c1ea1212a
Add the TamperMachine module for runtime mods and cheats (#1928)
* Add initial implementation of the Tamper Machine

* Implement Atmosphere opcodes 0, 4 and 9

* Add missing TamperCompilationException class

* Implement Atmosphere conditional and loop opcodes 1, 2 and 3

* Inplement input conditional opcode 8

* Add register store opcode A

* Implement extended pause/resume opcodes FF0 and FF1

* Implement extended log opcode FFF

* Implement extended register conditional opcode C0

* Refactor TamperProgram to an interface

* Moved Atmosphere classes to a separate subdirectory

* Fix OpProcCtrl class not setting process

* Implement extended register save/restore opcodes C1, C2 and C3

* Refactor code emitters to separate classes

* Supress memory access errors from the Tamper Machine

* Add debug information to tamper register and memory writes

* Add block stack check to Atmosphere Cheat compiler

* Add handheld input support to Tamper Machine

* Fix code styling

* Fix build id and cheat case mismatch

* Fix invalid immediate size selection

* Print build ids of the title

* Prevent Tamper Machine from change code regions

* Remove Atmosphere namespace

* Remove empty cheats from the list

* Prevent code modification without disabling the tampering

* Fix missing addressing mode in LoadRegisterWithMemory

* Fix wrong addressing in RegisterConditional

* Add name to the tamper machine thread

* Fix code styling
2021-03-27 15:12:05 +01:00

131 lines
5.6 KiB
C#

using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Tamper.CodeEmitters;
using Ryujinx.HLE.HOS.Tamper.Operations;
using System;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Tamper
{
class AtmosphereCompiler
{
public ITamperProgram Compile(IEnumerable<string> rawInstructions, ulong exeAddress, ulong heapAddress, ITamperedProcess process)
{
Logger.Debug?.Print(LogClass.TamperMachine, $"Executable address: {exeAddress:X16}");
Logger.Debug?.Print(LogClass.TamperMachine, $"Heap address: {heapAddress:X16}");
try
{
return CompileImpl(rawInstructions, exeAddress, heapAddress, process);
}
catch(TamperCompilationException exception)
{
// Just print the message without the stack trace.
Logger.Error?.Print(LogClass.TamperMachine, exception.Message);
}
catch (Exception exception)
{
Logger.Error?.Print(LogClass.TamperMachine, exception.ToString());
}
Logger.Error?.Print(LogClass.TamperMachine, "There was a problem while compiling the Atmosphere cheat");
return null;
}
private ITamperProgram CompileImpl(IEnumerable<string> rawInstructions, ulong exeAddress, ulong heapAddress, ITamperedProcess process)
{
CompilationContext context = new CompilationContext(exeAddress, heapAddress, process);
context.BlockStack.Push(new OperationBlock(null));
// Parse the instructions.
foreach (string rawInstruction in rawInstructions)
{
Logger.Debug?.Print(LogClass.TamperMachine, $"Compiling instruction {rawInstruction}");
byte[] instruction = InstructionHelper.ParseRawInstruction(rawInstruction);
CodeType codeType = InstructionHelper.GetCodeType(instruction);
switch (codeType)
{
case CodeType.StoreConstantToAddress:
StoreConstantToAddress.Emit(instruction, context);
break;
case CodeType.BeginMemoryConditionalBlock:
BeginConditionalBlock.Emit(instruction, context);
break;
case CodeType.EndConditionalBlock:
EndConditionalBlock.Emit(instruction, context);
break;
case CodeType.StartEndLoop:
StartEndLoop.Emit(instruction, context);
break;
case CodeType.LoadRegisterWithContant:
LoadRegisterWithConstant.Emit(instruction, context);
break;
case CodeType.LoadRegisterWithMemory:
LoadRegisterWithMemory.Emit(instruction, context);
break;
case CodeType.StoreConstantToMemory:
StoreConstantToMemory.Emit(instruction, context);
break;
case CodeType.LegacyArithmetic:
LegacyArithmetic.Emit(instruction, context);
break;
case CodeType.BeginKeypressConditionalBlock:
BeginConditionalBlock.Emit(instruction, context);
break;
case CodeType.Arithmetic:
Arithmetic.Emit(instruction, context);
break;
case CodeType.StoreRegisterToMemory:
StoreRegisterToMemory.Emit(instruction, context);
break;
case CodeType.BeginRegisterConditionalBlock:
BeginConditionalBlock.Emit(instruction, context);
break;
case CodeType.SaveOrRestoreRegister:
SaveOrRestoreRegister.Emit(instruction, context);
break;
case CodeType.SaveOrRestoreRegisterWithMask:
SaveOrRestoreRegisterWithMask.Emit(instruction, context);
break;
case CodeType.ReadOrWriteStaticRegister:
ReadOrWriteStaticRegister.Emit(instruction, context);
break;
case CodeType.PauseProcess:
PauseProcess.Emit(instruction, context);
break;
case CodeType.ResumeProcess:
ResumeProcess.Emit(instruction, context);
break;
case CodeType.DebugLog:
DebugLog.Emit(instruction, context);
break;
default:
throw new TamperCompilationException($"Code type {codeType} not implemented in Atmosphere cheat");
}
}
// Initialize only the registers used.
Value<ulong> zero = new Value<ulong>(0UL);
int position = 0;
foreach (Register register in context.Registers.Values)
{
context.CurrentOperations.Insert(position, new OpMov<ulong>(register, zero));
position++;
}
if (context.BlockStack.Count != 1)
{
throw new TamperCompilationException($"Reached end of compilation with unmatched conditional(s) or loop(s)");
}
return new AtmosphereProgram(process, context.PressedKeys, new Block(context.CurrentOperations));
}
}
}