<?xml version="1.0" encoding="utf-8"?>
<CheatTable CheatEngineTableVersion="52">
  <CheatEntries>
    <CheatEntry>
      <ID>572570</ID>
      <Description>"Enable - Steam 57257 - Resources, Research and Housing"</Description>
      <VariableType>Auto Assembler Script</VariableType>
      <AssemblerScript>[ENABLE]
{$lua}
if syntaxcheck then return end
-- Steam 101.103.57257.0; single player. Static analysis; live testing required.
-- Reads only on activation. No injection or execution of game functions.
local module = getAddressSafe("AoE2DE_s.exe")
assert(module, "Attach to AoE2DE_s.exe first.")
local function verify(rva, expected)
  local want = {}
  for s in expected:gmatch("%x%x") do want[#want+1] = tonumber(s,16) end
  local got = readBytes(module+rva,#want,true)
  assert(got and #got==#want, "Cannot read game code.")
  for i=1,#want do
    assert(got[i]==want[i], string.format(
      "Build check failed at +%X. This table requires the supplied 57257 build.",rva+i-1))
  end
end
verify(0xB49C00, "48 83 EC 28 48 8B 0D AD B6 80 03 E8 E0 2F 22 00 48 8B 88 98 00 00 00 48 8B 41 30 48 B9 11 45 F0 52 33 4D E2 6A 48 89 44 24 38 48 8B 44 24 38 48 33 C1 48 B9 77 39 83 6D 49 6B 2D C2 48 89 44 24 38 48 8B 44 24 38 48 33 C1 48 B9 F5 CE FC 76 29 C4 2A 04 48 89 44 24 38 48 8B 44 24 38 48 2B C1 48 B9 67 D1 7D 8A 73 4B 83 A9 48 89 44 24 38 48 8B 44 24 38 48 33 C1 48 89 44 24 38 48 8B 44 24 38 48 89 44 24 38 F3 0F 2C 44 24 38")
verify(0xD6CBF0, "48 8B 89 C0 05 00 00 48 85 C9 0F 85 E0 58 89 00 33 C0 C3")
verify(0x16024E0, "8B 91 B0 06 00 00 39 15 68 4B 7A 02 74 24 4C 8B 81 70 07 00 00 8B C2 48 8B 89 78 07 00 00 49 2B C8 48 C1 F9 04 48 3B D1 73 08 48 03 C0 49 8B 04 C0 C3 33 C0 C3")

local function pointer(address, label)
  local p=readPointer(address)
  assert(p and p~=0, label.." unavailable. Load a single-player match.")
  return p
end
-- Mirrors B49C00 -&gt; D6CBF0 -&gt; 16024E0 without calling game code.
local function locate()
  local game=pointer(module+0x43552B8,"Game")
  local world=pointer(game+0x5C0,"World")
  local index=readInteger(world+0x6B0)
  local first=pointer(world+0x770,"Player list")
  local last=pointer(world+0x778,"Player list end")
  assert(last&gt;=first and (last-first)%16==0 and last-first&lt;=16*64,
    "Unexpected player list layout.")
  assert(index and index&gt;=0 and index&lt;(last-first)//16,
    "No active player. Load a single-player match.")
  local player=pointer(first+index*16,"Player")
  local resources=pointer(player+0x98,"Resources")
  return resources, player, world, game, index
end
local resources,player,world,game,index=locate()
local function checkMatch()
  local r,p,w,g,i=locate()
  assert(r==resources and p==player and w==world and g==game and i==index,
    "Match/player changed: disable and re-enable the table.")
end
local function decode(v)
  v=(v ~ 0x52F04511) &amp; 0xFFFFFFFF
  v=(v ~ 0x6D833977) &amp; 0xFFFFFFFF
  v=(v - 0x76FCCEF5) &amp; 0xFFFFFFFF
  v=(v ~ 0x8A7DD167) &amp; 0xFFFFFFFF
  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 resources.")
  local v=decode(raw)
  assert(v==v and v&gt;=0 and v&lt;=100000000,
    "Resource validation failed. No values were written.")
end
local function encode(value)
  checkMatch()
  value=tonumber(value)
  assert(value and value==value and value&gt;=0 and value&lt;=100000000,
    "Enter a value between 0 and 100000000.")
  local v=string.unpack("&lt;I4",string.pack("&lt;f",value))
  v=(v ~ 0x8A7DD167) &amp; 0xFFFFFFFF
  v=(v + 0x76FCCEF5) &amp; 0xFFFFFFFF
  v=(v ~ 0x6D833977) &amp; 0xFFFFFFFF
  v=(v ~ 0x52F04511) &amp; 0xFFFFFFFF
  -- Preserve the upper four bytes of each eight-byte resource slot.
  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 fromBytes(b1,b2,b3,b4)
  return decode(b1+b2*256+b3*65536+b4*16777216)
end
AoEResourceType57257=registerCustomTypeLua("AoERes57257",4,
  function(b1,b2,b3,b4)
    local v=fromBytes(b1,b2,b3,b4)
    if v~=v or v==math.huge or v==-math.huge then return 0 end
    return math.floor(v)
  end,encode,false,false)
assert(AoEResourceType57257,"Resource type registration failed.")
AoEResourceFloat57257=registerCustomTypeLua("AoEResFloat57257",4,
  fromBytes,encode,true,false)
assert(AoEResourceFloat57257,"Research type registration failed.")

-- Housing offsets verified by the user in a normal skirmish:
-- +20: available space 21 -&gt; 20; +58: used population 4 -&gt; 5; HUD 5/25.
-- One-time edits only for now. Do not tick the housing row to freeze it.
local function usedPopulation()
  checkMatch()
  local raw=readInteger(resources+0x58)
  assert(raw, "Cannot read population used.")
  local used=decode(raw)
  assert(used==used and used&gt;=0 and used&lt;=100000,
    "Unexpected population-used value; do not edit housing.")
  return used
end
AoEHousingType57257=registerCustomTypeLua("AoEHousingTotal57257",4,
  function(b1,b2,b3,b4)
    local available=fromBytes(b1,b2,b3,b4)
    local total=available+usedPopulation()
    assert(total==total and total&gt;=0 and total&lt;=100000,
      "Unexpected housing total; check the match and HUD.")
    return math.floor(total+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 between 1 and 100000.")
    local used=usedPopulation()
    assert(total&gt;=used, "Housing total must be at least the population currently used.")
    return encode(total-used)
  end,false,false)
assert(AoEHousingType57257, "Housing type registration failed.")

unregisterSymbol("AoEResourceBase57257")
registerSymbol("AoEResourceBase57257",resources)
{$asm}
[DISABLE]
{$lua}
if syntaxcheck then return end
for _,id in ipairs({572571,572572,572573,572574,572575,572577}) do
  local entry=getAddressList().getMemoryRecordByID(id)
  if entry then entry.Active=false end
end
unregisterSymbol("AoEResourceBase57257")
{$asm}
</AssemblerScript>
      <CheatEntries>
        <CheatEntry>
          <ID>572571</ID>
          <Description>"Food - edit value; leave UNTICKED"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes57257</CustomType>
          <Address>AoEResourceBase57257+0</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>572572</ID>
          <Description>"Wood - edit value; leave UNTICKED"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes57257</CustomType>
          <Address>AoEResourceBase57257+8</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>572573</ID>
          <Description>"Stone - edit value; leave UNTICKED"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes57257</CustomType>
          <Address>AoEResourceBase57257+10</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>572574</ID>
          <Description>"Gold - edit value; leave UNTICKED"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoERes57257</CustomType>
          <Address>AoEResourceBase57257+18</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>572575</ID>
          <Description>"Housing capacity TOTAL - edit value; leave UNTICKED"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoEHousingTotal57257</CustomType>
          <Address>AoEResourceBase57257+20</Address>
        </CheatEntry>
        <CheatEntry>
          <ID>572577</ID>
          <Description>"Research time multiplier - 0.01 = near instant - 1 = normal speed"</Description>
          <VariableType>Custom</VariableType>
          <CustomType>AoEResFloat57257</CustomType>
          <Address>AoEResourceBase57257+2B0</Address>
        </CheatEntry>
      </CheatEntries>
    </CheatEntry>
  </CheatEntries>
  <UserdefinedSymbols/>
  <Comments>Steam 101.103.57257.0 (#188329) 26249352 - SINGLE PLAYER ONLY
Resources and research were confirmed working by the user in this build.
Housing +20 (available) and +58 (used) were verified in a normal skirmish.
The new TOTAL conversion still needs an in-game check. Scenario cap override is not included.

SETUP
Disable the previous table and open this one without merging.
Attach to AoE2DE_s.exe, load a match, then enable the main entry.
Activation does not change game resource or housing values.
Confirm the four resource values match your HUD before editing.

HOUSING CAPACITY TOTAL
Compare this row with your HUD: at 5/25 it should display 25.
Pause, double-click the Value and enter the desired total, e.g. 30.
Leave the housing checkbox UNTICKED. Unpause and verify the HUD shows 5/30.
The table subtracts the current population used automatically.
Only the available-space slot is changed; population used is never written.
For this version, totals below current population used are rejected.
Housing freezing has not been verified and should remain off.
This does not override the scenario maximum or scripted population restrictions.
If a scenario HUD differs from the displayed housing total, do not edit it.

RESOURCES AND RESEARCH
Edit Food/Wood/Stone/Gold and tick those rows to freeze if desired.
Research multiplier: note the original value before using 0.01 for near-instant research.
Unticking stops freezing; it does not restore a previous value.

Disable the main entry BEFORE changing matches, loading saves or switching players.
Re-enable after loading and recheck the displayed values.
Old standalone Housing TEST rows use fixed addresses: do not merge or reuse them in another match.
</Comments>
</CheatTable>
