1 /*
2  * Copyright (c) 2018-2019 Atmosphère-NX
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 /*
18  * Adapted by DarkLordZach for use/interaction with yuzu
19  *
20  * Modifications Copyright 2019 yuzu emulator team
21  * Licensed under GPLv2 or any later version
22  * Refer to the license.txt file included.
23  */
24 
25 #pragma once
26 
27 #include <variant>
28 #include <vector>
29 #include <fmt/printf.h>
30 #include "common/common_types.h"
31 #include "core/memory/dmnt_cheat_types.h"
32 
33 namespace Core::Memory {
34 
35 enum class CheatVmOpcodeType : u32 {
36     StoreStatic = 0,
37     BeginConditionalBlock = 1,
38     EndConditionalBlock = 2,
39     ControlLoop = 3,
40     LoadRegisterStatic = 4,
41     LoadRegisterMemory = 5,
42     StoreStaticToAddress = 6,
43     PerformArithmeticStatic = 7,
44     BeginKeypressConditionalBlock = 8,
45 
46     // These are not implemented by Gateway's VM.
47     PerformArithmeticRegister = 9,
48     StoreRegisterToAddress = 10,
49     Reserved11 = 11,
50 
51     // This is a meta entry, and not a real opcode.
52     // This is to facilitate multi-nybble instruction decoding.
53     ExtendedWidth = 12,
54 
55     // Extended width opcodes.
56     BeginRegisterConditionalBlock = 0xC0,
57     SaveRestoreRegister = 0xC1,
58     SaveRestoreRegisterMask = 0xC2,
59     ReadWriteStaticRegister = 0xC3,
60 
61     // This is a meta entry, and not a real opcode.
62     // This is to facilitate multi-nybble instruction decoding.
63     DoubleExtendedWidth = 0xF0,
64 
65     // Double-extended width opcodes.
66     DebugLog = 0xFFF,
67 };
68 
69 enum class MemoryAccessType : u32 {
70     MainNso = 0,
71     Heap = 1,
72 };
73 
74 enum class ConditionalComparisonType : u32 {
75     GT = 1,
76     GE = 2,
77     LT = 3,
78     LE = 4,
79     EQ = 5,
80     NE = 6,
81 };
82 
83 enum class RegisterArithmeticType : u32 {
84     Addition = 0,
85     Subtraction = 1,
86     Multiplication = 2,
87     LeftShift = 3,
88     RightShift = 4,
89 
90     // These are not supported by Gateway's VM.
91     LogicalAnd = 5,
92     LogicalOr = 6,
93     LogicalNot = 7,
94     LogicalXor = 8,
95 
96     None = 9,
97 };
98 
99 enum class StoreRegisterOffsetType : u32 {
100     None = 0,
101     Reg = 1,
102     Imm = 2,
103     MemReg = 3,
104     MemImm = 4,
105     MemImmReg = 5,
106 };
107 
108 enum class CompareRegisterValueType : u32 {
109     MemoryRelAddr = 0,
110     MemoryOfsReg = 1,
111     RegisterRelAddr = 2,
112     RegisterOfsReg = 3,
113     StaticValue = 4,
114     OtherRegister = 5,
115 };
116 
117 enum class SaveRestoreRegisterOpType : u32 {
118     Restore = 0,
119     Save = 1,
120     ClearSaved = 2,
121     ClearRegs = 3,
122 };
123 
124 enum class DebugLogValueType : u32 {
125     MemoryRelAddr = 0,
126     MemoryOfsReg = 1,
127     RegisterRelAddr = 2,
128     RegisterOfsReg = 3,
129     RegisterValue = 4,
130 };
131 
132 union VmInt {
133     u8 bit8;
134     u16 bit16;
135     u32 bit32;
136     u64 bit64;
137 };
138 
139 struct StoreStaticOpcode {
140     u32 bit_width{};
141     MemoryAccessType mem_type{};
142     u32 offset_register{};
143     u64 rel_address{};
144     VmInt value{};
145 };
146 
147 struct BeginConditionalOpcode {
148     u32 bit_width{};
149     MemoryAccessType mem_type{};
150     ConditionalComparisonType cond_type{};
151     u64 rel_address{};
152     VmInt value{};
153 };
154 
155 struct EndConditionalOpcode {};
156 
157 struct ControlLoopOpcode {
158     bool start_loop{};
159     u32 reg_index{};
160     u32 num_iters{};
161 };
162 
163 struct LoadRegisterStaticOpcode {
164     u32 reg_index{};
165     u64 value{};
166 };
167 
168 struct LoadRegisterMemoryOpcode {
169     u32 bit_width{};
170     MemoryAccessType mem_type{};
171     u32 reg_index{};
172     bool load_from_reg{};
173     u64 rel_address{};
174 };
175 
176 struct StoreStaticToAddressOpcode {
177     u32 bit_width{};
178     u32 reg_index{};
179     bool increment_reg{};
180     bool add_offset_reg{};
181     u32 offset_reg_index{};
182     u64 value{};
183 };
184 
185 struct PerformArithmeticStaticOpcode {
186     u32 bit_width{};
187     u32 reg_index{};
188     RegisterArithmeticType math_type{};
189     u32 value{};
190 };
191 
192 struct BeginKeypressConditionalOpcode {
193     u32 key_mask{};
194 };
195 
196 struct PerformArithmeticRegisterOpcode {
197     u32 bit_width{};
198     RegisterArithmeticType math_type{};
199     u32 dst_reg_index{};
200     u32 src_reg_1_index{};
201     u32 src_reg_2_index{};
202     bool has_immediate{};
203     VmInt value{};
204 };
205 
206 struct StoreRegisterToAddressOpcode {
207     u32 bit_width{};
208     u32 str_reg_index{};
209     u32 addr_reg_index{};
210     bool increment_reg{};
211     StoreRegisterOffsetType ofs_type{};
212     MemoryAccessType mem_type{};
213     u32 ofs_reg_index{};
214     u64 rel_address{};
215 };
216 
217 struct BeginRegisterConditionalOpcode {
218     u32 bit_width{};
219     ConditionalComparisonType cond_type{};
220     u32 val_reg_index{};
221     CompareRegisterValueType comp_type{};
222     MemoryAccessType mem_type{};
223     u32 addr_reg_index{};
224     u32 other_reg_index{};
225     u32 ofs_reg_index{};
226     u64 rel_address{};
227     VmInt value{};
228 };
229 
230 struct SaveRestoreRegisterOpcode {
231     u32 dst_index{};
232     u32 src_index{};
233     SaveRestoreRegisterOpType op_type{};
234 };
235 
236 struct SaveRestoreRegisterMaskOpcode {
237     SaveRestoreRegisterOpType op_type{};
238     std::array<bool, 0x10> should_operate{};
239 };
240 
241 struct ReadWriteStaticRegisterOpcode {
242     u32 static_idx{};
243     u32 idx{};
244 };
245 
246 struct DebugLogOpcode {
247     u32 bit_width{};
248     u32 log_id{};
249     DebugLogValueType val_type{};
250     MemoryAccessType mem_type{};
251     u32 addr_reg_index{};
252     u32 val_reg_index{};
253     u32 ofs_reg_index{};
254     u64 rel_address{};
255 };
256 
257 struct UnrecognizedInstruction {
258     CheatVmOpcodeType opcode{};
259 };
260 
261 struct CheatVmOpcode {
262     bool begin_conditional_block{};
263     std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
264                  LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
265                  PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
266                  PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
267                  BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
268                  SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode,
269                  UnrecognizedInstruction>
270         opcode{};
271 };
272 
273 class DmntCheatVm {
274 public:
275     /// Helper Type for DmntCheatVm <=> yuzu Interface
276     class Callbacks {
277     public:
278         virtual ~Callbacks();
279 
280         virtual void MemoryRead(VAddr address, void* data, u64 size) = 0;
281         virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0;
282 
283         virtual u64 HidKeysDown() = 0;
284 
285         virtual void DebugLog(u8 id, u64 value) = 0;
286         virtual void CommandLog(std::string_view data) = 0;
287     };
288 
289     static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
290     static constexpr std::size_t NumRegisters = 0x10;
291     static constexpr std::size_t NumReadableStaticRegisters = 0x80;
292     static constexpr std::size_t NumWritableStaticRegisters = 0x80;
293     static constexpr std::size_t NumStaticRegisters =
294         NumReadableStaticRegisters + NumWritableStaticRegisters;
295 
296     explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks);
297     ~DmntCheatVm();
298 
GetProgramSize()299     std::size_t GetProgramSize() const {
300         return this->num_opcodes;
301     }
302 
303     bool LoadProgram(const std::vector<CheatEntry>& cheats);
304     void Execute(const CheatProcessMetadata& metadata);
305 
306 private:
307     std::unique_ptr<Callbacks> callbacks;
308 
309     std::size_t num_opcodes = 0;
310     std::size_t instruction_ptr = 0;
311     std::size_t condition_depth = 0;
312     bool decode_success = false;
313     std::array<u32, MaximumProgramOpcodeCount> program{};
314     std::array<u64, NumRegisters> registers{};
315     std::array<u64, NumRegisters> saved_values{};
316     std::array<u64, NumStaticRegisters> static_registers{};
317     std::array<std::size_t, NumRegisters> loop_tops{};
318 
319     bool DecodeNextOpcode(CheatVmOpcode& out);
320     void SkipConditionalBlock();
321     void ResetState();
322 
323     // For implementing the DebugLog opcode.
324     void DebugLog(u32 log_id, u64 value);
325 
326     void LogOpcode(const CheatVmOpcode& opcode);
327 
328     static u64 GetVmInt(VmInt value, u32 bit_width);
329     static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
330                                       MemoryAccessType mem_type, u64 rel_address);
331 };
332 
333 }; // namespace Core::Memory
334