diff --git a/src/core/core.cpp b/src/core/core.cpp
index fc70394213..76bb2bae9d 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -17,6 +17,7 @@
 #include "core/file_sys/bis_factory.h"
 #include "core/file_sys/card_image.h"
 #include "core/file_sys/mode.h"
+#include "core/file_sys/patch_manager.h"
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/romfs_factory.h"
 #include "core/file_sys/savedata_factory.h"
@@ -43,8 +44,6 @@
 #include "core/settings.h"
 #include "core/telemetry_session.h"
 #include "core/tools/freezer.h"
-#include "file_sys/cheat_engine.h"
-#include "file_sys/patch_manager.h"
 #include "video_core/debug_utils/debug_utils.h"
 #include "video_core/renderer_base.h"
 #include "video_core/video_core.h"
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index ea5c76fc05..b56cb06277 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -102,7 +102,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
     std::optional<u64> current_entry = std::nullopt;
 
     for (std::size_t i = 0; i < data.size(); ++i) {
-        if (std::isspace(data[i])) {
+        if (::isspace(data[i])) {
             continue;
         }
 
@@ -143,7 +143,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
                 '\0';
 
             i += name.length() + 1;
-        } else if (std::isxdigit(data[i])) {
+        } else if (::isxdigit(data[i])) {
             if (!current_entry || out[*current_entry].definition.num_opcodes >=
                                       out[*current_entry].definition.opcodes.size()) {
                 return {};
@@ -188,7 +188,7 @@ CheatEngine::~CheatEngine() {
 
 void CheatEngine::Initialize() {
     event = core_timing.RegisterEvent(
-        "CheatEngine::FrameCallback::" + Common::HexArrayToString(metadata.main_nso_build_id),
+        "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
         [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
     core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
 
diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp
index a3f450dac4..cc16d15a4c 100644
--- a/src/core/memory/dmnt_cheat_vm.cpp
+++ b/src/core/memory/dmnt_cheat_vm.cpp
@@ -29,264 +29,272 @@
 
 namespace Memory {
 
+DmntCheatVm::DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {}
+
+DmntCheatVm::~DmntCheatVm() = default;
+
 void DmntCheatVm::DebugLog(u32 log_id, u64 value) {
     callbacks->DebugLog(static_cast<u8>(log_id), value);
 }
 
 void DmntCheatVm::LogOpcode(const CheatVmOpcode& opcode) {
-    switch (opcode.opcode) {
-    case CheatVmOpcodeType_StoreStatic:
-        this->LogToDebugFile("Opcode: Store Static\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.store_static.bit_width);
-        this->LogToDebugFile("Mem Type:  %x\n", opcode.store_static.mem_type);
-        this->LogToDebugFile("Reg Idx:   %x\n", opcode.store_static.offset_register);
-        this->LogToDebugFile("Rel Addr:  %lx\n", opcode.store_static.rel_address);
-        this->LogToDebugFile("Value:     %lx\n", opcode.store_static.value.bit64);
-        break;
-    case CheatVmOpcodeType_BeginConditionalBlock:
-        this->LogToDebugFile("Opcode: Begin Conditional\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.begin_cond.bit_width);
-        this->LogToDebugFile("Mem Type:  %x\n", opcode.begin_cond.mem_type);
-        this->LogToDebugFile("Cond Type: %x\n", opcode.begin_cond.cond_type);
-        this->LogToDebugFile("Rel Addr:  %lx\n", opcode.begin_cond.rel_address);
-        this->LogToDebugFile("Value:     %lx\n", opcode.begin_cond.value.bit64);
-        break;
-    case CheatVmOpcodeType_EndConditionalBlock:
-        this->LogToDebugFile("Opcode: End Conditional\n");
-        break;
-    case CheatVmOpcodeType_ControlLoop:
-        if (opcode.ctrl_loop.start_loop) {
-            this->LogToDebugFile("Opcode: Start Loop\n");
-            this->LogToDebugFile("Reg Idx:   %x\n", opcode.ctrl_loop.reg_index);
-            this->LogToDebugFile("Num Iters: %x\n", opcode.ctrl_loop.num_iters);
+    if (auto store_static = std::get_if<StoreStaticOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Store Static");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", store_static->bit_width));
+        callbacks->CommandLog(
+            fmt::format("Mem Type:  {:X}", static_cast<u32>(store_static->mem_type)));
+        callbacks->CommandLog(fmt::format("Reg Idx:   {:X}", store_static->offset_register));
+        callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", store_static->rel_address));
+        callbacks->CommandLog(fmt::format("Value:     {:X}", store_static->value.bit64));
+    } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Begin Conditional");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_cond->bit_width));
+        callbacks->CommandLog(
+            fmt::format("Mem Type:  {:X}", static_cast<u32>(begin_cond->mem_type)));
+        callbacks->CommandLog(
+            fmt::format("Cond Type: {:X}", static_cast<u32>(begin_cond->cond_type)));
+        callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", begin_cond->rel_address));
+        callbacks->CommandLog(fmt::format("Value:     {:X}", begin_cond->value.bit64));
+    } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: End Conditional");
+    } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&opcode.opcode)) {
+        if (ctrl_loop->start_loop) {
+            callbacks->CommandLog("Opcode: Start Loop");
+            callbacks->CommandLog(fmt::format("Reg Idx:   {:X}", ctrl_loop->reg_index));
+            callbacks->CommandLog(fmt::format("Num Iters: {:X}", ctrl_loop->num_iters));
         } else {
-            this->LogToDebugFile("Opcode: End Loop\n");
-            this->LogToDebugFile("Reg Idx:   %x\n", opcode.ctrl_loop.reg_index);
+            callbacks->CommandLog("Opcode: End Loop");
+            callbacks->CommandLog(fmt::format("Reg Idx:   {:X}", ctrl_loop->reg_index));
         }
-        break;
-    case CheatVmOpcodeType_LoadRegisterStatic:
-        this->LogToDebugFile("Opcode: Load Register Static\n");
-        this->LogToDebugFile("Reg Idx:   %x\n", opcode.ldr_static.reg_index);
-        this->LogToDebugFile("Value:     %lx\n", opcode.ldr_static.value);
-        break;
-    case CheatVmOpcodeType_LoadRegisterMemory:
-        this->LogToDebugFile("Opcode: Load Register Memory\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.ldr_memory.bit_width);
-        this->LogToDebugFile("Reg Idx:   %x\n", opcode.ldr_memory.reg_index);
-        this->LogToDebugFile("Mem Type:  %x\n", opcode.ldr_memory.mem_type);
-        this->LogToDebugFile("From Reg:  %d\n", opcode.ldr_memory.load_from_reg);
-        this->LogToDebugFile("Rel Addr:  %lx\n", opcode.ldr_memory.rel_address);
-        break;
-    case CheatVmOpcodeType_StoreStaticToAddress:
-        this->LogToDebugFile("Opcode: Store Static to Address\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.str_static.bit_width);
-        this->LogToDebugFile("Reg Idx:   %x\n", opcode.str_static.reg_index);
-        if (opcode.str_static.add_offset_reg) {
-            this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_static.offset_reg_index);
+    } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Load Register Static");
+        callbacks->CommandLog(fmt::format("Reg Idx:   {:X}", ldr_static->reg_index));
+        callbacks->CommandLog(fmt::format("Value:     {:X}", ldr_static->value));
+    } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Load Register Memory");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", ldr_memory->bit_width));
+        callbacks->CommandLog(fmt::format("Reg Idx:   {:X}", ldr_memory->reg_index));
+        callbacks->CommandLog(
+            fmt::format("Mem Type:  {:X}", static_cast<u32>(ldr_memory->mem_type)));
+        callbacks->CommandLog(fmt::format("From Reg:  {:d}", ldr_memory->load_from_reg));
+        callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", ldr_memory->rel_address));
+    } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Store Static to Address");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_static->bit_width));
+        callbacks->CommandLog(fmt::format("Reg Idx:   {:X}", str_static->reg_index));
+        if (str_static->add_offset_reg) {
+            callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_static->offset_reg_index));
         }
-        this->LogToDebugFile("Incr Reg:  %d\n", opcode.str_static.increment_reg);
-        this->LogToDebugFile("Value:     %lx\n", opcode.str_static.value);
-        break;
-    case CheatVmOpcodeType_PerformArithmeticStatic:
-        this->LogToDebugFile("Opcode: Perform Static Arithmetic\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_static.bit_width);
-        this->LogToDebugFile("Reg Idx:   %x\n", opcode.perform_math_static.reg_index);
-        this->LogToDebugFile("Math Type: %x\n", opcode.perform_math_static.math_type);
-        this->LogToDebugFile("Value:     %lx\n", opcode.perform_math_static.value);
-        break;
-    case CheatVmOpcodeType_BeginKeypressConditionalBlock:
-        this->LogToDebugFile("Opcode: Begin Keypress Conditional\n");
-        this->LogToDebugFile("Key Mask:  %x\n", opcode.begin_keypress_cond.key_mask);
-        break;
-    case CheatVmOpcodeType_PerformArithmeticRegister:
-        this->LogToDebugFile("Opcode: Perform Register Arithmetic\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.perform_math_reg.bit_width);
-        this->LogToDebugFile("Dst Idx:   %x\n", opcode.perform_math_reg.dst_reg_index);
-        this->LogToDebugFile("Src1 Idx:  %x\n", opcode.perform_math_reg.src_reg_1_index);
-        if (opcode.perform_math_reg.has_immediate) {
-            this->LogToDebugFile("Value:     %lx\n", opcode.perform_math_reg.value.bit64);
+        callbacks->CommandLog(fmt::format("Incr Reg:  {:d}", str_static->increment_reg));
+        callbacks->CommandLog(fmt::format("Value:     {:X}", str_static->value));
+    } else if (auto perform_math_static =
+                   std::get_if<PerformArithmeticStaticOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Perform Static Arithmetic");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_static->bit_width));
+        callbacks->CommandLog(fmt::format("Reg Idx:   {:X}", perform_math_static->reg_index));
+        callbacks->CommandLog(
+            fmt::format("Math Type: {:X}", static_cast<u32>(perform_math_static->math_type)));
+        callbacks->CommandLog(fmt::format("Value:     {:X}", perform_math_static->value));
+    } else if (auto begin_keypress_cond =
+                   std::get_if<BeginKeypressConditionalOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Begin Keypress Conditional");
+        callbacks->CommandLog(fmt::format("Key Mask:  {:X}", begin_keypress_cond->key_mask));
+    } else if (auto perform_math_reg =
+                   std::get_if<PerformArithmeticRegisterOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Perform Register Arithmetic");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", perform_math_reg->bit_width));
+        callbacks->CommandLog(fmt::format("Dst Idx:   {:X}", perform_math_reg->dst_reg_index));
+        callbacks->CommandLog(fmt::format("Src1 Idx:  {:X}", perform_math_reg->src_reg_1_index));
+        if (perform_math_reg->has_immediate) {
+            callbacks->CommandLog(fmt::format("Value:     {:X}", perform_math_reg->value.bit64));
         } else {
-            this->LogToDebugFile("Src2 Idx:  %x\n", opcode.perform_math_reg.src_reg_2_index);
+            callbacks->CommandLog(
+                fmt::format("Src2 Idx:  {:X}", perform_math_reg->src_reg_2_index));
         }
-        break;
-    case CheatVmOpcodeType_StoreRegisterToAddress:
-        this->LogToDebugFile("Opcode: Store Register to Address\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.str_register.bit_width);
-        this->LogToDebugFile("S Reg Idx: %x\n", opcode.str_register.str_reg_index);
-        this->LogToDebugFile("A Reg Idx: %x\n", opcode.str_register.addr_reg_index);
-        this->LogToDebugFile("Incr Reg:  %d\n", opcode.str_register.increment_reg);
-        switch (opcode.str_register.ofs_type) {
-        case StoreRegisterOffsetType_None:
+    } else if (auto str_register = std::get_if<StoreRegisterToAddressOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Store Register to Address");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", str_register->bit_width));
+        callbacks->CommandLog(fmt::format("S Reg Idx: {:X}", str_register->str_reg_index));
+        callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", str_register->addr_reg_index));
+        callbacks->CommandLog(fmt::format("Incr Reg:  {:d}", str_register->increment_reg));
+        switch (str_register->ofs_type) {
+        case StoreRegisterOffsetType::None:
             break;
-        case StoreRegisterOffsetType_Reg:
-            this->LogToDebugFile("O Reg Idx: %x\n", opcode.str_register.ofs_reg_index);
+        case StoreRegisterOffsetType::Reg:
+            callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", str_register->ofs_reg_index));
             break;
-        case StoreRegisterOffsetType_Imm:
-            this->LogToDebugFile("Rel Addr:  %lx\n", opcode.str_register.rel_address);
+        case StoreRegisterOffsetType::Imm:
+            callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", str_register->rel_address));
             break;
-        case StoreRegisterOffsetType_MemReg:
-            this->LogToDebugFile("Mem Type:  %x\n", opcode.str_register.mem_type);
+        case StoreRegisterOffsetType::MemReg:
+            callbacks->CommandLog(
+                fmt::format("Mem Type:  {:X}", static_cast<u32>(str_register->mem_type)));
             break;
-        case StoreRegisterOffsetType_MemImm:
-        case StoreRegisterOffsetType_MemImmReg:
-            this->LogToDebugFile("Mem Type:  %x\n", opcode.str_register.mem_type);
-            this->LogToDebugFile("Rel Addr:  %lx\n", opcode.str_register.rel_address);
+        case StoreRegisterOffsetType::MemImm:
+        case StoreRegisterOffsetType::MemImmReg:
+            callbacks->CommandLog(
+                fmt::format("Mem Type:  {:X}", static_cast<u32>(str_register->mem_type)));
+            callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", str_register->rel_address));
             break;
         }
-        break;
-    case CheatVmOpcodeType_BeginRegisterConditionalBlock:
-        this->LogToDebugFile("Opcode: Begin Register Conditional\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.begin_reg_cond.bit_width);
-        this->LogToDebugFile("Cond Type: %x\n", opcode.begin_reg_cond.cond_type);
-        this->LogToDebugFile("V Reg Idx: %x\n", opcode.begin_reg_cond.val_reg_index);
-        switch (opcode.begin_reg_cond.comp_type) {
-        case CompareRegisterValueType_StaticValue:
-            this->LogToDebugFile("Comp Type: Static Value\n");
-            this->LogToDebugFile("Value:     %lx\n", opcode.begin_reg_cond.value.bit64);
+    } else if (auto begin_reg_cond = std::get_if<BeginRegisterConditionalOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Begin Register Conditional");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", begin_reg_cond->bit_width));
+        callbacks->CommandLog(
+            fmt::format("Cond Type: {:X}", static_cast<u32>(begin_reg_cond->cond_type)));
+        callbacks->CommandLog(fmt::format("V Reg Idx: {:X}", begin_reg_cond->val_reg_index));
+        switch (begin_reg_cond->comp_type) {
+        case CompareRegisterValueType::StaticValue:
+            callbacks->CommandLog("Comp Type: Static Value");
+            callbacks->CommandLog(fmt::format("Value:     {:X}", begin_reg_cond->value.bit64));
             break;
-        case CompareRegisterValueType_OtherRegister:
-            this->LogToDebugFile("Comp Type: Other Register\n");
-            this->LogToDebugFile("X Reg Idx: %x\n", opcode.begin_reg_cond.other_reg_index);
+        case CompareRegisterValueType::OtherRegister:
+            callbacks->CommandLog("Comp Type: Other Register");
+            callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", begin_reg_cond->other_reg_index));
             break;
-        case CompareRegisterValueType_MemoryRelAddr:
-            this->LogToDebugFile("Comp Type: Memory Relative Address\n");
-            this->LogToDebugFile("Mem Type:  %x\n", opcode.begin_reg_cond.mem_type);
-            this->LogToDebugFile("Rel Addr:  %lx\n", opcode.begin_reg_cond.rel_address);
+        case CompareRegisterValueType::MemoryRelAddr:
+            callbacks->CommandLog("Comp Type: Memory Relative Address");
+            callbacks->CommandLog(
+                fmt::format("Mem Type:  {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
+            callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", begin_reg_cond->rel_address));
             break;
-        case CompareRegisterValueType_MemoryOfsReg:
-            this->LogToDebugFile("Comp Type: Memory Offset Register\n");
-            this->LogToDebugFile("Mem Type:  %x\n", opcode.begin_reg_cond.mem_type);
-            this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
+        case CompareRegisterValueType::MemoryOfsReg:
+            callbacks->CommandLog("Comp Type: Memory Offset Register");
+            callbacks->CommandLog(
+                fmt::format("Mem Type:  {:X}", static_cast<u32>(begin_reg_cond->mem_type)));
+            callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
             break;
-        case CompareRegisterValueType_RegisterRelAddr:
-            this->LogToDebugFile("Comp Type: Register Relative Address\n");
-            this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
-            this->LogToDebugFile("Rel Addr:  %lx\n", opcode.begin_reg_cond.rel_address);
+        case CompareRegisterValueType::RegisterRelAddr:
+            callbacks->CommandLog("Comp Type: Register Relative Address");
+            callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
+            callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", begin_reg_cond->rel_address));
             break;
-        case CompareRegisterValueType_RegisterOfsReg:
-            this->LogToDebugFile("Comp Type: Register Offset Register\n");
-            this->LogToDebugFile("A Reg Idx: %x\n", opcode.begin_reg_cond.addr_reg_index);
-            this->LogToDebugFile("O Reg Idx: %x\n", opcode.begin_reg_cond.ofs_reg_index);
+        case CompareRegisterValueType::RegisterOfsReg:
+            callbacks->CommandLog("Comp Type: Register Offset Register");
+            callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", begin_reg_cond->addr_reg_index));
+            callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", begin_reg_cond->ofs_reg_index));
             break;
         }
-        break;
-    case CheatVmOpcodeType_SaveRestoreRegister:
-        this->LogToDebugFile("Opcode: Save or Restore Register\n");
-        this->LogToDebugFile("Dst Idx:   %x\n", opcode.save_restore_reg.dst_index);
-        this->LogToDebugFile("Src Idx:   %x\n", opcode.save_restore_reg.src_index);
-        this->LogToDebugFile("Op Type:   %d\n", opcode.save_restore_reg.op_type);
-        break;
-    case CheatVmOpcodeType_SaveRestoreRegisterMask:
-        this->LogToDebugFile("Opcode: Save or Restore Register Mask\n");
-        this->LogToDebugFile("Op Type:   %d\n", opcode.save_restore_regmask.op_type);
-        for (size_t i = 0; i < NumRegisters; i++) {
-            this->LogToDebugFile("Act[%02x]:   %d\n", i,
-                                 opcode.save_restore_regmask.should_operate[i]);
+    } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Save or Restore Register");
+        callbacks->CommandLog(fmt::format("Dst Idx:   {:X}", save_restore_reg->dst_index));
+        callbacks->CommandLog(fmt::format("Src Idx:   {:X}", save_restore_reg->src_index));
+        callbacks->CommandLog(
+            fmt::format("Op Type:   {:d}", static_cast<u32>(save_restore_reg->op_type)));
+    } else if (auto save_restore_regmask =
+                   std::get_if<SaveRestoreRegisterMaskOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Save or Restore Register Mask");
+        callbacks->CommandLog(
+            fmt::format("Op Type:   {:d}", static_cast<u32>(save_restore_regmask->op_type)));
+        for (std::size_t i = 0; i < NumRegisters; i++) {
+            callbacks->CommandLog(
+                fmt::format("Act[{:02X}]:   {:d}", i, save_restore_regmask->should_operate[i]));
         }
-        break;
-    case CheatVmOpcodeType_DebugLog:
-        this->LogToDebugFile("Opcode: Debug Log\n");
-        this->LogToDebugFile("Bit Width: %x\n", opcode.debug_log.bit_width);
-        this->LogToDebugFile("Log ID:    %x\n", opcode.debug_log.log_id);
-        this->LogToDebugFile("Val Type:  %x\n", opcode.debug_log.val_type);
-        switch (opcode.debug_log.val_type) {
-        case DebugLogValueType_RegisterValue:
-            this->LogToDebugFile("Val Type:  Register Value\n");
-            this->LogToDebugFile("X Reg Idx: %x\n", opcode.debug_log.val_reg_index);
+    } else if (auto debug_log = std::get_if<DebugLogOpcode>(&opcode.opcode)) {
+        callbacks->CommandLog("Opcode: Debug Log");
+        callbacks->CommandLog(fmt::format("Bit Width: {:X}", debug_log->bit_width));
+        callbacks->CommandLog(fmt::format("Log ID:    {:X}", debug_log->log_id));
+        callbacks->CommandLog(
+            fmt::format("Val Type:  {:X}", static_cast<u32>(debug_log->val_type)));
+        switch (debug_log->val_type) {
+        case DebugLogValueType::RegisterValue:
+            callbacks->CommandLog("Val Type:  Register Value");
+            callbacks->CommandLog(fmt::format("X Reg Idx: {:X}", debug_log->val_reg_index));
             break;
-        case DebugLogValueType_MemoryRelAddr:
-            this->LogToDebugFile("Val Type:  Memory Relative Address\n");
-            this->LogToDebugFile("Mem Type:  %x\n", opcode.debug_log.mem_type);
-            this->LogToDebugFile("Rel Addr:  %lx\n", opcode.debug_log.rel_address);
+        case DebugLogValueType::MemoryRelAddr:
+            callbacks->CommandLog("Val Type:  Memory Relative Address");
+            callbacks->CommandLog(
+                fmt::format("Mem Type:  {:X}", static_cast<u32>(debug_log->mem_type)));
+            callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", debug_log->rel_address));
             break;
-        case DebugLogValueType_MemoryOfsReg:
-            this->LogToDebugFile("Val Type:  Memory Offset Register\n");
-            this->LogToDebugFile("Mem Type:  %x\n", opcode.debug_log.mem_type);
-            this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
+        case DebugLogValueType::MemoryOfsReg:
+            callbacks->CommandLog("Val Type:  Memory Offset Register");
+            callbacks->CommandLog(
+                fmt::format("Mem Type:  {:X}", static_cast<u32>(debug_log->mem_type)));
+            callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
             break;
-        case DebugLogValueType_RegisterRelAddr:
-            this->LogToDebugFile("Val Type:  Register Relative Address\n");
-            this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
-            this->LogToDebugFile("Rel Addr:  %lx\n", opcode.debug_log.rel_address);
+        case DebugLogValueType::RegisterRelAddr:
+            callbacks->CommandLog("Val Type:  Register Relative Address");
+            callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
+            callbacks->CommandLog(fmt::format("Rel Addr:  {:X}", debug_log->rel_address));
             break;
-        case DebugLogValueType_RegisterOfsReg:
-            this->LogToDebugFile("Val Type:  Register Offset Register\n");
-            this->LogToDebugFile("A Reg Idx: %x\n", opcode.debug_log.addr_reg_index);
-            this->LogToDebugFile("O Reg Idx: %x\n", opcode.debug_log.ofs_reg_index);
+        case DebugLogValueType::RegisterOfsReg:
+            callbacks->CommandLog("Val Type:  Register Offset Register");
+            callbacks->CommandLog(fmt::format("A Reg Idx: {:X}", debug_log->addr_reg_index));
+            callbacks->CommandLog(fmt::format("O Reg Idx: {:X}", debug_log->ofs_reg_index));
             break;
         }
-    default:
-        this->LogToDebugFile("Unknown opcode: %x\n", opcode.opcode);
-        break;
+    } else if (auto instr = std::get_if<UnrecognizedInstruction>(&opcode.opcode)) {
+        callbacks->CommandLog(fmt::format("Unknown opcode: {:X}", static_cast<u32>(instr->opcode)));
     }
 }
 
 DmntCheatVm::Callbacks::~Callbacks() = default;
 
 bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
-    /* If we've ever seen a decode failure, return false. */
-    bool valid = this->decode_success;
+    // If we've ever seen a decode failure, return false.
+    bool valid = decode_success;
     CheatVmOpcode opcode = {};
     SCOPE_EXIT({
-        this->decode_success &= valid;
+        decode_success &= valid;
         if (valid) {
             out = opcode;
         }
     });
 
-    /* Helper function for getting instruction dwords. */
-    auto GetNextDword = [&]() {
-        if (this->instruction_ptr >= this->num_opcodes) {
+    // Helper function for getting instruction dwords.
+    const auto GetNextDword = [&] {
+        if (instruction_ptr >= num_opcodes) {
             valid = false;
             return static_cast<u32>(0);
         }
-        return this->program[this->instruction_ptr++];
+        return program[instruction_ptr++];
     };
 
-    /* Helper function for parsing a VmInt. */
-    auto GetNextVmInt = [&](const u32 bit_width) {
-        VmInt val = {0};
+    // Helper function for parsing a VmInt.
+    const auto GetNextVmInt = [&](const u32 bit_width) {
+        VmInt val{};
 
         const u32 first_dword = GetNextDword();
         switch (bit_width) {
         case 1:
-            val.bit8 = (u8)first_dword;
+            val.bit8 = static_cast<u8>(first_dword);
             break;
         case 2:
-            val.bit16 = (u16)first_dword;
+            val.bit16 = static_cast<u16>(first_dword);
             break;
         case 4:
             val.bit32 = first_dword;
             break;
         case 8:
-            val.bit64 = (((u64)first_dword) << 32ul) | ((u64)GetNextDword());
+            val.bit64 = (static_cast<u64>(first_dword) << 32ul) | static_cast<u64>(GetNextDword());
             break;
         }
 
         return val;
     };
 
-    /* Read opcode. */
+    // Read opcode.
     const u32 first_dword = GetNextDword();
     if (!valid) {
         return valid;
     }
 
-    opcode.opcode = (CheatVmOpcodeType)(((first_dword >> 28) & 0xF));
-    if (opcode.opcode >= CheatVmOpcodeType_ExtendedWidth) {
-        opcode.opcode =
-            (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 24) & 0xF));
+    auto opcode_type = static_cast<CheatVmOpcodeType>(((first_dword >> 28) & 0xF));
+    if (opcode_type >= CheatVmOpcodeType::ExtendedWidth) {
+        opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
+                                                     ((first_dword >> 24) & 0xF));
     }
-    if (opcode.opcode >= CheatVmOpcodeType_DoubleExtendedWidth) {
-        opcode.opcode =
-            (CheatVmOpcodeType)((((u32)opcode.opcode) << 4) | ((first_dword >> 20) & 0xF));
+    if (opcode_type >= CheatVmOpcodeType::DoubleExtendedWidth) {
+        opcode_type = static_cast<CheatVmOpcodeType>((static_cast<u32>(opcode_type) << 4) |
+                                                     ((first_dword >> 20) & 0xF));
     }
 
-    /* detect condition start. */
-    switch (opcode.opcode) {
-    case CheatVmOpcodeType_BeginConditionalBlock:
-    case CheatVmOpcodeType_BeginKeypressConditionalBlock:
-    case CheatVmOpcodeType_BeginRegisterConditionalBlock:
+    // detect condition start.
+    switch (opcode_type) {
+    case CheatVmOpcodeType::BeginConditionalBlock:
+    case CheatVmOpcodeType::BeginKeypressConditionalBlock:
+    case CheatVmOpcodeType::BeginRegisterConditionalBlock:
         opcode.begin_conditional_block = true;
         break;
     default:
@@ -294,299 +302,335 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
         break;
     }
 
-    switch (opcode.opcode) {
-    case CheatVmOpcodeType_StoreStatic: {
-        /* 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
-        /* Read additional words. */
+    switch (opcode_type) {
+    case CheatVmOpcodeType::StoreStatic: {
+        StoreStaticOpcode store_static{};
+        // 0TMR00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
+        // Read additional words.
         const u32 second_dword = GetNextDword();
-        opcode.store_static.bit_width = (first_dword >> 24) & 0xF;
-        opcode.store_static.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
-        opcode.store_static.offset_register = ((first_dword >> 16) & 0xF);
-        opcode.store_static.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
-        opcode.store_static.value = GetNextVmInt(opcode.store_static.bit_width);
+        store_static.bit_width = (first_dword >> 24) & 0xF;
+        store_static.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
+        store_static.offset_register = ((first_dword >> 16) & 0xF);
+        store_static.rel_address =
+            (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
+        store_static.value = GetNextVmInt(store_static.bit_width);
+        opcode.opcode = store_static;
     } break;
-    case CheatVmOpcodeType_BeginConditionalBlock: {
-        /* 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY) */
-        /* Read additional words. */
+    case CheatVmOpcodeType::BeginConditionalBlock: {
+        BeginConditionalOpcode begin_cond{};
+        // 1TMC00AA AAAAAAAA YYYYYYYY (YYYYYYYY)
+        // Read additional words.
         const u32 second_dword = GetNextDword();
-        opcode.begin_cond.bit_width = (first_dword >> 24) & 0xF;
-        opcode.begin_cond.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
-        opcode.begin_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
-        opcode.begin_cond.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
-        opcode.begin_cond.value = GetNextVmInt(opcode.store_static.bit_width);
+        begin_cond.bit_width = (first_dword >> 24) & 0xF;
+        begin_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
+        begin_cond.cond_type = static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
+        begin_cond.rel_address =
+            (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
+        begin_cond.value = GetNextVmInt(begin_cond.bit_width);
+        opcode.opcode = begin_cond;
     } break;
-    case CheatVmOpcodeType_EndConditionalBlock: {
-        /* 20000000 */
-        /* There's actually nothing left to process here! */
+    case CheatVmOpcodeType::EndConditionalBlock: {
+        // 20000000
+        // There's actually nothing left to process here!
+        opcode.opcode = EndConditionalOpcode{};
     } break;
-    case CheatVmOpcodeType_ControlLoop: {
-        /* 300R0000 VVVVVVVV */
-        /* 310R0000 */
-        /* Parse register, whether loop start or loop end. */
-        opcode.ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
-        opcode.ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
+    case CheatVmOpcodeType::ControlLoop: {
+        ControlLoopOpcode ctrl_loop{};
+        // 300R0000 VVVVVVVV
+        // 310R0000
+        // Parse register, whether loop start or loop end.
+        ctrl_loop.start_loop = ((first_dword >> 24) & 0xF) == 0;
+        ctrl_loop.reg_index = ((first_dword >> 20) & 0xF);
 
-        /* Read number of iters if loop start. */
-        if (opcode.ctrl_loop.start_loop) {
-            opcode.ctrl_loop.num_iters = GetNextDword();
+        // Read number of iters if loop start.
+        if (ctrl_loop.start_loop) {
+            ctrl_loop.num_iters = GetNextDword();
         }
+        opcode.opcode = ctrl_loop;
     } break;
-    case CheatVmOpcodeType_LoadRegisterStatic: {
-        /* 400R0000 VVVVVVVV VVVVVVVV */
-        /* Read additional words. */
-        opcode.ldr_static.reg_index = ((first_dword >> 16) & 0xF);
-        opcode.ldr_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
+    case CheatVmOpcodeType::LoadRegisterStatic: {
+        LoadRegisterStaticOpcode ldr_static{};
+        // 400R0000 VVVVVVVV VVVVVVVV
+        // Read additional words.
+        ldr_static.reg_index = ((first_dword >> 16) & 0xF);
+        ldr_static.value =
+            (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
+        opcode.opcode = ldr_static;
     } break;
-    case CheatVmOpcodeType_LoadRegisterMemory: {
-        /* 5TMRI0AA AAAAAAAA */
-        /* Read additional words. */
+    case CheatVmOpcodeType::LoadRegisterMemory: {
+        LoadRegisterMemoryOpcode ldr_memory{};
+        // 5TMRI0AA AAAAAAAA
+        // Read additional words.
         const u32 second_dword = GetNextDword();
-        opcode.ldr_memory.bit_width = (first_dword >> 24) & 0xF;
-        opcode.ldr_memory.mem_type = (MemoryAccessType)((first_dword >> 20) & 0xF);
-        opcode.ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
-        opcode.ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
-        opcode.ldr_memory.rel_address = ((u64)(first_dword & 0xFF) << 32ul) | ((u64)second_dword);
+        ldr_memory.bit_width = (first_dword >> 24) & 0xF;
+        ldr_memory.mem_type = static_cast<MemoryAccessType>((first_dword >> 20) & 0xF);
+        ldr_memory.reg_index = ((first_dword >> 16) & 0xF);
+        ldr_memory.load_from_reg = ((first_dword >> 12) & 0xF) != 0;
+        ldr_memory.rel_address =
+            (static_cast<u64>(first_dword & 0xFF) << 32ul) | static_cast<u64>(second_dword);
+        opcode.opcode = ldr_memory;
     } break;
-    case CheatVmOpcodeType_StoreStaticToAddress: {
-        /* 6T0RIor0 VVVVVVVV VVVVVVVV */
-        /* Read additional words. */
-        opcode.str_static.bit_width = (first_dword >> 24) & 0xF;
-        opcode.str_static.reg_index = ((first_dword >> 16) & 0xF);
-        opcode.str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
-        opcode.str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
-        opcode.str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
-        opcode.str_static.value = (((u64)GetNextDword()) << 32ul) | ((u64)GetNextDword());
+    case CheatVmOpcodeType::StoreStaticToAddress: {
+        StoreStaticToAddressOpcode str_static{};
+        // 6T0RIor0 VVVVVVVV VVVVVVVV
+        // Read additional words.
+        str_static.bit_width = (first_dword >> 24) & 0xF;
+        str_static.reg_index = ((first_dword >> 16) & 0xF);
+        str_static.increment_reg = ((first_dword >> 12) & 0xF) != 0;
+        str_static.add_offset_reg = ((first_dword >> 8) & 0xF) != 0;
+        str_static.offset_reg_index = ((first_dword >> 4) & 0xF);
+        str_static.value =
+            (static_cast<u64>(GetNextDword()) << 32ul) | static_cast<u64>(GetNextDword());
+        opcode.opcode = str_static;
     } break;
-    case CheatVmOpcodeType_PerformArithmeticStatic: {
-        /* 7T0RC000 VVVVVVVV */
-        /* Read additional words. */
-        opcode.perform_math_static.bit_width = (first_dword >> 24) & 0xF;
-        opcode.perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
-        opcode.perform_math_static.math_type = (RegisterArithmeticType)((first_dword >> 12) & 0xF);
-        opcode.perform_math_static.value = GetNextDword();
+    case CheatVmOpcodeType::PerformArithmeticStatic: {
+        PerformArithmeticStaticOpcode perform_math_static{};
+        // 7T0RC000 VVVVVVVV
+        // Read additional words.
+        perform_math_static.bit_width = (first_dword >> 24) & 0xF;
+        perform_math_static.reg_index = ((first_dword >> 16) & 0xF);
+        perform_math_static.math_type =
+            static_cast<RegisterArithmeticType>((first_dword >> 12) & 0xF);
+        perform_math_static.value = GetNextDword();
+        opcode.opcode = perform_math_static;
     } break;
-    case CheatVmOpcodeType_BeginKeypressConditionalBlock: {
-        /* 8kkkkkkk */
-        /* Just parse the mask. */
-        opcode.begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
+    case CheatVmOpcodeType::BeginKeypressConditionalBlock: {
+        BeginKeypressConditionalOpcode begin_keypress_cond{};
+        // 8kkkkkkk
+        // Just parse the mask.
+        begin_keypress_cond.key_mask = first_dword & 0x0FFFFFFF;
     } break;
-    case CheatVmOpcodeType_PerformArithmeticRegister: {
-        /* 9TCRSIs0 (VVVVVVVV (VVVVVVVV)) */
-        opcode.perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
-        opcode.perform_math_reg.math_type = (RegisterArithmeticType)((first_dword >> 20) & 0xF);
-        opcode.perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
-        opcode.perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
-        opcode.perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
-        if (opcode.perform_math_reg.has_immediate) {
-            opcode.perform_math_reg.src_reg_2_index = 0;
-            opcode.perform_math_reg.value = GetNextVmInt(opcode.perform_math_reg.bit_width);
+    case CheatVmOpcodeType::PerformArithmeticRegister: {
+        PerformArithmeticRegisterOpcode perform_math_reg{};
+        // 9TCRSIs0 (VVVVVVVV (VVVVVVVV))
+        perform_math_reg.bit_width = (first_dword >> 24) & 0xF;
+        perform_math_reg.math_type = static_cast<RegisterArithmeticType>((first_dword >> 20) & 0xF);
+        perform_math_reg.dst_reg_index = ((first_dword >> 16) & 0xF);
+        perform_math_reg.src_reg_1_index = ((first_dword >> 12) & 0xF);
+        perform_math_reg.has_immediate = ((first_dword >> 8) & 0xF) != 0;
+        if (perform_math_reg.has_immediate) {
+            perform_math_reg.src_reg_2_index = 0;
+            perform_math_reg.value = GetNextVmInt(perform_math_reg.bit_width);
         } else {
-            opcode.perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
+            perform_math_reg.src_reg_2_index = ((first_dword >> 4) & 0xF);
         }
+        opcode.opcode = perform_math_reg;
     } break;
-    case CheatVmOpcodeType_StoreRegisterToAddress: {
-        /* ATSRIOxa (aaaaaaaa) */
-        /* A = opcode 10 */
-        /* T = bit width */
-        /* S = src register index */
-        /* R = address register index */
-        /* I = 1 if increment address register, 0 if not increment address register */
-        /* O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region,
-                4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region +
-           Relative Address */
-        /* x = offset register (for offset type 1), memory type (for offset type 3) */
-        /* a = relative address (for offset type 2+3) */
-        opcode.str_register.bit_width = (first_dword >> 24) & 0xF;
-        opcode.str_register.str_reg_index = ((first_dword >> 20) & 0xF);
-        opcode.str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
-        opcode.str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
-        opcode.str_register.ofs_type = (StoreRegisterOffsetType)(((first_dword >> 8) & 0xF));
-        opcode.str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
-        switch (opcode.str_register.ofs_type) {
-        case StoreRegisterOffsetType_None:
-        case StoreRegisterOffsetType_Reg:
-            /* Nothing more to do */
+    case CheatVmOpcodeType::StoreRegisterToAddress: {
+        StoreRegisterToAddressOpcode str_register{};
+        // ATSRIOxa (aaaaaaaa)
+        // A = opcode 10
+        // T = bit width
+        // S = src register index
+        // R = address register index
+        // I = 1 if increment address register, 0 if not increment address register
+        // O = offset type, 0 = None, 1 = Register, 2 = Immediate, 3 = Memory Region,
+        //       4 = Memory Region + Relative Address (ignore address register), 5 = Memory Region +
+        //  Relative Address
+        // x = offset register (for offset type 1), memory type (for offset type 3)
+        // a = relative address (for offset type 2+3)
+        str_register.bit_width = (first_dword >> 24) & 0xF;
+        str_register.str_reg_index = ((first_dword >> 20) & 0xF);
+        str_register.addr_reg_index = ((first_dword >> 16) & 0xF);
+        str_register.increment_reg = ((first_dword >> 12) & 0xF) != 0;
+        str_register.ofs_type = static_cast<StoreRegisterOffsetType>(((first_dword >> 8) & 0xF));
+        str_register.ofs_reg_index = ((first_dword >> 4) & 0xF);
+        switch (str_register.ofs_type) {
+        case StoreRegisterOffsetType::None:
+        case StoreRegisterOffsetType::Reg:
+            // Nothing more to do
             break;
-        case StoreRegisterOffsetType_Imm:
-            opcode.str_register.rel_address =
-                (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+        case StoreRegisterOffsetType::Imm:
+            str_register.rel_address =
+                ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
             break;
-        case StoreRegisterOffsetType_MemReg:
-            opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
+        case StoreRegisterOffsetType::MemReg:
+            str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
             break;
-        case StoreRegisterOffsetType_MemImm:
-        case StoreRegisterOffsetType_MemImmReg:
-            opcode.str_register.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
-            opcode.str_register.rel_address =
-                (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+        case StoreRegisterOffsetType::MemImm:
+        case StoreRegisterOffsetType::MemImmReg:
+            str_register.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+            str_register.rel_address =
+                ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
             break;
         default:
-            opcode.str_register.ofs_type = StoreRegisterOffsetType_None;
+            str_register.ofs_type = StoreRegisterOffsetType::None;
             break;
         }
+        opcode.opcode = str_register;
     } break;
-    case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
-        /* C0TcSX## */
-        /* C0TcS0Ma aaaaaaaa */
-        /* C0TcS1Mr */
-        /* C0TcS2Ra aaaaaaaa */
-        /* C0TcS3Rr */
-        /* C0TcS400 VVVVVVVV (VVVVVVVV) */
-        /* C0TcS5X0 */
-        /* C0 = opcode 0xC0 */
-        /* T = bit width */
-        /* c = condition type. */
-        /* S = source register. */
-        /* X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
-         * register, */
-        /*     2 = register with relative offset, 3 = register with offset register, 4 = static
-         * value, 5 = other register. */
-        /* M = memory type. */
-        /* R = address register. */
-        /* a = relative address. */
-        /* r = offset register. */
-        /* X = other register. */
-        /* V = value. */
-        opcode.begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
-        opcode.begin_reg_cond.cond_type = (ConditionalComparisonType)((first_dword >> 16) & 0xF);
-        opcode.begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
-        opcode.begin_reg_cond.comp_type = (CompareRegisterValueType)((first_dword >> 8) & 0xF);
+    case CheatVmOpcodeType::BeginRegisterConditionalBlock: {
+        BeginRegisterConditionalOpcode begin_reg_cond{};
+        // C0TcSX##
+        // C0TcS0Ma aaaaaaaa
+        // C0TcS1Mr
+        // C0TcS2Ra aaaaaaaa
+        // C0TcS3Rr
+        // C0TcS400 VVVVVVVV (VVVVVVVV)
+        // C0TcS5X0
+        // C0 = opcode 0xC0
+        // T = bit width
+        // c = condition type.
+        // S = source register.
+        // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
+        // register,
+        //     2 = register with relative offset, 3 = register with offset register, 4 = static
+        // value, 5 = other register.
+        // M = memory type.
+        // R = address register.
+        // a = relative address.
+        // r = offset register.
+        // X = other register.
+        // V = value.
+        begin_reg_cond.bit_width = (first_dword >> 20) & 0xF;
+        begin_reg_cond.cond_type =
+            static_cast<ConditionalComparisonType>((first_dword >> 16) & 0xF);
+        begin_reg_cond.val_reg_index = ((first_dword >> 12) & 0xF);
+        begin_reg_cond.comp_type = static_cast<CompareRegisterValueType>((first_dword >> 8) & 0xF);
 
-        switch (opcode.begin_reg_cond.comp_type) {
-        case CompareRegisterValueType_StaticValue:
-            opcode.begin_reg_cond.value = GetNextVmInt(opcode.begin_reg_cond.bit_width);
+        switch (begin_reg_cond.comp_type) {
+        case CompareRegisterValueType::StaticValue:
+            begin_reg_cond.value = GetNextVmInt(begin_reg_cond.bit_width);
             break;
-        case CompareRegisterValueType_OtherRegister:
-            opcode.begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
+        case CompareRegisterValueType::OtherRegister:
+            begin_reg_cond.other_reg_index = ((first_dword >> 4) & 0xF);
             break;
-        case CompareRegisterValueType_MemoryRelAddr:
-            opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
-            opcode.begin_reg_cond.rel_address =
-                (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+        case CompareRegisterValueType::MemoryRelAddr:
+            begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+            begin_reg_cond.rel_address =
+                ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
             break;
-        case CompareRegisterValueType_MemoryOfsReg:
-            opcode.begin_reg_cond.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
-            opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+        case CompareRegisterValueType::MemoryOfsReg:
+            begin_reg_cond.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+            begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
             break;
-        case CompareRegisterValueType_RegisterRelAddr:
-            opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
-            opcode.begin_reg_cond.rel_address =
-                (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+        case CompareRegisterValueType::RegisterRelAddr:
+            begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+            begin_reg_cond.rel_address =
+                ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
             break;
-        case CompareRegisterValueType_RegisterOfsReg:
-            opcode.begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
-            opcode.begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
+        case CompareRegisterValueType::RegisterOfsReg:
+            begin_reg_cond.addr_reg_index = ((first_dword >> 4) & 0xF);
+            begin_reg_cond.ofs_reg_index = (first_dword & 0xF);
             break;
         }
+        opcode.opcode = begin_reg_cond;
     } break;
-    case CheatVmOpcodeType_SaveRestoreRegister: {
-        /* C10D0Sx0 */
-        /* C1 = opcode 0xC1 */
-        /* D = destination index. */
-        /* S = source index. */
-        /* x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
-         * a register. */
-        /* NOTE: If we add more save slots later, current encoding is backwards compatible. */
-        opcode.save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
-        opcode.save_restore_reg.src_index = (first_dword >> 8) & 0xF;
-        opcode.save_restore_reg.op_type = (SaveRestoreRegisterOpType)((first_dword >> 4) & 0xF);
+    case CheatVmOpcodeType::SaveRestoreRegister: {
+        SaveRestoreRegisterOpcode save_restore_reg{};
+        // C10D0Sx0
+        // C1 = opcode 0xC1
+        // D = destination index.
+        // S = source index.
+        // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving a register, 0 if restoring
+        // a register.
+        // NOTE: If we add more save slots later, current encoding is backwards compatible.
+        save_restore_reg.dst_index = (first_dword >> 16) & 0xF;
+        save_restore_reg.src_index = (first_dword >> 8) & 0xF;
+        save_restore_reg.op_type = static_cast<SaveRestoreRegisterOpType>((first_dword >> 4) & 0xF);
+        opcode.opcode = save_restore_reg;
     } break;
-    case CheatVmOpcodeType_SaveRestoreRegisterMask: {
-        /* C2x0XXXX */
-        /* C2 = opcode 0xC2 */
-        /* x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring. */
-        /* X = 16-bit bitmask, bit i --> save or restore register i. */
-        opcode.save_restore_regmask.op_type =
-            (SaveRestoreRegisterOpType)((first_dword >> 20) & 0xF);
-        for (size_t i = 0; i < NumRegisters; i++) {
-            opcode.save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
+    case CheatVmOpcodeType::SaveRestoreRegisterMask: {
+        SaveRestoreRegisterMaskOpcode save_restore_regmask{};
+        // C2x0XXXX
+        // C2 = opcode 0xC2
+        // x = 3 if clearing reg, 2 if clearing saved value, 1 if saving, 0 if restoring.
+        // X = 16-bit bitmask, bit i --> save or restore register i.
+        save_restore_regmask.op_type =
+            static_cast<SaveRestoreRegisterOpType>((first_dword >> 20) & 0xF);
+        for (std::size_t i = 0; i < NumRegisters; i++) {
+            save_restore_regmask.should_operate[i] = (first_dword & (1u << i)) != 0;
         }
+        opcode.opcode = save_restore_regmask;
     } break;
-    case CheatVmOpcodeType_DebugLog: {
-        /* FFFTIX## */
-        /* FFFTI0Ma aaaaaaaa */
-        /* FFFTI1Mr */
-        /* FFFTI2Ra aaaaaaaa */
-        /* FFFTI3Rr */
-        /* FFFTI4X0 */
-        /* FFF = opcode 0xFFF */
-        /* T = bit width. */
-        /* I = log id. */
-        /* X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
-         * register, */
-        /*     2 = register with relative offset, 3 = register with offset register, 4 = register
-         * value. */
-        /* M = memory type. */
-        /* R = address register. */
-        /* a = relative address. */
-        /* r = offset register. */
-        /* X = value register. */
-        opcode.debug_log.bit_width = (first_dword >> 16) & 0xF;
-        opcode.debug_log.log_id = ((first_dword >> 12) & 0xF);
-        opcode.debug_log.val_type = (DebugLogValueType)((first_dword >> 8) & 0xF);
+    case CheatVmOpcodeType::DebugLog: {
+        DebugLogOpcode debug_log{};
+        // FFFTIX##
+        // FFFTI0Ma aaaaaaaa
+        // FFFTI1Mr
+        // FFFTI2Ra aaaaaaaa
+        // FFFTI3Rr
+        // FFFTI4X0
+        // FFF = opcode 0xFFF
+        // T = bit width.
+        // I = log id.
+        // X = value operand type, 0 = main/heap with relative offset, 1 = main/heap with offset
+        // register,
+        //     2 = register with relative offset, 3 = register with offset register, 4 = register
+        // value.
+        // M = memory type.
+        // R = address register.
+        // a = relative address.
+        // r = offset register.
+        // X = value register.
+        debug_log.bit_width = (first_dword >> 16) & 0xF;
+        debug_log.log_id = ((first_dword >> 12) & 0xF);
+        debug_log.val_type = static_cast<DebugLogValueType>((first_dword >> 8) & 0xF);
 
-        switch (opcode.debug_log.val_type) {
-        case DebugLogValueType_RegisterValue:
-            opcode.debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
+        switch (debug_log.val_type) {
+        case DebugLogValueType::RegisterValue:
+            debug_log.val_reg_index = ((first_dword >> 4) & 0xF);
             break;
-        case DebugLogValueType_MemoryRelAddr:
-            opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
-            opcode.debug_log.rel_address =
-                (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+        case DebugLogValueType::MemoryRelAddr:
+            debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+            debug_log.rel_address =
+                ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
             break;
-        case DebugLogValueType_MemoryOfsReg:
-            opcode.debug_log.mem_type = (MemoryAccessType)((first_dword >> 4) & 0xF);
-            opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
+        case DebugLogValueType::MemoryOfsReg:
+            debug_log.mem_type = static_cast<MemoryAccessType>((first_dword >> 4) & 0xF);
+            debug_log.ofs_reg_index = (first_dword & 0xF);
             break;
-        case DebugLogValueType_RegisterRelAddr:
-            opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
-            opcode.debug_log.rel_address =
-                (((u64)(first_dword & 0xF) << 32ul) | ((u64)GetNextDword()));
+        case DebugLogValueType::RegisterRelAddr:
+            debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
+            debug_log.rel_address =
+                ((static_cast<u64>(first_dword & 0xF) << 32ul) | static_cast<u64>(GetNextDword()));
             break;
-        case DebugLogValueType_RegisterOfsReg:
-            opcode.debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
-            opcode.debug_log.ofs_reg_index = (first_dword & 0xF);
+        case DebugLogValueType::RegisterOfsReg:
+            debug_log.addr_reg_index = ((first_dword >> 4) & 0xF);
+            debug_log.ofs_reg_index = (first_dword & 0xF);
             break;
         }
+        opcode.opcode = debug_log;
     } break;
-    case CheatVmOpcodeType_ExtendedWidth:
-    case CheatVmOpcodeType_DoubleExtendedWidth:
+    case CheatVmOpcodeType::ExtendedWidth:
+    case CheatVmOpcodeType::DoubleExtendedWidth:
     default:
-        /* Unrecognized instruction cannot be decoded. */
+        // Unrecognized instruction cannot be decoded.
         valid = false;
+        opcode.opcode = UnrecognizedInstruction{opcode_type};
         break;
     }
 
-    /* End decoding. */
+    // End decoding.
     return valid;
 }
 
 void DmntCheatVm::SkipConditionalBlock() {
-    if (this->condition_depth > 0) {
-        /* We want to continue until we're out of the current block. */
-        const size_t desired_depth = this->condition_depth - 1;
+    if (condition_depth > 0) {
+        // We want to continue until we're out of the current block.
+        const std::size_t desired_depth = condition_depth - 1;
 
         CheatVmOpcode skip_opcode{};
-        while (this->condition_depth > desired_depth && this->DecodeNextOpcode(skip_opcode)) {
-            /* Decode instructions until we see end of the current conditional block. */
-            /* NOTE: This is broken in gateway's implementation. */
-            /* Gateway currently checks for "0x2" instead of "0x20000000" */
-            /* In addition, they do a linear scan instead of correctly decoding opcodes. */
-            /* This causes issues if "0x2" appears as an immediate in the conditional block... */
+        while (condition_depth > desired_depth && DecodeNextOpcode(skip_opcode)) {
+            // Decode instructions until we see end of the current conditional block.
+            // NOTE: This is broken in gateway's implementation.
+            // Gateway currently checks for "0x2" instead of "0x20000000"
+            // In addition, they do a linear scan instead of correctly decoding opcodes.
+            // This causes issues if "0x2" appears as an immediate in the conditional block...
 
-            /* We also support nesting of conditional blocks, and Gateway does not. */
+            // We also support nesting of conditional blocks, and Gateway does not.
             if (skip_opcode.begin_conditional_block) {
-                this->condition_depth++;
-            } else if (skip_opcode.opcode == CheatVmOpcodeType_EndConditionalBlock) {
-                this->condition_depth--;
+                condition_depth++;
+            } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) {
+                condition_depth--;
             }
         }
     } else {
-        /* Skipping, but this->condition_depth = 0. */
-        /* This is an error condition. */
-        /* However, I don't actually believe it is possible for this to happen. */
-        /* I guess we'll throw a fatal error here, so as to encourage me to fix the VM */
-        /* in the event that someone triggers it? I don't know how you'd do that. */
+        // Skipping, but condition_depth = 0.
+        // This is an error condition.
+        // However, I don't actually believe it is possible for this to happen.
+        // I guess we'll throw a fatal error here, so as to encourage me to fix the VM
+        // in the event that someone triggers it? I don't know how you'd do that.
         UNREACHABLE_MSG("Invalid condition depth in DMNT Cheat VM");
     }
 }
@@ -602,7 +646,7 @@ u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
     case 8:
         return value.bit64;
     default:
-        /* Invalid bit width -> return 0. */
+        // Invalid bit width -> return 0.
         return 0;
     }
 }
@@ -610,39 +654,37 @@ u64 DmntCheatVm::GetVmInt(VmInt value, u32 bit_width) {
 u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata,
                                         MemoryAccessType mem_type, u64 rel_address) {
     switch (mem_type) {
-    case MemoryAccessType_MainNso:
+    case MemoryAccessType::MainNso:
     default:
         return metadata.main_nso_extents.base + rel_address;
-    case MemoryAccessType_Heap:
+    case MemoryAccessType::Heap:
         return metadata.heap_extents.base + rel_address;
     }
 }
 
 void DmntCheatVm::ResetState() {
-    for (size_t i = 0; i < DmntCheatVm::NumRegisters; i++) {
-        this->registers[i] = 0;
-        this->saved_values[i] = 0;
-        this->loop_tops[i] = 0;
-    }
-    this->instruction_ptr = 0;
-    this->condition_depth = 0;
-    this->decode_success = true;
+    registers.fill(0);
+    saved_values.fill(0);
+    loop_tops.fill(0);
+    instruction_ptr = 0;
+    condition_depth = 0;
+    decode_success = true;
 }
 
 bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
-    /* Reset opcode count. */
-    this->num_opcodes = 0;
+    // Reset opcode count.
+    num_opcodes = 0;
 
-    for (size_t i = 0; i < entries.size(); i++) {
+    for (std::size_t i = 0; i < entries.size(); i++) {
         if (entries[i].enabled) {
-            /* Bounds check. */
-            if (entries[i].definition.num_opcodes + this->num_opcodes > MaximumProgramOpcodeCount) {
-                this->num_opcodes = 0;
+            // Bounds check.
+            if (entries[i].definition.num_opcodes + num_opcodes > MaximumProgramOpcodeCount) {
+                num_opcodes = 0;
                 return false;
             }
 
-            for (size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
-                this->program[this->num_opcodes++] = entries[i].definition.opcodes[n];
+            for (std::size_t n = 0; n < entries[i].definition.num_opcodes; n++) {
+                program[num_opcodes++] = entries[i].definition.opcodes[n];
             }
         }
     }
@@ -653,262 +695,249 @@ bool DmntCheatVm::LoadProgram(const std::vector<CheatEntry>& entries) {
 void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
     CheatVmOpcode cur_opcode{};
 
-    /* Get Keys down. */
+    // Get Keys down.
     u64 kDown = callbacks->HidKeysDown();
 
-    this->LogToDebugFile("Started VM execution.\n");
-    this->LogToDebugFile("Main NSO:  %012lx\n", metadata.main_nso_extents.base);
-    this->LogToDebugFile("Heap:      %012lx\n", metadata.main_nso_extents.base);
-    this->LogToDebugFile("Keys Down: %08x\n", (u32)(kDown & 0x0FFFFFFF));
+    callbacks->CommandLog("Started VM execution.");
+    callbacks->CommandLog(fmt::format("Main NSO:  {:012X}", metadata.main_nso_extents.base));
+    callbacks->CommandLog(fmt::format("Heap:      {:012X}", metadata.main_nso_extents.base));
+    callbacks->CommandLog(fmt::format("Keys Down: {:08X}", static_cast<u32>(kDown & 0x0FFFFFFF)));
 
-    /* Clear VM state. */
-    this->ResetState();
+    // Clear VM state.
+    ResetState();
 
-    /* Loop until program finishes. */
-    while (this->DecodeNextOpcode(cur_opcode)) {
-        this->LogToDebugFile("Instruction Ptr: %04x\n", (u32)this->instruction_ptr);
+    // Loop until program finishes.
+    while (DecodeNextOpcode(cur_opcode)) {
+        callbacks->CommandLog(
+            fmt::format("Instruction Ptr: {:04X}", static_cast<u32>(instruction_ptr)));
 
-        for (size_t i = 0; i < NumRegisters; i++) {
-            this->LogToDebugFile("Registers[%02x]: %016lx\n", i, this->registers[i]);
+        for (std::size_t i = 0; i < NumRegisters; i++) {
+            callbacks->CommandLog(fmt::format("Registers[{:02X}]: {:016X}", i, registers[i]));
         }
 
-        for (size_t i = 0; i < NumRegisters; i++) {
-            this->LogToDebugFile("SavedRegs[%02x]: %016lx\n", i, this->saved_values[i]);
+        for (std::size_t i = 0; i < NumRegisters; i++) {
+            callbacks->CommandLog(fmt::format("SavedRegs[{:02X}]: {:016X}", i, saved_values[i]));
         }
-        this->LogOpcode(cur_opcode);
+        LogOpcode(cur_opcode);
 
-        /* Increment conditional depth, if relevant. */
+        // Increment conditional depth, if relevant.
         if (cur_opcode.begin_conditional_block) {
-            this->condition_depth++;
+            condition_depth++;
         }
 
-        switch (cur_opcode.opcode) {
-        case CheatVmOpcodeType_StoreStatic: {
-            /* Calculate address, write value to memory. */
-            u64 dst_address = GetCheatProcessAddress(
-                metadata, cur_opcode.store_static.mem_type,
-                cur_opcode.store_static.rel_address +
-                    this->registers[cur_opcode.store_static.offset_register]);
-            u64 dst_value =
-                GetVmInt(cur_opcode.store_static.value, cur_opcode.store_static.bit_width);
-            switch (cur_opcode.store_static.bit_width) {
+        if (auto store_static = std::get_if<StoreStaticOpcode>(&cur_opcode.opcode)) {
+            // Calculate address, write value to memory.
+            u64 dst_address = GetCheatProcessAddress(metadata, store_static->mem_type,
+                                                     store_static->rel_address +
+                                                         registers[store_static->offset_register]);
+            u64 dst_value = GetVmInt(store_static->value, store_static->bit_width);
+            switch (store_static->bit_width) {
             case 1:
             case 2:
             case 4:
             case 8:
-                callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.store_static.bit_width);
+                callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width);
                 break;
             }
-        } break;
-        case CheatVmOpcodeType_BeginConditionalBlock: {
-            /* Read value from memory. */
-            u64 src_address = GetCheatProcessAddress(metadata, cur_opcode.begin_cond.mem_type,
-                                                     cur_opcode.begin_cond.rel_address);
+        } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) {
+            // Read value from memory.
+            u64 src_address =
+                GetCheatProcessAddress(metadata, begin_cond->mem_type, begin_cond->rel_address);
             u64 src_value = 0;
-            switch (cur_opcode.store_static.bit_width) {
+            switch (store_static->bit_width) {
             case 1:
             case 2:
             case 4:
             case 8:
-                callbacks->MemoryRead(src_address, &src_value, cur_opcode.begin_cond.bit_width);
+                callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width);
                 break;
             }
-            /* Check against condition. */
-            u64 cond_value = GetVmInt(cur_opcode.begin_cond.value, cur_opcode.begin_cond.bit_width);
+            // Check against condition.
+            u64 cond_value = GetVmInt(begin_cond->value, begin_cond->bit_width);
             bool cond_met = false;
-            switch (cur_opcode.begin_cond.cond_type) {
-            case ConditionalComparisonType_GT:
+            switch (begin_cond->cond_type) {
+            case ConditionalComparisonType::GT:
                 cond_met = src_value > cond_value;
                 break;
-            case ConditionalComparisonType_GE:
+            case ConditionalComparisonType::GE:
                 cond_met = src_value >= cond_value;
                 break;
-            case ConditionalComparisonType_LT:
+            case ConditionalComparisonType::LT:
                 cond_met = src_value < cond_value;
                 break;
-            case ConditionalComparisonType_LE:
+            case ConditionalComparisonType::LE:
                 cond_met = src_value <= cond_value;
                 break;
-            case ConditionalComparisonType_EQ:
+            case ConditionalComparisonType::EQ:
                 cond_met = src_value == cond_value;
                 break;
-            case ConditionalComparisonType_NE:
+            case ConditionalComparisonType::NE:
                 cond_met = src_value != cond_value;
                 break;
             }
-            /* Skip conditional block if condition not met. */
+            // Skip conditional block if condition not met.
             if (!cond_met) {
-                this->SkipConditionalBlock();
+                SkipConditionalBlock();
             }
-        } break;
-        case CheatVmOpcodeType_EndConditionalBlock:
-            /* Decrement the condition depth. */
-            /* We will assume, graciously, that mismatched conditional block ends are a nop. */
-            if (this->condition_depth > 0) {
-                this->condition_depth--;
+        } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) {
+            // Decrement the condition depth.
+            // We will assume, graciously, that mismatched conditional block ends are a nop.
+            if (condition_depth > 0) {
+                condition_depth--;
             }
-            break;
-        case CheatVmOpcodeType_ControlLoop:
-            if (cur_opcode.ctrl_loop.start_loop) {
-                /* Start a loop. */
-                this->registers[cur_opcode.ctrl_loop.reg_index] = cur_opcode.ctrl_loop.num_iters;
-                this->loop_tops[cur_opcode.ctrl_loop.reg_index] = this->instruction_ptr;
+        } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) {
+            if (ctrl_loop->start_loop) {
+                // Start a loop.
+                registers[ctrl_loop->reg_index] = ctrl_loop->num_iters;
+                loop_tops[ctrl_loop->reg_index] = instruction_ptr;
             } else {
-                /* End a loop. */
-                this->registers[cur_opcode.ctrl_loop.reg_index]--;
-                if (this->registers[cur_opcode.ctrl_loop.reg_index] != 0) {
-                    this->instruction_ptr = this->loop_tops[cur_opcode.ctrl_loop.reg_index];
+                // End a loop.
+                registers[ctrl_loop->reg_index]--;
+                if (registers[ctrl_loop->reg_index] != 0) {
+                    instruction_ptr = loop_tops[ctrl_loop->reg_index];
                 }
             }
-            break;
-        case CheatVmOpcodeType_LoadRegisterStatic:
-            /* Set a register to a static value. */
-            this->registers[cur_opcode.ldr_static.reg_index] = cur_opcode.ldr_static.value;
-            break;
-        case CheatVmOpcodeType_LoadRegisterMemory: {
-            /* Choose source address. */
+        } else if (auto ldr_static = std::get_if<LoadRegisterStaticOpcode>(&cur_opcode.opcode)) {
+            // Set a register to a static value.
+            registers[ldr_static->reg_index] = ldr_static->value;
+        } else if (auto ldr_memory = std::get_if<LoadRegisterMemoryOpcode>(&cur_opcode.opcode)) {
+            // Choose source address.
             u64 src_address;
-            if (cur_opcode.ldr_memory.load_from_reg) {
-                src_address = this->registers[cur_opcode.ldr_memory.reg_index] +
-                              cur_opcode.ldr_memory.rel_address;
+            if (ldr_memory->load_from_reg) {
+                src_address = registers[ldr_memory->reg_index] + ldr_memory->rel_address;
             } else {
-                src_address = GetCheatProcessAddress(metadata, cur_opcode.ldr_memory.mem_type,
-                                                     cur_opcode.ldr_memory.rel_address);
+                src_address =
+                    GetCheatProcessAddress(metadata, ldr_memory->mem_type, ldr_memory->rel_address);
             }
-            /* Read into register. Gateway only reads on valid bitwidth. */
-            switch (cur_opcode.ldr_memory.bit_width) {
+            // Read into register. Gateway only reads on valid bitwidth.
+            switch (ldr_memory->bit_width) {
             case 1:
             case 2:
             case 4:
             case 8:
-                callbacks->MemoryRead(src_address,
-                                      &this->registers[cur_opcode.ldr_memory.reg_index],
-                                      cur_opcode.ldr_memory.bit_width);
+                callbacks->MemoryRead(src_address, &registers[ldr_memory->reg_index],
+                                      ldr_memory->bit_width);
                 break;
             }
-        } break;
-        case CheatVmOpcodeType_StoreStaticToAddress: {
-            /* Calculate address. */
-            u64 dst_address = this->registers[cur_opcode.str_static.reg_index];
-            u64 dst_value = cur_opcode.str_static.value;
-            if (cur_opcode.str_static.add_offset_reg) {
-                dst_address += this->registers[cur_opcode.str_static.offset_reg_index];
+        } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) {
+            // Calculate address.
+            u64 dst_address = registers[str_static->reg_index];
+            u64 dst_value = str_static->value;
+            if (str_static->add_offset_reg) {
+                dst_address += registers[str_static->offset_reg_index];
             }
-            /* Write value to memory. Gateway only writes on valid bitwidth. */
-            switch (cur_opcode.str_static.bit_width) {
+            // Write value to memory. Gateway only writes on valid bitwidth.
+            switch (str_static->bit_width) {
             case 1:
             case 2:
             case 4:
             case 8:
-                callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_static.bit_width);
+                callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width);
                 break;
             }
-            /* Increment register if relevant. */
-            if (cur_opcode.str_static.increment_reg) {
-                this->registers[cur_opcode.str_static.reg_index] += cur_opcode.str_static.bit_width;
+            // Increment register if relevant.
+            if (str_static->increment_reg) {
+                registers[str_static->reg_index] += str_static->bit_width;
             }
-        } break;
-        case CheatVmOpcodeType_PerformArithmeticStatic: {
-            /* Do requested math. */
-            switch (cur_opcode.perform_math_static.math_type) {
-            case RegisterArithmeticType_Addition:
-                this->registers[cur_opcode.perform_math_static.reg_index] +=
-                    (u64)cur_opcode.perform_math_static.value;
+        } else if (auto perform_math_static =
+                       std::get_if<PerformArithmeticStaticOpcode>(&cur_opcode.opcode)) {
+            // Do requested math.
+            switch (perform_math_static->math_type) {
+            case RegisterArithmeticType::Addition:
+                registers[perform_math_static->reg_index] +=
+                    static_cast<u64>(perform_math_static->value);
                 break;
-            case RegisterArithmeticType_Subtraction:
-                this->registers[cur_opcode.perform_math_static.reg_index] -=
-                    (u64)cur_opcode.perform_math_static.value;
+            case RegisterArithmeticType::Subtraction:
+                registers[perform_math_static->reg_index] -=
+                    static_cast<u64>(perform_math_static->value);
                 break;
-            case RegisterArithmeticType_Multiplication:
-                this->registers[cur_opcode.perform_math_static.reg_index] *=
-                    (u64)cur_opcode.perform_math_static.value;
+            case RegisterArithmeticType::Multiplication:
+                registers[perform_math_static->reg_index] *=
+                    static_cast<u64>(perform_math_static->value);
                 break;
-            case RegisterArithmeticType_LeftShift:
-                this->registers[cur_opcode.perform_math_static.reg_index] <<=
-                    (u64)cur_opcode.perform_math_static.value;
+            case RegisterArithmeticType::LeftShift:
+                registers[perform_math_static->reg_index] <<=
+                    static_cast<u64>(perform_math_static->value);
                 break;
-            case RegisterArithmeticType_RightShift:
-                this->registers[cur_opcode.perform_math_static.reg_index] >>=
-                    (u64)cur_opcode.perform_math_static.value;
+            case RegisterArithmeticType::RightShift:
+                registers[perform_math_static->reg_index] >>=
+                    static_cast<u64>(perform_math_static->value);
                 break;
             default:
-                /* Do not handle extensions here. */
+                // Do not handle extensions here.
                 break;
             }
-            /* Apply bit width. */
-            switch (cur_opcode.perform_math_static.bit_width) {
+            // Apply bit width.
+            switch (perform_math_static->bit_width) {
             case 1:
-                this->registers[cur_opcode.perform_math_static.reg_index] =
-                    static_cast<u8>(this->registers[cur_opcode.perform_math_static.reg_index]);
+                registers[perform_math_static->reg_index] =
+                    static_cast<u8>(registers[perform_math_static->reg_index]);
                 break;
             case 2:
-                this->registers[cur_opcode.perform_math_static.reg_index] =
-                    static_cast<u16>(this->registers[cur_opcode.perform_math_static.reg_index]);
+                registers[perform_math_static->reg_index] =
+                    static_cast<u16>(registers[perform_math_static->reg_index]);
                 break;
             case 4:
-                this->registers[cur_opcode.perform_math_static.reg_index] =
-                    static_cast<u32>(this->registers[cur_opcode.perform_math_static.reg_index]);
+                registers[perform_math_static->reg_index] =
+                    static_cast<u32>(registers[perform_math_static->reg_index]);
                 break;
             case 8:
-                this->registers[cur_opcode.perform_math_static.reg_index] =
-                    static_cast<u64>(this->registers[cur_opcode.perform_math_static.reg_index]);
+                registers[perform_math_static->reg_index] =
+                    static_cast<u64>(registers[perform_math_static->reg_index]);
                 break;
             }
-        } break;
-        case CheatVmOpcodeType_BeginKeypressConditionalBlock:
-            /* Check for keypress. */
-            if ((cur_opcode.begin_keypress_cond.key_mask & kDown) !=
-                cur_opcode.begin_keypress_cond.key_mask) {
-                /* Keys not pressed. Skip conditional block. */
-                this->SkipConditionalBlock();
+        } else if (auto begin_keypress_cond =
+                       std::get_if<BeginKeypressConditionalOpcode>(&cur_opcode.opcode)) {
+            // Check for keypress.
+            if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) {
+                // Keys not pressed. Skip conditional block.
+                SkipConditionalBlock();
             }
-            break;
-        case CheatVmOpcodeType_PerformArithmeticRegister: {
-            const u64 operand_1_value =
-                this->registers[cur_opcode.perform_math_reg.src_reg_1_index];
+        } else if (auto perform_math_reg =
+                       std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) {
+            const u64 operand_1_value = registers[perform_math_reg->src_reg_1_index];
             const u64 operand_2_value =
-                cur_opcode.perform_math_reg.has_immediate
-                    ? GetVmInt(cur_opcode.perform_math_reg.value,
-                               cur_opcode.perform_math_reg.bit_width)
-                    : this->registers[cur_opcode.perform_math_reg.src_reg_2_index];
+                perform_math_reg->has_immediate
+                    ? GetVmInt(perform_math_reg->value, perform_math_reg->bit_width)
+                    : registers[perform_math_reg->src_reg_2_index];
 
             u64 res_val = 0;
-            /* Do requested math. */
-            switch (cur_opcode.perform_math_reg.math_type) {
-            case RegisterArithmeticType_Addition:
+            // Do requested math.
+            switch (perform_math_reg->math_type) {
+            case RegisterArithmeticType::Addition:
                 res_val = operand_1_value + operand_2_value;
                 break;
-            case RegisterArithmeticType_Subtraction:
+            case RegisterArithmeticType::Subtraction:
                 res_val = operand_1_value - operand_2_value;
                 break;
-            case RegisterArithmeticType_Multiplication:
+            case RegisterArithmeticType::Multiplication:
                 res_val = operand_1_value * operand_2_value;
                 break;
-            case RegisterArithmeticType_LeftShift:
+            case RegisterArithmeticType::LeftShift:
                 res_val = operand_1_value << operand_2_value;
                 break;
-            case RegisterArithmeticType_RightShift:
+            case RegisterArithmeticType::RightShift:
                 res_val = operand_1_value >> operand_2_value;
                 break;
-            case RegisterArithmeticType_LogicalAnd:
+            case RegisterArithmeticType::LogicalAnd:
                 res_val = operand_1_value & operand_2_value;
                 break;
-            case RegisterArithmeticType_LogicalOr:
+            case RegisterArithmeticType::LogicalOr:
                 res_val = operand_1_value | operand_2_value;
                 break;
-            case RegisterArithmeticType_LogicalNot:
+            case RegisterArithmeticType::LogicalNot:
                 res_val = ~operand_1_value;
                 break;
-            case RegisterArithmeticType_LogicalXor:
+            case RegisterArithmeticType::LogicalXor:
                 res_val = operand_1_value ^ operand_2_value;
                 break;
-            case RegisterArithmeticType_None:
+            case RegisterArithmeticType::None:
                 res_val = operand_1_value;
                 break;
             }
 
-            /* Apply bit width. */
-            switch (cur_opcode.perform_math_reg.bit_width) {
+            // Apply bit width.
+            switch (perform_math_reg->bit_width) {
             case 1:
                 res_val = static_cast<u8>(res_val);
                 break;
@@ -923,282 +952,259 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
                 break;
             }
 
-            /* Save to register. */
-            this->registers[cur_opcode.perform_math_reg.dst_reg_index] = res_val;
-        } break;
-        case CheatVmOpcodeType_StoreRegisterToAddress: {
-            /* Calculate address. */
-            u64 dst_value = this->registers[cur_opcode.str_register.str_reg_index];
-            u64 dst_address = this->registers[cur_opcode.str_register.addr_reg_index];
-            switch (cur_opcode.str_register.ofs_type) {
-            case StoreRegisterOffsetType_None:
-                /* Nothing more to do */
+            // Save to register.
+            registers[perform_math_reg->dst_reg_index] = res_val;
+        } else if (auto str_register =
+                       std::get_if<StoreRegisterToAddressOpcode>(&cur_opcode.opcode)) {
+            // Calculate address.
+            u64 dst_value = registers[str_register->str_reg_index];
+            u64 dst_address = registers[str_register->addr_reg_index];
+            switch (str_register->ofs_type) {
+            case StoreRegisterOffsetType::None:
+                // Nothing more to do
                 break;
-            case StoreRegisterOffsetType_Reg:
-                dst_address += this->registers[cur_opcode.str_register.ofs_reg_index];
+            case StoreRegisterOffsetType::Reg:
+                dst_address += registers[str_register->ofs_reg_index];
                 break;
-            case StoreRegisterOffsetType_Imm:
-                dst_address += cur_opcode.str_register.rel_address;
+            case StoreRegisterOffsetType::Imm:
+                dst_address += str_register->rel_address;
                 break;
-            case StoreRegisterOffsetType_MemReg:
-                dst_address =
-                    GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
-                                           this->registers[cur_opcode.str_register.addr_reg_index]);
+            case StoreRegisterOffsetType::MemReg:
+                dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
+                                                     registers[str_register->addr_reg_index]);
                 break;
-            case StoreRegisterOffsetType_MemImm:
-                dst_address = GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
-                                                     cur_opcode.str_register.rel_address);
+            case StoreRegisterOffsetType::MemImm:
+                dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
+                                                     str_register->rel_address);
                 break;
-            case StoreRegisterOffsetType_MemImmReg:
-                dst_address =
-                    GetCheatProcessAddress(metadata, cur_opcode.str_register.mem_type,
-                                           this->registers[cur_opcode.str_register.addr_reg_index] +
-                                               cur_opcode.str_register.rel_address);
+            case StoreRegisterOffsetType::MemImmReg:
+                dst_address = GetCheatProcessAddress(metadata, str_register->mem_type,
+                                                     registers[str_register->addr_reg_index] +
+                                                         str_register->rel_address);
                 break;
             }
 
-            /* Write value to memory. Write only on valid bitwidth. */
-            switch (cur_opcode.str_register.bit_width) {
+            // Write value to memory. Write only on valid bitwidth.
+            switch (str_register->bit_width) {
             case 1:
             case 2:
             case 4:
             case 8:
-                callbacks->MemoryWrite(dst_address, &dst_value, cur_opcode.str_register.bit_width);
+                callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width);
                 break;
             }
 
-            /* Increment register if relevant. */
-            if (cur_opcode.str_register.increment_reg) {
-                this->registers[cur_opcode.str_register.addr_reg_index] +=
-                    cur_opcode.str_register.bit_width;
+            // Increment register if relevant.
+            if (str_register->increment_reg) {
+                registers[str_register->addr_reg_index] += str_register->bit_width;
             }
-        } break;
-        case CheatVmOpcodeType_BeginRegisterConditionalBlock: {
-            /* Get value from register. */
+        } else if (auto begin_reg_cond =
+                       std::get_if<BeginRegisterConditionalOpcode>(&cur_opcode.opcode)) {
+            // Get value from register.
             u64 src_value = 0;
-            switch (cur_opcode.begin_reg_cond.bit_width) {
+            switch (begin_reg_cond->bit_width) {
             case 1:
-                src_value = static_cast<u8>(
-                    this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFul);
+                src_value = static_cast<u8>(registers[begin_reg_cond->val_reg_index] & 0xFFul);
                 break;
             case 2:
-                src_value = static_cast<u16>(
-                    this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFul);
+                src_value = static_cast<u16>(registers[begin_reg_cond->val_reg_index] & 0xFFFFul);
                 break;
             case 4:
-                src_value = static_cast<u32>(
-                    this->registers[cur_opcode.begin_reg_cond.val_reg_index] & 0xFFFFFFFFul);
+                src_value =
+                    static_cast<u32>(registers[begin_reg_cond->val_reg_index] & 0xFFFFFFFFul);
                 break;
             case 8:
-                src_value =
-                    static_cast<u64>(this->registers[cur_opcode.begin_reg_cond.val_reg_index] &
-                                     0xFFFFFFFFFFFFFFFFul);
+                src_value = static_cast<u64>(registers[begin_reg_cond->val_reg_index] &
+                                             0xFFFFFFFFFFFFFFFFul);
                 break;
             }
 
-            /* Read value from memory. */
+            // Read value from memory.
             u64 cond_value = 0;
-            if (cur_opcode.begin_reg_cond.comp_type == CompareRegisterValueType_StaticValue) {
-                cond_value =
-                    GetVmInt(cur_opcode.begin_reg_cond.value, cur_opcode.begin_reg_cond.bit_width);
-            } else if (cur_opcode.begin_reg_cond.comp_type ==
-                       CompareRegisterValueType_OtherRegister) {
-                switch (cur_opcode.begin_reg_cond.bit_width) {
+            if (begin_reg_cond->comp_type == CompareRegisterValueType::StaticValue) {
+                cond_value = GetVmInt(begin_reg_cond->value, begin_reg_cond->bit_width);
+            } else if (begin_reg_cond->comp_type == CompareRegisterValueType::OtherRegister) {
+                switch (begin_reg_cond->bit_width) {
                 case 1:
-                    cond_value = static_cast<u8>(
-                        this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFul);
+                    cond_value =
+                        static_cast<u8>(registers[begin_reg_cond->other_reg_index] & 0xFFul);
                     break;
                 case 2:
-                    cond_value = static_cast<u16>(
-                        this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFul);
+                    cond_value =
+                        static_cast<u16>(registers[begin_reg_cond->other_reg_index] & 0xFFFFul);
                     break;
                 case 4:
-                    cond_value = static_cast<u32>(
-                        this->registers[cur_opcode.begin_reg_cond.other_reg_index] & 0xFFFFFFFFul);
+                    cond_value =
+                        static_cast<u32>(registers[begin_reg_cond->other_reg_index] & 0xFFFFFFFFul);
                     break;
                 case 8:
-                    cond_value = static_cast<u64>(
-                        this->registers[cur_opcode.begin_reg_cond.other_reg_index] &
-                        0xFFFFFFFFFFFFFFFFul);
+                    cond_value = static_cast<u64>(registers[begin_reg_cond->other_reg_index] &
+                                                  0xFFFFFFFFFFFFFFFFul);
                     break;
                 }
             } else {
                 u64 cond_address = 0;
-                switch (cur_opcode.begin_reg_cond.comp_type) {
-                case CompareRegisterValueType_MemoryRelAddr:
+                switch (begin_reg_cond->comp_type) {
+                case CompareRegisterValueType::MemoryRelAddr:
+                    cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
+                                                          begin_reg_cond->rel_address);
+                    break;
+                case CompareRegisterValueType::MemoryOfsReg:
+                    cond_address = GetCheatProcessAddress(metadata, begin_reg_cond->mem_type,
+                                                          registers[begin_reg_cond->ofs_reg_index]);
+                    break;
+                case CompareRegisterValueType::RegisterRelAddr:
                     cond_address =
-                        GetCheatProcessAddress(metadata, cur_opcode.begin_reg_cond.mem_type,
-                                               cur_opcode.begin_reg_cond.rel_address);
+                        registers[begin_reg_cond->addr_reg_index] + begin_reg_cond->rel_address;
                     break;
-                case CompareRegisterValueType_MemoryOfsReg:
-                    cond_address = GetCheatProcessAddress(
-                        metadata, cur_opcode.begin_reg_cond.mem_type,
-                        this->registers[cur_opcode.begin_reg_cond.ofs_reg_index]);
-                    break;
-                case CompareRegisterValueType_RegisterRelAddr:
-                    cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
-                                   cur_opcode.begin_reg_cond.rel_address;
-                    break;
-                case CompareRegisterValueType_RegisterOfsReg:
-                    cond_address = this->registers[cur_opcode.begin_reg_cond.addr_reg_index] +
-                                   this->registers[cur_opcode.begin_reg_cond.ofs_reg_index];
+                case CompareRegisterValueType::RegisterOfsReg:
+                    cond_address = registers[begin_reg_cond->addr_reg_index] +
+                                   registers[begin_reg_cond->ofs_reg_index];
                     break;
                 default:
                     break;
                 }
-                switch (cur_opcode.begin_reg_cond.bit_width) {
+                switch (begin_reg_cond->bit_width) {
                 case 1:
                 case 2:
                 case 4:
                 case 8:
-                    callbacks->MemoryRead(cond_address, &cond_value,
-                                          cur_opcode.begin_reg_cond.bit_width);
+                    callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width);
                     break;
                 }
             }
 
-            /* Check against condition. */
+            // Check against condition.
             bool cond_met = false;
-            switch (cur_opcode.begin_reg_cond.cond_type) {
-            case ConditionalComparisonType_GT:
+            switch (begin_reg_cond->cond_type) {
+            case ConditionalComparisonType::GT:
                 cond_met = src_value > cond_value;
                 break;
-            case ConditionalComparisonType_GE:
+            case ConditionalComparisonType::GE:
                 cond_met = src_value >= cond_value;
                 break;
-            case ConditionalComparisonType_LT:
+            case ConditionalComparisonType::LT:
                 cond_met = src_value < cond_value;
                 break;
-            case ConditionalComparisonType_LE:
+            case ConditionalComparisonType::LE:
                 cond_met = src_value <= cond_value;
                 break;
-            case ConditionalComparisonType_EQ:
+            case ConditionalComparisonType::EQ:
                 cond_met = src_value == cond_value;
                 break;
-            case ConditionalComparisonType_NE:
+            case ConditionalComparisonType::NE:
                 cond_met = src_value != cond_value;
                 break;
             }
 
-            /* Skip conditional block if condition not met. */
+            // Skip conditional block if condition not met.
             if (!cond_met) {
-                this->SkipConditionalBlock();
+                SkipConditionalBlock();
             }
-        } break;
-        case CheatVmOpcodeType_SaveRestoreRegister:
-            /* Save or restore a register. */
-            switch (cur_opcode.save_restore_reg.op_type) {
-            case SaveRestoreRegisterOpType_ClearRegs:
-                this->registers[cur_opcode.save_restore_reg.dst_index] = 0ul;
+        } else if (auto save_restore_reg =
+                       std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) {
+            // Save or restore a register.
+            switch (save_restore_reg->op_type) {
+            case SaveRestoreRegisterOpType::ClearRegs:
+                registers[save_restore_reg->dst_index] = 0ul;
                 break;
-            case SaveRestoreRegisterOpType_ClearSaved:
-                this->saved_values[cur_opcode.save_restore_reg.dst_index] = 0ul;
+            case SaveRestoreRegisterOpType::ClearSaved:
+                saved_values[save_restore_reg->dst_index] = 0ul;
                 break;
-            case SaveRestoreRegisterOpType_Save:
-                this->saved_values[cur_opcode.save_restore_reg.dst_index] =
-                    this->registers[cur_opcode.save_restore_reg.src_index];
+            case SaveRestoreRegisterOpType::Save:
+                saved_values[save_restore_reg->dst_index] = registers[save_restore_reg->src_index];
                 break;
-            case SaveRestoreRegisterOpType_Restore:
+            case SaveRestoreRegisterOpType::Restore:
             default:
-                this->registers[cur_opcode.save_restore_reg.dst_index] =
-                    this->saved_values[cur_opcode.save_restore_reg.src_index];
+                registers[save_restore_reg->dst_index] = saved_values[save_restore_reg->src_index];
                 break;
             }
-            break;
-        case CheatVmOpcodeType_SaveRestoreRegisterMask:
-            /* Save or restore register mask. */
+        } else if (auto save_restore_regmask =
+                       std::get_if<SaveRestoreRegisterMaskOpcode>(&cur_opcode.opcode)) {
+            // Save or restore register mask.
             u64* src;
             u64* dst;
-            switch (cur_opcode.save_restore_regmask.op_type) {
-            case SaveRestoreRegisterOpType_ClearSaved:
-            case SaveRestoreRegisterOpType_Save:
-                src = this->registers.data();
-                dst = this->saved_values.data();
+            switch (save_restore_regmask->op_type) {
+            case SaveRestoreRegisterOpType::ClearSaved:
+            case SaveRestoreRegisterOpType::Save:
+                src = registers.data();
+                dst = saved_values.data();
                 break;
-            case SaveRestoreRegisterOpType_ClearRegs:
-            case SaveRestoreRegisterOpType_Restore:
+            case SaveRestoreRegisterOpType::ClearRegs:
+            case SaveRestoreRegisterOpType::Restore:
             default:
-                src = this->registers.data();
-                dst = this->saved_values.data();
+                src = registers.data();
+                dst = saved_values.data();
                 break;
             }
-            for (size_t i = 0; i < NumRegisters; i++) {
-                if (cur_opcode.save_restore_regmask.should_operate[i]) {
-                    switch (cur_opcode.save_restore_regmask.op_type) {
-                    case SaveRestoreRegisterOpType_ClearSaved:
-                    case SaveRestoreRegisterOpType_ClearRegs:
+            for (std::size_t i = 0; i < NumRegisters; i++) {
+                if (save_restore_regmask->should_operate[i]) {
+                    switch (save_restore_regmask->op_type) {
+                    case SaveRestoreRegisterOpType::ClearSaved:
+                    case SaveRestoreRegisterOpType::ClearRegs:
                         dst[i] = 0ul;
                         break;
-                    case SaveRestoreRegisterOpType_Save:
-                    case SaveRestoreRegisterOpType_Restore:
+                    case SaveRestoreRegisterOpType::Save:
+                    case SaveRestoreRegisterOpType::Restore:
                     default:
                         dst[i] = src[i];
                         break;
                     }
                 }
             }
-            break;
-        case CheatVmOpcodeType_DebugLog: {
-            /* Read value from memory. */
+        } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) {
+            // Read value from memory.
             u64 log_value = 0;
-            if (cur_opcode.debug_log.val_type == DebugLogValueType_RegisterValue) {
-                switch (cur_opcode.debug_log.bit_width) {
+            if (debug_log->val_type == DebugLogValueType::RegisterValue) {
+                switch (debug_log->bit_width) {
                 case 1:
-                    log_value = static_cast<u8>(
-                        this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFul);
+                    log_value = static_cast<u8>(registers[debug_log->val_reg_index] & 0xFFul);
                     break;
                 case 2:
-                    log_value = static_cast<u16>(
-                        this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFul);
+                    log_value = static_cast<u16>(registers[debug_log->val_reg_index] & 0xFFFFul);
                     break;
                 case 4:
-                    log_value = static_cast<u32>(
-                        this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFul);
+                    log_value =
+                        static_cast<u32>(registers[debug_log->val_reg_index] & 0xFFFFFFFFul);
                     break;
                 case 8:
-                    log_value = static_cast<u64>(
-                        this->registers[cur_opcode.debug_log.val_reg_index] & 0xFFFFFFFFFFFFFFFFul);
+                    log_value = static_cast<u64>(registers[debug_log->val_reg_index] &
+                                                 0xFFFFFFFFFFFFFFFFul);
                     break;
                 }
             } else {
                 u64 val_address = 0;
-                switch (cur_opcode.debug_log.val_type) {
-                case DebugLogValueType_MemoryRelAddr:
-                    val_address = GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type,
-                                                         cur_opcode.debug_log.rel_address);
+                switch (debug_log->val_type) {
+                case DebugLogValueType::MemoryRelAddr:
+                    val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
+                                                         debug_log->rel_address);
                     break;
-                case DebugLogValueType_MemoryOfsReg:
+                case DebugLogValueType::MemoryOfsReg:
+                    val_address = GetCheatProcessAddress(metadata, debug_log->mem_type,
+                                                         registers[debug_log->ofs_reg_index]);
+                    break;
+                case DebugLogValueType::RegisterRelAddr:
+                    val_address = registers[debug_log->addr_reg_index] + debug_log->rel_address;
+                    break;
+                case DebugLogValueType::RegisterOfsReg:
                     val_address =
-                        GetCheatProcessAddress(metadata, cur_opcode.debug_log.mem_type,
-                                               this->registers[cur_opcode.debug_log.ofs_reg_index]);
-                    break;
-                case DebugLogValueType_RegisterRelAddr:
-                    val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
-                                  cur_opcode.debug_log.rel_address;
-                    break;
-                case DebugLogValueType_RegisterOfsReg:
-                    val_address = this->registers[cur_opcode.debug_log.addr_reg_index] +
-                                  this->registers[cur_opcode.debug_log.ofs_reg_index];
+                        registers[debug_log->addr_reg_index] + registers[debug_log->ofs_reg_index];
                     break;
                 default:
                     break;
                 }
-                switch (cur_opcode.debug_log.bit_width) {
+                switch (debug_log->bit_width) {
                 case 1:
                 case 2:
                 case 4:
                 case 8:
-                    callbacks->MemoryRead(val_address, &log_value, cur_opcode.debug_log.bit_width);
+                    callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width);
                     break;
                 }
             }
 
-            /* Log value. */
-            this->DebugLog(cur_opcode.debug_log.log_id, log_value);
-        } break;
-        default:
-            /* By default, we do a no-op. */
-            break;
+            // Log value.
+            DebugLog(debug_log->log_id, log_value);
         }
     }
 }
diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h
index bea451db40..d1580d7f61 100644
--- a/src/core/memory/dmnt_cheat_vm.h
+++ b/src/core/memory/dmnt_cheat_vm.h
@@ -24,6 +24,7 @@
 
 #pragma once
 
+#include <variant>
 #include <vector>
 #include <fmt/printf.h>
 #include "common/common_types.h"
@@ -31,100 +32,100 @@
 
 namespace Memory {
 
-enum CheatVmOpcodeType : u32 {
-    CheatVmOpcodeType_StoreStatic = 0,
-    CheatVmOpcodeType_BeginConditionalBlock = 1,
-    CheatVmOpcodeType_EndConditionalBlock = 2,
-    CheatVmOpcodeType_ControlLoop = 3,
-    CheatVmOpcodeType_LoadRegisterStatic = 4,
-    CheatVmOpcodeType_LoadRegisterMemory = 5,
-    CheatVmOpcodeType_StoreStaticToAddress = 6,
-    CheatVmOpcodeType_PerformArithmeticStatic = 7,
-    CheatVmOpcodeType_BeginKeypressConditionalBlock = 8,
+enum class CheatVmOpcodeType : u32 {
+    StoreStatic = 0,
+    BeginConditionalBlock = 1,
+    EndConditionalBlock = 2,
+    ControlLoop = 3,
+    LoadRegisterStatic = 4,
+    LoadRegisterMemory = 5,
+    StoreStaticToAddress = 6,
+    PerformArithmeticStatic = 7,
+    BeginKeypressConditionalBlock = 8,
 
-    /* These are not implemented by Gateway's VM. */
-    CheatVmOpcodeType_PerformArithmeticRegister = 9,
-    CheatVmOpcodeType_StoreRegisterToAddress = 10,
-    CheatVmOpcodeType_Reserved11 = 11,
+    // These are not implemented by Gateway's VM.
+    PerformArithmeticRegister = 9,
+    StoreRegisterToAddress = 10,
+    Reserved11 = 11,
 
-    /* This is a meta entry, and not a real opcode. */
-    /* This is to facilitate multi-nybble instruction decoding. */
-    CheatVmOpcodeType_ExtendedWidth = 12,
+    // This is a meta entry, and not a real opcode.
+    // This is to facilitate multi-nybble instruction decoding.
+    ExtendedWidth = 12,
 
-    /* Extended width opcodes. */
-    CheatVmOpcodeType_BeginRegisterConditionalBlock = 0xC0,
-    CheatVmOpcodeType_SaveRestoreRegister = 0xC1,
-    CheatVmOpcodeType_SaveRestoreRegisterMask = 0xC2,
+    // Extended width opcodes.
+    BeginRegisterConditionalBlock = 0xC0,
+    SaveRestoreRegister = 0xC1,
+    SaveRestoreRegisterMask = 0xC2,
 
-    /* This is a meta entry, and not a real opcode. */
-    /* This is to facilitate multi-nybble instruction decoding. */
-    CheatVmOpcodeType_DoubleExtendedWidth = 0xF0,
+    // This is a meta entry, and not a real opcode.
+    // This is to facilitate multi-nybble instruction decoding.
+    DoubleExtendedWidth = 0xF0,
 
-    /* Double-extended width opcodes. */
-    CheatVmOpcodeType_DebugLog = 0xFFF,
+    // Double-extended width opcodes.
+    DebugLog = 0xFFF,
 };
 
-enum MemoryAccessType : u32 {
-    MemoryAccessType_MainNso = 0,
-    MemoryAccessType_Heap = 1,
+enum class MemoryAccessType : u32 {
+    MainNso = 0,
+    Heap = 1,
 };
 
-enum ConditionalComparisonType : u32 {
-    ConditionalComparisonType_GT = 1,
-    ConditionalComparisonType_GE = 2,
-    ConditionalComparisonType_LT = 3,
-    ConditionalComparisonType_LE = 4,
-    ConditionalComparisonType_EQ = 5,
-    ConditionalComparisonType_NE = 6,
+enum class ConditionalComparisonType : u32 {
+    GT = 1,
+    GE = 2,
+    LT = 3,
+    LE = 4,
+    EQ = 5,
+    NE = 6,
 };
 
-enum RegisterArithmeticType : u32 {
-    RegisterArithmeticType_Addition = 0,
-    RegisterArithmeticType_Subtraction = 1,
-    RegisterArithmeticType_Multiplication = 2,
-    RegisterArithmeticType_LeftShift = 3,
-    RegisterArithmeticType_RightShift = 4,
+enum class RegisterArithmeticType : u32 {
+    Addition = 0,
+    Subtraction = 1,
+    Multiplication = 2,
+    LeftShift = 3,
+    RightShift = 4,
 
-    /* These are not supported by Gateway's VM. */
-    RegisterArithmeticType_LogicalAnd = 5,
-    RegisterArithmeticType_LogicalOr = 6,
-    RegisterArithmeticType_LogicalNot = 7,
-    RegisterArithmeticType_LogicalXor = 8,
+    // These are not supported by Gateway's VM.
+    LogicalAnd = 5,
+    LogicalOr = 6,
+    LogicalNot = 7,
+    LogicalXor = 8,
 
-    RegisterArithmeticType_None = 9,
+    None = 9,
 };
 
-enum StoreRegisterOffsetType : u32 {
-    StoreRegisterOffsetType_None = 0,
-    StoreRegisterOffsetType_Reg = 1,
-    StoreRegisterOffsetType_Imm = 2,
-    StoreRegisterOffsetType_MemReg = 3,
-    StoreRegisterOffsetType_MemImm = 4,
-    StoreRegisterOffsetType_MemImmReg = 5,
+enum class StoreRegisterOffsetType : u32 {
+    None = 0,
+    Reg = 1,
+    Imm = 2,
+    MemReg = 3,
+    MemImm = 4,
+    MemImmReg = 5,
 };
 
-enum CompareRegisterValueType : u32 {
-    CompareRegisterValueType_MemoryRelAddr = 0,
-    CompareRegisterValueType_MemoryOfsReg = 1,
-    CompareRegisterValueType_RegisterRelAddr = 2,
-    CompareRegisterValueType_RegisterOfsReg = 3,
-    CompareRegisterValueType_StaticValue = 4,
-    CompareRegisterValueType_OtherRegister = 5,
+enum class CompareRegisterValueType : u32 {
+    MemoryRelAddr = 0,
+    MemoryOfsReg = 1,
+    RegisterRelAddr = 2,
+    RegisterOfsReg = 3,
+    StaticValue = 4,
+    OtherRegister = 5,
 };
 
-enum SaveRestoreRegisterOpType : u32 {
-    SaveRestoreRegisterOpType_Restore = 0,
-    SaveRestoreRegisterOpType_Save = 1,
-    SaveRestoreRegisterOpType_ClearSaved = 2,
-    SaveRestoreRegisterOpType_ClearRegs = 3,
+enum class SaveRestoreRegisterOpType : u32 {
+    Restore = 0,
+    Save = 1,
+    ClearSaved = 2,
+    ClearRegs = 3,
 };
 
-enum DebugLogValueType : u32 {
-    DebugLogValueType_MemoryRelAddr = 0,
-    DebugLogValueType_MemoryOfsReg = 1,
-    DebugLogValueType_RegisterRelAddr = 2,
-    DebugLogValueType_RegisterOfsReg = 3,
-    DebugLogValueType_RegisterValue = 4,
+enum class DebugLogValueType : u32 {
+    MemoryRelAddr = 0,
+    MemoryOfsReg = 1,
+    RegisterRelAddr = 2,
+    RegisterOfsReg = 3,
+    RegisterValue = 4,
 };
 
 union VmInt {
@@ -247,26 +248,19 @@ struct DebugLogOpcode {
     u64 rel_address;
 };
 
-struct CheatVmOpcode {
+struct UnrecognizedInstruction {
     CheatVmOpcodeType opcode;
+};
+
+struct CheatVmOpcode {
     bool begin_conditional_block;
-    union {
-        StoreStaticOpcode store_static;
-        BeginConditionalOpcode begin_cond;
-        EndConditionalOpcode end_cond;
-        ControlLoopOpcode ctrl_loop;
-        LoadRegisterStaticOpcode ldr_static;
-        LoadRegisterMemoryOpcode ldr_memory;
-        StoreStaticToAddressOpcode str_static;
-        PerformArithmeticStaticOpcode perform_math_static;
-        BeginKeypressConditionalOpcode begin_keypress_cond;
-        PerformArithmeticRegisterOpcode perform_math_reg;
-        StoreRegisterToAddressOpcode str_register;
-        BeginRegisterConditionalOpcode begin_reg_cond;
-        SaveRestoreRegisterOpcode save_restore_reg;
-        SaveRestoreRegisterMaskOpcode save_restore_regmask;
-        DebugLogOpcode debug_log;
-    };
+    std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
+                 LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
+                 PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
+                 PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
+                 BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
+                 SaveRestoreRegisterMaskOpcode, DebugLogOpcode, UnrecognizedInstruction>
+        opcode;
 };
 
 class DmntCheatVm {
@@ -285,50 +279,43 @@ public:
         virtual void CommandLog(std::string_view data) = 0;
     };
 
-    constexpr static size_t MaximumProgramOpcodeCount = 0x400;
-    constexpr static size_t NumRegisters = 0x10;
+    static constexpr std::size_t MaximumProgramOpcodeCount = 0x400;
+    static constexpr std::size_t NumRegisters = 0x10;
+
+    explicit DmntCheatVm(std::unique_ptr<Callbacks> callbacks);
+    ~DmntCheatVm();
+
+    std::size_t GetProgramSize() const {
+        return this->num_opcodes;
+    }
+
+    bool LoadProgram(const std::vector<CheatEntry>& cheats);
+    void Execute(const CheatProcessMetadata& metadata);
 
 private:
     std::unique_ptr<Callbacks> callbacks;
 
-    size_t num_opcodes = 0;
-    size_t instruction_ptr = 0;
-    size_t condition_depth = 0;
+    std::size_t num_opcodes = 0;
+    std::size_t instruction_ptr = 0;
+    std::size_t condition_depth = 0;
     bool decode_success = false;
     std::array<u32, MaximumProgramOpcodeCount> program{};
     std::array<u64, NumRegisters> registers{};
     std::array<u64, NumRegisters> saved_values{};
-    std::array<size_t, NumRegisters> loop_tops{};
+    std::array<std::size_t, NumRegisters> loop_tops{};
 
-private:
     bool DecodeNextOpcode(CheatVmOpcode& out);
     void SkipConditionalBlock();
     void ResetState();
 
-    /* For implementing the DebugLog opcode. */
+    // For implementing the DebugLog opcode.
     void DebugLog(u32 log_id, u64 value);
 
-    /* For debugging. These will be IFDEF'd out normally. */
-    template <typename... Args>
-    void LogToDebugFile(const char* format, const Args&... args) {
-        callbacks->CommandLog(fmt::sprintf(format, args...));
-    }
-
     void LogOpcode(const CheatVmOpcode& opcode);
 
     static u64 GetVmInt(VmInt value, u32 bit_width);
     static u64 GetCheatProcessAddress(const CheatProcessMetadata& metadata,
                                       MemoryAccessType mem_type, u64 rel_address);
-
-public:
-    DmntCheatVm(std::unique_ptr<Callbacks> callbacks) : callbacks(std::move(callbacks)) {}
-
-    size_t GetProgramSize() {
-        return this->num_opcodes;
-    }
-
-    bool LoadProgram(const std::vector<CheatEntry>& cheats);
-    void Execute(const CheatProcessMetadata& metadata);
 };
 
 }; // namespace Memory