<?xml version='1.0' encoding='utf-8'?>
<CheatTable CheatEngineTableVersion="52">
  <CheatEntries>
    <CheatEntry>
      <ID>1</ID>
      <Description>"Auto-Attach and Version Check"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
// Placeholder. Auto-attach/version check runs from the table Lua below.
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>18</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>0</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. The table detects official TUC 2.03 or OpenTS 0.1.0 automatically.

Infinite Money
Keeps at least 30,000 credits, including stored Tiberium, and allows further earnings.

Infinite Power
Adds enough power to meet demand. Turning it off restores normal power behavior.

Instant Build
Speeds construction and unit production. Each producer still needs to release its unit; keep factory exits clear.

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. Required buildings, power and targeting rules still apply. Turning it off restores normal recharge after use.

OpenTS compatibility
MAIN applies a reversible fix for the OpenTS 0.1.0 fogged-building palette crash. It also applies when enabling an option after Clear All. Disable MAIN, Clear All, table replacement, or orderly CE close restores the original engine code. No game files are changed. Restart after a crash before using this version; it prevents the faulty draw but cannot repair an already corrupted map.

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 a bright highlight. 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.

Reveal Map - Full
Explores the map and allows travel, which may affect mission events. Use only one Reveal Map mode at a time.

Reveal Map - Visual Only
Shows terrain and radar while preserving exploration triggers. Unexplored-area restrictions remain. Existing radar pixels may need a reload.

Promote Selected Units - Maximum Veterancy (One Shot)
Select units you own, then click this action to grant Elite, the highest native rank. It only promotes infantry, vehicles, and aircraft whose game type allows experience. Buildings and untrainable units are skipped. Already-Elite experience is preserved. The action stays unticked and may be used again for another selection; promotions remain after Clear All or closing the table and are saved normally by the game.

Clear All
Turns cheats off and removes recognized leftover patches. This action unticks itself.]])

-- Run after activation completes, once the help message is dismissed.
local record = memrec or getAddressList().getMemoryRecordByID(0)
local timer = createTimer(nil, false)
timer.Interval = 100
timer.OnTimer = function(sender)
  sender.destroy()
  if record and record.Active then record.Active = false end
end
timer.Enabled = true
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>3</ID>
      <Description>"Clear All"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.clear()
-- Let activation finish before unchecking this action record.
local record = memrec or getAddressList().getMemoryRecordByID(3)
local timer = createTimer(nil, false)
timer.Interval = 100
timer.OnTimer = function(sender)
  sender.destroy()
  if record and record.Active then record.Active = false end
end
timer.Enabled = true
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>10</ID>
      <Description>"[MAIN] Command &amp; Conquer: Tiberian Sun and Firestorm"</Description>
      <Options moHideChildren="1" />
      <CheatEntries>
        <CheatEntry>
          <ID>11</ID>
          <Description>"Infinite Money"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('money',true)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('money',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_TS.toggle('power',true)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('power',false)
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>12</ID>
          <Description>"Instant Build"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('build',true)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('build',false)
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>16</ID>
          <Description>"Infinite Aircraft Ammo"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('ammo',true)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('ammo',false)
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>15</ID>
          <Description>"Instant Superweapons"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('superweapon',true)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('superweapon',false)
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>19</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>13</ID>
          <Description>"Reveal Map - Full"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('fog',true)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('fog',false)
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>17</ID>
          <Description>"Reveal Map - Visual Only"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('visualfog',true)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.toggle('visualfog',false)
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>20</ID>
          <Description>"Promote Selected Units - Maximum Veterancy (One Shot)"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
// One-shot promotion runs in the activation callback.
[DISABLE]
</AssemblerScript>
        </CheatEntry>
      </CheatEntries>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
[DISABLE]
</AssemblerScript>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols />
  <LuaScript>--[[
Table identification: Katzy
Command &amp; Conquer Tiberian Sun / Firestorm | 2.03 TUC / OpenTS
Table version: v1.102
Safety maintenance; existing cheat targets and gameplay hook bodies preserved.
]]
if KATZY_SELECTED_TS then KATZY_SELECTED_TS.shutdown() end

-- Carry only verified pending products across an in-session table upgrade.
local otRetryCarry=nil
if KATZY_TS and KATZY_TS.context and KATZY_TS.active and KATZY_TS.active.build
    and KATZY_TS.active.build.engine=='opents' then
  local ctx=KATZY_TS.context('opents')
  local records=getAddressSafe('OT_BUILD_WAIT_ITEMS')
  if ctx and records then
    otRetryCarry={pid=ctx.pid,base=ctx.base,player=ctx.player,items={}}
    for slot=0,3 do
      local a=records+slot*16
      local f,obj,builder,owner=readInteger(a),readInteger(a+4),readInteger(a+8),readInteger(a+12)
      if f and f~=0 and obj and builder and owner==ctx.player
          and readInteger(owner+0x1D8+slot*4)==f and readInteger(f+0x38)==obj then
        otRetryCarry.items[#otRetryCarry.items+1]={slot=slot,f=f,obj=obj,builder=builder,owner=owner}
      end
    end
  end
end
if KATZY_TS and KATZY_TS.active and KATZY_TS.toggle then
  local pending = {}
  for feature in pairs(KATZY_TS.active) do pending[#pending+1] = feature end
  for _, feature in ipairs(pending) do pcall(KATZY_TS.toggle,feature,false) end
end
-- Safety helpers are table-local; existing cheat addresses and mechanics are unchanged.
KATZY_TS = KATZY_TS or { saved = {}, failures = {} }
local S = KATZY_TS
S.otRetryCarry=otRetryCarry

function S.context(engine)
  local pid = getProcessIDFromProcessName('Game.exe')
  if not pid or pid == 0 or getOpenedProcessID() ~= pid then return nil end
  local base = getAddressSafe('Game.exe')
  if not base or base == 0 then return nil end
  local pe = readInteger(base + 0x3C)
  if not pe or pe &lt; 0x40 or pe &gt; 0x1000 then return nil end
  if readInteger(base + pe) ~= 0x4550 then return nil end
  local size = readInteger(base + pe + 0x50)
  local actual = size and size &gt; 0x500000 and 'opents' or 'official'
  if actual ~= engine then return nil end
  if engine == 'official' then
    if base ~= 0x400000 or size ~= 5033984 or readInteger(base + pe + 8) ~= 960240402 then return nil end
  else
    -- Match two independent OpenTS instruction layouts, allowing these table's
    -- existing ammo/power patches. A large Game.exe alone is not an engine ID.
    local ammo = readBytes(base + 0x9FB0, 6, true)
    local power = readBytes(base + 0xBEE16, 6, true)
    if type(ammo) ~= 'table' or type(power) ~= 'table' or #ammo ~= 6 or #power ~= 6 then return nil end
    local ammoOriginal = ammo[1]==0xFF and ammo[2]==0x8F and ammo[3]==0x34 and ammo[4]==1 and ammo[5]==0 and ammo[6]==0
    local ammoPatched = true
    for i=1,6 do if ammo[i]~=0x90 then ammoPatched=false end end
    local powerOriginal = power[1]==1 and power[2]==0x81 and power[3]==0xD4 and power[4]==1 and power[5]==0 and power[6]==0
    local powerPatched = power[1]==0xE9 and power[6]==0x90
    if not (ammoOriginal or ammoPatched) or not (powerOriginal or powerPatched) then return nil end
  end
  return {pid=pid, base=base, player=readInteger(base + (engine == 'official' and 0x3E2284 or 0x602958))}
end

function S.requireEngine(engine)
  if not S.context(engine) then
    error('This option requires the ' .. engine .. ' Tiberian Sun engine in the attached Game.exe. No changes were made.')
  end
end

local function same(a, b)
  return a and b and a.pid == b.pid and a.base == b.base and a.player == b.player
end

function S.warn(key, message)
  if not S.failures[key] then
    S.failures[key] = true
    showMessage('Katzy table: ' .. message)
  end
end

function S.pin(feature, engine, address, value)
  local ctx = S.context(engine)
  if not ctx or not ctx.player or ctx.player == 0 then return false end
  local item = S.saved[feature]
  if not item or not same(item.context, ctx) then
    -- A replacement house/process cannot inherit another session's baseline.
    item = {context=ctx, engine=engine, values={}}
    S.saved[feature] = item
  end
  if item.values[address] == nil then
    local original = readInteger(address)
    if original == nil then return false end
    item.values[address] = original
  end
  local ok = writeInteger(address, value)
  if ok == false or readInteger(address) ~= value then
    local timers = {money='INFMONEY_TIMER',power='INFPOWER_TIMER',ot_money='OT_MONEY_TIMER',ot_power='OT_POWER_TIMER'}
    local timer = _G[timers[feature]]
    if timer then timer.Enabled = false end
    S.restore(feature)
    local ids = {money=11,power=14,ot_money=11,ot_power=14}
    local mr = getAddressList().getMemoryRecordByID(ids[feature])
    if mr and mr.Active then pcall(function() mr.Active = false end) end
    S.warn(feature, feature .. ' could not be written. Its timer was stopped and cleanup attempted; use Clear State to retry any pending restoration.')
    error(feature .. ' write failed')
  end
  return true
end

function S.pinOnce(feature, engine, address, value)
  local item = S.saved[feature]
  if item and same(item.context, S.context(engine)) and item.values[address] ~= nil then return true end
  return S.pin(feature, engine, address, value)
end

function S.restore(feature)
  local item = S.saved[feature]
  if not item then return true end
  local ctx = S.context(item.engine)
  if not ctx then return false end
  if not same(item.context, ctx) then
    -- Never restore snapshots to another process, house or unreadable session.
    S.saved[feature] = nil
    return true
  end
  local complete = true
  for address, value in pairs(item.values) do
    local ok = writeInteger(address, value)
    if ok ~= false and readInteger(address) == value then
      item.values[address] = nil
    else
      complete = false
    end
  end
  if complete then
    S.saved[feature] = nil
    S.failures[feature] = nil
  else
    S.warn(feature .. ':restore', feature .. ' could not be fully restored. Use Clear State to retry.')
  end
  return complete
end

function S.clear()
  -- Each active option owns its disable path. Never apply official-engine reset
  -- bytes blindly to OpenTS, and never write to an inactive option's addresses.
  local al = getAddressList()
  local failures = {}
  for _, id in ipairs({11,12,13,14,15,16,17}) do
    local mr = al.getMemoryRecordByID(id)
    if mr and mr.Active then
      local ok, err = pcall(function() mr.Active = false end)
      if not ok or mr.Active then failures[#failures+1] = mr.Description .. ': ' .. tostring(err or 'disable failed') end
    end
  end
  if #failures &gt; 0 then error(table.concat(failures, '\n')) end
  local pending = {}
  for feature in pairs(S.saved) do pending[#pending+1] = feature end
  for _, feature in ipairs(pending) do
    if not S.restore(feature) then failures[#failures+1] = feature .. ': restoration pending' end
  end
  if #failures &gt; 0 then error(table.concat(failures, '\n')) end
end


S.backends = {official={},opents={}}
S.timerNames = {official={},opents={}}
S.backends.official.money = [====[[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
{$lua}
if syntaxcheck then return end
-- PlayerPtr = [Game.exe+3E2284]; live credits = HouseClass+0x1A4, storage capacity
-- = HouseClass+0x1A8, per-refinery capacity = BuildingTypeClass+0x3EC (all verified).
-- Combined credits and stored Tiberium have a 30000 minimum and may grow from
-- tiberium processing. Capacity + refinery storage are pinned huge so the refinery
-- never fills and the game stops nagging "Silos needed".
if not INFMONEY_TIMER then
  INFMONEY_TIMER = createTimer(nil)
  INFMONEY_TIMER.Interval = 200
  INFMONEY_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('official') then return end
    local ok = pcall(function()
      local p = readInteger(0x7E2284)
      if not p or p == 0 then return end
      local cr = readInteger(p + 0x1A4)
      local stored = KATZY_TS.storedTiberiumValue(p)
      if cr and stored and cr + stored &lt; 30000 then
        KATZY_TS.pin('money','official',p + 0x1A4,30000 - stored)
      end
      KATZY_TS.pin('money','official',p + 0x1A8,1000000000)
      local bcnt = readInteger(0x7E4718)
      local barr = readInteger(0x7E470C)
      if bcnt and barr and bcnt &gt; 0 and bcnt &lt; 2000 then
        local checkedTypes = {}
        for i = 0, bcnt - 1 do
          local b = readInteger(barr + i * 4)
          if b and b ~= 0 then
            local typ = readInteger(b + 0x220)
            if typ and typ ~= 0 and not checkedTypes[typ] then
              checkedTypes[typ] = KATZY_TS.pinOnce('money','official',typ + 0x3EC,1000000000)
            end
          end
        end
      end
    end)
  end
end
INFMONEY_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
{$lua}
if syntaxcheck then return end
if INFMONEY_TIMER then INFMONEY_TIMER.Enabled = false end
KATZY_TS.restore('money')
{$asm}
]====]
S.timerNames.official.money = {'INFMONEY_TIMER'}
S.backends.official.build = [====[define(BUILD_CANCEL,Game.exe+0xBECCE)
define(BUILD_NATIVE_CANCEL,Game.exe+0xBE6A0)
define(BUILD_PLAYER,Game.exe+0x3E2284)
[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}

assert(BUILD_CANCEL,E8 CD F9 FF FF)
alloc(BUILD_WAIT_CODE,256,BUILD_CANCEL)
alloc(BUILD_WAIT_ITEMS,64)
registersymbol(BUILD_WAIT_ITEMS)
label(BUILD_WAIT_SCAN)
label(BUILD_WAIT_STORE)
label(BUILD_WAIT_RETURN)
label(BUILD_WAIT_NATIVE)
BUILD_WAIT_ITEMS:
  dd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
BUILD_WAIT_CODE:
  cmp ecx,[BUILD_PLAYER]
  jne BUILD_WAIT_NATIVE
  xor eax,eax
BUILD_WAIT_SCAN:
  cmp [ecx+eax*4+0x200],ebx
  je BUILD_WAIT_STORE
  inc eax
  cmp eax,4
  jl BUILD_WAIT_SCAN
  // Unexpected factory ownership: keep the native cancellation path.
  jmp BUILD_WAIT_NATIVE
BUILD_WAIT_STORE:
  shl eax,4
  mov [BUILD_WAIT_ITEMS+eax],ebx
  mov [BUILD_WAIT_ITEMS+eax+4],esi
  mov [BUILD_WAIT_ITEMS+eax+8],edi
  mov [BUILD_WAIT_ITEMS+eax+0xC],ecx
BUILD_WAIT_RETURN:
  xor eax,eax
  ret 8
BUILD_WAIT_NATIVE:
  jmp BUILD_NATIVE_CANCEL
BUILD_CANCEL:
  call BUILD_WAIT_CODE

{$lua}
if syntaxcheck then return end
-- Factories: array [0x7E234C], count [0x7E2358] (holds ALL houses). Each
-- factory's production counter is +0x14 (counts 0 -&gt; 0x36); its own AI completes
-- + releases the unit/building at 0x36. We nudge it to 0x35 when behind and not
-- suspended, but ONLY the player's factories (+0x58 = owner house == PlayerPtr).
-- Progress is checked every second; producer readiness determines departures.
-- Failed exits retain the same completed product for a later native retry.
if not INSTABUILD_TIMER then
  INSTABUILD_TIMER = createTimer(nil)
  INSTABUILD_TIMER.Interval = 1000
  INSTABUILD_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('official') then return end
    local ok = pcall(function()
      local player = readInteger(0x7E2284)
      if not player or player == 0 then return end
      local cnt = readInteger(0x7E2358)
      if not cnt or cnt &gt; 1000 then return end
      local arr = readInteger(0x7E234C)
      if not arr or arr == 0 then return end
      for i = 0, cnt - 1 do
        local f = readInteger(arr + i * 4)
        if f and f ~= 0 then
          if readInteger(f + 0x58) == player then
            local obj = readInteger(f + 0x44)
            if obj and obj ~= 0 then
              local status = readInteger(f + 0x14)
              if status == 0x36 then KATZY_TS.retryProduction(f,obj,player) end
              if status and status &lt; 0x35 and status &gt;= 0 then
                local suspended = readInteger(f + 0x5C)
                if suspended and (suspended % 256) == 0 then
                  writeInteger(f + 0x14, 0x35)
                end
              end
            end
          end
        end
      end
    end)
  end
end
INSTABUILD_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
{$lua}
if syntaxcheck then return end
if INSTABUILD_TIMER then INSTABUILD_TIMER.Enabled = false end
{$asm}

{$asm}

BUILD_CANCEL:
  db E8 CD F9 FF FF
unregistersymbol(BUILD_WAIT_ITEMS)
dealloc(BUILD_WAIT_ITEMS)
dealloc(BUILD_WAIT_CODE)
]====]
S.timerNames.official.build = {'INSTABUILD_TIMER'}
S.backends.official.power = [====[define(INFPOWER_NODRAIN,Game.exe+0x33E60)

[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
// The drain getter (0x433e60) returns each building's power drain; the house's
// Recalc_Power sums it into Drain (+0x1FC) and goes low-power when Drain &gt; Power.
// Hook it to return 0 ONLY for the player's buildings (house +0xEC == PlayerPtr);
// enemy buildings re-run the original code so they keep their normal drain.
registersymbol(INFPOWER_NODRAIN)
alloc(INFPOWER_CAVE, 64)
label(INFPOWER_ZERO)

INFPOWER_NODRAIN:
  jmp INFPOWER_CAVE

INFPOWER_CAVE:
  mov eax, [ecx+0xEC]
  cmp eax, [7E2284]
  je INFPOWER_ZERO
  mov al, [ecx+2FC]
  test al, al
  je INFPOWER_ZERO
  jmp 433E6A

INFPOWER_ZERO:
  xor eax, eax
  ret

{$lua}
if syntaxcheck then return end
-- Also pin Power (+0x1F8) to 10000 so the power bar reads "infinite".
if not INFPOWER_TIMER then
  INFPOWER_TIMER = createTimer(nil)
  INFPOWER_TIMER.Interval = 200
  INFPOWER_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('official') then return end
    local ok, base = pcall(getAddress, "Game.exe+3E2284")
    if not ok or not base then return end
    local p = readInteger(base)
    if p and p ~= 0 then KATZY_TS.pin('power','official',p + 0x1F8,10000) end
  end
end
INFPOWER_TIMER.Enabled = true
{$asm}

[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
INFPOWER_NODRAIN:
  db 8A 81 FC 02 00 00 84 C0 74 36
unregistersymbol(INFPOWER_NODRAIN)
dealloc(INFPOWER_CAVE)

{$lua}
if syntaxcheck then return end
if INFPOWER_TIMER then INFPOWER_TIMER.Enabled = false end
KATZY_TS.restore('power')
{$asm}
]====]
S.timerNames.official.power = {'INFPOWER_TIMER'}
S.backends.official.superweapon = [====[define(CHEM_FIRE,Game.exe+0x20B86A)
define(CHEM_AUTO,Game.exe+0xBD373)
define(CHEM_WEED,Game.exe+0xBD396)

// Address definitions must be available to both On and Off.

[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
// Chemical missile: CHEM_FIRE NOPs the refuse-while-ready branch; CHEM_AUTO uses the
// recharge timer (+0x24) as the fire gate; CHEM_WEED consumes 0 weed so the bar stays full.
CHEM_FIRE+5:
  db 90 90
CHEM_AUTO:
  db 8B 46 24 85 C0 75 24
CHEM_WEED:
  db 6A 00 90

{$lua}
if syntaxcheck then return end
-- Superweapons live in a global SuperClass list: array [0x7E21F4], count [0x7E2200].
-- Each SuperClass: +0x14 = type, +0x18 = house (owner), +0x2b = ready flag (1=ready),
-- +0x24 = charge remaining (0=charged), +0x20 = charge timer (INT_MAX = not charging).
-- Writing ready=1, remaining=0, timer=0 makes the player's superweapons always-ready.
-- The Chemical Missile additionally needs the Tiberium Waste Facility FULL: the game
-- compares weed/capacity to 1.0 with an EQUALITY test, so weed must EQUAL WeedCapacity
-- ([Rules+0xF38], normally 56) exactly -- not "at least". We pin weed to exactly the
-- capacity value so the missile always reads as full. Player-only.
KATZY_TS.swRedrawPending = true
if not SWCOOLDOWN_TIMER then
  SWCOOLDOWN_TIMER = createTimer(nil)
  SWCOOLDOWN_TIMER.Interval = 200
  SWCOOLDOWN_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('official') then return end
    local ok = pcall(function()
      local player = readInteger(0x7E2284)
      if not player or player == 0 then return end
      local redraw = KATZY_TS.swRedrawPending == true
      local rules = readInteger(0x74C488)
      local cap = 56
      if rules and rules ~= 0 then
        local c = readInteger(rules + 0xF38)
        if c and c ~= 0 then cap = c end
      end
      writeInteger(player + 0x1AC, cap)
      writeInteger(player + 0x1B0, 0)
      writeInteger(player + 0x1B4, 0)
      writeInteger(player + 0x1B8, 0)
      local cnt = readInteger(0x7E2200)
      if not cnt or cnt &gt; 200 then return end
      local arr = readInteger(0x7E21F4)
      if not arr or arr == 0 then return end
      for i = 0, cnt - 1 do
        local sc = readInteger(arr + i * 4)
        if sc and sc ~= 0 then
          if readInteger(sc + 0x18) == player then
            -- The manual ready transition bypasses SuperClass::AI's redraw result.
            if readBytes(sc + 0x2B,1) ~= 1 or readInteger(sc + 0x24) ~= 0 then
              redraw = true
            end
            writeBytes(sc + 0x2B, 1)
            writeInteger(sc + 0x24, 0)
            writeInteger(sc + 0x20, 0)
          end
        end
      end
      if redraw then
        KATZY_TS.swRedrawPending = not KATZY_TS.refreshSuperweaponSidebar()
      end
    end)
  end
end
SWCOOLDOWN_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
{$lua}
if syntaxcheck then return end
if SWCOOLDOWN_TIMER then SWCOOLDOWN_TIMER.Enabled = false end
{$asm}
CHEM_FIRE+5:
  db 75 64
CHEM_AUTO:
  db 8A 46 2B 84 C0 75 24
CHEM_WEED:
  db 50 8B CB
]====]
S.timerNames.official.superweapon = {'SWCOOLDOWN_TIMER'}
S.backends.official.ammo = [====[define(INFAMMO_LAND,Game.exe+0x8A1F)
define(INFAMMO_DEC1,Game.exe+0xBAAA)
define(INFAMMO_DEC2,Game.exe+0xBB0F)
define(INFAMMO_DEC3,Game.exe+0xC484)

[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
// Aircraft ammo drops in two ways: (1) 'mov [esi+0x134],0' on land at 0x408A1F, and
// (2) three 'dec eax; mov [esi+0x134],eax' decrements when firing at 0x40BAAA,
// 0x40BB0F and 0x40C484. For the PLAYER's aircraft (house +0xEC == PlayerPtr
// [0x7E2284]) we skip all of these so ammo stays full; enemy aircraft keep the
// original code.
registersymbol(INFAMMO_LAND)
registersymbol(INFAMMO_DEC1)
registersymbol(INFAMMO_DEC2)
registersymbol(INFAMMO_DEC3)
alloc(INFAMMO_CAVE1, 64)
alloc(INFAMMO_CAVE2, 64)
alloc(INFAMMO_CAVE3, 64)
alloc(INFAMMO_CAVE4, 64)
label(INFAMMO_L_ENEMY)
label(INFAMMO_D1_ENEMY)
label(INFAMMO_D2_ENEMY)
label(INFAMMO_D3_ENEMY)

INFAMMO_LAND:
  jmp INFAMMO_CAVE1
  nop
INFAMMO_DEC1:
  jmp INFAMMO_CAVE2
  nop
INFAMMO_DEC2:
  jmp INFAMMO_CAVE3
  nop
INFAMMO_DEC3:
  jmp INFAMMO_CAVE4
  nop

INFAMMO_CAVE1:
  push edx
  mov edx, [esi+0xEC]
  cmp edx, [0x7E2284]
  pop edx
  jne INFAMMO_L_ENEMY
  jmp 0x408A2C
INFAMMO_L_ENEMY:
  mov [esi+0x134], edi
  jmp 0x408A25

INFAMMO_CAVE2:
  push edx
  mov edx, [esi+0xEC]
  cmp edx, [0x7E2284]
  pop edx
  jne INFAMMO_D1_ENEMY
  jmp 0x40BAB0
INFAMMO_D1_ENEMY:
  mov [esi+0x134], eax
  jmp 0x40BAB0

INFAMMO_CAVE3:
  push edx
  mov edx, [esi+0xEC]
  cmp edx, [0x7E2284]
  pop edx
  jne INFAMMO_D2_ENEMY
  jmp 0x40BB15
INFAMMO_D2_ENEMY:
  mov [esi+0x134], eax
  jmp 0x40BB15

INFAMMO_CAVE4:
  push edx
  mov edx, [esi+0xEC]
  cmp edx, [0x7E2284]
  pop edx
  jne INFAMMO_D3_ENEMY
  jmp 0x40C48A
INFAMMO_D3_ENEMY:
  mov [esi+0x134], eax
  jmp 0x40C48A

[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
INFAMMO_LAND:
  db 89 BE 34 01 00 00
INFAMMO_DEC1:
  db 89 86 34 01 00 00
INFAMMO_DEC2:
  db 89 86 34 01 00 00
INFAMMO_DEC3:
  db 89 86 34 01 00 00
unregistersymbol(INFAMMO_LAND)
unregistersymbol(INFAMMO_DEC1)
unregistersymbol(INFAMMO_DEC2)
unregistersymbol(INFAMMO_DEC3)
dealloc(INFAMMO_CAVE1)
dealloc(INFAMMO_CAVE2)
dealloc(INFAMMO_CAVE3)
dealloc(INFAMMO_CAVE4)
]====]
S.timerNames.official.ammo = {}
S.backends.official.visualfog = [====[define(VISUAL_RADAR,Game.exe+0x1BB284)
define(VISUAL_SHROUD,Game.exe+0x5638D)
define(VISUAL_FOG,Game.exe+0x563E6)
[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
// CellClass::Draw_Shroud_And_Fog: the normal cache writes have already run.
// Choose transparent frame zero only in the local drawing register.
// No changes to mapped/visible/discovery flags, caches, or game logic.
assert(VISUAL_RADAR,84 C0)
assert(VISUAL_SHROUD,0F BE C0)
assert(VISUAL_FOG,0F BE C0)
VISUAL_SHROUD:
  db 31 C0 90
VISUAL_FOG:
  db 31 C0 90

// RadarClass::Plot_Radar_Pixel local shadow result only.
VISUAL_RADAR:
  db 30 C0
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
VISUAL_SHROUD:
  db 0F BE C0
VISUAL_FOG:
  db 0F BE C0

VISUAL_RADAR:
  db 84 C0
]====]
S.timerNames.official.visualfog = {}
S.backends.official.fog = [====[define(FOG_CTOR,Game.exe+0x51841)
define(FOG_INIT,Game.exe+0x12BC20)
define(FOG_LOAD,Game.exe+0x5993D)

[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
// Default cell construction and the native fog-initialization loop cover
// fresh maps and loaded maps. Keep the placement-cursor code untouched.
assert(FOG_CTOR,81 E2 00 00 00 F8)
assert(FOG_INIT,F6 80 A4 00 00 00 20 75 07 8B C8 E8 D0 0A F3 FF)
assert(FOG_LOAD,89 34 B8 8D 46 1C)
alloc(FOG_LOADC,64,FOG_LOAD)
label(FOG_LOADB)
alloc(FOG_CTORC,64,FOG_CTOR)
label(FOG_CTORB)

// CellClass::Load has already restored the cell's saved fields here.
// Reveal it before the loader inserts it into the current map's cell array.
// Preserve flags and replay both complete displaced instructions.
FOG_LOADC:
  pushfd
  or dword ptr [esi+0xA4],0x3C
  popfd
  mov [eax+edi*4],esi
  lea eax,[esi+0x1C]
  jmp FOG_LOADB

FOG_LOAD:
  jmp FOG_LOADC
  nop
FOG_LOADB:

FOG_CTORC:
  and edx,0xF8000000
  or edx,0x3C
  jmp FOG_CTORB

FOG_CTOR:
  jmp FOG_CTORC
  nop
FOG_CTORB:

FOG_INIT:
  db 83 88 A4 00 00 00 3C
  db 90 90 90 90 90 90 90 90 90

{$lua}
if syntaxcheck then return end
-- One-time reveal of the already-loaded map (data-only, PlayerPtr-gated, one-shot).
-- FOG_CTOR keeps new cells revealed on level advance, so no repeating timer.
pcall(function()
  if not KATZY_TS.context('official') then return end
  local player = readInteger(0x7E2284)
  if not player or player == 0 then return end
  local arr = readInteger(0x7484B0)
  local cnt = readInteger(0x7484B4)
  if not arr or arr == 0 then return end
  if not cnt or cnt &lt;= 0 or cnt &gt; 0x40000 then return end
  for i = 0, cnt - 1 do
    local cell = readInteger(arr + i * 4)
    if cell and cell ~= 0 then
      local s = readInteger(cell + 0xA4)
      if s and (s &amp; 0x3C) ~= 0x3C then
        writeInteger(cell + 0xA4, s | 0x3C)
      end
    end
  end
end)
{$asm}

[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('official')
{$asm}
FOG_LOAD:
  db 89 34 B8 8D 46 1C
FOG_CTOR:
  db 81 E2 00 00 00 F8
FOG_INIT:
  db F6 80 A4 00 00 00 20 75 07 8B C8 E8 D0 0A F3 FF

dealloc(FOG_LOADC)
dealloc(FOG_CTORC)

{$lua}
if syntaxcheck then return end
-- Building/unit sight recovery on toggle-off: clear the forced reveal, then
-- re-reveal each player object's actual per-type sight. Cell visibility is
-- cell+0xA4 (mapped/visible/fog-visible/fog-mapped = 0x3C). Object lists are {vector base,
-- count offset, type offset}: Infantry 0x7E2300/+0x350, Aircraft 0x7E4058/+0x354,
-- Buildings 0x7E4708/+0x220, Units 0x7B3458/+0x360. Position (leptons) is
-- obj+0x40 / obj+0x44; SightRange is type+0x320; house obj+0xEC == PlayerPtr.
pcall(function()
  if not KATZY_TS.context('official') then return end
  local player = readInteger(0x7E2284)
  if not player or player == 0 then return end
  local arr = readInteger(0x7484B0)
  local cnt = readInteger(0x7484B4)
  if not arr or arr == 0 then return end
  if not cnt or cnt &lt;= 0 or cnt &gt; 0x40000 then return end
  for i = 0, cnt - 1 do
    local cell = readInteger(arr + i * 4)
    if cell and cell ~= 0 then
      local s = readInteger(cell + 0xA4)
      if s then writeInteger(cell + 0xA4, s &amp; 0xFFFFFFC3) end
    end
  end
  local function reveal(cx, cy)
    if cx &gt;= 0 and cy &gt;= 0 and cx &lt; 512 and cy &lt; 512 then
      local index = cy * 512 + cx
      if index &gt;= cnt then return end
      local cell = readInteger(arr + index * 4)
      if cell and cell ~= 0 then
        local s = readInteger(cell + 0xA4)
        if s then writeInteger(cell + 0xA4, s | 0x3C) end
      end
    end
  end
  local lists = {
    {0x7E2300, 8,    0x350},
    {0x7E4058, 0x10, 0x354},
    {0x7E4708, 0x10, 0x220},
    {0x7B3458, 0x10, 0x360},
  }
  for li = 1, 4 do
    local vec = readInteger(lists[li][1] + 4)
    local n = readInteger(lists[li][1] + lists[li][2])
    if vec and vec ~= 0 and n and n &gt; 0 and n &lt; 20000 then
      for j = 0, n - 1 do
        local obj = readInteger(vec + j * 4)
        if obj and obj ~= 0 and readInteger(obj + 0xEC) == player then
          local typ = readInteger(obj + lists[li][3])
          local px = readInteger(obj + 0x40)
          local py = readInteger(obj + 0x44)
          if typ and typ ~= 0 and px and py then
            local sight = readInteger(typ + 0x320)
            if sight and sight &gt; 0 and sight &lt; 24 then
              local bx = px // 256
              local by = py // 256
              local r2 = sight * sight
              for dy = -sight, sight do
                for dx = -sight, sight do
                  if dx * dx + dy * dy &lt;= r2 then
                    reveal(bx + dx, by + dy)
                  end
                end
              end
            end
          end
        end
      end
    end
  end
end)
{$asm}
]====]
S.timerNames.official.fog = {}
S.backends.opents.money = [====[[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
{$lua}
if syntaxcheck then return end
if OT_MONEY_TIMER then OT_MONEY_TIMER.destroy(); OT_MONEY_TIMER=nil end
if not OT_MONEY_TIMER then
  OT_MONEY_TIMER = createTimer(nil)
  OT_MONEY_TIMER.Interval = 200
  OT_MONEY_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('opents') then return end
    pcall(function()
      local base = getAddress("Game.exe")
      if not base or base == 0 then return end
      local player = readInteger(base + 0x602958)
      if not player or player == 0 then return end
      local cr = readInteger(player + 0x17C)
      local stored = KATZY_TS.storedTiberiumValueOpenTS(player)
      if cr and stored and cr &lt; 0x80000000 and cr+stored &lt; 30000 then
        KATZY_TS.pin('ot_money','opents',player + 0x17C,30000-stored)
      end
      KATZY_TS.pin('ot_money','opents',player + 0x180,1000000000)
    end)
  end
end
OT_MONEY_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
{$lua}
if syntaxcheck then return end
if OT_MONEY_TIMER then OT_MONEY_TIMER.Enabled = false end
KATZY_TS.restore('ot_money')
{$asm}
]====]
S.timerNames.opents.money = {'OT_MONEY_TIMER'}
S.backends.opents.build = [====[define(OT_BUILD_CANCEL,Game.exe+0xC6A83)
define(OT_BUILD_NATIVE_CANCEL,Game.exe+0xBE540)
define(OT_BUILD_PLAYER,Game.exe+0x602958)
[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}

assert(OT_BUILD_CANCEL,E8 B8 7A FF FF)
alloc(OT_BUILD_WAIT_CODE,256,OT_BUILD_CANCEL)
alloc(OT_BUILD_WAIT_ITEMS,64)
registersymbol(OT_BUILD_WAIT_ITEMS)
label(OT_BUILD_SCAN)
label(OT_BUILD_STORE)
label(OT_BUILD_NATIVE)
OT_BUILD_WAIT_ITEMS:
 dd 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
OT_BUILD_WAIT_CODE:
 cmp ecx,[OT_BUILD_PLAYER]
 jne OT_BUILD_NATIVE
 // OpenTS Place_Object retains factory in its frame, product EDI, builder ESI.
 mov edx,[ebp-8]
 xor eax,eax
OT_BUILD_SCAN:
 cmp [ecx+eax*4+0x1D8],edx
 je OT_BUILD_STORE
 inc eax
 cmp eax,4
 jl OT_BUILD_SCAN
 jmp OT_BUILD_NATIVE
OT_BUILD_STORE:
 shl eax,4
 mov [OT_BUILD_WAIT_ITEMS+eax],edx
 mov [OT_BUILD_WAIT_ITEMS+eax+4],edi
 mov [OT_BUILD_WAIT_ITEMS+eax+8],esi
 mov [OT_BUILD_WAIT_ITEMS+eax+0xC],ecx
 xor eax,eax
 ret 8
OT_BUILD_NATIVE:
 jmp OT_BUILD_NATIVE_CANCEL
OT_BUILD_CANCEL:
 call OT_BUILD_WAIT_CODE

{$lua}
if syntaxcheck then return end
if OT_BUILD_TIMER then OT_BUILD_TIMER.destroy(); OT_BUILD_TIMER=nil end
if not OT_BUILD_TIMER then
  OT_BUILD_TIMER = createTimer(nil)
  OT_BUILD_TIMER.Interval = 100
  OT_BUILD_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('opents') then return end
    pcall(function()
      local base = getAddress("Game.exe")
      if not base or base == 0 then return end
      local player = readInteger(base + 0x602958)
      if not player or player == 0 then return end
      local fvec = readInteger(base + 0x514D10)
      local fcnt = readInteger(base + 0x514D18)
      if fvec and fvec ~= 0 and fcnt and fcnt &gt; 0 and fcnt &lt; 1000 then
        for i = 0, fcnt - 1 do
          local f = readInteger(fvec + i * 4)
          if f and f ~= 0 then
            if readInteger(f + 0x4C) == player then
              local obj = readInteger(f + 0x38)
              if obj and obj ~= 0 then
                local stage = readInteger(f + 0x10)
                if stage == 54 then -- completed products are natively suspended
                  KATZY_TS.retryProductionOpenTS(f,obj,player)
                end
                if stage and stage &lt; 53 and stage &gt;= 0 then
                  local suspended = readInteger(f + 0x50)
                  if suspended and (suspended % 256) == 0 then
                    if readInteger(base + 0x602958) == 0 then return end
                    writeInteger(f + 0x10, 53)
                  end
                end
              end
            end
          end
        end
      end
    end)
  end
end
OT_BUILD_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
{$lua}
if syntaxcheck then return end
if OT_BUILD_TIMER then OT_BUILD_TIMER.Enabled = false end
{$asm}


OT_BUILD_CANCEL:
 db E8 B8 7A FF FF
unregistersymbol(OT_BUILD_WAIT_ITEMS)
dealloc(OT_BUILD_WAIT_ITEMS)
dealloc(OT_BUILD_WAIT_CODE)
]====]
S.timerNames.opents.build = {'OT_BUILD_TIMER'}
S.backends.opents.power = [====[define(OT_PWR1,Game.exe+0xBEE16)
define(OT_PWR2,Game.exe+0xB883F)
define(OT_PWR3,Game.exe+0xC7A21)
define(OT_PWR4,Game.exe+0xCA528)

[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}

alloc(OT_PWR1C,32,OT_PWR1)
alloc(OT_PWR2C,32,OT_PWR2)
alloc(OT_PWR3C,32,OT_PWR3)
alloc(OT_PWR4C,32,OT_PWR4)

label(OT_PWR1R)
label(OT_PWR2R)
label(OT_PWR3R)
label(OT_PWR4R)
label(OT_PWR1B)
label(OT_PWR2B)
label(OT_PWR3B)
label(OT_PWR4B)

OT_PWR1C:
  cmp ecx,[Game.exe+0x602958]
  je OT_PWR1R
  add [ecx+0x1D4],eax
OT_PWR1R:
  jmp OT_PWR1B

OT_PWR2C:
  cmp edi,[Game.exe+0x602958]
  je OT_PWR2R
  add [edi+0x1D4],eax
OT_PWR2R:
  jmp OT_PWR2B

OT_PWR3C:
  cmp esi,[Game.exe+0x602958]
  je OT_PWR3R
  add [esi+0x1D4],eax
OT_PWR3R:
  jmp OT_PWR3B

OT_PWR4C:
  cmp edi,[Game.exe+0x602958]
  je OT_PWR4R
  add [edi+0x1D4],eax
OT_PWR4R:
  jmp OT_PWR4B

OT_PWR1:
  jmp OT_PWR1C
  nop
OT_PWR1B:

OT_PWR2:
  jmp OT_PWR2C
  nop
OT_PWR2B:

OT_PWR3:
  jmp OT_PWR3C
  nop
OT_PWR3B:

OT_PWR4:
  jmp OT_PWR4C
  nop
OT_PWR4B:

{$lua}
if syntaxcheck then return end
if not OT_POWER_TIMER then
  OT_POWER_TIMER = createTimer(nil)
  OT_POWER_TIMER.Interval = 200
  OT_POWER_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('opents') then return end
    pcall(function()
      local base = getAddress("Game.exe")
      if not base or base == 0 then return end
      local player = readInteger(base + 0x602958)
      if not player or player == 0 then return end
      KATZY_TS.pin('ot_power','opents',player + 0x1D0,10000)
      KATZY_TS.pin('ot_power','opents',player + 0x1D4,0)
    end)
  end
end
OT_POWER_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
{$lua}
if syntaxcheck then return end
if OT_POWER_TIMER then OT_POWER_TIMER.Enabled = false end
KATZY_TS.restore('ot_power')
{$asm}

OT_PWR1:
  db 01 81 D4 01 00 00
OT_PWR2:
  db 01 87 D4 01 00 00
OT_PWR3:
  db 01 86 D4 01 00 00
OT_PWR4:
  db 01 87 D4 01 00 00

dealloc(OT_PWR1C)
dealloc(OT_PWR2C)
dealloc(OT_PWR3C)
dealloc(OT_PWR4C)
]====]
S.timerNames.opents.power = {'OT_POWER_TIMER'}
S.backends.opents.superweapon = [====[[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
{$lua}
if syntaxcheck then return end
KATZY_TS.otSwRedrawPending = true
if OT_SW_TIMER then OT_SW_TIMER.destroy(); OT_SW_TIMER=nil end
if not OT_SW_TIMER then
  OT_SW_TIMER = createTimer(nil)
  OT_SW_TIMER.Interval = 200
  OT_SW_TIMER.OnTimer = function(t)
    if not KATZY_TS.context('opents') then return end
    pcall(function()
      local base = getAddress("Game.exe")
      if not base or base == 0 then return end
      local player = readInteger(base + 0x602958)
      if not player or player == 0 then return end
      local redraw = KATZY_TS.otSwRedrawPending == true
      local swv = readInteger(base + 0x514E60)
      local swc = readInteger(base + 0x514E68)
      if swv and swv ~= 0 and swc and swc &gt; 0 and swc &lt; 200 then
        for i = 0, swc - 1 do
          local sc = readInteger(swv + i * 4)
          if sc and sc ~= 0 then
            if readInteger(base + 0x602958) == 0 then return end
            local cls = readInteger(sc + 0x10)
            local hse = readInteger(sc + 0x14)
            if cls and cls ~= 0 and hse and hse == player then
              if readBytes(sc+0x27,1)~=1 or readInteger(sc+0x20)~=0 then redraw=true end
              writeBytes(sc + 0x27, 1)
              writeInteger(sc + 0x18, 0)
              writeInteger(sc + 0x20, 0)
            end
          end
        end
      end
      writeInteger(player + 0x184, 56)
      writeInteger(player + 0x188, 0)
      writeInteger(player + 0x18C, 0)
      writeInteger(player + 0x190, 0)
      if redraw then KATZY_TS.otSwRedrawPending=not KATZY_TS.refreshSuperweaponSidebarOpenTS() end
    end)
  end
end
OT_SW_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
{$lua}
if syntaxcheck then return end
if OT_SW_TIMER then OT_SW_TIMER.Enabled = false end
{$asm}
]====]
S.timerNames.opents.superweapon = {'OT_SW_TIMER'}
S.backends.opents.ammo = [====[define(OT_AMMO1,Game.exe+0x9FB0)
define(OT_AMMO2,Game.exe+0xB0E7)
define(OT_AMMO3,Game.exe+0xB182)
define(OT_AMMO4,Game.exe+0xBAE3)

[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
OT_AMMO1:
  db 90 90 90 90 90 90
OT_AMMO2:
  db 90 90 90 90 90 90
OT_AMMO3:
  db 90 90 90 90 90 90
OT_AMMO4:
  db 90 90 90 90 90 90
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
OT_AMMO1:
  db FF 8F 34 01 00 00
OT_AMMO2:
  db FF 8F 34 01 00 00
OT_AMMO3:
  db FF 8F 34 01 00 00
OT_AMMO4:
  db FF 8F 34 01 00 00
]====]
S.timerNames.opents.ammo = {}
S.backends.opents.visualfog = [====[define(OT_VISUAL_RADAR,Game.exe+0x1AC47D)
define(OT_VISUAL_SHROUD,Game.exe+0x5734E)
define(OT_VISUAL_FOG,Game.exe+0x573A4)
[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
// CellClass::Draw_Shroud_And_Fog: the normal cache writes have already run.
// Choose transparent frame zero only in the local drawing register.
// No changes to mapped/visible/discovery flags, caches, or game logic.
assert(OT_VISUAL_RADAR,84 C0)
assert(OT_VISUAL_SHROUD,0F BE C0)
assert(OT_VISUAL_FOG,0F BE C0)
OT_VISUAL_SHROUD:
  db 31 C0 90
OT_VISUAL_FOG:
  db 31 C0 90

// RadarClass::Plot_Radar_Pixel local shadow result only.
OT_VISUAL_RADAR:
  db 30 C0
[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
OT_VISUAL_SHROUD:
  db 0F BE C0
OT_VISUAL_FOG:
  db 0F BE C0

OT_VISUAL_RADAR:
  db 84 C0
]====]
S.timerNames.opents.visualfog = {}
S.backends.opents.fog = [====[define(OT_FOG_CTOR,Game.exe+0x54AD8)
define(OT_FOG_SMAP,Game.exe+0x125FB0)
define(OT_FOG_SCELL,Game.exe+0x7F3F0)
define(OT_FOG_FCELL,Game.exe+0x7B010)
define(OT_FOG_IFOG,Game.exe+0x117610)

[ENABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}

alloc(OT_FOG_CTORC,32,OT_FOG_CTOR)
label(OT_FOG_CTORB)

OT_FOG_CTORC:
  db 81 A6 A0 00 00 00 00 00 00 F8
  db 81 8E A0 00 00 00 3C 00 00 00
  jmp OT_FOG_CTORB

OT_FOG_CTOR:
  jmp OT_FOG_CTORC
  nop
  nop
  nop
  nop
  nop
OT_FOG_CTORB:

OT_FOG_SMAP:
  db 83 88 A0 00 00 00 3C
OT_FOG_SCELL:
  db C2 04 00
OT_FOG_FCELL:
  db C2 04 00
OT_FOG_IFOG:
  db 83 88 A0 00 00 00 3C
  db 90 90 90 90 90 90 90 90 90

{$lua}
if syntaxcheck then return end
pcall(function()
  if KATZY_TS.context('opents') then
    local base = getAddress("Game.exe")
    if base and base ~= 0 then
      local player = readInteger(base + 0x602958)
      if player and player ~= 0 then
        local arr = readInteger(base + 0x52D144)
        local cnt = readInteger(base + 0x52D148)
        if arr and arr ~= 0 and cnt and cnt &gt; 0 then
          if cnt &gt; 0x40000 then cnt = 0x40000 end
          for i = 0, cnt - 1 do
            local cell = readInteger(arr + i * 4)
            if cell and cell ~= 0 then
              local s = readInteger(cell + 0xA0)
              if s ~= nil and (s &amp; 0x3C) ~= 0x3C then
                writeInteger(cell + 0xA0, s | 0x3C)
              end
            end
          end
        end
      end
    end
  end
end)
{$asm}

[DISABLE]
{$lua}
if syntaxcheck then return end
KATZY_TS.requireEngine('opents')
{$asm}
OT_FOG_CTOR:
  db 81 A6 A0 00 00 00 00 00 00 F8
OT_FOG_SMAP:
  db 83 A0 A0 00 00 00 C3
OT_FOG_SCELL:
  db 55 8B EC
OT_FOG_FCELL:
  db 55 8B EC
OT_FOG_IFOG:
  db F6 80 A0 00 00 00 20 75 07 8B C8 E8 90 0B F4 FF

{$lua}
if syntaxcheck then return end
pcall(function()
  if KATZY_TS.context('opents') then
    local base = getAddress("Game.exe")
    if base and base ~= 0 then
      local player = readInteger(base + 0x602958)
      if player and player ~= 0 then
        local arr = readInteger(base + 0x52D144)
        local cnt = readInteger(base + 0x52D148)
        if arr and arr ~= 0 and cnt and cnt &gt; 0 then
          if cnt &gt; 0x40000 then cnt = 0x40000 end
          local function reveal(cellx, celly)
            if cellx &gt;= 0 and celly &gt;= 0 and cellx &lt; 512 and celly &lt; 512 then
              local idx = celly * 512 + cellx
              local cell = readInteger(arr + idx * 4)
              if cell and cell ~= 0 then
                local s = readInteger(cell + 0xA0)
                if s ~= nil and (s &amp; 0x3C) ~= 0x3C then
                  writeInteger(cell + 0xA0, s | 0x3C)
                end
              end
            end
          end
          for i = 0, cnt - 1 do
            local cell = readInteger(arr + i * 4)
            if cell and cell ~= 0 then
              local s = readInteger(cell + 0xA0)
              if s ~= nil then
                writeInteger(cell + 0xA0, s &amp; 0xFFFFFFC3)
              end
            end
          end
          local lists = {{0x514CA0, 0x350}, {0x514CD0, 0x360}, {0x514CB0, 0x230}, {0x514CC0, 0x354}}
          for li = 1, 4 do
            local vec = readInteger(base + lists[li][1])
            local n = readInteger(base + lists[li][1] + 8)
            if vec and vec ~= 0 and n and n &gt; 0 and n &lt; 20000 then
              for j = 0, n - 1 do
                local obj = readInteger(vec + j * 4)
                if obj and obj ~= 0 then
                  if readInteger(obj + 0xEC) == player then
                    local typ = readInteger(obj + lists[li][2])
                    local cx = readInteger(obj + 0x3C)
                    local cy = readInteger(obj + 0x40)
                    if typ and typ ~= 0 and cx and cy then
                      local sight = readInteger(typ + 0x2b8)
                      if sight and sight &gt; 0 and sight &lt; 24 then
                        local bx = cx // 256
                        local by = cy // 256
                        local r2 = sight * sight
                        for dy = -sight, sight do
                          for dx = -sight, sight do
                            if dx * dx + dy * dy &lt;= r2 then
                              reveal(bx + dx, by + dy)
                            end
                          end
                        end
                      end
                    end
                  end
                end
              end
            end
          end
        end
      end
    end
  end
end)
{$asm}

dealloc(OT_FOG_CTORC)
]====]
S.timerNames.opents.fog = {}
-- One user-facing option, with the original engine-specific scripts retained.
S.active = S.active or {}
local rawContext = S.context
function S.context(engine)
  local ctx = rawContext(engine)
  if ctx and S.bound and (ctx.pid ~= S.bound.pid or ctx.base ~= S.bound.base or engine ~= S.bound.engine) then
    return nil
  end
  return ctx
end

function S.detect()
  for _, engine in ipairs({'official','opents'}) do
    local ctx = rawContext(engine)
    if ctx then ctx.engine = engine; return ctx end
  end
end

function S.stopTimers(feature, engine)
  local names = S.timerNames[engine][feature] or {}
  for _, name in ipairs(names) do
    if _G[name] then _G[name].Enabled = false end
  end
end

function S.toggle(feature, enabled)
  local item = S.active[feature]
  if enabled then
    if item then return end
    local ctx = S.detect()
    if not ctx then error('Attach to a supported Official or OpenTS Game.exe before enabling options.') end
    if S.bound and (S.bound.pid ~= ctx.pid or S.bound.base ~= ctx.base or S.bound.engine ~= ctx.engine) then
      error('Clear the previous game state before enabling options in another process.')
    end
    S.bound = ctx
    if feature=='fog' or feature=='visualfog' then
      local other=feature=='fog' and 'visualfog' or 'fog'
      S.toggle(other,false)
      local record=getAddressList().getMemoryRecordByID(other=='fog' and 13 or 17)
      if record and record.Active then record.Active=false end
      if record and record.Active then error('The previous reveal mode could not be disabled.') end
      S.bound=ctx
    end
    if feature=='build' and ctx.engine=='opents' then S.recoverOpenTSBuild(true) end
    local script = S.backends[ctx.engine][feature]
    local called, ok, info = pcall(autoAssemble, script, false)
    if not called or not ok then
      S.stopTimers(feature, ctx.engine)
      if not next(S.active) then S.bound = nil end
      error('Could not enable '..feature..': '..tostring(called and info or ok))
    end
    S.active[feature] = {engine=ctx.engine,pid=ctx.pid,base=ctx.base,script=script,disableInfo=info}
  elseif item then
    -- Stop writers even when the original process has already gone away.
    S.stopTimers(feature, item.engine)
    local ctx = rawContext(item.engine)
    if ctx and ctx.pid == item.pid and ctx.base == item.base then
      local called, ok, message = pcall(autoAssemble, item.script, false, item.disableInfo)
      if not called or not ok then
        error('Could not disable '..feature..': '..tostring(called and message or ok)..'. Cleanup remains available for retry.')
      end
    else
      -- Never replay a previous process's code restoration into its replacement.
      local savedKey = item.engine == 'opents' and 'ot_'..feature or feature
      S.saved[savedKey] = nil
    end
    S.active[feature] = nil
    if not next(S.active) then S.bound = nil end
  end
end

function S.detachExpired()
  if not S.bound then return end
  local current = S.detect()
  if current and current.pid == S.bound.pid and current.base == S.bound.base and current.engine == S.bound.engine then return end
  for feature, item in pairs(S.active) do S.stopTimers(feature,item.engine) end
  -- No game writes: discard obsolete bookkeeping before unchecking records.
  S.active = {}; S.saved = {}; S.bound = nil
  for _, id in ipairs({11,12,13,14,15,16,17}) do
    local record = getAddressList().getMemoryRecordByID(id)
    if record and record.Active then record.Active = false end
  end
end

if S.attachTimer then S.attachTimer.destroy() end
S.attachTimer = createTimer(nil,false)
S.attachTimer.Interval = 500
S.attachTimer.OnTimer = function()
  S.detachExpired()
  local pid = getProcessIDFromProcessName('Game.exe')
  if pid and pid ~= 0 and getOpenedProcessID() ~= pid then openProcess(pid) end
  local ctx = S.detect()
  ENGINE = ctx and ctx.engine or 'unknown'
  local record = getAddressList().getMemoryRecordByID(1)
  if record then
    local label = ctx and (ctx.engine == 'official' and 'Official 2.03 TUC' or 'OpenTS') or 'waiting for a supported Game.exe'
    local description = 'Auto-Attach and Version Check - '..label
    if record.Description ~= description then record.Description = description end
  end
end
S.attachTimer.Enabled = true

S.recoverySites = {official={},opents={}}
S.recoverySites.official[#S.recoverySites.official+1] = {name="INFPOWER_NODRAIN",feature="power",offset=212576,original="8A 81 FC 02 00 00 84 C0 74 36"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="CHEM_FIRE+5",feature="superweapon",offset=2144367,original="75 64",patched="90 90"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="CHEM_AUTO",feature="superweapon",offset=775027,original="8A 46 2B 84 C0 75 24",patched="8B 46 24 85 C0 75 24"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="CHEM_WEED",feature="superweapon",offset=775062,original="50 8B CB",patched="6A 00 90"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="INFAMMO_LAND",feature="ammo",offset=35359,original="89 BE 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="INFAMMO_DEC1",feature="ammo",offset=47786,original="89 86 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="INFAMMO_DEC2",feature="ammo",offset=47887,original="89 86 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="INFAMMO_DEC3",feature="ammo",offset=50308,original="89 86 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="FOG_LOAD",feature="fog",offset=366909,original="89 34 B8 8D 46 1C",cave="9C 83 8E A4 00 00 00 3C 9D 89 34 B8 8D 46 1C"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="FOG_CTOR",feature="fog",offset=333889,original="81 E2 00 00 00 F8",cave="81 E2 00 00 00 F8 83 CA 3C"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="FOG_INIT",feature="fog",offset=1227808,original="F6 80 A4 00 00 00 20 75 07 8B C8 E8 D0 0A F3 FF",patched="83 88 A4 00 00 00 3C 90 90 90 90 90 90 90 90 90"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="legacy fog cursor",feature="fog",offset=485652,original="83 E1 FD",patched="83 C9 02"}
S.recoverySites.official[#S.recoverySites.official+1] = {name="legacy fog loading constructor",feature="fog",offset=331631,original="C7 06 98 CE 6C 00",cave="C7 06 98 CE 6C 00 C7 86 A4 00 00 00 06 00 00 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_PWR1",feature="power",offset=781846,original="01 81 D4 01 00 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_PWR2",feature="power",offset=755775,original="01 87 D4 01 00 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_PWR3",feature="power",offset=817697,original="01 86 D4 01 00 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_PWR4",feature="power",offset=828712,original="01 87 D4 01 00 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_AMMO1",feature="ammo",offset=40880,original="FF 8F 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_AMMO2",feature="ammo",offset=45287,original="FF 8F 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_AMMO3",feature="ammo",offset=45442,original="FF 8F 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_AMMO4",feature="ammo",offset=47843,original="FF 8F 34 01 00 00",patched="90 90 90 90 90 90"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_FOG_CTOR",feature="fog",offset=346840,original="81 A6 A0 00 00 00 00 00 00 F8",cave="81 A6 A0 00 00 00 00 00 00 F8 81 8E A0 00 00 00 3C 00 00 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_FOG_SMAP",feature="fog",offset=1204144,original="83 A0 A0 00 00 00 C3",patched="83 88 A0 00 00 00 3C"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_FOG_SCELL",feature="fog",offset=521200,original="55 8B EC",patched="C2 04 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_FOG_FCELL",feature="fog",offset=503824,original="55 8B EC",patched="C2 04 00"}
S.recoverySites.opents[#S.recoverySites.opents+1] = {name="OT_FOG_IFOG",feature="fog",offset=1144336,original="F6 80 A0 00 00 00 20 75 07 8B C8 E8 90 0B F4 FF",patched="83 88 A0 00 00 00 3C 90 90 90 90 90 90 90 90 90"}
-- Recover only exact, recognizable patches. Never free an orphan code cave:
-- without its allocation metadata another owner cannot safely be ruled out.
local function equalBytes(a,b)
  if type(a)~='table' or #a~=#b then return false end
  for i=1,#b do if a[i]~=b[i] then return false end end
  return true
end
local function hexBytes(s)
  local bytes={}
  for b in s:gmatch('%x%x') do bytes[#bytes+1]=tonumber(b,16) end
  return bytes
end
local function signedJump(address)
  local bytes=readBytes(address,5,true)
  if not bytes or bytes[1]~=0xE9 then return nil end
  local n=bytes[2]+bytes[3]*256+bytes[4]*65536+bytes[5]*16777216
  if n&gt;=0x80000000 then n=n-0x100000000 end
  return address+5+n
end
function S.recoverPatches()
  local ctx=S.detect()
  if not ctx then error('Clear State could not identify the attached game. No leftover patches were changed.') end
  local failures={}
  local fogChanged=false
  for _,site in ipairs(S.recoverySites[ctx.engine]) do
    local address=ctx.base+site.offset
    local original=hexBytes(site.original)
    local current=readBytes(address,#original,true)
    if not equalBytes(current,original) then
      local recognized=site.patched and equalBytes(current,hexBytes(site.patched))
      if not recognized and site.cave then
        local target=signedJump(address)
        if target then
          local prefix=hexBytes(site.cave)
          recognized=equalBytes(readBytes(target,#prefix,true),prefix)
            and signedJump(target+#prefix)==address+#original
          if recognized and #original&gt;5 then
            for i=6,#original do if current[i]~=0x90 then recognized=false end end
          end
        end
      end
      if recognized then
        local ok=writeBytes(address,original)
        if ok==false or not equalBytes(readBytes(address,#original,true),original) then
          failures[#failures+1]=site.name..': restoration failed'
        elseif site.feature=='fog' then fogChanged=true end
      else
        failures[#failures+1]=site.name..': unrecognized or unreadable patch; left untouched'
      end
    end
  end
  -- Reuse the existing engine's data cleanup after recovering its fog hooks.
  if fogChanged then
    local off=S.backends[ctx.engine].fog:match('%[DISABLE%](.*)')
    for chunk in off:gmatch('%{%$lua%}(.-)%{%$asm%}') do
      local fn,err=load(chunk,'recovered fog cleanup')
      if not fn then failures[#failures+1]=err else
        local ok,message=pcall(fn)
        if not ok then failures[#failures+1]=tostring(message) end
      end
    end
  end
  if #failures&gt;0 then error(table.concat(failures,'\n')) end
end

function S.clear()
  -- Stop every known writer, including timers no longer linked to a checkbox.
  for engine,features in pairs(S.timerNames) do
    for feature in pairs(features) do S.stopTimers(feature,engine) end
  end
  local failures={}
  local pending={}
  for feature in pairs(S.active) do pending[#pending+1]=feature end
  for _,feature in ipairs(pending) do
    local ok,err=pcall(S.toggle,feature,false)
    if not ok then failures[#failures+1]=tostring(err) end
  end
  local featureIDs={money=11,build=12,fog=13,visualfog=17,power=14,superweapon=15,ammo=16}
  for feature,id in pairs(featureIDs) do
    local record=getAddressList().getMemoryRecordByID(id)
    if not S.active[feature] and record and record.Active then
      local unchecked,message=pcall(function() record.Active=false end)
      if not unchecked or record.Active then failures[#failures+1]=feature..': checkbox cleanup failed: '..tostring(message) end
    end
  end
  local ok,err=pcall(S.recoverPatches)
  if not ok then failures[#failures+1]=tostring(err) end
  local saved={}
  for feature in pairs(S.saved) do saved[#saved+1]=feature end
  for _,feature in ipairs(saved) do
    if not S.restore(feature) then failures[#failures+1]=feature..': snapshot restoration pending' end
  end
  if #failures&gt;0 then error(table.concat(failures,'\n')) end
  for _,id in ipairs({11,12,13,14,15,16,17}) do
    local record=getAddressList().getMemoryRecordByID(id)
    if record and record.Active then record.Active=false end
  end
end

S.recoverySites.official[#S.recoverySites.official+1]={name='visual shroud',feature='visualfog',offset=0x5638D,original='0F BE C0',patched='31 C0 90'}
S.recoverySites.official[#S.recoverySites.official+1]={name='visual fog',feature='visualfog',offset=0x563E6,original='0F BE C0',patched='31 C0 90'}

S.recoverySites.official[#S.recoverySites.official+1]={name="visual radar",feature='visualfog',offset=0x1BB284,original="84 C0",patched="30 C0"}

-- Called only for a live factory whose owner and pending product were checked
-- by the existing production callback. Each House factory slot has its own record.
function S.retryProduction(factory, object, player)
  local records=getAddressSafe('BUILD_WAIT_ITEMS')
  if not records then return end
  for slot=0,3 do
    local record=records+slot*16
    if readInteger(record)==factory then
      if readInteger(record+4)~=object or readInteger(record+12)~=player
          or readInteger(player+0x200+slot*4)~=factory then
        writeInteger(record,0)
        return
      end
      local builder=readInteger(record+8)
      -- Confirm the producer still belongs to the current building list.
      local list=readInteger(0x7E470C)
      local count=readInteger(0x7E4718)
      local found=false
      if list and list~=0 and count and count&gt;=0 and count&lt;20000 then
        for i=0,count-1 do if readInteger(list+i*4)==builder then found=true;break end end
      end
      if not found or not builder or builder==0 or readInteger(builder+0xEC)~=player then return end
      local mission=readInteger(builder+0x4C)
      if mission==0xFFFFFFFF or mission==-1 then mission=readInteger(builder+0x54) end
      if mission==nil or mission==15 then return end -- native MISSION_UNLOAD
      local typ=readInteger(builder+0x220)
      if not typ or typ==0 then return end
      -- Barracks serialize departures through the radio-contact slot.
      if readInteger(typ+0x508)==16 then
        local contact=readInteger(builder+0x78)
        if contact==nil or contact~=0 then return end
      end
      -- Ask the normal sidebar logic to re-submit this same completed product.
      -- The normal exit routine still decides whether it can actually leave.
      if writeBytes(factory+0x48,1)~=false then writeInteger(record,0) end
      return
    end
  end
end

S.recoverySites.official[#S.recoverySites.official+1]={name="production exit retry",feature="build",offset=0xBECCE,original="E8 CD F9 FF FF"}

-- Mirror StripClass::Flag_To_Redraw and its Flag_To_Redraw(false) request.
-- These are transient render flags consumed by the native drawing loop.
function S.refreshSuperweaponSidebar()
  if not S.context('official') then return false end
  local tactical=readInteger(0x74C5F4)
  if not tactical or tactical==0 then return false end
  local sidebar=writeBytes(0x749C7C,1)
  local draw=writeBytes(tactical+0xD31,1)
  return sidebar~=false and draw~=false
end

-- Matches Official StorageClass::Get_Total_Value: four amounts multiplied by
-- each loaded TiberiumType's Value. Ignore non-positive amounts as the game does.
function S.storedTiberiumValue(player)
  local types=readInteger(0x80F40C)
  if not types or types==0 then return nil end
  local total=0
  for i=0,3 do
    local amount=readInteger(player+0x194+i*4)
    if amount==nil then return nil end
    if amount&gt;0 and amount&lt;0x80000000 then
      local typ=readInteger(types+i*4)
      if not typ or typ==0 then return nil end
      local value=readInteger(typ+0x80)
      if value==nil or value&gt;=0x80000000 then return nil end
      total=total+amount*value
    end
  end
  return total
end

-- Matches OpenTS StorageClass::Get_Total_Value: four amounts multiplied by
-- each loaded TiberiumType's Value. Ignore non-positive amounts as the game does.
function S.storedTiberiumValueOpenTS(player)
  local ctx=S.context("opents")
  if not ctx or ctx.player~=player then return nil end
  local types=readInteger(ctx.base+0x51AE24)
  if not types or types==0 then return nil end
  local total=0
  for i=0,3 do
    local amount=readInteger(player+0x16C+i*4)
    if amount==nil then return nil end
    if amount&gt;0 and amount&lt;0x80000000 then
      local typ=readInteger(types+i*4)
      if not typ or typ==0 then return nil end
      local value=readInteger(typ+0x80)
      if value==nil or value&gt;=0x80000000 then return nil end
      total=total+amount*value
    end
  end
  return total
end

-- Called only for a live factory whose owner and pending product were checked
-- by the existing production callback. Each House factory slot has its own record.
function S.retryProductionOpenTS(factory, object, player)
  local ctx=S.context("opents")
  if not ctx or ctx.player~=player then return end
  local records=getAddressSafe('OT_BUILD_WAIT_ITEMS')
  if not records then return end
  local carry=S.otRetryCarry
  if carry then
    S.otRetryCarry=nil
    if carry.pid==ctx.pid and carry.base==ctx.base and carry.player==player then
      for _,item in ipairs(carry.items) do
        if readInteger(player+0x1D8+item.slot*4)==item.f
            and readInteger(item.f+0x38)==item.obj and readInteger(item.f+0x4C)==player then
          local a=records+item.slot*16
          writeInteger(a+4,item.obj)
          writeInteger(a+8,item.builder)
          writeInteger(a+12,item.owner)
          writeInteger(a,item.f)
        end
      end
    end
  end
  for slot=0,3 do
    local record=records+slot*16
    if readInteger(record)==factory then
      if readInteger(record+4)~=object or readInteger(record+12)~=player
          or readInteger(player+0x1D8+slot*4)~=factory then
        writeInteger(record,0)
        return
      end
      local builder=readInteger(record+8)
      -- Confirm the producer still belongs to the current building list.
      local list=readInteger(ctx.base+0x514CB0)
      local count=readInteger(ctx.base+0x514CB8)
      local found=false
      if list and list~=0 and count and count&gt;=0 and count&lt;20000 then
        for i=0,count-1 do if readInteger(list+i*4)==builder then found=true;break end end
      end
      if not found or not builder or builder==0 or readInteger(builder+0xEC)~=player then return end
      local mission=readInteger(builder+0x48)
      if mission==0xFFFFFFFF or mission==-1 then mission=readInteger(builder+0x50) end
      if mission==nil or mission==15 then return end -- native MISSION_UNLOAD
      local typ=readInteger(builder+0x230)
      if not typ or typ==0 then return end
      -- Barracks serialize departures through the radio-contact slot.
      if readInteger(typ+0x478)==16 then
        local contact=readInteger(builder+0x74)
        if contact==nil or contact~=0 then return end
      end
      -- Ask the normal sidebar logic to re-submit this same completed product.
      -- The normal exit routine still decides whether it can actually leave.
      if writeBytes(factory+0x3C,1)~=false then writeInteger(record,0) end
      return
    end
  end
end

S.recoverySites.opents[#S.recoverySites.opents+1]={name="production exit retry",feature="build",offset=0xC6A83,original="E8 B8 7A FF FF"}

-- OpenTS StripClass::Flag_To_Redraw and MapClass::Flag_To_Redraw(false).
function S.refreshSuperweaponSidebarOpenTS()
  local ctx=S.context('opents')
  if not ctx then return false end
  local tactical=readInteger(ctx.base+0x531BE8)
  if not tactical or tactical==0 then return false end
  local sidebar=writeBytes(ctx.base+0x52EF4C,1)
  local draw=writeBytes(tactical+0xAD,1)
  return sidebar~=false and draw~=false
end

S.recoverySites.opents[#S.recoverySites.opents+1]={name="visual shroud",feature="visualfog",offset=0x5734E,original="0F BE C0",patched="31 C0 90"}

S.recoverySites.opents[#S.recoverySites.opents+1]={name="visual fog",feature="visualfog",offset=0x573A4,original="0F BE C0",patched="31 C0 90"}

S.recoverySites.opents[#S.recoverySites.opents+1]={name="visual radar",feature="visualfog",offset=0x1AC47D,original="84 C0",patched="30 C0"}

-- Recover only the complete, byte-verified OpenTS production hook from v1.91/1.92.
function S.recoverOpenTSBuild(carryPending)
  local ctx=S.context('opents')
  if not ctx then return end
  local site=ctx.base+0xC6A83
  local function sameBytes(address,expected)
    local actual=readBytes(address,#expected,true)
    if type(actual)~='table' or #actual~=#expected then return false end
    for i=1,#expected do if actual[i]~=expected:byte(i) then return false end end
    return true
  end
  local original=string.char(0xE8,0xB8,0x7A,0xFF,0xFF)
  if sameBytes(site,original) then return end
  if readBytes(site,1)~=0xE8 then error('Unrecognized production patch; left untouched.') end
  local rel=readInteger(site+1)
  if not rel then error('Unreadable production hook; left untouched.') end
  if rel&gt;=0x80000000 then rel=rel-0x100000000 end
  local target=site+5+rel
  local records=readInteger(target+0x2E)
  if not records or records==0 then error('Unreadable production records; left untouched.') end
  local function hex(s) return (s:gsub('%x%x',function(b) return string.char(tonumber(b,16)) end)) end
  local function d(n) return string.pack('&lt;I4',n &amp; 0xFFFFFFFF) end
  local expected=hex('3B0D')..d(ctx.base+0x602958)..hex('0F853D0000008B55F831C0399481D80100000F840B0000004083F8047CEDE920000000C1E0048990')..d(records)
    ..hex('89B8')..d(records+4)..hex('89B0')..d(records+8)..hex('8988')..d(records+12)
    ..hex('31C0C20800E9')..d(ctx.base+0xBE540-(target+0x4E))
  if not sameBytes(target,expected) then error('Unrecognized production hook body; left untouched.') end
  if OT_BUILD_TIMER then OT_BUILD_TIMER.Enabled=false end
  if carryPending then
    local carry={pid=ctx.pid,base=ctx.base,player=ctx.player,items={}}
    if ctx.player and ctx.player~=0 then
      for slot=0,3 do
        local a=records+slot*16
        local f,obj,builder,owner=readInteger(a),readInteger(a+4),readInteger(a+8),readInteger(a+12)
        if f and f~=0 and obj and builder and owner==ctx.player
            and readInteger(owner+0x1D8+slot*4)==f and readInteger(f+0x38)==obj
            and readInteger(f+0x4C)==owner then
          carry.items[#carry.items+1]={slot=slot,f=f,obj=obj,builder=builder,owner=owner}
        end
      end
    end
    S.otRetryCarry=carry
  end
  if writeBytes(site,{original:byte(1,#original)})==false or not sameBytes(site,original) then
    error('Production hook restoration failed; retry Clear State.')
  end
  if getAddressSafe('OT_BUILD_WAIT_ITEMS')==records then unregisterSymbol('OT_BUILD_WAIT_ITEMS') end
  -- Old allocations are retained because the lost CE disable metadata owns them.
end
local recoverOtherPatches=S.recoverPatches
function S.recoverPatches()
  S.recoverOpenTSBuild(false)
  return recoverOtherPatches()
end

-- Katzy: captured-selection protection. No unit data fields are overwritten.
local P={active={},retired={},profiles={[ [====[ts]====] ]={[ [====[module]====] ]=[====[Game.exe]====],[ [====[player]====] ]=4072068,[ [====[frame]====] ]=4081956,[ [====[selection]====] ]=4081752,[ [====[listoff]====] ]=4,[ [====[countoff]====] ]=16,[ [====[owner]====] ]=236,[ [====[health]====] ]=40,[ [====[id]====] ]=8,[ [====[ret]====] ]=24,[ [====[record]====] ]=19,[ [====[parent]====] ]=10,[ [====[vts]====] ]={2957596,2984812,2928120,2933496},[ [====[sites]====] ]={{[ [====[name]====] ]=[====[damage]====],[ [====[rva]====] ]=1597392,[ [====[length]====] ]=5,[ [====[original]====] ]=[====[83 EC 14 53 55]====],[ [====[pattern]====] ]=[====[83 EC 14 53 55 56 8B F1 57 C7 44 24 10 00 00 00 00 8B 46 28 85 C0 89 44 24 14 0F 8E E5 03 00 00 8B 6C 24 28 83 7D 00 00 0F 84 D7 03 00 00 8A 5C 24 38 84 DB 75 16 8B 06 FF 90 88 00 00 00 8A 88 CC 00 00 00 84 C9 0F 85 B9 03 00 00 8B 16 8B CE]====]},{[ [====[name]====] ]=[====[visual]====],[ [====[rva]====] ]=2333808,[ [====[length]====] ]=6,[ [====[original]====] ]=[====[8B 41 7C 83 E0 02]====],[ [====[pattern]====] ]=[====[8B 41 7C 83 E0 02 3C 02 75 1D 8B 4C 24 04 33 C0 81 F9 DC 05 00 00 0F 9E C0 48 25 24 FA FF FF 05 D0 07 00 00 C2 04 00 8B 44 24 04 C2 04 00 90 90 83 EC 10 53 55 56 8B F1 8B 06 FF 90 84 00 00 00 8A 88 4A 04 00 00 84 C9 0F 85 9C 01 00 00 8A 88]====]},{[ [====[name]====] ]=[====[building_visual]====],[ [====[rva]====] ]=240704,[ [====[length]====] ]=6,[ [====[original]====] ]=[====[8B 41 7C 83 E0 02]====],[ [====[pattern]====] ]=[====[8B 41 7C 83 E0 02 3C 02 8B 44 24 04 75 14 3D DC 05 00 00 7E 08 05 0C FE FF FF C2 04 00 05 F4 01 00 00 C2 04 00 90 90 90 90 90 90 90 90 90 90 90 83 EC 24 53 56 8B F1 8B 86 20 02 00 00 8A 88 4A 04 00 00 84 C9 0F 85 3F 01 00 00 8A 88 4B 04 00]====]},{[ [====[name]====] ]=[====[reset]====],[ [====[rva]====] ]=1950992,[ [====[length]====] ]=5,[ [====[original]====] ]=[====[A1 38 24 7E 00]====],[ [====[pattern]====] ]=[====[A1 38 24 7E 00 53 33 DB 56 C7 80 14 02 00 00 40 42 0F 00 8B 0D 38 24 7E 00 89 1D 84 22 7E 00 E8 5C E9 FF FF E8 37 07 F1 FF 8B 0D 60 45 7E 00 8D 71 FF 3B F3 7C 49 8B 15 54 45 7E 00 8B 04 B2 83 C0 04 50 8B 08 FF 51 0C 83 F8 12 75 2F 8B 0D 60]====]}}},[ [====[ot]====] ]={[ [====[module]====] ]=[====[Game.exe]====],[ [====[player]====] ]=6302040,[ [====[frame]====] ]=6301956,[ [====[selection]====] ]=5328576,[ [====[listoff]====] ]=0,[ [====[countoff]====] ]=8,[ [====[owner]====] ]=236,[ [====[health]====] ]=36,[ [====[id]====] ]=4,[ [====[ret]====] ]=24,[ [====[record]====] ]=19,[ [====[parent]====] ]=10,[ [====[vts]====] ]={4647676,4714100,4581356,4606788},[ [====[sites]====] ]={{[ [====[name]====] ]=[====[damage]====],[ [====[rva]====] ]=1527408,[ [====[length]====] ]=5,[ [====[original]====] ]=[====[55 8B EC 6A FF]====],[ [====[pattern]====] ]=[====[55 8B EC 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 83 EC 14 53 56 57 A1 ?? ?? ?? ?? 33 C5 50 8D 45 F4 64 A3 00 00 00 00 8B F1 8B 5E 24 33 FF 89 5D F0 85 DB 0F 8E F0 03 00 00 8B 45 08 39 38 0F 84 E5 03 00 00 8B 06 FF 90 94 00 00 00 80 7D 18]====]},{[ [====[name]====] ]=[====[visual]====],[ [====[rva]====] ]=2223472,[ [====[length]====] ]=7,[ [====[original]====] ]=[====[55 8B EC F6 41 78 02]====],[ [====[pattern]====] ]=[====[55 8B EC F6 41 78 02 74 18 81 7D 08 DC 05 00 00 B8 D0 07 00 00 B9 F4 01 00 00 0F 4F C1 5D C2 04 00 8B 45 08 5D C2 04 00 CC CC CC CC CC CC CC CC 55 8B EC 83 EC 28 53 8B 5D 08 56 8B F1 6A 00 89 75 FC 8B 06 FF 90 18 03 00 00 83 38 00 0F 84 AB]====]},{[ [====[name]====] ]=[====[building_visual]====],[ [====[rva]====] ]=184800,[ [====[length]====] ]=7,[ [====[original]====] ]=[====[55 8B EC F6 41 78 02]====],[ [====[pattern]====] ]=[====[55 8B EC F6 41 78 02 8B 55 08 74 19 81 FA DC 05 00 00 B8 0C FE FF FF B9 F4 01 00 00 0F 4E C1 03 C2 5D C2 04 00 8B C2 5D C2 04 00 CC CC CC CC CC 55 8B EC 56 8B F1 83 7E 48 12 74 34 8B 06 8B 80 2C 02 00 00 FF D0 84 C0 75 0F 8B 86 30 02 00 00]====]},{[ [====[name]====] ]=[====[reset]====],[ [====[rva]====] ]=1891696,[ [====[length]====] ]=5,[ [====[original]====] ]=[====[55 8B EC 6A FF]====],[ [====[pattern]====] ]=[====[55 8B EC 6A FF 68 ?? ?? ?? ?? 64 A1 00 00 00 00 50 51 56 57 A1 ?? ?? ?? ?? 33 C5 50 8D 45 F4 64 A3 00 00 00 00 A1 ?? ?? ?? ?? C7 80 14 02 00 00 40 42 0F 00 8B 0D ?? ?? ?? ?? C7 05 ?? ?? ?? ?? 00 00 00 00 E8 C7 4C 00 00 E8 E2 EB F1 FF 8B 35]====]}}}}}
KATZY_SELECTED_TS=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
local function context(key)
  local p=P.profiles[key]
  if not p then error('This game is not supported by this option.',0) end
  local engine=(key=='ot' or key=='fogfix') and 'opents' or 'official'
  KATZY_TS.requireEngine(engine)
  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)
  local frame=readInteger(base+p.frame)
  if key=='fogfix' then player=player or 0;frame=frame or 0 end
  if key~='fogfix' and (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
    local fn=readInteger(vt+0x18)
    local code=fn and readBytes(fn,3,true)
    return type(code)=='table' and code[1]==0xB0 and code[3]==0xC3 and (code[2]==1 or code[2]==3 or code[2]==5 or code[2]==7)
  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)
    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..')',
    'registersymbol('..state..')','registersymbol('..code..')'}
  if c.key~='fogfix' then
    for _,name in ipairs({'match_loop','match_next','match_yes','match_no','match_expired'}) do
      b[#b+1]='label('..name..')'
    end
  end
  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..')'
    if s.name~='fogcompat' then b[#b+1]='label(original_'..i..')' end
    b[#b+1]='registersymbol('..n..')'
  end
  if c.key=='fogfix' then b[#b+1]=code..':\n'..matcher..':' else
  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,['..p.module..'+'..hex(p.player)..']\ncmp edx,['..state..'+8]\njne match_expired'
  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'
  end
  for i,s in ipairs(p.sites) do
    local original='original_'..i
    b[#b+1]='hook_'..i..':'
    if s.name=='fogcompat' then
      -- OpenTS 0.1.0 incorrectly casts CellClass* to ConvertClass* here.
      -- Match the corrected source: terrain drawer or house colour converter.
      b[#b+1]='label(fog_terrain)\nlabel(fog_store)\ncmp byte [edi+0x80A],0\njne fog_terrain\nmov ecx,[esi+0x14]\nmov ecx,[ecx+0x10D48]\npush eax\nmov eax,[Game.exe+0x519EF0]\nmov ecx,[eax+ecx*4]\nmov ecx,[ecx+0x30C]\npop eax\njmp fog_store\nfog_terrain:\nmov ecx,[eax]\nfog_store:\nmov [ebp-0x28],ecx\njmp '..s.symbol..'+'..hex(s.length)
    elseif 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
    if s.name~='fogcompat' then
    b[#b+1]='readmem('..s.symbol..','..s.length..')\njmp '..s.symbol..'+'..hex(s.length)
    end
  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()
    if key=='fogfix' then c.targets={} else capture(c) end
    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((key=='fogfix' and 'OpenTS fog-rendering fix' or 'Selected-unit protection')..' could not be installed.\n\nCheat Engine: '..P.lastError,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=(KATZY_TS.detect() or {}).engine=='opents' and 'ot' or 'ts'
      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
        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(P.shutdown)
  if not ok then P.lastError=tostring(err) 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_TS.clear
KATZY_TS.clear=function() P.clear();getAddressList().getMemoryRecordByID(19).Active=false;return previousClear() end


-- Compatibility belongs to MAIN, independent of selected-unit protection.
P.profiles.fogfix={module='Game.exe',player=0x602958,frame=0x602904,owner=0xEC,health=0x24,record=10,parent=10,
  sites={{name='fogcompat',rva=0xA2D1B,length=14,pattern='80 BF 0A 08 00 00 00 74 05 8B 08 89 4D D8 83 BD 5C FF FF FF 00 0F 8E A9 00 00 00 8B 55 C8 B8 FE FF FF FF 2B C2 80 7D FF 00 89 45 C0 74 0F 6A 00 6A 00 6A 00 6A 00 FF 75 E0 6A 00 EB 18 FF 75 D0 8B 45 9C FF 75 DC 2B C2 6A 00 FF 75 A0 83 E8 02',original='80 BF 0A 08 00 00 00 74 05 8B 08 89 4D D8'}}}
local previousToggle=KATZY_TS.toggle
KATZY_TS.toggle=function(feature,enabled)
  local ctx=enabled and KATZY_TS.detect()
  if ctx and ctx.engine=='opents' then P.enable('fogfix') end
  return previousToggle(feature,enabled)
end
local previousProtectionEnable=P.enable
P.enable=function(key)
  if key=='ot' then previousProtectionEnable('fogfix') end
  return previousProtectionEnable(key)
end

getAddressList().getMemoryRecordByID(10).OnActivate=function(_,before)
  if not before then return true end
  local ctx=KATZY_TS.detect()
  if not ctx then showMessage('Start the supported Tiberian Sun game and allow the table to attach before enabling MAIN.');return false end
  if ctx.engine=='opents' then
    local ok,err=pcall(P.enable,'fogfix')
    if not ok then report(err);return false end
  end
  KATZY_TS.mainContext={pid=ctx.pid,base=ctx.base,engine=ctx.engine}
  return true
end
local previousDetach=KATZY_TS.detachExpired
KATZY_TS.detachExpired=function()
  previousDetach()
  local bound=KATZY_TS.mainContext
  if not bound then return end
  local ctx=KATZY_TS.detect()
  if not ctx or ctx.pid~=bound.pid or ctx.base~=bound.base or ctx.engine~=bound.engine then
    KATZY_TS.mainContext=nil
    getAddressList().getMemoryRecordByID(10).Active=false
  end
end
getAddressList().getMemoryRecordByID(10).OnDestroy=function()
  if not P.closing then local ok,err=pcall(P.shutdown);if not ok then report(err) end end
end

-- One-shot native Elite rank. The game retains this promotion normally.
function P.promoteSelected()
  local engine=KATZY_TS.detect()
  if not engine then error('Start the supported game and select your units first.',0) end
  local key=engine.engine=='opents' and 'ot' or 'ts'
  local c=context(key)
  local promoted=0
  transaction(function()
    capture(c)
    local pending={}
    local typeOffsets={0x350,0x360,0x354}
    for _,t in ipairs(c.targets) do
      local offset=nil
      for i=1,3 do if t.vt==c.base+c.p.vts[i] then offset=typeOffsets[i];break end end
      if offset then
        local typ=readInteger(t.obj+offset)
        if not typ or typ==0 then error('A selected unit type could not be read. No promotions were applied.',0) end
        local trainable=readBytes(typ+(key=='ot' and 0x3B3 or 0x43E),1)
        if trainable==nil then error('Unit eligibility could not be read. No promotions were applied.',0) end
        if trainable~=0 then
          local address=t.obj+0xA8
          local xp=readDouble(address)
          if not xp or xp~=xp or xp==math.huge or xp==-math.huge then error('Unit experience could not be read. No promotions were applied.',0) end
          if xp&lt;2 then pending[#pending+1]={address=address,old=xp} end
        end
      end
    end
    local attempted={}
    local ok,err=pcall(function()
      for _,v in ipairs(pending) do
        attempted[#attempted+1]=v
        if writeDouble(v.address,2)==false or readDouble(v.address)~=2 then error('The Elite promotion could not be written.',0) end
      end
    end)
    if not ok then
      local restored=true
      for _,v in ipairs(attempted) do
        local success=pcall(function() assert(writeDouble(v.address,v.old)~=false and readDouble(v.address)==v.old) end)
        restored=restored and success
      end
      error(tostring(err)..(restored and ' Original experience was restored.' or ' Some original experience could not be restored.'),0)
    end
    promoted=#pending
  end)
  redraw(c)
  return promoted
end
getAddressList().getMemoryRecordByID(20).OnActivate=function(_,before)
  if before then
    local ok,err=pcall(P.promoteSelected)
    if not ok then report(err) end
  end
  -- An action, not a persistent toggle: remains unticked and can be used again.
  return false
end
</LuaScript>
</CheatTable>