OpenTibia [7.72] Tibia Dat Editor

Nekiro

Support Team
Support Team
Joined
Sep 7, 2015
Messages
1,929
Best answers
82
Reaction score
709
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:

Under Influence

Active Member
Joined
Jul 27, 2009
Messages
61
Best answers
0
Reaction score
47
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:
Top