<?xml version="1.0" encoding="utf-8"?>
<CheatTable CheatEngineTableVersion="52">
  <CheatEntries>
    <CheatEntry>
      <ID>548000</ID>
      <Description>"Enable resources, population and research - Steam 101.103.54800.0"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
-- Single-player resource table; target Steam 101.103.54800.0.
-- Based on the supplied table and live disassembly. No code injection.
local root = readPointer("AoE2DE_s.exe+435A150")
assert(root and root ~= 0, "Load a single-player match and attach to AoE2DE_s.exe")
local player = readPointer(root + 0x28)
assert(player and player ~= 0, "Player pointer unavailable")
local resources = readPointer(player + 0x98)
assert(resources and resources ~= 0, "Resource pointer unavailable")
local pattern = "80 ?? ?? ?? 0F ?? ?? ?? 00 00 49 8B ?? 18 49 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48 ?? ?? ?? ?? ?? ?? ?? ?? ?? 48"
local address = aOBScanModuleUnique("AoE2DE_s.exe", pattern)
assert(address, "Decoder pattern unavailable: no changes made")
local constants, operations = {}, {}
local finished = false
for i = 1, 64 do
  local instruction = disassemble(address):match(".- %- .* %- (.*)")
  assert(instruction, "Cannot parse disassembly")
  instruction = instruction:lower()
  local reg, hex = instruction:match("^mov%s+(%w+),%s*(%x+)%s*$")
  if reg and hex then constants[reg] = tonumber(hex:sub(-8), 16) end
  local op, source = instruction:match("^(%w+)%s+rax,%s*(%w+)%s*$")
  if op == "xor" or op == "sub" or op == "add" then
    assert(constants[source], "Unsupported decoder operand")
    operations[#operations+1] = {op, constants[source]}
  end
  if instruction:find("cvttss2si", 1, true) then finished = true; break end
  local size = getInstructionSize(address)
  assert(size and size &gt; 0, "Invalid instruction size")
  address = address + size
end
assert(finished and #operations == 4, "Decoder layout changed: no changes made")
local function decode(v)
  for _, item in ipairs(operations) do
    if item[1] == "xor" then v = v ~ item[2]
    elseif item[1] == "sub" then v = v - item[2]
    else v = v + item[2] end
    v = v &amp; 0xFFFFFFFF
  end
  return string.unpack("&lt;f", string.pack("&lt;I4", v))
end
for i = 0, 3 do
  local raw = readInteger(resources + i*8)
  assert(raw, "Cannot read resource slot")
  local v = decode(raw)
  assert(v == v and v &gt;= 0 and v &lt;= 100000000, "Implausible resource values: stopped")
end
local function encodeSigned(value)
  value = tonumber(value)
  assert(value and value == value and value &gt;= -100000000 and value &lt;= 100000000, "Encoded value out of range")
  local v = string.unpack("&lt;I4", string.pack("&lt;f", value))
  for i = #operations, 1, -1 do
    local item = operations[i]
    if item[1] == "xor" then v = v ~ item[2]
    elseif item[1] == "sub" then v = v + item[2]
    else v = v - item[2] end
    v = v &amp; 0xFFFFFFFF
  end
  return {v &amp; 255, (v &gt;&gt; 8) &amp; 255, (v &gt;&gt; 16) &amp; 255, (v &gt;&gt; 24) &amp; 255}
end
local function encode(value)
  value = tonumber(value)
  assert(value and value &gt;= 0 and value &lt;= 100000000, "Use a value from 0 to 100000000")
  return encodeSigned(value)
end
-- Match the original table's four-byte writes, preserving the upper four bytes.
AoEResourceType54800 = registerCustomTypeLua("AoERes54800", 4,
  function(b1,b2,b3,b4)
    local v = decode(b1 + b2*256 + b3*65536 + b4*16777216)
    if v ~= v or v == math.huge or v == -math.huge then return 0 end
    return math.floor(v)
  end, encode, false, false)
assert(AoEResourceType54800, "Custom type registration failed")
-- Resource 86: technology research time multiplier; keep fractional values visible.
AoEResourceFloat54800 = registerCustomTypeLua("AoEResFloat54800", 4,
  function(b1,b2,b3,b4)
    return decode(b1 + b2*256 + b3*65536 + b4*16777216)
  end, encode, true, false)
assert(AoEResourceFloat54800, "Float resource type registration failed")

-- Available space (0x20) + population used (0x58) = total housing capacity.
-- User verified: -7 available, 27 used, HUD 27/20.
local function usedPopulation()
  local raw = readInteger(resources + 0x58)
  assert(raw, "Cannot read current population")
  local v = decode(raw)
  assert(v == v and v &gt;= 0 and v &lt;= 1000000, "Invalid current population")
  return v
end
AoEPopulationType54800 = registerCustomTypeLua("AoEPopTotal54800", 4,
  function(b1,b2,b3,b4)
    local available = decode(b1 + b2*256 + b3*65536 + b4*16777216)
    return math.floor(available + usedPopulation() + 0.0001)
  end,
  function(total)
    total = tonumber(total)
    assert(total and total == math.floor(total) and total &gt;= 1 and total &lt;= 100000,
      "Enter a whole-number total capacity between 1 and 100000")
    local currentRoot = readPointer("AoE2DE_s.exe+435A150")
    assert(currentRoot == root, "Match changed: disable and re-enable the table")
    local currentPlayer = readPointer(currentRoot + 0x28)
    assert(currentPlayer == player and readPointer(currentPlayer + 0x98) == resources,
      "Match changed: disable and re-enable the table")
    return encodeSigned(total - usedPopulation())
  end, false, false)
assert(AoEPopulationType54800, "Population type registration failed")

unregisterSymbol("AoEResourceBase54800")
registerSymbol("AoEResourceBase54800", resources)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
for id = 548001, 548007 do
  local entry = getAddressList().getMemoryRecordByID(id)
  if entry then entry.Active = false end
end
unregisterSymbol("AoEResourceBase54800")
{$asm}
</AssemblerScript>
      <CheatEntries>
        <CheatEntry>
          <ID>548001</ID>
          <Description>"Food (100% working)"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes54800</CustomType>
          <Address>AoEResourceBase54800+0</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>548002</ID>
          <Description>"Wood (100% working)"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes54800</CustomType>
          <Address>AoEResourceBase54800+8</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>548003</ID>
          <Description>"Stone (100% working)"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes54800</CustomType>
          <Address>AoEResourceBase54800+10</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>548004</ID>
          <Description>"Gold (100% working)"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes54800</CustomType>
          <Address>AoEResourceBase54800+18</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>548005</ID>
          <Description>"Housing capacity total - (still not working all the time, work in progress)"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoEPopTotal54800</CustomType>
          <Address>AoEResourceBase54800+20</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>548006</ID>
          <Description>"Scenario cap BONUS - (still not working all the time, work in progress)"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes54800</CustomType>
          <Address>AoEResourceBase54800+100</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>548007</ID>
          <Description>"Research time multiplier - 0.01 = near instant (100% working)"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoEResFloat54800</CustomType>
          <Address>AoEResourceBase54800+2B0</Address>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols/>
  <Comments>AoE2 Definitive Edition - Steam 101.103.54800.0 - SINGLE PLAYER ONLY

Includes user-confirmed resource editing/freezing, adjustable population limits,
and near-instant research. Instant troop recruitment is not included.

SETUP
Disable the previous table. Open this table without merging it with the old one.
Attach Cheat Engine to AoE2DE_s.exe and load a single-player match.
Enable the main entry and check the displayed values before editing.

RESOURCES
Double-click each Value to enter an amount (for example, 99999).
Tick Food, Wood, Stone and Gold to freeze them at the chosen amounts.

POPULATION
Set Housing capacity TOTAL to the desired total population.
Scenario cap BONUS adds capacity above the base scenario maximum.
Example: for a base scenario maximum of 200 and a desired maximum of 500,
set Housing capacity TOTAL to 500 and Scenario cap BONUS to 300.
If the current effective maximum includes an existing bonus, use:
new bonus = old bonus + desired maximum - current effective maximum.
Tick both population entries to hold the values.

RESEARCH
Note the original multiplier, then set Research time multiplier to 0.01.
This was confirmed to make research nearly instant. Tick to hold if needed.
Do not use 0 (reported ineffective) or 100 (reported extremely slow).

CHANGING MATCHES AND RESTORING VALUES
Disable the main entry BEFORE loading a save or changing matches.
Re-enable it after loading and check the displayed values again.
Unticking a child entry stops freezing; it does not restore its previous value.
To restore a value, untick that entry and enter its original amount.
Disabling the main entry stops all seven child entries from freezing.
No resource, population or research value is changed automatically on activation.
</Comments>
</CheatTable>
