<?xml version='1.0' encoding='utf-8'?>
<CheatTable CheatEngineTableVersion="52">
  <CheatEntries>
    <CheatEntry>
      <ID>15</ID>
      <Description>"Auto-Attach and Version Check"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
ShowMessage('This table auto-attaches to InstanceServerG.exe, auto-detects which game is loaded, and verifies version 1.153 automatically.')
{$ASM}

[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>26</ID>
      <Description>"☕ Fuel the Cheats — Tip Katzy (Toggle to open my Ko-fi page)"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
local record = memrec
shellExecute('https://ko-fi.com/katzy')
if record then
  createTimer(100, function() record.Active = false end)
end
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>1</ID>
      <Description>"How to use - Katzy"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
ShowMessage([[Table identification: Katzy.

Start the game, open the table in Cheat Engine and allow its Lua script. Tick the matching MAIN section, then choose your options. Use a single-player campaign or skirmish with one human-controlled house.

Infinite Money
Adds a 30,000-credit allowance, preserves further earnings and removes the allowance when switched off.

Infinite Power
Adds enough power to meet demand. Turning it off restores normal power behavior.

Instant Build
Finishes paid production. Normal prices, prerequisites, placement and factory exit rules still apply. Turning it off restores normal future production; completed items stay completed.

Infinite Aircraft Ammo
Stops your aircraft from spending ammunition normally reloaded at an airfield. Empty aircraft need one normal reload first. Firing speed stays normal. Turning it off restores ammunition use.

Instant Superweapons
Readies available superweapons after each use while enabled. Red Alert includes all eight powers; GPS launches automatically after charging. Allow the next game update for Ready to appear. Required buildings, power and targeting rules still apply. Single-use powers remain single-use. Turning off restores normal recharge after the next use.

Engineer Capture at Any Health
Red Alert only: lets engineers capture buildings regardless of their remaining health.

Invulnerability - Selected Units and Buildings
Select up to 128 units and buildings you own in a live match, then enable to protect that group from damage. Changing selection does not move protection. Protected units and buildings show the native white target highlight (Remastered graphics). Buildings are included. Turn off, disable MAIN, or use Clear All to remove protection and the highlight. Select another group and enable again to change targets. Loading or starting a map clears protection; it is not saved with the game. Normal repairs still work. Selling, scripted removal, and other non-damage events can still remove a unit or building. Capture and selling are not blocked.

No Friendly Fire
Prevents splash damage between your own units and buildings. Direct hits remain unchanged.

Reveal Map - Full
Shows terrain and objects. This can affect mission events; use Visual Only for campaigns. Use only one Reveal Map mode at a time.

Reveal Map - Visual Only
Shows terrain without forcing object visibility. Use only one Reveal Map mode at a time. Map selections wait during loading; normal exploration and movement rules remain.

Clear All
Turns off all cheats and both MAIN sections. This action unticks itself.]])

local record=memrec or getAddressList().getMemoryRecordByID(1)
local timer=createTimer(nil,false)
timer.Interval=100
timer.OnTimer=function(t) t.destroy();if record and record.Active then record.Active=false end end
timer.Enabled=true
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>18</ID>
      <Description>"Clear All"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_RE_CLEAR()

local record=memrec or getAddressList().getMemoryRecordByID(18)
local timer=createTimer(nil,false)
timer.Interval=100
timer.OnTimer=function(t) t.destroy();if record and record.Active then record.Active=false end end
timer.Enabled=true
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>2</ID>
      <Description>"[MAIN] Command &amp; Conquer: Red Alert Remastered"</Description>
      <Options moHideChildren="1" />
      <GroupHeader>1</GroupHeader>
      <CheatEntries>
        <CheatEntry>
          <ID>4</ID>
          <Description>"Infinite Money"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
RA_MMM = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_MMM = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>13</ID>
          <Description>"Infinite Power"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
RA_PWR = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_PWR = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>5</ID>
          <Description>"Instant Build"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
RA_MCP = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_MCP = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>19</ID>
          <Description>"Infinite Aircraft Ammo"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
RA_AMMO = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_AMMO = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>20</ID>
          <Description>"Instant Superweapons"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
RA_SW = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_SW = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>23</ID>
          <Description>"Engineer Capture at Any Health"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
RA_ENG = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_ENG = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>27</ID>
          <Description>"Invulnerability - Selected Units and Buildings"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
// Validated activation and cleanup run through the record callbacks.
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>16</ID>
          <Description>"No Friendly Fire"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
RA_FF = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_FF = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>11</ID>
          <Description>"Reveal Map - Full"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
RA_VISUAL=false
local other=getAddressList().getMemoryRecordByID(24)
if other and other.Active then other.Active=false end
RA_FOG = true
CLEARED=false
if not KATZY_RE_REFRESH_MAP then error('Allow the new table Lua script before enabling this option.') end
local ok,message=pcall(KATZY_RE_REFRESH_MAP,'RedAlert.dll')
if not ok then
  RA_FOG=false
  if MAIN_TIMER and MAIN_TIMER.OnTimer then MAIN_TIMER.OnTimer(MAIN_TIMER) end
  error(message)
end
{$asm}

[DISABLE]
{$LUA}
if syntaxcheck then return end
RA_FOG = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>24</ID>
          <Description>"Reveal Map - Visual Only"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('RedAlert.dll')
RA_FOG=false
local other=getAddressList().getMemoryRecordByID(11)
if other and other.Active then other.Active=false end
RA_VISUAL=true
CLEARED=false
if not KATZY_RE_REFRESH_MAP then error('Allow the new table Lua script before enabling this option.') end
local ok,message=pcall(KATZY_RE_REFRESH_MAP,'RedAlert.dll')
if not ok then
  RA_VISUAL=false
  if MAIN_TIMER and MAIN_TIMER.OnTimer then MAIN_TIMER.OnTimer(MAIN_TIMER) end
  error(message)
end
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
RA_VISUAL=false
{$asm}
</AssemblerScript>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
    <CheatEntry>
      <ID>6</ID>
      <Description>"[MAIN] Command &amp; Conquer: Tiberian Dawn Remastered"</Description>
      <Options moHideChildren="1" />
      <GroupHeader>1</GroupHeader>
      <CheatEntries>
        <CheatEntry>
          <ID>8</ID>
          <Description>"Infinite Money"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
TD_MMM = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
TD_MMM = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>14</ID>
          <Description>"Infinite Power"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
TD_PWR = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
TD_PWR = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>9</ID>
          <Description>"Instant Build"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
TD_MCP = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
TD_MCP = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>22</ID>
          <Description>"Infinite Aircraft Ammo"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
TD_AMMO = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
TD_AMMO = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>21</ID>
          <Description>"Instant Superweapons"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
TD_SW = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
TD_SW = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>28</ID>
          <Description>"Invulnerability - Selected Units and Buildings"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
// Validated activation and cleanup run through the record callbacks.
[DISABLE]
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>17</ID>
          <Description>"No Friendly Fire"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
local expected = getProcessIDFromProcessName('InstanceServerG.exe')
if not expected or expected == 0 or getOpenedProcessID() ~= expected then error('Attach to InstanceServerG.exe before changing this option.') end
TD_FF = true
CLEARED=false
{$ASM}

[DISABLE]
{$LUA}
if syntaxcheck then return end
TD_FF = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>12</ID>
          <Description>"Reveal Map - Full"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[enable]
{$LUA}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
TD_VISUAL=false
local other=getAddressList().getMemoryRecordByID(25)
if other and other.Active then other.Active=false end
TD_FOG = true
CLEARED=false
if not KATZY_RE_REFRESH_MAP then error('Allow the new table Lua script before enabling this option.') end
local ok,message=pcall(KATZY_RE_REFRESH_MAP,'TiberianDawn.dll')
if not ok then
  TD_FOG=false
  if MAIN_TIMER and MAIN_TIMER.OnTimer then MAIN_TIMER.OnTimer(MAIN_TIMER) end
  error(message)
end
{$asm}

[DISABLE]
{$LUA}
if syntaxcheck then return end
TD_FOG = false
{$ASM}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>25</ID>
          <Description>"Reveal Map - Visual Only"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_RE_REQUIRE_GAME('TiberianDawn.dll')
TD_FOG=false
local other=getAddressList().getMemoryRecordByID(12)
if other and other.Active then other.Active=false end
TD_VISUAL=true
CLEARED=false
if not KATZY_RE_REFRESH_MAP then error('Allow the new table Lua script before enabling this option.') end
local ok,message=pcall(KATZY_RE_REFRESH_MAP,'TiberianDawn.dll')
if not ok then
  TD_VISUAL=false
  if MAIN_TIMER and MAIN_TIMER.OnTimer then MAIN_TIMER.OnTimer(MAIN_TIMER) end
  error(message)
end
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
TD_VISUAL=false
{$asm}
</AssemblerScript>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols />
  <Comments>Katzy | Remastered 1.153.745903 | Table v2.68

Allow the table Lua script. Choose the matching MAIN section.
For single-player campaigns and skirmishes with one human-controlled house.
Infinite Money adds a 30,000-credit allowance and retains net earnings when switched off.
Choose one Reveal Map mode at a time: Full or Visual Only.
Selected-group protection captures your units and buildings when enabled.
MAIN Off and Clear All restore temporary changes. Game exit clears the switches.
</Comments>
  <LuaScript>--[[
Table identification: Katzy
Command &amp; Conquer Remastered Collection | 1.153.745903
Table version: v2.68
Player ownership, resource accounting and lifecycle corrections.
]]
if KATZY_SELECTED_RE then KATZY_SELECTED_RE.shutdown() end


-- PlayerPtr is a temporary simulation context in Remastered skirmishes.
-- Resolve the single human house without changing the game's context.
function KATZY_RE_TD_HUMAN(base)
  local count=readInteger(base+0x129BD8)
  local list=readInteger(base+0x129BFD)
  if not count or count&lt;1 or count&gt;10 or not list or list==0 then return nil end
  local player,index
  for i=0,count-1 do
    local house=readInteger(list+i*4)
    local flags=house and house~=0 and readBytes(house+0x29,1)
    if flags and (flags &amp; 2)~=0 then
      if player then return nil end
      local typ=readInteger(house)
      index=typ and readBytes(typ,1)
      if not index or index&gt;=10 then return nil end
      player=house
    end
  end
  return player,index
end

function KATZY_RE_HUMAN(base,key)
  if key=='td' then return KATZY_RE_TD_HUMAN(base) end
  local heap=readInteger(base+0x12C580)
  if not heap or heap==0 then return nil end
  local count,list=readInteger(heap+16),readInteger(heap+53)
  local types=readInteger(base+0x12C578)
  if not count or count&lt;1 or count&gt;20 or not list or list==0 or not types or types==0 then return nil end
  local stride,data,total=readInteger(types+8),readInteger(types+20),readInteger(types+12)
  if not stride or stride&lt;0x26 or stride&gt;4096 or not data or data==0 or not total or total&lt;1 or total&gt;20 then return nil end
  local player,index
  for i=0,count-1 do
    local house=readInteger(list+4*i)
    local flags=house and house~=0 and readBytes(house+0x54,1)
    if flags and (flags &amp; 3)==3 then
      if player then return nil end
      local typ=readInteger(house+5)
      if not typ or typ&lt;0 or typ&gt;=total then return nil end
      index=readBytes(data+typ*stride+0x25,1)
      if not index or index&gt;=20 then return nil end
      player=house
    end
  end
  return player,index
end

if not syntaxcheck then
  getAutoAttachList().add('InstanceServerG.exe')

  -- Verified against the installed game logic DLL PE headers, not the host EXE version.
  local selectedClient, selectedGame, queriedClient
  local function refreshSelection()
    local client=getProcessIDFromProcessName('ClientG.exe')
    if client~=selectedClient then selectedClient=client;selectedGame=nil;queriedClient=nil end
    local job=KATZY_RE_SELECTION_JOB
    local line
    if job and job.thread and job.thread.Finished then
      line=job.thread.Result or '';job.thread.destroy()
    elseif job and job.path then
      local file=io.open(job.path,'rb')
      if file then line=file:read('*a') or '';file:close();os.remove(job.path)
      elseif ((getTickCount()-job.started) % 4294967296)&gt;30000 then line='' end
    end
    if line~=nil then
      KATZY_RE_SELECTION_JOB=nil
      if job.client==client then
        line=line:upper()
        if line:match('%f[%w]REDALERT%f[%W]') then selectedGame='redalert.dll'
        elseif line:match('%f[%w]TIBERIANDAWN%f[%W]') then selectedGame='tiberiandawn.dll' end
        queriedClient=client
      end
    end
    if client and not selectedGame and queriedClient~=client and not KATZY_RE_SELECTION_JOB then
      local temp=os.getenv('TEMP') or os.getenv('TMP')
      if not temp then queriedClient=client;return client end
      local path=temp..'\\Katzy-RE-selection-'..tostring(client)..'-'..tostring(getTickCount())..'-'..tostring(math.random(100000,999999))..'.txt'
      local escaped=path:gsub("'","''")
      local script="$ErrorActionPreference='Stop';$s='';try{$p=Get-CimInstance Win32_Process -Filter 'ProcessId = "..tostring(client).."';if($p.Name -eq 'ClientG.exe'){$s=$p.CommandLine}}catch{};[IO.File]::WriteAllText('"..escaped..".pending',$s);[IO.File]::Move('"..escaped..".pending','"..escaped.."')"
      local executable=(os.getenv('SystemRoot') or 'C:\\Windows')..'\\System32\\WindowsPowerShell\\v1.0\\powershell.exe'
      KATZY_RE_SELECTION_JOB={client=client,path=path,started=getTickCount()}
      shellExecute(executable,'-NoProfile -NonInteractive -WindowStyle Hidden -Command "'..script..'"',nil,0)
    end
    return client
  end
  function KATZY_RE_REQUIRE_GAME(wanted)
    local target=getProcessIDFromProcessName('InstanceServerG.exe')
    local modules=target and enumModules(target) or {}
    local current
    for _,m in ipairs(modules or {}) do
      local name=(m.Name or ''):lower()
      if name=='redalert.dll' or name=='tiberiandawn.dll' then
        if current and current~=name then error('Please wait for the game to finish loading.', 0) end
        current=name
      end
    end
    local client=refreshSelection()
    if current then selectedGame=current end
    current=current or (client and selectedGame)
    if not current then
      if KATZY_RE_SELECTION_JOB then error('Checking the selected game. Please try again in a moment.',0) end
      error('Please launch Red Alert or Tiberian Dawn first.', 0)
    end
    if current~=wanted:lower() then
      error(current=='redalert.dll' and 'Tiberian Dawn options are unavailable while Red Alert is running.' or 'Red Alert options are unavailable while Tiberian Dawn is running.', 0)
    end
    if target and getOpenedProcessID()==target and DSH_GAME_VERSION then
      local valid,why=DSH_GAME_VERSION()
      if valid~=true then error('Game version check: '..tostring(why),0) end
    end
  end

  local verifiedModules = {}
  local symbolKey
  function DSH_GAME_VERSION()
    verifiedModules = {}
    local modules = enumModules(getOpenedProcessID())
    if type(modules) ~= 'table' then return nil, 'waiting for the game module list' end
    local current = {}
    for _, module in ipairs(modules) do
      if module.Name and module.Address then current[module.Name:lower()] = module.Address end
    end
    local key=tostring(getOpenedProcessID())..':'..tostring(current['redalert.dll'])..':'..tostring(current['tiberiandawn.dll'])
    if symbolKey~=key then
      pcall(loadNewSymbols)
      symbolKey=key
    end
    local found = false
    for _, spec in ipairs({{'RedAlert.dll',1600875353,4239360}, {'TiberianDawn.dll',1600875351,1765376}}) do
      local base = current[spec[1]:lower()]
      if base and base ~= 0 then
        local pe = readInteger(base + 0x3C)
        if not pe then return nil, 'waiting for ' .. spec[1] .. ' to finish loading' end
        if pe &lt; 0x40 or pe &gt; 0x1000 then return false, spec[1] .. ' invalid PE header' end
        if readInteger(base + pe) ~= 0x4550 or readInteger(base + pe + 8) ~= spec[2]
          or readInteger(base + pe + 0x50) ~= spec[3] then return false, spec[1] .. ' unsupported build' end
        verifiedModules[spec[1]] = base
        found = true
      end
    end
    if not found then return nil, 'waiting for a game logic DLL' end
    return true, '1.153.745903'
  end


if MAIN_TIMER then
  if KATZY_RE_CLEAR then KATZY_RE_CLEAR() else
    CLEARED=true
    for _,v in ipairs({'RA_MMM','RA_MCP','RA_PWR','RA_FOG','RA_FF','RA_AMMO','RA_SW','RA_ENG','TD_MMM','TD_MCP','TD_PWR','TD_FOG','TD_FF','TD_SW','TD_AMMO'}) do _G[v]=false end
    if MAIN_TIMER.OnTimer then MAIN_TIMER.OnTimer(MAIN_TIMER) end
    local pid=getProcessIDFromProcessName('InstanceServerG.exe')
    if pid and pid==getOpenedProcessID() then
      for dll,offsets in pairs({['RedAlert.dll']={0x50FC0,0xA7430,0xAF76A,0x6A3A9},['TiberianDawn.dll']={0x40147,0x92930,0x9108C}}) do
        local base=getAddressSafe(dll)
        if base then for _,off in ipairs(offsets) do
          if readBytes(base+off,1)==0xE9 then error('Previous table cleanup is incomplete. Click its Clear State and retry loading.') end
        end end
      end
    end
  end
  MAIN_TIMER.destroy();MAIN_TIMER=nil
end
CLEARED=false
DIAG=nil

  if MAIN_TIMER == nil then
    local RA_build_hookScript = [=[
assert(RedAlert.dll+50FC0,55 8B EC 83 EC 10)
alloc(RA_Build_Original0,6)
registersymbol(RA_Build_Original0)
RA_Build_Original0:
readmem(RedAlert.dll+50FC0,6)
alloc(RA_Build,96)
label(RA_Build_Ret)
label(RA_Build_Skip)
label(iEnableMCP)
label(iEnableMCP_Player)
RedAlert.dll+50FC0:
jmp RA_Build
nop
RA_Build_Ret:
RA_Build:
  push eax
  cmp dword [iEnableMCP],0
  je RA_Build_Skip
  mov eax,[iEnableMCP_Player]
  test eax,eax
  je RA_Build_Skip
  cmp [ecx+2B],eax
  jne RA_Build_Skip
  mov dword [ecx+0E],1
  cmp dword [ecx],#53
  jge RA_Build_Skip
  mov dword [ecx],#53
RA_Build_Skip:
  pop eax
  push ebp
  mov ebp,esp
  sub esp,10
  jmp RA_Build_Ret
align 10
iEnableMCP:
dd 0
iEnableMCP_Player:
dd 0
registersymbol(iEnableMCP)
]=]
local RA_FF_hookScript = [=[
assert(RedAlert.dll+A7430,55 8B EC 83 EC 14)
alloc(RA_FF_Original0,6)
registersymbol(RA_FF_Original0)
RA_FF_Original0:
readmem(RedAlert.dll+A7430,6)
alloc(RA_FF,256)
label(RA_FF_Original)
label(iEnableFF)
label(iEnableFF_Player)
RedAlert.dll+A7430:
jmp RA_FF
nop
RA_FF:
  cmp dword [iEnableFF],0
  je RA_FF_Original
  mov eax,[esp+4]
  mov eax,[eax]
  test eax,eax
  jle RA_FF_Original
  cmp dword [esp+8],0
  jle RA_FF_Original
  mov eax,[esp+10]
  test eax,eax
  je RA_FF_Original
  mov edx,[eax+107]
  mov eax,[RedAlert.dll+12c580]
  imul edx,[eax+8]
  add edx,[eax+14]
  cmp edx,[iEnableFF_Player]
  jne RA_FF_Original
  mov edx,[ecx+107]
  mov eax,[RedAlert.dll+12c580]
  imul edx,[eax+8]
  add edx,[eax+14]
  cmp edx,[iEnableFF_Player]
  jne RA_FF_Original
  xor eax,eax
  ret 14
RA_FF_Original:
  push ebp
  mov ebp,esp
  sub esp,14
  jmp RedAlert.dll+A7436
align 10
iEnableFF:
dd 0
iEnableFF_Player:
dd 0
registersymbol(iEnableFF)
]=]

    local RA_ammo_hookScript = [=[
assert(RedAlert.dll+AF76A,FF 8E 3C 01 00 00)
assert(RedAlert.dll+A4904,8B 87 3C 01 00 00)
alloc(RA_Ammo_Original0,6)
registersymbol(RA_Ammo_Original0)
RA_Ammo_Original0:
readmem(RedAlert.dll+AF76A,6)
alloc(RA_Ammo_Original1,6)
registersymbol(RA_Ammo_Original1)
RA_Ammo_Original1:
readmem(RedAlert.dll+A4904,6)
alloc(RA_Ammo,256)
label(RA_Ammo_Back)
label(RA_Ammo_Dec)
label(RA_Ammo2_Back)
label(RA_Ammo2_Original)
label(iEnableAmmo)
label(iEnableAmmo_Player)
RedAlert.dll+AF76A:
jmp RA_Ammo
nop
RA_Ammo_Back:
RedAlert.dll+A4904:
jmp RA_Ammo2
nop
RA_Ammo2_Back:
RA_Ammo:
  cmp dword [iEnableAmmo],0
  je RA_Ammo_Dec
  mov eax,[esi+107]
  mov edx,[RedAlert.dll+12c580]
  imul eax,[edx+8]
  add eax,[edx+14]
  cmp eax,[iEnableAmmo_Player]
  jne RA_Ammo_Dec
  jmp RA_Ammo_Back
RA_Ammo_Dec:
  dec dword [esi+13c]
  jmp RA_Ammo_Back
RA_Ammo2:
  cmp dword [iEnableAmmo],0
  je RA_Ammo2_Original
  mov eax,[edi+107]
  mov edx,[RedAlert.dll+12c580]
  imul eax,[edx+8]
  add eax,[edx+14]
  cmp eax,[iEnableAmmo_Player]
  jne RA_Ammo2_Original
  jmp RedAlert.dll+A4915
RA_Ammo2_Original:
  mov eax,[edi+13c]
  jmp RA_Ammo2_Back
align 10
iEnableAmmo:
dd 0
iEnableAmmo_Player:
dd 0
registersymbol(iEnableAmmo)
]=]

    local RA_build_unhookScript = [=[
RedAlert.dll+50FC0:
readmem(RA_Build_Original0,6)
unregistersymbol(RA_Build_Original0)
dealloc(RA_Build_Original0)
unregistersymbol(iEnableMCP)
dealloc(RA_Build)
]=]
local RA_FF_unhookScript = [=[
RedAlert.dll+A7430:
readmem(RA_FF_Original0,6)
unregistersymbol(RA_FF_Original0)
dealloc(RA_FF_Original0)
unregistersymbol(iEnableFF)
dealloc(RA_FF)
]=]

local RA_ammo_unhookScript = [=[
RedAlert.dll+AF76A:
readmem(RA_Ammo_Original0,6)
unregistersymbol(RA_Ammo_Original0)
dealloc(RA_Ammo_Original0)
RedAlert.dll+A4904:
readmem(RA_Ammo_Original1,6)
unregistersymbol(RA_Ammo_Original1)
dealloc(RA_Ammo_Original1)
unregistersymbol(iEnableAmmo)
dealloc(RA_Ammo)
]=]

local RA_engineer_hookScript = [=[
assert(RedAlert.dll+6a3a9,3B 05 ?? ?? ?? ?? 77 5B)
alloc(RA_Engineer_Original0,8)
registersymbol(RA_Engineer_Original0)
RA_Engineer_Original0:
readmem(RedAlert.dll+6a3a9,8)
alloc(RA_Engineer,256)
label(RA_Engineer_Original)
label(iEnableEngineer)
label(iEnableEngineer_Player)
RedAlert.dll+6a3a9:
jmp RA_Engineer
nop
nop
nop
RA_Engineer:
  cmp dword [iEnableEngineer],0
  je RA_Engineer_Original
  push eax
  mov eax,[esi+107]
  mov edx,[RedAlert.dll+12c580]
  imul eax,[edx+8]
  add eax,[edx+14]
  cmp eax,[iEnableEngineer_Player]
  pop eax
  jne RA_Engineer_Original
  jmp RedAlert.dll+6a3b1
RA_Engineer_Original:
  cmp eax,[RedAlert.dll+104ca8]
  ja RedAlert.dll+6a40c
  jmp RedAlert.dll+6a3b1
align 10
iEnableEngineer:
dd 0
iEnableEngineer_Player:
dd 0
registersymbol(iEnableEngineer)
]=]

local RA_engineer_unhookScript = [=[
RedAlert.dll+6a3a9:
readmem(RA_Engineer_Original0,8)
unregistersymbol(RA_Engineer_Original0)
dealloc(RA_Engineer_Original0)
unregistersymbol(iEnableEngineer)
dealloc(RA_Engineer)
]=]

    local TD_exit_hookScript = [=[
assert(TiberianDawn.dll+1B2C1,FF 05 70 81 12 10)
alloc(TD_Exit_Original,6)
registersymbol(TD_Exit_Original)
TD_Exit_Original:
readmem(TiberianDawn.dll+1B2C1,6)
alloc(TD_Exit,160)
label(TD_Exit_Normal)
label(TD_Exit_Blocked)
label(iEnableBuildExit_TD)
label(iEnableBuildExit_TD_Player)
TiberianDawn.dll+1B2C1:
jmp TD_Exit
nop
TD_Exit:
  pushfd
  push eax
  cmp dword [iEnableBuildExit_TD],0
  je TD_Exit_Normal
  mov eax,[iEnableBuildExit_TD_Player]
  test eax,eax
  je TD_Exit_Normal
  cmp [ebx+93],eax
  jne TD_Exit_Normal
  cmp dword [ebx+45],0
  jne TD_Exit_Blocked
  cmp byte [ebx+38],0F
  je TD_Exit_Blocked
  cmp byte [ebx+3A],0F
  je TD_Exit_Blocked
TD_Exit_Normal:
  pop eax
  popfd
  inc dword [TiberianDawn.dll+128170]
  jmp TiberianDawn.dll+1B2C7
TD_Exit_Blocked:
  pop eax
  popfd
  jmp TiberianDawn.dll+1B163
align 10
iEnableBuildExit_TD:
dd 0
iEnableBuildExit_TD_Player:
dd 0
registersymbol(iEnableBuildExit_TD)
]=]
    local TD_exit_unhookScript = [=[
TiberianDawn.dll+1B2C1:
readmem(TD_Exit_Original,6)
unregistersymbol(TD_Exit_Original)
dealloc(TD_Exit_Original)
unregistersymbol(iEnableBuildExit_TD)
dealloc(TD_Exit)
]=]
    local TD_build_hookScript = [=[
assert(TiberianDawn.dll+40147,80 3D ?? ?? ?? ?? 00)
alloc(TD_Build_Original0,7)
registersymbol(TD_Build_Original0)
TD_Build_Original0:
readmem(TiberianDawn.dll+40147,7)
alloc(TD_Build,128)
label(TD_Build_Normal)
label(TD_Build_Debug)
label(iEnableMCP_TD)
label(iEnableMCP_TD_Player)
TiberianDawn.dll+40147:
jmp TD_Build
nop
nop
TD_Build:
  push eax
  cmp dword [iEnableMCP_TD],0
  je TD_Build_Normal
  mov eax,[iEnableMCP_TD_Player]
  test eax,eax
  je TD_Build_Normal
  cmp [esi+18],eax
  jne TD_Build_Normal
  mov byte [esi+3],1
  cmp word [esi],#107
  jge TD_Build_Normal
  mov word [esi],#107
TD_Build_Normal:
  pop eax
  cmp byte [TiberianDawn.dll+126BD9],0
  mov edi,#108
  jne TD_Build_Debug
  jmp TiberianDawn.dll+4015a
TD_Build_Debug:
  jmp TiberianDawn.dll+40155
align 10
iEnableMCP_TD:
dd 0
iEnableMCP_TD_Player:
dd 0
registersymbol(iEnableMCP_TD)
]=]
local TD_FF_hookScript = [=[
assert(TiberianDawn.dll+92930,55 8B EC 53 56)
alloc(TD_FF_Original0,5)
registersymbol(TD_FF_Original0)
TD_FF_Original0:
readmem(TiberianDawn.dll+92930,5)
alloc(TD_FF,256)
label(TD_FF_Original)
label(iEnableFF_TD)
label(iEnableFF_TD_Player)
TiberianDawn.dll+92930:
jmp TD_FF
TD_FF:
  cmp dword [iEnableFF_TD],0
  je TD_FF_Original
  mov eax,[esp+4]
  mov eax,[eax]
  test eax,eax
  jle TD_FF_Original
  cmp dword [esp+8],0
  jle TD_FF_Original
  mov eax,[esp+10]
  test eax,eax
  je TD_FF_Original
  mov eax,[eax+93]
  test eax,eax
  je TD_FF_Original
  cmp eax,[iEnableFF_TD_Player]
  jne TD_FF_Original
  mov eax,[ecx+93]
  test eax,eax
  je TD_FF_Original
  cmp eax,[iEnableFF_TD_Player]
  jne TD_FF_Original
  xor eax,eax
  ret 10
TD_FF_Original:
  push ebp
  mov ebp,esp
  push ebx
  push esi
  jmp TiberianDawn.dll+92935
align 10
iEnableFF_TD:
dd 0
iEnableFF_TD_Player:
dd 0
registersymbol(iEnableFF_TD)
]=]

    local TD_build_unhookScript = [=[
TiberianDawn.dll+40147:
readmem(TD_Build_Original0,7)
unregistersymbol(TD_Build_Original0)
dealloc(TD_Build_Original0)
unregistersymbol(iEnableMCP_TD)
dealloc(TD_Build)
]=]
local TD_FF_unhookScript = [=[
TiberianDawn.dll+92930:
readmem(TD_FF_Original0,5)
unregistersymbol(TD_FF_Original0)
dealloc(TD_FF_Original0)
unregistersymbol(iEnableFF_TD)
dealloc(TD_FF)
]=]

local TD_ammo_hookScript = [=[
assert(TiberianDawn.dll+9108C,8B 83 A3 00 00 00)
alloc(TD_Ammo_Original0,6)
registersymbol(TD_Ammo_Original0)
TD_Ammo_Original0:
readmem(TiberianDawn.dll+9108C,6)
alloc(TD_Ammo,256)
label(TD_Ammo_Original)
label(iEnableAmmo_TD)
label(iEnableAmmo_TD_Player)
TiberianDawn.dll+9108C:
jmp TD_Ammo
nop
TD_Ammo:
  cmp dword [iEnableAmmo_TD],0
  je TD_Ammo_Original
  mov eax,[ebx+93]
  test eax,eax
  je TD_Ammo_Original
  cmp eax,[iEnableAmmo_TD_Player]
  jne TD_Ammo_Original
  jmp TiberianDawn.dll+9109d
TD_Ammo_Original:
  mov eax,[ebx+0xa3]
  jmp TiberianDawn.dll+91092
align 10
iEnableAmmo_TD:
dd 0
iEnableAmmo_TD_Player:
dd 0
registersymbol(iEnableAmmo_TD)
]=]

local TD_ammo_unhookScript = [=[
TiberianDawn.dll+9108C:
readmem(TD_Ammo_Original0,6)
unregistersymbol(TD_Ammo_Original0)
dealloc(TD_Ammo_Original0)
unregistersymbol(iEnableAmmo_TD)
dealloc(TD_Ammo)
]=]

    local function dllBase(name)
      local b = verifiedModules[name]
      if b == nil or b == 0 then return nil end
      return b
    end

    local function byteIs(addr, val)
      local b = readBytes(addr, 1)
      return b ~= nil and b == val
    end

    local RA_FOG_ON = [=[
assert(RedAlert.dll+44BD0,E8 0B D4 FE FF)
assert(RedAlert.dll+44BE0,E8 8B D3 FE FF)
assert(RedAlert.dll+44BEF,E8 2C D3 FE FF)
RedAlert.dll+44bd0:
db B0 01 83 C4 04
RedAlert.dll+44be0:
db B0 01 83 C4 04
RedAlert.dll+44bef:
db B0 00 83 C4 04
]=]
local RA_FOG_OFF = [=[
RedAlert.dll+44bd0:
db E8 0B D4 FE FF
RedAlert.dll+44be0:
db E8 8B D3 FE FF
RedAlert.dll+44bef:
db E8 2C D3 FE FF
]=]
local TD_FOG_ON = [=[
assert(TiberianDawn.dll+37ECD,E8 CE B7 FE FF)
assert(TiberianDawn.dll+37EDC,E8 8F B7 FE FF)
TiberianDawn.dll+37ecd:
db B0 01 83 C4 04
TiberianDawn.dll+37edc:
db B0 01 83 C4 04
]=]
local TD_FOG_OFF = [=[
TiberianDawn.dll+37ecd:
db E8 CE B7 FE FF
TiberianDawn.dll+37edc:
db E8 8F B7 FE FF
]=]
local RA_ENG_CUR_ON = [=[
assert(RedAlert.dll+6C842,1A C0)
RedAlert.dll+6c842:
db 30 C0
]=]
local RA_ENG_CUR_OFF = [=[
RedAlert.dll+6c842:
db 1A C0
]=]

    local RA_exit_hookScript = [=[
assert(RedAlert.dll+631C1,85 C0 0F 84 31 01 00 00)
alloc(RA_Exit_Original0,8)
registersymbol(RA_Exit_Original0)
RA_Exit_Original0:
readmem(RedAlert.dll+631C1,8)
alloc(RA_BuildExit,256)
label(iEnableBuildExit)
label(iEnableBuildExit_Player)
label(RA_Exit_Normal)
label(RA_Exit_Blocked)
RedAlert.dll+631C1:
jmp RA_BuildExit
nop 3
RA_BuildExit:
 cmp dword [iEnableBuildExit],0
 je RA_Exit_Normal
 cmp edi,[iEnableBuildExit_Player]
 jne RA_Exit_Normal
 cmp eax,1
 jne RA_Exit_Normal
 // Exit_Object returns 1 while every available war factory is still unloading.
 // House::Place_Object otherwise mistakes this for success and deletes the product.
 xor eax,eax
RA_Exit_Normal:
 test eax,eax
 je RA_Exit_Blocked
 jmp RedAlert.dll+631C9
RA_Exit_Blocked:
 jmp RedAlert.dll+632FA
align 10
iEnableBuildExit:
dd 0
iEnableBuildExit_Player:
dd 0
registersymbol(iEnableBuildExit)
]=]
    local RA_exit_unhookScript = [=[
RedAlert.dll+631C1:
readmem(RA_Exit_Original0,8)
unregistersymbol(RA_Exit_Original0)
dealloc(RA_Exit_Original0)
unregistersymbol(iEnableBuildExit)
dealloc(RA_BuildExit)
]=]
-- Red Alert BuildingClass::Power_Output, using LastStrength and native 16.16 rounding.
local function redAlertPower(base,player)
  local heap=base+0x19EAC8
  local stride,count,objects=readInteger(heap+8),readInteger(heap+12),readInteger(heap+20)
  local houses,types=readInteger(base+0x12C580),readInteger(base+0x12C584)
  if stride~=0x1B4 or not count or count&lt;0 or count&gt;100000 or not objects or objects==0
      or not houses or houses==0 or not types or types==0 then return nil end
  local houseStride,houseData=readInteger(houses+8),readInteger(houses+20)
  local typeStride,typeData,typeCount=readInteger(types+8),readInteger(types+20),readInteger(types+12)
  if not houseStride or not houseData or not typeStride or not typeData or not typeCount then return nil end
  local total=0
  for i=0,count-1 do
    local object=objects+i*stride
    local flags=readBytes(object+0x11,1)
    if flags==nil then return nil end
    if (flags &amp; 1)~=0 then
      local owner=readInteger(object+0x107)
      if owner and owner&lt;0x80000000 and houseData+owner*houseStride==player then
        local index=readInteger(object+0x164)
        if not index or index&gt;=typeCount then return nil end
        local typ=typeData+index*typeStride
        local power=readSmallInteger(typ+0x1AE)
        local health=readInteger(object+0x182)
        local maxHealth=readSmallInteger(typ+0x12A)
        if power==nil or health==nil or maxHealth==nil or power&lt;0 or health&gt;=0x80000000 then return nil end
        if power&gt;0 then
          if maxHealth&lt;=0 then return nil end
          total=total+math.floor((power*math.floor(health*65536/maxHealth)+32768)/65536)
        end
      end
    end
  end
  if KATZY_RE_HUMAN(base,'ra')~=player or readInteger(heap+20)~=objects or readInteger(heap+12)~=count then return nil end
  return total
end

    local GAMES = {
      { dll='RedAlert.dll', ptr=0x194088,
        patches={ {on='RA_VISUAL',applied=false,probe=0x43716,probeVal=0x90,apply=[=[assert(RedAlert.dll+43716,74 18)
RedAlert.dll+43716:
db 90 90
]=],remove=[=[RedAlert.dll+43716:
db 74 18
]=]}, {on='RA_FOG', visual='RA_VISUAL', applied=false, probe=0x44bd0, probeVal=0xB0, apply=RA_FOG_ON, remove=RA_FOG_OFF, flag=0x19bda4},
                  {on='RA_ENG', applied=false, probe=0x6c842, probeVal=0x30, apply=RA_ENG_CUR_ON, remove=RA_ENG_CUR_OFF} },
        res={ {on='RA_MMM', off=0x1B4, saved='RA_MMM_SAVED', val=30000, stored=0x1B0},
              {on='RA_PWR', off=0x200, saved='RA_PWR_SAVED', val=10000, recompute=redAlertPower, repair=true},
              {on='RA_MMM', off=0x1B8, saved='RA_CAP_SAVED', val=100000000} },
        sw={ on='RA_SW', off=0x5d, n=8, stride=26, present=2, ready=8, frame=0x194090 },
        hooks={
          {name='production exit', off=0x631C1, flagOff=2, hook=RA_exit_hookScript, unhook=RA_exit_unhookScript, sym='iEnableBuildExit', toggle='RA_MCP'},
          {name='build', off=0x50FC0, flagOff=3, hook=RA_build_hookScript, unhook=RA_build_unhookScript, sym='iEnableMCP', toggle='RA_MCP'},
          {name='friendly fire', off=0xA7430, flagOff=2, hook=RA_FF_hookScript, unhook=RA_FF_unhookScript, sym='iEnableFF', toggle='RA_FF'},
          {name='ammo', off=0xAF76A, flagOff=2, hook=RA_ammo_hookScript, unhook=RA_ammo_unhookScript, sym='iEnableAmmo', toggle='RA_AMMO'},
          {name='engineer', off=0x6a3a9, flagOff=2, hook=RA_engineer_hookScript, unhook=RA_engineer_unhookScript, sym='iEnableEngineer', toggle='RA_ENG'},
        } },
      { dll='TiberianDawn.dll', ptr=0x1296c0,
        patches={ {on='TD_VISUAL',applied=false,probe=0x37CEA,probeVal=0x90,apply=[=[assert(TiberianDawn.dll+37CEA,74 27)
TiberianDawn.dll+37CEA:
db 90 90
]=],remove=[=[TiberianDawn.dll+37CEA:
db 74 27
]=]}, {on='TD_FOG', visual='TD_VISUAL', applied=false, probe=0x37ecd, probeVal=0xB0, apply=TD_FOG_ON, remove=TD_FOG_OFF, flag=0x126bd1} },
        res={ {on='TD_MMM', off=0x10E, saved='TD_MMM_SAVED', val=30000, stored=0x10A},
              {on='TD_PWR', off=0x15a, saved='TD_PWR_SAVED', val=10000},
              {on='TD_MMM', off=0x112, saved='TD_CAP_SAVED', val=100000000} },
        sw={ on='TD_SW', off=0x35, n=3, stride=28, present=1, ready=4 },
        hooks={
          {name='vehicle exit', off=0x1B2C1, flagOff=4, hook=TD_exit_hookScript, unhook=TD_exit_unhookScript, sym='iEnableBuildExit_TD', toggle='TD_MCP'},
          {name='build', off=0x40147, flagOff=3, hook=TD_build_hookScript, unhook=TD_build_unhookScript, sym='iEnableMCP_TD', toggle='TD_MCP'},
          {name='friendly fire', off=0x92930, flagOff=2, hook=TD_FF_hookScript, unhook=TD_FF_unhookScript, sym='iEnableFF_TD', toggle='TD_FF'},
          {name='ammo', off=0x9108C, flagOff=2, hook=TD_ammo_hookScript, unhook=TD_ammo_unhookScript, sym='iEnableAmmo_TD', toggle='TD_AMMO'},
        } },
    }

    local function recoverTDFF(g,h)
      if g.dll~='TiberianDawn.dll' or h.sym~='iEnableFF_TD' or not g.base or not byteIs(g.base+h.off,0xE9) then return end
      local rel=readInteger(g.base+h.off+1)
      if not rel then return end
      local cave=(g.base+h.off+5+rel)%4294967296
      local flag=readInteger(cave+2)
      local original=cave-5
      if flag~=cave+123 then return end
      local pattern='83 3D ?? ?? ?? ?? 00 0F 84 5E 00 00 00 8B 44 24 04 8B 00 85 C0 0F 8E 50 00 00 00 83 7C 24 08 00 0F 8E 45 00 00 00 8B 44 24 10 85 C0 0F 84 39 00 00 00 8B 80 93 00 00 00 85 C0 0F 84 2B 00 00 00 3B 05 ?? ?? ?? ?? 0F 85 1F 00 00 00 8B 81 93 00 00 00 85 C0 0F 84 11 00 00 00 3B 05 ?? ?? ?? ?? 0F 85 05 00 00 00 31 C0 C2 10 00 55 8B EC 53 56 E9 ?? ?? ?? ??'
      local i=0
      for byte in pattern:gmatch('%S+') do
        if byte~='??' and readBytes(cave+i,1)~=tonumber(byte,16) then return end
        i=i+1
      end
      if readInteger(cave+2)~=flag or readInteger(cave+66)~=g.base+0x1296C0 or readInteger(cave+92)~=g.base+0x1296C0 then return end
      local back=readInteger(cave+113)
      if not back or (cave+117+back)%4294967296~=g.base+0x92935 then return end
      for j,b in ipairs({0x55,0x8B,0xEC,0x53,0x56}) do if readBytes(original+j-1,1)~=b then return end end
      local jump={}
      for j=0,4 do jump[#jump+1]=string.format('%02X',readBytes(g.base+h.off+j,1)) end
      local address=string.format('%X',g.base+h.off)
      h.unhook='assert('..address..','..table.concat(jump,' ')..')\n'..address..':\ndb 55 8B EC 53 56\n'..string.format('%X',flag)..':\ndd 0'
      h.recovered=true;h.needsUpgrade=true
      h.hooked=true;h.installedBase=g.base;h.flag=flag;h.errShown=nil
    end

    local orphanProfiles={
      [111297]={length=6,flagOff=4,pattern='9C 50 83 3D ?? ?? ?? ?? 00 0F 84 38 00 00 00 8B 05 ?? ?? ?? ?? 85 C0 0F 84 2A 00 00 00 39 83 93 00 00 00 0F 85 1E 00 00 00 83 7B 45 00 0F 85 21 00 00 00 80 7B 38 0F 0F 84 17 00 00 00 80 7B 3A 0F 0F 84 0D 00 00 00 58 9D FF 05 ?? ?? ?? ?? E9 ?? ?? ?? ?? 58 9D E9 ?? ?? ?? ?? 00 00',original='ff 05 70 81 12 10',fix={{4,'cave',106,0},{17,'base',1218240,0},{75,'base',1212784,0},{80,'base',111303,84},{87,'base',110947,91}}},
      [262471]={length=7,flagOff=3,pattern='50 83 3D ?? ?? ?? ?? 00 0F 84 2A 00 00 00 8B 05 ?? ?? ?? ?? 85 C0 0F 84 1C 00 00 00 39 46 18 0F 85 13 00 00 00 C6 46 03 01 66 83 3E 6B 0F 8D 05 00 00 00 66 C7 06 6B 00 58 E9 ?? ?? ?? ??',original='80 3d d9 6b 12 10 00',fix={{3,'cave',73,0},{16,'base',1218240,0},{58,'base',262490,62}}},
      [594060]={length=6,flagOff=2,pattern='83 3D ?? ?? ?? ?? 00 0F 84 1F 00 00 00 8B 83 93 00 00 00 85 C0 0F 84 11 00 00 00 3B 05 ?? ?? ?? ?? 0F 85 05 00 00 00 E9 ?? ?? ?? ?? 8B 83 A3 00 00 00 E9 ?? ?? ?? ??',original='8b 83 a3 00 00 00',fix={{2,'cave',58,0},{29,'base',1218240,0},{40,'base',594077,44},{51,'base',594066,55}}},
    }
    local currentTDBuildProfile={length=7,flagOff=3,pattern='50 83 3D ?? ?? ?? ?? 00 0F 84 2A 00 00 00 8B 05 ?? ?? ?? ?? 85 C0 0F 84 1C 00 00 00 39 46 18 0F 85 13 00 00 00 C6 46 03 01 66 83 3E 6B 0F 8D 05 00 00 00 66 C7 06 6B 00 58 80 3D ?? ?? ?? ?? 00 BF 6C 00 00 00 0F 85 05 00 00 00 E9 ?? ?? ?? ?? E9 ?? ?? ?? ??',original='80 3D D9 6B 12 10 00',fix={{3,'cave',89,0},{16,'base',1218240,0},{59,'base',1207257,0},{76,'base',262490,80},{81,'base',262485,85}}}
    local function recoverOtherTD(g,h)
      local profile=g.dll=='TiberianDawn.dll' and orphanProfiles[h.off]
      if not profile or not g.base or not byteIs(g.base+h.off,0xE9) then return end
      local rel=readInteger(g.base+h.off+1)
      if not rel then return end
      local cave=(g.base+h.off+5+rel)%4294967296
      if h.off==0x40147 and readBytes(cave+57,1)==0x80 then profile=currentTDBuildProfile end
      local i=0
      for b in profile.pattern:gmatch('%S+') do
        if b~='??' and readBytes(cave+i,1)~=tonumber(b,16) then return end;i=i+1
      end
      for _,f in ipairs(profile.fix) do
        local value=readInteger(cave+f[1]);if not value then return end
        if f[4]~=0 then value=(cave+f[4]+value)%4294967296 end
        local expected=(f[2]=='base' and g.base or cave)+f[3]
        if value~=expected then return end
      end
      i=0
      for b in profile.original:gmatch('%x%x') do
        if readBytes(cave-profile.length+i,1)~=tonumber(b,16) then return end;i=i+1
      end
      local flag=readInteger(cave+profile.flagOff)
      local jump={}
      for j=0,profile.length-1 do jump[#jump+1]=string.format('%02X',readBytes(g.base+h.off+j,1)) end
      local address=string.format('%X',g.base+h.off)
      h.unhook='assert('..address..','..table.concat(jump,' ')..')\n'..address..':\ndb '..profile.original..'\n'..string.format('%X',flag)..':\ndd 0'
      h.recovered=true;h.needsUpgrade=true;h.hooked=true;h.installedBase=g.base;h.flag=flag;h.errShown=nil
    end

    local function hookSnapshot(g,h,before)
      local sites={}
      for dll,offset,pattern in h.hook:gmatch('assert%((%w+%.dll)%+(%x+),([^%)]+)%)') do
        local n=0;for _ in pattern:gmatch('%S+') do n=n+1 end
        local address=g.base+tonumber(offset,16)
        local bytes=readBytes(address,n,true)
        if not bytes then error('Could not read installed hook bytes.') end
        sites[#sites+1]={address=address,bytes=bytes}
      end
      if before then h.originals=sites;return end
      h.patches=sites
      local rel=readInteger(g.base+h.off+1)
      h.cave=(g.base+h.off+5+rel)%4294967296
      h.caveBytes=readBytes(h.cave,32,true)
      for name in h.hook:gmatch('alloc%(([^,]+),') do
        if not name:find('Original',1,true) then h.codeName=name;break end
      end
    end
    local function hookOwned(h)
      if not h.patches then return true end -- recovered scripts have their own complete assertions
      for _,p in ipairs(h.patches) do
        local bytes=readBytes(p.address,#p.bytes,true)
        if type(bytes)~='table' then return false end
        for i,v in ipairs(p.bytes) do if bytes[i]~=v then return false end end
      end
      local bytes=readBytes(h.cave,32,true)
      if type(bytes)~='table' then return false end
      for i,v in ipairs(h.caveBytes) do if bytes[i]~=v then return false end end
      return true
    end
    local function removeHook(g,h,releaseOnly)
      if not releaseOnly and not hookOwned(h) then
        local original=h.originals and #h.originals&gt;0
        for _,p in ipairs(h.originals or {}) do
          local bytes=readBytes(p.address,#p.bytes,true)
          if type(bytes)~='table' then original=false;break end
          for i,v in ipairs(p.bytes) do if bytes[i]~=v then original=false;break end end
        end
        if not original then error('Hook ownership changed; refusing to overwrite '..g.dll..' '..h.name..'.') end
        releaseOnly=true
      end
      local script=h.unhook
      if releaseOnly then
        local lines={}
        for line in script:gmatch('[^\n]+') do
          if line:match('^dealloc%(') or line:match('^unregistersymbol%(') then lines[#lines+1]=line end
        end
        script=table.concat(lines,'\n')
      end
      local P=KATZY_SELECTED_RE
      local busy=h.cave and not P.caveIdle({cave=h.cave})
      if busy and h.codeName then script=script:gsub('dealloc%('..h.codeName..'%)','') end
      local called,ok=pcall(autoAssemble,script)
      if not called or not ok then return false end
      if busy and h.codeName then
        P.retired[#P.retired+1]={pid=getOpenedProcessID(),cave=h.cave,caveBytes=h.caveBytes}
      end
      h.patches=nil;h.originals=nil;h.cave=nil;h.caveBytes=nil;h.codeName=nil
      return true
    end

    local function handleHookUnsafe(g, h)
      h.normalUnhook=h.normalUnhook or h.unhook
      if not h.hooked then h.unhook=h.normalUnhook;h.recovered=nil;recoverTDFF(g,h);if not h.hooked then recoverOtherTD(g,h) end end
      if h.hooked and (g.base == nil or h.installedBase ~= g.base) then
        if not removeHook(g,h,true) then return end
        h.hooked = false; h.flag = nil; h.installedBase = nil
      end
      local want = (g.base ~= nil and not CLEARED and _G[h.toggle])
      if h.name=='build' and not g.hooks[1].hooked then want=false end
      if want then
        if h.needsUpgrade or not h.hooked or h.installedBase ~= g.base or not byteIs(g.base + h.off, 0xE9) then
          if h.hooked then
            local unhookOK, removed = pcall(removeHook,g,h)
            if not unhookOK then error(removed,0) end;if not removed then error('Hook cleanup failed.',0) end
            h.hooked = false
          end
          h.unhook=h.normalUnhook;h.needsUpgrade=nil
          hookSnapshot(g,h,true)
          local ok, r, detail = pcall(autoAssemble, h.hook)
          if ok and r then
            h.hooked = true
            h.installedBase = g.base
            h.recovered=nil
            hookSnapshot(g,h)
            local rel = readInteger(g.base + h.off + 1)
            h.flag = rel ~= nil and readInteger((g.base + h.off + 5 + rel + h.flagOff)%4294967296) or nil
          elseif not h.errShown then
            h.errShown = true
            KATZY_RE_LAST_ERROR=tostring(ok and detail or r)
            ShowMessage(g.dll .. ' ' .. h.name .. ' hook failed: ' .. KATZY_RE_LAST_ERROR)
          end
        end
      elseif h.hooked then
        local unhookOK, removed = pcall(removeHook,g,h)
        if not unhookOK then error(removed,0) end;if not removed then error('Hook cleanup failed.',0) end
        h.hooked = false
        h.flag = nil
        h.installedBase = nil
      end
      if h.hooked then
        if not hookOwned(h) then error('The active hook changed; disable the conflicting modification first.') end
        local fa = h.flag or getAddressSafe(h.sym)
        if fa ~= nil and fa ~= 0 then
          local enabled=_G[h.toggle] and 1 or 0
          if not h.recovered then
            local human=KATZY_RE_HUMAN(g.base,g.dll=='TiberianDawn.dll' and 'td' or 'ra')
            if writeInteger(fa+4,human or 0)==false then error('Could not update the hook owner.') end
            if not human then enabled=0 end
          end
          if writeInteger(fa,enabled)==false then error('Could not update the hook switch.') end
        end
      end
    end

    local function handleHook(g,h)
      local want=g.base and not CLEARED and _G[h.toggle]
      if not h.hooked and (not g.base or (not want and not byteIs(g.base+h.off,0xE9))) then return end
      if h.hooked and not h.needsUpgrade and not h.recovered and want and h.installedBase==g.base and hookOwned(h) then return handleHookUnsafe(g,h) end
      return KATZY_SELECTED_RE.transaction(function() return handleHookUnsafe(g,h) end)
    end

    local function clampRes(p,r,base)
      if r.owner~=p then _G[r.saved]=nil;r.owner=p;r.last=nil end
      local address=p+r.off
      local value=readInteger(address)
      if value==nil then error('Could not read the resource value.') end
      local function write(v)
        if writeInteger(address,v)==false or readInteger(address)~=v then error('Resource write failed.') end
      end
      if r.stored then
        local stored=readInteger(p+r.stored)
        if not stored or stored&gt;=0x80000000 or value&gt;=0x80000000 then error('Could not read the credit balance.') end
        if _G[r.on] then
          if _G[r.saved]==nil then
            if value&gt;0x7FFFFFFF-r.val then error('Credit balance is too large for the allowance.') end
            write(value+r.val);_G[r.saved]=r.val
          elseif value+stored&lt;r.val then write(r.val-stored) end
        elseif _G[r.saved]~=nil then
          -- Remove only the allowance; preserve income and ordinary spending.
          local removed=math.min(value,_G[r.saved])
          write(value-removed);_G[r.saved]=_G[r.saved]-removed
          if _G[r.saved]&gt;0 then
            local balance=math.max(0,stored-_G[r.saved])
            if writeInteger(p+r.stored,balance)==false or readInteger(p+r.stored)~=balance then error('Could not remove the remaining credit allowance.') end
          end
          _G[r.saved]=nil
        end
        return
      end
      if r.recompute and not _G[r.on] and (r.repair or _G[r.saved]~=nil) then
        local natural=r.recompute(base,p)
        if natural==nil then error('Could not read current power plants; restoration will retry.') end
        write(natural);_G[r.saved]=nil;r.repair=false;r.last=nil;return
      end
      local power=r.on=='TD_PWR'
      local anyPower=power or r.on=='RA_PWR'
      if power and _G[r.saved]~=nil and r.last then
        _G[r.saved]=math.max(0,_G[r.saved]+value-r.last)
      end
      if _G[r.on] then
        if _G[r.saved]==nil then _G[r.saved]=value end
        local wanted=r.val
        if anyPower then
          local drain=readInteger(p+r.off+4)
          if not drain or drain&gt;=0x80000000 then error('Could not read power demand.') end
          wanted=math.max(r.val,_G[r.saved],drain)
        end
        write(wanted);r.last=wanted
      elseif _G[r.saved]~=nil then
        write(_G[r.saved]);_G[r.saved]=nil;r.last=nil
      end
    end

    local function chargeSW(p,sw,base)
      if not _G[sw.on] then return end
      local td=sw.on=='TD_SW'
      p=KATZY_RE_HUMAN(base,td and 'td' or 'ra')
      if not p then return end
      for i=0,sw.n-1 do
        local address=p+sw.off+i*sw.stride
        local flags=readInteger(address)
        if flags and (flags &amp; sw.present)~=0 and (flags &amp; sw.ready)==0 then
          local active
          if td then active=(flags &amp; 8)==0
          else
            local start=readInteger(address+5)
            active=start and start~=0xFFFFFFFF and start~=-1
          end
          if active then
            local timer=address+(td and 8 or 9)
            local duration=readInteger(timer)
            if duration==nil then error('Could not read the superweapon recharge timer.') end
            if duration~=0 and (writeInteger(timer,0)==false or readInteger(timer)~=0) then error('Could not complete the superweapon recharge timer.') end
          end
        end
      end
    end

    local function mapApplyScript(g,p)
      local replacements={}
      local address
      for line in p.apply:gmatch('[^\n]+') do
        local off=line:match('^%w+%.dll%+(%x+):')
        if off then address=tonumber(off,16) end
        local bytes=line:match('^db (.+)')
        if bytes and address then replacements[address]=bytes end
      end
      local adopted=false
      local script=p.apply:gsub('assert%((%w+%.dll)%+(%x+),([^%)]+)%)',function(dll,offset,original)
        local off=tonumber(offset,16)
        local replacement=replacements[off]
        if not replacement then return 'assert('..dll..'+'..offset..','..original..')' end
        local function matches(pattern)
          local i=0
          for byte in pattern:gmatch('%x%x') do
            if readBytes(g.base+off+i,1)~=tonumber(byte,16) then return false end
            i=i+1
          end
          return true
        end
        if matches(original) then return 'assert('..dll..'+'..offset..','..original..')' end
        if matches(replacement) then adopted=true;return 'assert('..dll..'+'..offset..','..replacement..')' end
        error('Map option could not be applied: unexpected code at '..dll..'+'..offset..'.',0)
      end)
      return script,adopted
    end

    local function patchComplete(g,p)
      local address
      for line in p.apply:gmatch('[^\n]+') do
        local offset=line:match('^%w+%.dll%+(%x+):')
        if offset then address=g.base+tonumber(offset,16) end
        local bytes=line:match('^db (.+)')
        if bytes and address then
          local i=0
          for byte in bytes:gmatch('%x%x') do
            if readBytes(address+i,1)~=tonumber(byte,16) then return false end
            i=i+1
          end
        end
      end
      return true
    end

    local function patchDirect(g, p)
      if g.base == nil then return end
      local want=_G[p.on] or (p.visual and _G[p.visual])
      if CLEARED and not want and not p.applied then
        local _,adopted=mapApplyScript(g,p)
        if adopted then p.applied=true;if p.flag then p.savedFlag=0 end end
      end
      if want then mapApplyScript(g,p) end
      if want and (not p.applied or not patchComplete(g,p)) then
        local apply,adopted=mapApplyScript(g,p)
        if p.flag and p.savedFlag == nil then
          p.savedFlag = adopted and 0 or readBytes(g.base + p.flag, 1)
          if p.savedFlag == nil then return end
        end
        local ok, r = pcall(KATZY_SELECTED_RE.transaction,function() return autoAssemble(apply) end)
        if ok and r then
          p.applied=true
          if p.flag then
            local value=_G[p.on] and 1 or 0
            if writeBytes(g.base+p.flag,value)==false or readBytes(g.base+p.flag,1)~=value then error("Map display flag write failed.") end
          end
          p.applied = true
        else
          error('Could not apply '..g.dll..' map patch: '..tostring(r))
        end
      elseif want and p.applied and p.flag then
        local value=_G[p.on] and 1 or 0
        if readBytes(g.base+p.flag,1)~=value then
          if writeBytes(g.base+p.flag,value)==false then error('Map display flag write failed.') end
        end
      elseif not want and p.applied then
        mapApplyScript(g,p)
        local ok, r = pcall(KATZY_SELECTED_RE.transaction,function() return autoAssemble(p.remove) end)
        if ok and r then
          if p.flag and p.savedFlag ~= nil then
            local ok = pcall(writeBytes, g.base + p.flag, p.savedFlag)
            if not ok or readBytes(g.base + p.flag, 1) ~= p.savedFlag then return end
            p.savedFlag = nil
          end
          p.applied = false
        end
      end
    end

    local Checked = false
    local Err = false

    MAIN_TIMER = createTimer(nil, false)
    MAIN_TIMER.Interval = 500
    MAIN_TIMER.OnTimer = function(t)
      local failures={}
      local function attempt(fn,...)
        local ok,why=pcall(fn,...)
        if not ok then failures[#failures+1]=tostring(why) end
      end
      local ok, err = pcall(function()
        refreshSelection()
        local target = getProcessIDFromProcessName('InstanceServerG.exe')
        if not target or target==0 then
          if KATZY_RE_PID then
            KATZY_RE_PID=nil;CLEARED=true
            for _,g in ipairs(GAMES) do
              g.base=nil
              for _,r in ipairs(g.res) do _G[r.on]=false;_G[r.saved]=nil;r.owner=nil;r.last=nil end
              for _,h in ipairs(g.hooks) do _G[h.toggle]=false;h.hooked=false;h.flag=nil;h.installedBase=nil end
              for _,p in ipairs(g.patches) do _G[p.on]=false;if p.visual then _G[p.visual]=false end;p.applied=false;p.savedFlag=nil end
              _G[g.sw.on]=false
            end
            for _,id in ipairs({4,5,13,11,16,19,20,23,8,9,14,12,17,21,22,24,25,27,28,2,6}) do
              local record=getAddressList().getMemoryRecordByID(id)
              if record then record.Active=false end
            end
          end
          return
        end
        if getOpenedProcessID() ~= target then openProcess(target) end
        if getOpenedProcessID() ~= target then return end
        if KATZY_RE_PID ~= target then
          KATZY_RE_PID = target
          for _, g in ipairs(GAMES) do
            for _, r in ipairs(g.res) do _G[r.saved] = nil; r.owner = nil end
            for _, h in ipairs(g.hooks) do h.hooked = false; h.flag = nil; h.installedBase = nil end
            for _, patch in ipairs(g.patches) do patch.applied = false; patch.savedFlag = nil end
          end
        end
        local vok, vver = DSH_GAME_VERSION()
        if vok ~= true then
          if vok == false and not Checked then
            Checked = true
            KATZY_RE_LAST_ERROR=tostring(vver)
            if not (KATZY_SELECTED_RE and KATZY_SELECTED_RE.closing) then ShowMessage('Unsupported Command &amp; Conquer build: '..tostring(vver)..'. Cheats are paused.') end
          end
          return
        end
        Checked = false


        for _, g in ipairs(GAMES) do
          g.base = dllBase(g.dll)
          local reloaded = false
          if g.base then
            for _, h in ipairs(g.hooks) do
              if h.hooked and (h.installedBase ~= g.base or not byteIs(g.base + h.off, 0xE9)) then reloaded = true end
            end
          end
          if reloaded then
            for _, r in ipairs(g.res) do _G[r.saved] = nil; r.owner = nil end
            for _, patch in ipairs(g.patches) do patch.applied = false; patch.savedFlag = nil end
          end
          for _, h in ipairs(g.hooks) do attempt(handleHook,g,h) end
          if g.base ~= nil then
            local p=KATZY_RE_HUMAN(g.base,g.dll=='TiberianDawn.dll' and 'td' or 'ra')
            if p ~= nil and p ~= 0 then
              for _, r in ipairs(g.res) do attempt(clampRes,p,r,g.base) end
              attempt(chargeSW,p,g.sw,g.base)
            end
            for _, p in ipairs(g.patches) do attempt(patchDirect,g,p) end
          else
            for _, r in ipairs(g.res) do _G[r.saved] = nil end
            for _, patch in ipairs(g.patches) do patch.applied = false; patch.savedFlag = nil end
          end
        end
      end)
      if ok and #failures&gt;0 then ok=false;err=table.concat(failures,'; ') end
      if ok then Err=false end
      if not ok and not Err then
        KATZY_RE_LAST_ERROR=tostring(err)
        if not (KATZY_SELECTED_RE and KATZY_SELECTED_RE.closing) then ShowMessage('Cheat script error: ' .. tostring(err)) end
        Err = true
      end
    end
    KATZY_RE_REFRESH_MAP=function(dll)
      local target=getProcessIDFromProcessName('InstanceServerG.exe')
      if not target or target==0 then return end
      MAIN_TIMER.OnTimer(MAIN_TIMER)
      if getOpenedProcessID()~=target then return end
      local ok,message=DSH_GAME_VERSION()
      if ok~=true then return end
      for _,g in ipairs(GAMES) do
        if g.dll==dll then
          if not verifiedModules[dll] or not g.base then return end
          for _,p in ipairs(g.patches) do
            if _G[p.on] or (p.visual and _G[p.visual]) then
              if not p.applied or not patchComplete(g,p) then
                error('Map option did not apply. Allow the table Lua script and check the reported patch error.')
              end
            end
          end
        end
      end
    end
    KATZY_RE_CLEAR=function()
      CLEARED=true
      for _,g in ipairs(GAMES) do
        for _,h in ipairs(g.hooks) do _G[h.toggle]=false end
        for _,p in ipairs(g.patches) do _G[p.on]=false;if p.visual then _G[p.visual]=false end end
        for _,resource in ipairs(g.res) do _G[resource.on]=false end
        _G[g.sw.on]=false
      end
      MAIN_TIMER.OnTimer(MAIN_TIMER)
      local pid=getProcessIDFromProcessName('InstanceServerG.exe')
      if pid and pid==getOpenedProcessID() then
        for _,g in ipairs(GAMES) do
          for _,h in ipairs(g.hooks) do if h.hooked then error('Could not remove '..g.dll..' '..h.name..'. Retry Clear State.') end end
          for _,p in ipairs(g.patches) do if p.applied then error('Map cleanup incomplete. Retry Clear State.') end end
          for _,resource in ipairs(g.res) do if _G[resource.saved]~=nil or (resource.recompute and resource.repair and g.base and readInteger(g.base+g.ptr)~=0) then error('Resource cleanup incomplete. Retry Clear State.') end end
        end
      end
      for _,id in ipairs({4,5,13,11,16,19,20,23,8,9,14,12,17,21,22,24,25,2,6}) do
        local record=getAddressList().getMemoryRecordByID(id)
        if record and record.Active then record.Active=false end
      end
    end
    for _,spec in ipairs({{2,'RedAlert.dll'},{6,'TiberianDawn.dll'}}) do
      local record=getAddressList().getMemoryRecordByID(spec[1])
      local game=spec[2]
      if record then
        record.OnActivate=function(_,before)
          if before then
            local ok,message=pcall(KATZY_RE_REQUIRE_GAME,game)
            if not ok then ShowMessage(tostring(message));return false end
          end
          return true
        end
      end
    end
    refreshSelection()
    MAIN_TIMER.Enabled = true
  end
end

-- Katzy: captured-selection protection. No unit data fields are overwritten.
local P={active={},retired={},profiles={[ [====[ra]====] ]={[ [====[module]====] ]=[====[RedAlert.dll]====],[ [====[player]====] ]=1654920,[ [====[frame]====] ]=1654928,[ [====[selection]====] ]=1701704,[ [====[context]====] ]=1702184,[ [====[stride]====] ]=24,[ [====[listoff]====] ]=4,[ [====[countoff]====] ]=16,[ [====[owner]====] ]=263,[ [====[health]====] ]=45,[ [====[word]====] ]=true,[ [====[flasher]====] ]=87,[ [====[ret]====] ]=20,[ [====[record]====] ]=27,[ [====[parent]====] ]=2,[ [====[sites]====] ]={{[ [====[name]====] ]=[====[damage]====],[ [====[rva]====] ]=523232,[ [====[length]====] ]=6,[ [====[original]====] ]=[====[55 8B EC 83 EC 10]====],[ [====[pattern]====] ]=[====[55 8B EC 83 EC 10 53 56 57 8B F9 32 DB 0F B7 47 2D 8B C8 98 89 4D F4 89 4D F0 89 45 F8 85 C0 0F 84 14 02 00 00 8B 75 08 83 3E 00 0F 84 08 02 00 00 8A 7D 18 84 FF 75 14 8B 07 8B CF FF 50 34 F6 80 25 01 00 00 20 0F 85 ED 01 00 00 8B 07 8B CF]====]},{[ [====[name]====] ]=[====[visual]====],[ [====[rva]====] ]=336784,[ [====[length]====] ]=5,[ [====[original]====] ]=[====[33 C0 8D 51 08]====],[ [====[pattern]====] ]=[====[33 C0 8D 51 08 56 57 8D 70 02 8D 78 04 0F 1F 00 83 7A FC 00 76 06 8D 4E FE 0F AB C8 83 3A 00 76 06 8D 4E FF 0F AB C8 83 7A 04 00 76 03 0F AB F0 83 7A 08 00 76 06 8D 4E 01 0F AB C8 83 7A 0C 00 76 06 8D 4E 02 0F AB C8 83 C2 14 83 C6 05 83 EF]====]},{[ [====[name]====] ]=[====[construct]====],[ [====[rva]====] ]=520016,[ [====[length]====] ]=6,[ [====[original]====] ]=[====[55 8B EC 8A 45 08]====],[ [====[pattern]====] ]=[====[55 8B EC 8A 45 08 56 8B F1 6A 00 83 66 1D FC 8D 4E 29 88 46 04 8B 45 0C 89 46 05 8B 46 15 83 E0 E8 C7 46 09 FF FF FF FF 83 C8 08 C7 46 0D 00 00 00 00 C7 06 ?? ?? ?? ?? 89 46 15 C7 46 19 00 00 00 00 C7 46 21 00 00 00 00 C7 46 25 00 00 00 00]====]},{[ [====[name]====] ]=[====[load]====],[ [====[rva]====] ]=307248,[ [====[length]====] ]=7,[ [====[original]====] ]=[====[55 8B EC 80 7D 08 00]====],[ [====[pattern]====] ]=[====[55 8B EC 80 7D 08 00 53 56 74 16 68 ?? ?? ?? ?? FF 75 0C E8 08 2E 04 00 83 C4 08 8A D8 5E 5B 5D C3 8B 75 10 85 F6 0F 84 25 01 00 00 68 ?? ?? ?? ?? 56 E8 84 66 08 00 83 C4 08 85 C0 75 07 A3 ?? ?? ?? ?? EB 20 68 ?? ?? ?? ?? 56 E8 6B 66 08 00]====]}}},[ [====[td]====] ]={[ [====[module]====] ]=[====[TiberianDawn.dll]====],[ [====[player]====] ]=1218240,[ [====[frame]====] ]=1207232,[ [====[selection]====] ]=934192,[ [====[context]====] ]=934432,[ [====[stride]====] ]=24,[ [====[listoff]====] ]=4,[ [====[countoff]====] ]=16,[ [====[owner]====] ]=147,[ [====[health]====] ]=38,[ [====[word]====] ]=true,[ [====[flasher]====] ]=73,[ [====[ret]====] ]=16,[ [====[record]====] ]=28,[ [====[parent]====] ]=6,[ [====[sites]====] ]={{[ [====[name]====] ]=[====[damage]====],[ [====[rva]====] ]=470928,[ [====[length]====] ]=6,[ [====[original]====] ]=[====[55 8B EC 83 EC 10]====],[ [====[pattern]====] ]=[====[55 8B EC 83 EC 10 53 56 8B F1 32 DB 57 89 75 F8 0F B7 46 26 8B C8 0F BF F8 89 4D F4 89 4D F0 85 FF 0F 84 F9 00 00 00 8B 45 08 83 38 00 0F 84 ED 00 00 00 8B 06 8B CE FF 50 34 F6 40 11 20 0F 85 DC 00 00 00 8B 06 8B CE FF 50 34 FF 75 0C 8B CE]====]},{[ [====[name]====] ]=[====[visual]====],[ [====[rva]====] ]=271120,[ [====[length]====] ]=5,[ [====[original]====] ]=[====[33 C0 39 41 04]====],[ [====[pattern]====] ]=[====[33 C0 39 41 04 76 04 0F BA E8 00 83 79 08 00 76 04 0F BA E8 01 83 79 0C 00 76 04 0F BA E8 02 83 79 10 00 76 04 0F BA E8 03 83 79 14 00 76 04 0F BA E8 04 83 79 18 00 76 04 0F BA E8 05 83 79 1C 00 76 04 0F BA E8 06 83 79 20 00 76 04 0F BA E8]====]},{[ [====[name]====] ]=[====[construct]====],[ [====[rva]====] ]=467376,[ [====[length]====] ]=7,[ [====[original]====] ]=[====[8B 41 14 83 61 1A FE]====],[ [====[pattern]====] ]=[====[8B 41 14 83 61 1A FE 83 E0 E8 83 C8 08 C7 01 ?? ?? ?? ?? 89 41 14 B8 FF 00 00 00 66 89 41 26 33 C0 66 89 41 18 8B C1 C7 41 04 FF FF FF FF C7 41 1E 00 00 00 00 C7 41 22 00 00 00 00 C3 CC CC CC 55 8B EC 56 57 FF 75 2C 8B F9 FF 75 28 E8 6E F8]====]},{[ [====[name]====] ]=[====[load]====],[ [====[rva]====] ]=236016,[ [====[length]====] ]=7,[ [====[original]====] ]=[====[55 8B EC 80 7D 08 00]====],[ [====[pattern]====] ]=[====[55 8B EC 80 7D 08 00 53 56 74 16 68 ?? ?? ?? ?? FF 75 0C E8 58 3F 04 00 83 C4 08 8A D8 5E 5B 5D C3 8B 75 10 85 F6 0F 84 BF 00 00 00 68 ?? ?? ?? ?? 56 E8 61 83 07 00 83 C4 08 85 C0 75 04 32 C0 EB 1F 68 ?? ?? ?? ?? 56 E8 4B 83 07 00 83 C4 08]====]},{[ [====[name]====] ]=[====[reset]====],[ [====[rva]====] ]=517632,[ [====[length]====] ]=5,[ [====[original]====] ]=[====[A1 C0 6B 12 10]====],[ [====[pattern]====] ]=[====[A1 ?? ?? ?? ?? B9 ?? ?? ?? ?? 56 C7 05 ?? ?? ?? ?? C2 01 00 00 C7 05 ?? ?? ?? ?? 00 00 00 00 A3 ?? ?? ?? ?? C7 05 ?? ?? ?? ?? 00 00 00 00 C6 05 ?? ?? ?? ?? 00 E8 26 9B FE FF 6A 30 6A 00 68 ?? ?? ?? ?? E8 A8 DA 02 00 A1 ?? ?? ?? ?? 83 C4 0C]====]}}}}}
KATZY_SELECTED_RE=P
local function hex(n) return string.format('0x%X',n) end
local function bytes(s)
  local t={} for b in s:gmatch('%x%x') do t[#t+1]=tonumber(b,16) end return t
end
local function same(a,b)
  local v=readBytes(a,#b,true)
  if type(v)~='table' or #v~=#b then return false end
  for i=1,#b do if v[i]~=b[i] then return false end end
  return true
end
local function moduleBase(name)
  for _,m in ipairs(enumModules() or {}) do
    if m.Name and m.Name:lower()==name:lower() then return m.Address end
  end
end
local function transaction(fn)
  pause()
  local ok,value=pcall(fn)
  unpause()
  if not ok then error(value,0) end
  return value
end
-- The hooks call only our own matcher. A suspended thread can return to a cave
-- only while its instruction pointer is inside that cave. Never free it then.
local function caveIdle(c)
  local threads=createStringList()
  local buffer=createMemoryStream()
  local handle
  local ok,idle=pcall(function()
    getThreadlist(threads)
    if threads.Count==0 then return false end
    buffer.Size=716
    local open=getAddress('kernel32.OpenThread',true)
    local close=getAddress('kernel32.CloseHandle',true)
    local context=getAddress(cheatEngineIs64Bit() and 'kernel32.Wow64GetThreadContext' or 'kernel32.GetThreadContext',true)
    for i=0,threads.Count-1 do
      handle=executeCodeLocalEx(open,0x48,0,tonumber(threads[i],16))
      if not handle or handle==0 then return false end
      writeIntegerLocal(buffer.Memory,0x10001)
      local read=executeCodeLocalEx(context,handle,buffer.Memory)
      executeCodeLocalEx(close,handle);handle=nil
      if not read or read==0 then return false end
      local ip=readIntegerLocal(buffer.Memory+184)
      if not ip or (ip&gt;=c.cave and ip&lt;c.cave+0x4000) then return false end
    end
    return true
  end)
  if handle and handle~=0 then pcall(executeCodeLocalEx,getAddress('kernel32.CloseHandle',true),handle) end
  buffer.destroy();threads.destroy()
  return ok and idle
end
P.transaction=transaction
P.caveIdle=caveIdle
local function context(key)
  local p=P.profiles[key]
  if not p then error('This game is not supported by this option.',0) end
  KATZY_RE_REQUIRE_GAME(p.module)
  local base=moduleBase(p.module)
  if not base then error('Start or resume a match before enabling this option.',0) end
  local player=readInteger(base+p.player)
  player=KATZY_RE_HUMAN(base,key)
  local frame=readInteger(base+p.frame)
  if not player or player==0 or not frame then error('Start or resume a match before enabling this option.',0) end
  return {key=key,p=p,base=base,pid=getOpenedProcessID(),player=player,frame=frame}
end
local function unitType(c,obj,vt)
  local p=c.p
  if p.vts then
    for _,v in ipairs(p.vts) do if vt==c.base+v then return true end end
    return false
  elseif c.key=='ra' then
    local t=readBytes(obj+4,1)
    return t==1 or t==5 or t==13 or t==28 or t==30
  else
    -- Native MSVC RTTI identifies these four concrete object classes.
    local rva=vt-c.base
    return rva==0xC70F0 or rva==0xC8958 or rva==0xCDFC0 or rva==0xCFC4C
  end
end
local function capture(c)
  local p=c.p
  local vector=c.base+p.selection
  if p.context then
    local index=readInteger(c.base+p.context)
    if c.key=='td' or c.key=='ra' then
      local owner,houseIndex=KATZY_RE_HUMAN(c.base,c.key)
      if owner~=c.player then error('The human player changed. Select your group again.',0) end
      index=houseIndex
    end
    local max=c.key=='td' and 10 or 20
    if not index or index&gt;=max then error('The local selection is unavailable. Resume your match and select your units.',0) end
    vector=vector+index*p.stride
  end
  local list,count=readInteger(vector+p.listoff),readInteger(vector+p.countoff)
  if not list or list==0 or not count or count==0 then error('Select units or buildings you own in the game, then enable this option.',0) end
  if count&gt;128 then error('Select a group of no more than 128 units.',0) end
  local targets,seen={},{}
  for i=0,count-1 do
    local obj=readInteger(list+i*4)
    local vt=obj and obj~=0 and readInteger(obj)
    if vt and unitType(c,obj,vt) and not seen[obj] then
      local owner=readInteger(obj+p.owner)
      local house=owner
      if c.key=='ra' then
        local heap=readInteger(c.base+0x12C580)
        local stride=heap and readInteger(heap+8)
        local storage=heap and readInteger(heap+0x14)
        house=owner and owner&lt;32 and stride and storage and storage+owner*stride or nil
      end
      local hp=p.word and readSmallInteger(obj+p.health) or readInteger(obj+p.health)
      local id=p.id and readInteger(obj+p.id) or 0
      if house==c.player and hp and hp&gt;0 and hp&lt;0x80000000 and id then
        targets[#targets+1]={obj=obj,vt=vt,id=id,owner=owner};seen[obj]=true
      end
    end
  end
  if #targets==0 then error('The selection contains no supported units or buildings you own.',0) end
  c.targets=targets
end
local function makeScript(c)
  local p=c.p
  local prefix='KZ_SELECTED_'..c.key
  c.prefix=prefix
  local state=prefix..'_state'
  local code=prefix..'_code'
  local matcher=prefix..'_match'
  local b={'[ENABLE]','alloc('..code..',4000,'..p.module..')','label('..state..')','label('..matcher..')',
    'label(match_loop)','label(match_next)','label(match_yes)','label(match_no)','label(match_expired)',
    'registersymbol('..state..')','registersymbol('..code..')'}
  for i,s in ipairs(p.sites) do
    local n=prefix..'_'..i
    s.symbol=n
    b[#b+1]='aobscanmodule('..n..','..p.module..','..s.pattern..')'
    b[#b+1]='label(hook_'..i..')\nlabel(original_'..i..')'
    b[#b+1]='registersymbol('..n..')'
  end
  b[#b+1]=code..':\n'..matcher..':\npush edx\nxor eax,eax\ncmp dword ['..state..'],1\njne match_no'
  b[#b+1]='mov edx,['..p.module..'+'..hex(p.frame)..']\ncmp edx,['..state..'+4]\njb match_expired\nmov ['..state..'+4],edx'

  b[#b+1]='mov edx,'..state..'+0x10\nmatch_loop:\ncmp [edx],ecx\nje match_yes\nmatch_next:\nadd edx,0x10\ncmp edx,'..state..'+'..hex(16+#c.targets*16)..'\njb match_loop\njmp match_no'
  b[#b+1]='match_yes:\nmov eax,[ecx]\ncmp eax,[edx+4]\njne match_expired\nmov eax,[ecx+'..hex(p.owner)..']\ncmp eax,[edx+0xC]\njne match_no'
  if p.id then b[#b+1]='mov eax,[ecx+'..hex(p.id)..']\ncmp eax,[edx+8]\njne match_no' end
  b[#b+1]='cmp '..(p.word and 'word' or 'dword')..' [ecx+'..hex(p.health)..'],0\njle match_no\nmov eax,1\npop edx\nret'
  b[#b+1]='match_expired:\nmov dword ['..state..'],0\nmatch_no:\nxor eax,eax\npop edx\nret'
  for i,s in ipairs(p.sites) do
    local original='original_'..i
    b[#b+1]='hook_'..i..':'
    if s.name=='damage' then
      b[#b+1]='pushfd\npush eax\ncall '..matcher..'\ntest eax,eax\njz '..original..'\nmov eax,[esp+0xC]\ncmp dword [eax],0\njle '..original..'\npop eax\npopfd\nxor eax,eax\nret '..hex(p.ret)..'\n'..original..':\npop eax\npopfd'
    elseif s.name=='visual' or s.name=='building_visual' then
      b[#b+1]='pushfd\npush ecx\npush edx'
      if p.flasher then b[#b+1]='sub ecx,'..hex(p.flasher) end
      b[#b+1]='call '..matcher..'\ntest eax,eax\njz '..original..'\npop edx\npop ecx\npopfd'
      if p.flasher then b[#b+1]='mov eax,0xFFFFF\nret' else b[#b+1]='mov eax,0x7D0\nret 4' end
      b[#b+1]=original..':\npop edx\npop ecx\npopfd'
    elseif s.name=='construct' then
      b[#b+1]='pushfd'
      for j,t in ipairs(c.targets) do
        local label='new_'..i..'_'..j
        b[#b+1]='label('..label..')\ncmp ecx,['..state..'+'..hex(j*16)..']\njne '..label..'\nmov dword ['..state..'+'..hex(j*16)..'],0\n'..label..':'
      end
      b[#b+1]='popfd\n'..original..':'
    elseif s.name=='load' then
      b[#b+1]='pushfd\ncmp byte [esp+8],0\njne '..original..'\nmov dword ['..state..'],0\n'..original..':\npopfd'
    else
      b[#b+1]='mov dword ['..state..'],0\n'..original..':'
    end
    b[#b+1]='readmem('..s.symbol..','..s.length..')\njmp '..s.symbol..'+'..hex(s.length)
  end
  b[#b+1]='align 10\n'..state..':\ndd 1 '..hex(c.frame)..' '..hex(c.player)..' 0'
  for _,t in ipairs(c.targets) do b[#b+1]='dd '..hex(t.obj)..' '..hex(t.vt)..' '..hex(t.id)..' '..hex(t.owner) end
  for i,s in ipairs(p.sites) do
    b[#b+1]=s.symbol..':\njmp hook_'..i
    if s.length&gt;5 then b[#b+1]='nop '..hex(s.length-5) end
  end
  b[#b+1]='[DISABLE]'
  for i,s in ipairs(p.sites) do
    local saved={} for _,v in ipairs(c.originals[i]) do saved[#saved+1]=string.format('%02X',v) end
    b[#b+1]=hex(c.base+s.rva)..':\ndb '..table.concat(saved,' ')..'\nunregistersymbol('..s.symbol..')'
  end
  b[#b+1]='unregistersymbol('..state..')\nunregistersymbol('..code..')\ndealloc('..code..')'
  return table.concat(b,'\n')
end
local function redraw(c)
  if c.key=='ot' or c.key=='ts' then
    local f=c.key=='ot' and KATZY_TS.refreshSuperweaponSidebarOpenTS or KATZY_TS.refreshSuperweaponSidebar
    if f then pcall(f) end
  end
end
function P.disable(key)
  local c=P.active[key]
  if not c then return true end
  if getOpenedProcessID()~=c.pid then
    local processes=getProcesslist()
    if not processes[c.pid] then P.active[key]=nil;return true end
    error('Return to the original game process to remove its selected-unit protection.',0)
  end
  local base=moduleBase(c.p.module)
  if base==c.base then
    transaction(function()
      local original,patched=true,true
      for i,s in ipairs(c.p.sites) do
        original=original and same(c.base+s.rva,c.originals[i])
        patched=patched and same(c.base+s.rva,c.patches[i])
      end
      if not patched and not original then error('Another change overlaps selected-unit protection. Undo that change, then retry Clear All.',0) end
      if patched or original then
        local idle=caveIdle(c)
        local cleanup=c.script
        if not idle then cleanup=cleanup:gsub('dealloc%('..c.prefix..'_code%)','') end
        local called,ok,detail=pcall(autoAssemble,cleanup,false,c.disableInfo)
        if not called or not ok then P.lastError=tostring(called and detail or ok);error('Selected-unit protection could not be removed. Retry Clear All.',0) end
        for i,s in ipairs(c.p.sites) do assert(same(c.base+s.rva,c.originals[i]),'The original game code could not be restored.') end
        if not idle then P.retired[#P.retired+1]=c end
      end
    end)
    redraw(c)
  else
    for _,s in ipairs(c.p.sites) do
      if getAddressSafe(s.symbol)==c.base+s.rva then unregisterSymbol(s.symbol) end
    end
    if getAddressSafe(c.prefix..'_state')==c.state then unregisterSymbol(c.prefix..'_state') end
    if getAddressSafe(c.prefix..'_code')==c.cave then unregisterSymbol(c.prefix..'_code') end
    P.retired[#P.retired+1]=c
  end
  P.active[key]=nil
  return true
end
function P.enable(key)
  if P.active[key] then return true end
  local c=context(key)
  transaction(function()
    capture(c)
    c.originals={}
    for i,s in ipairs(c.p.sites) do
      c.originals[i]=readBytes(c.base+s.rva,s.length,true)
      -- Full unique AOBs are checked again by CE before installation.
      if not c.originals[i] then error('The supported game code could not be read.',0) end
    end
    c.script=makeScript(c)
    local called,ok,info=pcall(autoAssemble,c.script,false)
    if not called or not ok then P.lastError=tostring(called and info or ok);error('Selected-unit protection could not be installed. Check the supported game version and remove conflicting trainers.',0) end
    c.disableInfo=info;c.patches={}
    P.active[key]=c
    for i,s in ipairs(c.p.sites) do
      local address=getAddressSafe(s.symbol)
      if address~=c.base+s.rva then error('The selected-unit hook resolved to an unexpected location.',0) end
      c.patches[i]=readBytes(address,s.length,true)
      assert(c.patches[i] and c.patches[i][1]==0xE9,'The selected-unit code patch was not installed.')
    end
    c.state=getAddressSafe(c.prefix..'_state')
    c.cave=getAddressSafe(c.prefix..'_code')
    assert(c.cave,'The protection memory could not be located.')
    c.caveBytes=readBytes(c.cave,32,true)
    assert(c.state and readInteger(c.state)==1,'The selected group could not be recorded.')
  end)
  redraw(c)
  return true
end
function P.clear()
  for key in pairs(P.active) do P.disable(key) end
end
function P.reap()
  for i=#P.retired,1,-1 do
    local c=P.retired[i]
    if getOpenedProcessID()==c.pid then
      local released=transaction(function()
        if not same(c.cave,c.caveBytes) then return true end
        if not caveIdle(c) then return false end
        return deAlloc(c.cave)~=false
      end)
      if released then table.remove(P.retired,i) end
    elseif not getProcesslist()[c.pid] then table.remove(P.retired,i) end
  end
end
function P.shutdown()
  P.clear()
  P.reap()
  if P.timer then P.timer.destroy();P.timer=nil end
  if getMainForm().OnClose==P.closeHandler then getMainForm().OnClose=P.previousClose end
end
local function report(err)
  P.lastError=tostring(err)
  if not P.closing then showMessage(tostring(err):gsub('^.-:%d+:%s*','')) end
end
local ids={}
for key,p in pairs(P.profiles) do
  if not ids[p.record] then
    ids[p.record]=true
    local record=getAddressList().getMemoryRecordByID(p.record)
    record.OnActivate=function(_,before)
      if not before then return true end
      local chosen=key
      local ok,err=pcall(P.enable,chosen)
      if not ok then report(err);return false end
      return true
    end
    record.OnDeactivate=function(_,before)
      if not before then return true end
      local ok,err=pcall(function()
        for k,c in pairs(P.active) do if c.p.record==p.record then P.disable(k) end end
      end)
      if not ok then report(err);return P.closing==true end
      return true
    end
    record.OnDestroy=function() if P.closing then return end;local ok,err=pcall(P.shutdown);if not ok then report(err) end end
  end
end
local parents={}
for _,p in pairs(P.profiles) do
  if not parents[p.parent] then
    parents[p.parent]=true
    local record=getAddressList().getMemoryRecordByID(p.parent)
    local previous=record.OnDeactivate
    record.OnDeactivate=function(m,before,current)
      if before then
        if not P.clearing then
          local ok,err=pcall(KATZY_RE_CLEAR)
          if not ok then report(err);return P.closing==true end
        end
        local ok,err=pcall(function() for k,c in pairs(P.active) do if c.p.parent==p.parent then P.disable(k) end end end)
        if not ok then report(err);return P.closing==true end
        local child=getAddressList().getMemoryRecordByID(p.record)
        if child then child.Active=false end
      end
      if previous then return previous(m,before,current) end
      return true
    end
  end
end
P.previousClose=getMainForm().OnClose
P.closeHandler=function(sender,action)
  P.closing=true
  local ok,err=pcall(function()
    if MAIN_TIMER then MAIN_TIMER.Enabled=false end
    if KATZY_RE_CLEAR then KATZY_RE_CLEAR() end
  end)
  if not ok then P.lastError=tostring(err) end
  local stopped,why=pcall(P.shutdown)
  if not stopped then P.lastError=tostring(why) end
  if P.previousClose then
    local chained,value=pcall(P.previousClose,sender,action)
    if chained and value~=nil then return value end
  end
  return action
end
getMainForm().OnClose=P.closeHandler
P.timer=createTimer(nil,false)
P.timer.Interval=500
P.timer.OnTimer=function()
  if #P.retired&gt;0 then pcall(P.reap) end
  for key,c in pairs(P.active) do
    if getOpenedProcessID()==c.pid then
      local base=moduleBase(c.p.module)
      local reset=base~=c.base or readInteger(c.state)~=1
      if not reset then
        local allOriginal=true
        for i,s in ipairs(c.p.sites) do allOriginal=allOriginal and same(c.base+s.rva,c.originals[i]) end
        reset=allOriginal
      end
      if reset then
        local ok,err=pcall(P.disable,key)
        if ok then
          local r=getAddressList().getMemoryRecordByID(c.p.record)
          if r then r.Active=false end
        elseif not c.warned then c.warned=true;report(err) end
      end
    elseif not getProcesslist()[c.pid] then
      P.active[key]=nil
      local r=getAddressList().getMemoryRecordByID(c.p.record)
      if r then r.Active=false end
    end
  end
end
P.timer.Enabled=true
local previousClear=KATZY_RE_CLEAR
KATZY_RE_CLEAR=function()
  if P.clearing then return end
  P.clearing=true
  local ok,err=pcall(function()
    local clean,why=pcall(P.clear)
    if clean then for _,p in pairs(P.profiles) do getAddressList().getMemoryRecordByID(p.record).Active=false end end
    local native,nativeWhy=pcall(previousClear)
    if not clean then error(why,0) end
    if not native then error(nativeWhy,0) end
  end)
  P.clearing=false
  if not ok then error(err,0) end
end
</LuaScript>
</CheatTable>