Level Entity (LENT) Section - Troodon80/Summoner-Save-File-Editor GitHub Wiki

Level Entity (LENT) Section

Contains data for player characters and other creatures:

Field Offset Size Description
Signature 0x00027667 4 bytes "LENT" ASCII string
FirstEntityOffset +0x04 4 bytes Relative offset to first entity?
EndRegionOffset +0x08 4 bytes Relative offset to end of entity region?
Status Bitfield +0x0C 4 bytes Bitfield for status (bit 26 indicates death/invalid/no longer loaded)
Summon Name +0x10 4 bytes Possibly a global save ID/hash?
EntityCount +0x30 4 bytes Number of characters and creatures
Character/Creature Data +0x34 Variable Individual character and creature data starts here

Character Data

Each entity contains:

Common Header Structure

Both character and creature entities begin with the same header structure:

Field Offset Size Description
Entity Hash +0x00 4 bytes String-hash identifier
Entity Type +0x04 4 bytes Type identifier (7 = Character, other values = Creature/NPC)
Entity block data follows +0x08 4 bytes See tables for type 7 and non-type 7 immediately following

Character Block (Type 7)

Characters (Joseph, Flece, Rosalind, Jekhar, and current Summon) have the following structure:

Field Offset Size Description
Position X 0x00 4 bytes X coordinate (float)
Position Z 0x04 4 bytes Z coordinate (float)
Position Y 0x08 4 bytes Y coordinate (float)
Rotation 0x0C 4 bytes Rotation angle (float)
Unknown Int 1 0x10 4 bytes First unknown integer
Unknown Int 2 0x14 4 bytes Second unknown integer
State Flags 0x18 4 bytes State flags (bit 26 indicates death/invalid/no longer loaded)
Status Effects 0x1C 76 bytes 4 sets of data, relating to status effects. Each containing:
- Flag? (1 byte) Possibly self-cast or buff or detrimental debuff?
- Type (1 byte) Type or Category?
- SubType (1 byte)
- Value 1 (4 bytes) Timing?
- Value 2 (4 bytes) Timing?
- Value 3 (4 bytes) Timing?
- Value 4 (4 bytes) Timing?
Equipped Items 0x68 36 bytes 9 equipment slots (4 bytes each):
- Amulet
- Ring 1
- Ring 2
- Gauntlets
- Boots
- Weapon
- Shield
- Torso
- Leggings
Unmapped Block 0x8C 144 bytes 36 integers of unmapped data
Hand to Hand 0x11C 1 byte Hand to Hand skill level (used by the player's Summon, e.g. Blackfire Elemental)
Unknown Skill 1 0x11D 1 byte Unknown character skill
Character Speed 0x11E 1 byte Movement/action speed
Sword 0x11F 1 byte Sword skill level
Axe 0x120 1 byte Axe skill level
Blunt 0x121 1 byte Blunt weapon skill level
Staff 0x122 1 byte Staff skill level
Bow 0x123 1 byte Bow skill level
Heavy Arms 0x124 1 byte Heavy weapons skill level
Parry 0x125 1 byte Parry skill level
Counter Attack 0x126 1 byte Counter Attack skill level
Dodge 0x127 1 byte Dodge skill level
Critical Hit 0x128 1 byte Critical Hit skill level
Double Attack 0x129 1 byte Double Attack skill level
Push 0x12A 1 byte Push skill level
Kick 0x12B 1 byte Kick skill level
Trip 0x12C 1 byte Trip skill level
Aimed Attack 0x12D 1 byte Aimed Attack skill level
Backstab 0x12E 1 byte Backstab skill level
Assess 0x12F 1 byte Assess skill level
Appraise 0x130 1 byte Appraise skill level
Picklock 0x131 1 byte Picklock skill level
Sneak 0x132 1 byte Sneak skill level
Hide 0x133 1 byte Hide skill level
Unknown Skills 0x134 3 bytes 3 unknown skill bytes
Summon 0x137 1 byte Summon skill level
Heal 0x138 1 byte Heal magic skill level
Dark 0x139 1 byte Dark magic skill level
Energy 0x13A 1 byte Energy magic skill level
Holy 0x13B 1 byte Holy magic skill level
Fire 0x13C 1 byte Fire magic skill level
Ice 0x13D 1 byte Ice magic skill level
Unknown Skill 3 0x13E 1 byte Unknown skill
Magic Resist 0x13F 1 byte Magic resistance skill level
Character Level 0x140 2 bytes Character level (short)
Experience 0x142 4 bytes Character experience points
Current HP 0x146 4 bytes Current hit points (float)
Base HP 0x14A 4 bytes Maximum hit points (float)
Current AP 0x14E 2 bytes Current ability points (short)
Base AP 0x150 2 bytes Maximum ability points (short)
Unknown Value 0x152 4 bytes Unknown purpose
AI Value 0x156 4 bytes AI behaviour control value (Melee, Support, Range, Healer, caster, Healer/Caster)
Unmapped Character Data 0x15A 34 bytes Additional unmapped character data
Skill Points 0x17C 4 bytes Available skill points

Note: Skills are 0-10 in-game, but stored as a multiple of 10. 5 becomes 50, 10 becomes 100, 25 becomes 250 (which would be the maximum here since it's only a byte). When editing, it can go to 255, which gets rounded to 26 in-game.

Creature Block (Non-Type 7; Type 4, 5, 6, etc.)

Non-player creatures (enemies, monsters, NPCs) have the following structure:

Field Offset Size Description
Position X 0x00 4 bytes X coordinate (float)
Position Z 0x04 4 bytes Z coordinate (float)
Position Y 0x08 4 bytes Y coordinate (float)
Rotation 0x0C 4 bytes Rotation angle (float)
Unknown Int 1 0x10 4 bytes Unknown integer
Unknown Int 2 0x14 4 bytes Unknown integer
State Flags 0x18 4 bytes State flags (bit 26 indicates death/invalid/no longer loaded)
Status Effects 0x1C 76 bytes 4 sets of data, relating to status effects. Each containing:
- Flag? (1 byte) Possibly self-cast or buff or detrimental debuff?
- Type (1 byte) Type or Category?
- SubType (1 byte)
- Value 1 (4 bytes) Timing?
- Value 2 (4 bytes) Timing?
- Value 3 (4 bytes) Timing?
- Value 4 (4 bytes) Timing?
Creature Level 0x68 2 bytes Creature level (short)
Current HP 0x6A 4 bytes Current hit points (float)
Base HP 0x6E 4 bytes Maximum hit points (float)
Current AP 0x72 2 bytes Current ability points (short)
Base AP 0x74 2 bytes Maximum ability points (short)
Unknown Value 0x76 4 bytes Integer, unknown purpose
AI Value 0x7A 4 bytes AI behaviour control value
Unmapped 0x7E 248 bytes Unmapped
Unknown/Padding 0x176 2 bytes Unknown end value (short)

Note: As previously mentioned, there is a section immediately following the main series of entity blocks that follows a pattern of count+hash and appears to be entity linking. I'm not doing anything with this block, but a proof of concept parser is as follows:

Click to expand...
public class LentRelatedBlock
{
    public static long CurrentPosition = 0;
    public class Entry
    {
        public uint EntityHash { get; set; }
        public List<uint> RelatedHashes { get; set; } = new();
        public int TrailingInt { get; set; }
    }

    public static List<Entry> ReadFromStream(BinaryReader reader, int offset, int baseOffset)
    {
        var entries = new List<Entry>();
        reader.BaseStream.Seek(offset, SeekOrigin.Begin);

        int outerCount = reader.ReadInt32();
        for (int i = 0; i < outerCount; i++)
        {
            uint entityHash = reader.ReadUInt32();
            if (entityHash == 0)
                continue;

            int innerCount = reader.ReadInt32();
            var related = new List<uint>();

            for (int j = 0; j < innerCount; j++)
            {
                uint relatedHash = reader.ReadUInt32();
                if (relatedHash != 0)
                    related.Add(relatedHash);
            }

            int unknownTrailingInt = reader.ReadInt32();

            entries.Add(new Entry
            {
                EntityHash = entityHash,
                RelatedHashes = related,
                UnknownInt = unknownTrailingInt
            });
        }
            CurrentPosition = reader.BaseStream.Position;
        return entries;
    }

    public static List<Entry> ReadFromFile(string path, int offset, int baseOffset)
    {
        using var stream = File.OpenRead(path);
        using var reader = new BinaryReader(stream);
        return ReadFromStream(reader, offset, baseOffset);
    }
}

Not that this does not parse to the beginning of the CMRA block. There is still more data to be read. I just don't know how yet or what it does; it's covered by le_size, so I'd guess it's more entity-related data or maybe it's just junk data not zeroed out. If it is additional used data and not junk data, then it doesn't appear to be read sequentially but rather as a block and then parsed later.

Screenshots...

image

⚠️ **GitHub.com Fallback** ⚠️