<?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>0</ID>
      <Description>"How to use"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
showMessage([[
Command &amp; Conquer: Tiberian Sun and Firestorm (Steam TUC)
Game.exe version 2.03 TUC  (32-bit, fixed base, no ASLR)
Works for BOTH Tiberian Sun and the Firestorm expansion.

HOW TO USE
1. Start Tiberian Sun OR Firestorm and get into a match.
2. Open this table in Cheat Engine 7.4+. It auto-attaches to Game.exe by PID.
3. Tick the cheats you want:
   - Infinite Money       (pins credits to 30,000)
   - Instant Build        (finishes production instantly)
   - Fog of War Removal   (reveals the whole map)
   - Infinite Power       (no power drain, never low-power)
4. Enable "Diagnosis Mode" to see addresses in the Lua console.

NOTES
- Made for Game.exe 2.03 TUC (version checked on attach).
- No hotkeys. Toggle with the checkboxes only.
]])
{$asm}
[DISABLE]
</AssemblerScript>
    </CheatEntry>
    <CheatEntry>
      <ID>10</ID>
      <Description>"[MAIN] Tiberian Sun 2.03 TUC"</Description>
      <GroupHeader>1</GroupHeader>
      <CheatEntries>
        <CheatEntry>
          <ID>11</ID>
          <Description>"Infinite Money"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
-- PlayerPtr = [Game.exe+3E2284]; live credits = HouseClass+0x1A4 (verified).
-- Pinned to 30000 = 10x the most expensive buildable (Mammoth Mk.II / Nod Temple
-- cost 3000). Keeping it at 10x max cost avoids overflowing the UI/balance.
if not INFMONEY_TIMER then
  INFMONEY_TIMER = createTimer(nil)
  INFMONEY_TIMER.Interval = 200
  INFMONEY_TIMER.OnTimer = function(t)
    if getOpenedProcessID() == nil 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 writeInteger(p + 0x1A4, 30000) end
  end
end
INFMONEY_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if INFMONEY_TIMER then INFMONEY_TIMER.Enabled = false end
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>12</ID>
          <Description>"Instant Build"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
{$lua}
-- Factories: array [0x7E234C], count [0x7E2358].
-- Each factory's production counter is +0x14 (counts 0 -&gt; 0x36). Its own AI
-- completes + places the building when it hits 0x36. We nudge it to 0x35 only
-- when it is actually behind (status &lt; 0x35) and not suspended, then let the
-- game finish naturally. Writes are wrapped in pcall to avoid a race crash.
if not INSTABUILD_TIMER then
  INSTABUILD_TIMER = createTimer(nil)
  INSTABUILD_TIMER.Interval = 200
  INSTABUILD_TIMER.OnTimer = function(t)
    if getOpenedProcessID() == nil then return end
    local ok = pcall(function()
      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
          local obj = readInteger(f + 0x44)
          if obj and obj ~= 0 then
            local status = readInteger(f + 0x14)
            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
INSTABUILD_TIMER.Enabled = true
{$asm}
[DISABLE]
{$lua}
if INSTABUILD_TIMER then INSTABUILD_TIMER.Enabled = false end
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>13</ID>
          <Description>"Fog of War Removal"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
// Stop the shroud update from clearing the "visible" bit (cell+0xA4 bit1):
// patch 'and ecx,-3' (clear) into 'or ecx,2' (set).
aobscanmodule(FOG_KEEPVIS,Game.exe,83 E1 FD 89 88 A4 00 00 00)
registersymbol(FOG_KEEPVIS)
FOG_KEEPVIS:
  db 83 C9 02

{$lua}
-- Reveal all cells: set bit1(visible)=2 and bit2(mapped)=4 on cell+0xA4.
-- Cell array [0x7484B0], cell count [0x7484B4] (map object 0x748348 + 0x168).
if not FOG_TIMER then
  FOG_TIMER = createTimer(nil)
  FOG_TIMER.Interval = 300
  FOG_TIMER.OnTimer = function(t)
    if getOpenedProcessID() == nil then return end
    local ok = pcall(function()
      local arr = readInteger(0x7484B0)
      local cnt = readInteger(0x7484B4)
      if not arr or arr == 0 then return end
      if not cnt or 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 + 0xA4)
          if s and (s &amp; 6) ~= 6 then
            writeInteger(cell + 0xA4, s | 6)
          end
        end
      end
    end)
  end
end
FOG_TIMER.Enabled = true
{$asm}

[DISABLE]
FOG_KEEPVIS:
  db 83 E1 FD
unregistersymbol(FOG_KEEPVIS)

{$lua}
if FOG_TIMER then FOG_TIMER.Enabled = false end
{$asm}
</AssemblerScript>
        </CheatEntry>
        <CheatEntry>
          <ID>14</ID>
          <Description>"Infinite Power"</Description>
          <VariableType>Auto Assembler Script</VariableType>
          <AssemblerScript>[ENABLE]
// 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.
// Patch the getter to return 0 so Drain is always 0 (never low-power).
aobscanmodule(INFPOWER_NODRAIN,Game.exe,8A 81 FC 02 00 00 84 C0 74 36)
registersymbol(INFPOWER_NODRAIN)
INFPOWER_NODRAIN:
  db 31 C0 C3 90 90 90

{$lua}
-- 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 getOpenedProcessID() == nil 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 writeInteger(p + 0x1F8, 10000) end
  end
end
INFPOWER_TIMER.Enabled = true
{$asm}

[DISABLE]
INFPOWER_NODRAIN:
  db 8A 81 FC 02 00 00
unregistersymbol(INFPOWER_NODRAIN)

{$lua}
if INFPOWER_TIMER then INFPOWER_TIMER.Enabled = false end
{$asm}
</AssemblerScript>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
    <CheatEntry>
      <ID>2</ID>
      <Description>"Diagnosis Mode"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
DIAGNOSIS_ENABLED = true
{$asm}
[DISABLE]
{$lua}
DIAGNOSIS_ENABLED = false
{$asm}
</AssemblerScript>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols/>
  <LuaScript>if not DIAGNOSIS_ENABLED then DIAGNOSIS_ENABLED = false end

local function getGameVersion()
  local ok, mods = pcall(enumModules)
  if not ok then return nil end
  for i, m in ipairs(mods) do
    if m and m.Name == "Game.exe" then
      local okv, ver = pcall(getFileVersion, m.Path)
      if okv then return ver, m.Path end
    end
  end
  return nil
end

local attachTimer = createTimer(nil)
attachTimer.Interval = 500
attachTimer.OnTimer = function(t)
  local ok, pid = pcall(getProcessIDFromProcessName, "Game.exe")
  if not ok or pid == nil then return end
  if getOpenedProcessID() ~= pid then
    local okp = pcall(openProcess, pid)
    if okp then
      local ver = getGameVersion()
      if ver and not tostring(ver):find("2.03", 1, true) then
        showMessage("Warning: Game.exe version " .. tostring(ver) ..
          " but table was built for 2.03 TUC.")
      end
      if DIAGNOSIS_ENABLED then
        print("[TS] Attached to Game.exe (PID " .. tostring(pid) .. ") v" .. tostring(ver))
        print("[TS] cellArray=" .. tostring(readInteger(0x7484B0)) ..
              " cellCount=" .. tostring(readInteger(0x7484B4)) ..
              " factoryCount=" .. tostring(readInteger(0x7E2358)))
      end
    end
  end
end
attachTimer.Enabled = true
</LuaScript>
</CheatTable>
