"""Read a current instantiated Nullable<Int32> without guessing its layout.

The existing file-only derive_map_field_offsets27 parser establishes the
Il2CppGenericClass(base, classInst, methodInst, cachedClass) four-pointer shape.
Actual closed generic field offsets are read and verified at runtime. Open
generic zero offsets, field-order guesses and missing cache fallbacks are not
used. This helper performs only reads via the caller's existing reader.
"""


def read_nullable_int32(session, pointer, schema, field_name):
    r=session.reader
    klass=r.u64(pointer)
    fields=session.fields(klass,schema)
    declaration=next(f for f in schema['fields'] if f['name']==field_name)
    if declaration['type']['type_code']!=21 or declaration['type'].get('name')!='Nullable`1' or declaration['type'].get('namespace')!='System':
        raise ValueError('nullable_int32_source_type_mismatch')
    if declaration['type'].get('arguments')!=[{'type_code':8,'attrs':0}]:
        raise ValueError('nullable_int32_source_argument_mismatch')
    table=r.u64(klass+0x80)
    type_pointer=r.u64(table+declaration['index']*32+8)
    if ((r.u32(type_pointer+8)>>16)&255)!=21:
        raise ValueError('nullable_int32_current_type_mismatch')
    generic=r.u64(type_pointer)
    if not generic:raise ValueError('current_nullable_int32_generic_missing')
    session.watch(generic+24,8)
    closed_class=r.u64(generic+24)
    if not closed_class:raise ValueError('current_nullable_int32_closed_class_not_initialized')
    if r.class_name(closed_class)!=('Nullable`1','System'):
        raise ValueError('current_nullable_int32_closed_class_mismatch')
    shape=r.layout(closed_class,['hasValue','value'])
    hf,vf=shape['fields']
    if (hf['type_code'],vf['type_code'])!=(2,8):
        raise ValueError('current_nullable_int32_field_types_changed')
    inline_size=min(f['offset'] for f in schema['fields'] if f['offset']>fields[field_name])-fields[field_name]
    ho,vo=hf['offset']-16,vf['offset']-16
    if not (inline_size==8 and 0<=ho<inline_size and 0<=vo<=inline_size-4 and not vo<=ho<vo+4):
        raise ValueError('current_nullable_int32_offsets_outside_source_span')
    address=pointer+fields[field_name]
    session.watch(address,inline_size)
    has_value=r.read(address+ho,1)[0]
    if has_value not in (0,1):raise ValueError('current_nullable_int32_presence_tag_invalid')
    return r.i32(address+vo) if has_value else None
