• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

OpenTibia [7.72] Tibia Dat Editor

Nekiro

Legendary OT User
TFS Developer
Joined
Sep 7, 2015
Messages
2,684
Solutions
127
Reaction score
2,130
Hello, recently I wrote this tool to edit tibia dat editor, it was tested on tibia dat version 7.72, it may work on older or slightly newer but I'm not sure about that. It's pretty basic with basic interface.

Repository: nekiro/7.72-dat-editor (https://github.com/nekiro/7.72-dat-editor)

You can find compiled binary in release section.
Enjoy!
 
Last edited:
I used his code and converted it to typescript/nodeJS and the output is a json file with Tibia.dat structure.

JavaScript:
interface Sprite {
    height?: number;
    width?: number;
    blendFrames?: number;
    divX?: number;
    divY?: number;
    divZ?: number;
    animations?: number;
}


interface Item
{
    id?: number;
    ground?: boolean;
    groundSpeed?: number;
    clip?: boolean;
    top?: boolean;
    bottom?: boolean;
    container?: boolean;
    stackable?: boolean;
    corpse?: boolean;
    usable?: boolean;
    writable?: boolean;
    writableLenght?: number;
    readable?: boolean;
    readableLenght?: number;
    fluid?: boolean;
    splash?: boolean;
    blocking?: boolean;
    immovable?: boolean;
    blocksMissile?: boolean;
    blocksPath?: boolean;
    pickupable?: boolean;
    hangable?: boolean;
    vertical?: boolean;
    horizontal?: boolean;
    rotatable?: boolean;
    hasLight?: boolean;
    lightLevel?: number;
    lightColor?: number;
    floorchange?: boolean;
    hasOffset?: boolean;
    dontHide?: boolean;
    offsetX?: number;
    offsetY?: number;
    hasHeight?: boolean;
    height?: number;
    layer?: boolean;
    idleAnimated?: boolean;
    minimap?: boolean;
    minimapColor?: number;
    actioned?: boolean;
    actions?: number;
    groundItem?: boolean;
    sprite?: Sprite;
}

const fs = require("fs");
const ffp = require("file-format-parser");

ffp.addDataFormat("MyFileType", [
    {
        type: "uint32",
        storageKey: "datSignature"
    },
    {
        type: "uint16",
        storageKey: "maxClientId"
    },
    {
        type: "uint16",
        storageKey: "looktypes"
    },
    {
        type: "uint16",
        storageKey: "effects"
    },
    {
        type: "uint16",
        storageKey: "missiles"
    },
    {
        type: "items",
        storageKey: "ParseDat"
    }
]);

ffp.addDataType("items", {
    read: (reader, entity, store) => {
        const maxClientID = store.maxClientId
        const itemArray = []
        const spriteArray = []
        let id = 100
        let flag;

        try {
            while (id <= maxClientID) {
                const item: Item = {}
                item.id = id;

                do {
                    flag = reader.read(1).toString("hex")
                    switch (flag) {
                    case "00": // ground
                        item.ground = true;
                        item.groundSpeed = reader.read(2).swap16().readUInt16BE() // ground speed
                        break;
                    case "01": // clip
                        item.clip = true;
                        break;
                    case "02": // bottom
                        item.bottom = true;
                        break;
                    case "03": // top
                        item.top = true;
                        break;
                    case "04": // container
                        item.container = true;
                        break;
                    case "05": // stackable
                        item.stackable = true;
                        break;
                    case "06": // corpse (force use)
                        item.corpse = true;
                        break;
                    case "07": // usable
                        item.usable = true;
                        break;
                    case "08": // writable
                        item.writable = true;
                        item.writableLenght = reader.read(2).swap16().readUInt16BE() // maxTextLenght
                        break;
                    case "09": // readable
                        item.readable = true;
                        item.readableLenght = reader.read(2).swap16().readUInt16BE() // maxTextLenght
                        break;
                    case "0A": // fluid container
                        item.fluid = true;
                        break;
                    case "0B": // splash
                        item.splash = true;
                        break;
                    case "0C": // blocking
                        item.blocking = true;
                        break;
                    case "0D": // not movable
                        item.immovable = true;
                        break;
                    case "0E": // blocks missile
                        item.blocksMissile = true;
                        break;
                    case "0F": // blocks path
                        item.blocksPath = true;
                        break;
                    case "10": // pickupable
                        item.pickupable = true;
                        break;
                    case "11": // hangable
                        item.hangable = true;
                        break;
                    case "12": // horizontal
                        item.horizontal = true;
                        break;
                    case "13": // vertical
                        item.vertical = true;
                        break;
                    case "14": // rotatable
                        item.rotatable = true;
                        break;

                    case "15": // light info
                        item.hasLight = true;
                        item.lightLevel = reader.read(2).swap16().readUInt16BE() // light level
                        item.lightColor = reader.read(2).swap16().readUInt16BE() // light color
                        break;
                    case "16": // don't hide
                        item.dontHide = true;
                        break;
                    case "17": // translucent
                        item.floorchange = true;
                        break;
                    case "18": // offset
                        item.hasOffset = true;
                        item.offsetX = reader.read(2).swap16().readUInt16BE() // X
                        item.offsetY = reader.read(2).swap16().readUInt16BE() // Y
                        break;
                    case "19": // height (elevation)
                        item.hasHeight = true;
                        item.height = reader.read(2).swap16().readUInt16BE() // height
                        break;
                    case "1a": // lying object
                        item.layer = true;
                        break;
                    case "1b": // idle animated
                        item.idleAnimated = true;
                        break;
                    case "1c": // minimap
                        item.minimap = true;
                        item.minimapColor = reader.read(2).swap16().readUInt16BE() // color
                        break;
                    case "1d": // actions
                        item.actioned = true;
                        item.actions = reader.read(2).swap16().readUInt16BE()
                        break;
                    case "1e": // full ground
                        item.groundItem = true;
                        break;
                    case "ff": // end of flags
                        break;
                    default:
                        break;
                    }
                } while (flag !== "ff");

                const sprite: Sprite = {}

                sprite.width = reader.read(1).readInt8()
                sprite.height = reader.read(1).readInt8()
                if (sprite.width > 1 || sprite.height > 1) {
                    reader.read(1) // skip byte
                }

                sprite.blendFrames = reader.read(1).readInt8()
                sprite.divX = reader.read(1).readInt8()
                sprite.divY = reader.read(1).readInt8()
                sprite.divZ = reader.read(1).readInt8()
                sprite.animations = reader.read(1).readInt8()

                const totalSprites = sprite.width * sprite.height * sprite.blendFrames
                    * sprite.divX * sprite.divY * sprite.divZ * sprite.animations;        
                for (let i = 0; i < totalSprites; i++) {
                    reader.read(2)
                }

                item.sprite = sprite

                itemArray.push(item)
                ++id
            }
        } catch (err) {
            console.log(err)
        } finally {
            return itemArray
        }
    }
});

const data = ffp.parseFile("./Tibia.dat", "MyFileType")
fs.writeFile("tibiadat.json", JSON.stringify(data), "utf8", (err) => {
    if (err) {
        console.log("An error occured while writing JSON Object to File.");
        return console.log(err);
    }

    console.log("JSON file has been saved.");
});

Sample output:

Code:
{
    "datSignature": 1134385715,
    "maxClientId": 5089,
    "looktypes": 254,
    "effects": 25,
    "missiles": 15,
    "ParseDat": [
        {
            "id": 100,
            "ground": true,
            "groundSpeed": 0,
            "groundItem": true,
            "sprite": {
                "width": 1,
                "height": 1,
                "blendFrames": 1,
                "divX": 4,
                "divY": 4,
                "divZ": 1,
                "animations": 1
            }
        },
        {
            "id": 101,
            "ground": true,
            "groundSpeed": 0,
            "groundItem": true,
            "sprite": {
                "width": 1,
                "height": 1,
                "blendFrames": 1,
                "divX": 1,
                "divY": 1,
                "divZ": 1,
                "animations": 1
            }
        },
        {
            "id": 102,
            "ground": true,
            "groundSpeed": 150,
            "minimap": true,
            "minimapColor": 24,
            "groundItem": true,
            "sprite": {
                "width": 1,
                "height": 1,
                "blendFrames": 1,
                "divX": 1,
                "divY": 1,
                "divZ": 1,
                "animations": 1
            }
        }
    ]
}
 
Last edited:
how can edit sprit phoenix egg, i have put in 4 animations and 5 parts. i dont know about div XYZ, in-game count 1 to 4 eggs ok, afte 5 back to one animation, also 10~20 egg show 2 egg, and 25~49 show 3 eggs. 50+ show 4 eggs.
I dont know why the last full animation sprit does not appear.
client 7.72 - edit demonic application


Wow, I opened version 8.6 and looked at the example, now everything is ok!
1615091267173.png
 
Last edited:
Back
Top