import struct,sys,bisect
sys.path.insert(0,'/tmp/claude-0/-root-ants/f0a0d69c-fbe4-4887-8c0d-a5302a0da398/scratchpad')
from meta import *

SO_PATH='extracted/lib/armeabi-v7a/libil2cpp.so'
B=open(SO_PATH,'rb').read()
e_shoff,=struct.unpack_from('<I',B,0x20)
esz,enum_,estr=struct.unpack_from('<HHH',B,0x2e)
raw=[struct.unpack_from('<10I',B,e_shoff+i*esz) for i in range(enum_)]
stro=raw[estr][4]
def sn(x):
    e=B.index(b'\0',stro+x); return B[stro+x:e].decode()
SEC={sn(r[0]):(r[3],r[4],r[5]) for r in raw}
def va2off(va):
    for n,(a,o,s) in SEC.items():
        if a and a<=va<a+s and n!='.bss': return o+(va-a)
    return None
def u32(va): return struct.unpack_from('<I',B,va2off(va))[0]

CODEREG=0x1994124
METAREG=0x199415c
methodPointersCount=u32(CODEREG); methodPointers=u32(CODEREG+4)
typesCount=u32(METAREG+0x18); typesPtr=u32(METAREG+0x1c)
metadataUsagesCount=u32(METAREG+0x38); metadataUsages=u32(METAREG+0x3c)
print('methodPointers  0x%x count=%d'%(methodPointers,methodPointersCount))
print('types           0x%x count=%d'%(typesPtr,typesCount))
print('metadataUsages  0x%x count=%d'%(metadataUsages,metadataUsagesCount))

MPOFF=va2off(methodPointers)
MP=struct.unpack_from('<%dI'%methodPointersCount,B,MPOFF)

# ---- SymbolMap: address -> size
SM=open('extracted/assets/bin/Data/Managed/SymbolMap-ARMv7','rb').read()
n,=struct.unpack_from('<I',SM,0)
sym=[struct.unpack_from('<QI',SM,4+i*12) for i in range(n)]
symaddr=[s[0] for s in sym]; symsize={s[0]:s[1] for s in sym}
print('symbolmap entries',n,'first 0x%x'%symaddr[0],'last 0x%x'%symaddr[-1])

# ---- metadata usage: destinationIndex -> (kind, srcIndex)
ul=SEC_ = None
from meta import SEC as MSEC, D as MD
up_off,up_size=MSEC['usagepairs'].off,MSEC['usagepairs'].size
NPAIR=up_size//8
usage={}
KIND={1:'TypeInfo',2:'Il2CppType',3:'MethodDef',4:'FieldInfo',5:'StringLiteral',6:'MethodRef'}
for i in range(NPAIR):
    dest,enc=struct.unpack_from('<II',MD,up_off+i*8)
    kind=(enc&0xE0000000)>>29
    idx=enc&0x1FFFFFFF
    usage[dest]=(kind,idx)
print('usage pairs',NPAIR)
# VA of usage slot -> string literal
slot2lit={}
for dest,(k,idx) in usage.items():
    if k==5:
        slot2lit[u32(metadataUsages+4*dest)]=idx
print('string-literal usage slots',len(slot2lit))

def method_addr(mi):
    """mi = Il2CppMethodDefinition.methodIndex"""
    if mi<0 or mi>=methodPointersCount: return 0
    return MP[mi]&~1

def method_strings(addr,size):
    """scan the function's bytes (incl. ARM literal pool) for usage-slot VAs"""
    o=va2off(addr)
    if o is None or size<=0: return []
    out=[]
    for k in range(0,size-3,2):
        w,=struct.unpack_from('<I',B,o+k)
        if w in slot2lit: out.append(literal(slot2lit[w]))
    # dedupe preserving order
    seen=set(); r=[]
    for x in out:
        if x not in seen: seen.add(x); r.append(x)
    return r

def fn_size(addr):
    i=bisect.bisect_left(symaddr,addr)
    if i<len(symaddr) and symaddr[i]==addr: return symsize[addr]
    return 0
