Deprecated: The behavior of unparenthesized expressions containing both '.' and '+'/'-' will change in PHP 8: '+'/'-' will take a higher precedence in /home/iano/public_html/tpforums-vb5/forum/includes/class_core.php on line 5842

PHP Warning: Use of undefined constant MYSQL_NUM - assumed 'MYSQL_NUM' (this will throw an Error in a future version of PHP) in ..../includes/init.php on line 165

PHP Warning: Use of undefined constant MYSQL_ASSOC - assumed 'MYSQL_ASSOC' (this will throw an Error in a future version of PHP) in ..../includes/init.php on line 165

PHP Warning: Use of undefined constant MYSQL_BOTH - assumed 'MYSQL_BOTH' (this will throw an Error in a future version of PHP) in ..../includes/init.php on line 165

PHP Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in ..../includes/functions_navigation.php on line 588

PHP Warning: "continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"? in ..../includes/functions_navigation.php on line 612

PHP Warning: Use of undefined constant misc - assumed 'misc' (this will throw an Error in a future version of PHP) in ..../global.php(29) : eval()'d code(6) : eval()'d code on line 1

PHP Warning: Use of undefined constant index - assumed 'index' (this will throw an Error in a future version of PHP) in ..../global.php(29) : eval()'d code(6) : eval()'d code on line 1

PHP Warning: Use of undefined constant misc - assumed 'misc' (this will throw an Error in a future version of PHP) in ..../includes/class_bootstrap.php(1422) : eval()'d code(4) : eval()'d code on line 1

PHP Warning: Use of undefined constant index - assumed 'index' (this will throw an Error in a future version of PHP) in ..../includes/class_bootstrap.php(1422) : eval()'d code(4) : eval()'d code on line 1

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6

PHP Warning: Use of undefined constant onlinestatusphrase - assumed 'onlinestatusphrase' (this will throw an Error in a future version of PHP) in ..../includes/class_core.php(4684) : eval()'d code on line 6
Tibia: Sprite File Structure - Page 2
Page 2 of 3 FirstFirst 123 LastLast
Results 11 to 20 of 23

Thread: Tibia: Sprite File Structure

  1. #11
    Junior Member
    Join Date
    Apr 2012
    Posts
    1

    RE: Tibia: Sprite File Structure

    I'm trying to read all the sprites file, but there is something wrong, this is my logical:

    Code:
    bytereader reader = new bytereader(filepath);
    read the signature 32bits; //read 4
    n = read the sprite number; //read 2
    myobj[] obj = new myobj[n]; //making an array of an object that I have created, this object would contain all sprites
    for (int i = 0; i < n; i++) {
        obj[i].offset = reader.readUInt32(); //reading all offsets and saving
    }
    
    for (int i = 0; i < n; i++) {
        reader.BaseStream.Seek(obj[i].Offset, SeekOrigin.Begin);
        obj.TPixel = Color.FromArgb(reader.ReadByte(), reader.ReadByte(), reader.ReadByte()); //here it calls a transparent pixel different of FF00FF
        obj.sprsize = math.sqrt(reader.ReadUInt16()); //The sqrt is returning 2 and not 32
    //then, here the program stops because it can't cotinue with a sprite 2x2
    }
    What is wrong?

    ----------------------------
    ::EDIT::
    I have studied the code from TibiaAPI that works perfectly: 6 + (spriteId - 1) * 4
    In the code, spriteId can't receive a value less than 2,
    so, if I choose 1, this will read the first offset (this is 241670), and the program will broken (this make non sense D: )
    but the sprites must begin reading the second one (241675, 5 bytes after the first), this make 4 bytes after the version and sprites number become useless and create confusion D:
    This make too, five useless bytes between 241670 and 241675... D:

    9.44
    Code:
    [F6] [31] [31] [4F] signature
    [00] [EC] number of sprites
    [06] [B0] [03] [00] useless offset that takes you to a position where you find five bytes: [00] [00] [00] [00] [00]
    [0B] [B0] [03] [00] second offset from the frog
    Hehe... If I'm having too much problem to read the Tibia.spr, I'll have too much more to compile it again after edit/add sprites
    D:

    ----------------------------
    ::EDIT::
    Another funny thing in that in the Tibia.spr 9.44 version it return a wrong number of sprites,
    there is not too many sprites as the number say that have (it says 60416, the file ends at 48798), but there are all the 60416 offsets (some of then offseting (if this word existis) to [00][00][00][00])

  2. #12
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723

    RE: Tibia: Sprite File Structure

    With the 9.60 client, currently the test server client, CipSoft had to increase the sprite count from an unsigned short (2 bytes) to an unsigned integer (4 bytes).

  3. #13
    Senior Member
    Join Date
    Nov 2010
    Posts
    397
    ty jo3 u should update the tag codes, kuz its hard to read this way ill private msg u <3

  4. #14
    Senior Member
    Join Date
    Jan 2008
    Location
    Cambridge, England
    Posts
    725
    Quote Originally Posted by Jo3Bingham View Post
    I've already written a thread on the sprite file structure, but I thought I'd post a new, updated, official one.

    Note: A big thanks to Sketchy for the help with greatly increasing the speed of reading the sprite, specifically creating a bitmap of the sprite, by replacing Bitmap.SetPixel() with BitMap.LockBits()/Bitmap.UnlockBits().

    - Like the Dat file structure, the sprite file begins with 4 bytes, which I call the SpriteVersion, and is unique for each client version so that the client knows that the correct sprite file is being used.
    - This is followed by 2 bytes that contains the number of sprites in the sprite file.

    - Immediately following this we have a 4-byte value for each sprite containing it's byte offset in the sprite file.

    [x] = Number of bytes.
    Code:
    [4] Sprite Version
    [2] Number of sprites
    [4] Offset of the first sprite
    [4] Offset of the second sprite
    [4] Offset of the third sprite
    ----Continue this for each sprite.
    - Now we start getting in to the actual sprite data. Each sprite begins with the transparent-pixels' color. In this case, the Tibia client uses magenta so the three bytes returned are 255, 0, 255. You don't need this pixel when creating the bitmap.
    - Following this is 2 bytes indicating the size of the sprite. (All sprites are 32x32 pixels, 1024 pixels, so this should never change.)
    - Next we start reading the actual pixel data of the sprite. This begins with 2 bytes holding the number of transparent pixels before colored pixel.

    - Then 2 bytes holding the number of colored pixels before a transparent pixel. Immediately following this is three 1-byte values holding the RGB value of the pixel. For example, if the number of colored pixels returned is 3 then you would read the next 9 bytes (3 bytes for each pixel) to get the pixel information of each.

    [x] = Number of bytes.
    Code:
    [1] - red value of transparent pixels
    [1] - green value of transparent pixels
    [1] - blue value of transparent pixels
    [2] - number of transparent pixels
    [2] - number of colored pixels (for this example we'll say it's 3)
    [1] - red value of the first colored pixel
    [1] - green value of the first colored pixel
    [1] - blue value of the first colored pixel
    [1] - red value of the second colored pixel
    [1] - green value of the second colored pixel
    [1] - blue value of the second colored pixel
    [1] - red value of the third colored pixel
    [1] - green value of the third colored pixel
    [1] - blue value of the third colored pixel
    [2] - number of transparent pixels
    [2] - number of colored pixels
    ----This will repeat for all pixels in the sprite.----
    - In the way we used to make bitmap's from the sprite data in TibiaAPI (which you can view here: http://code.google.com/p/tibiaapi/so...priteReader.cs ) we used Bitmap.SetPixel() to set the pixel color in the bitmap. While this is an easy way to achieve what we want, it's also a bit slow. But, thanks to Sketchy, you can achieve a 10x speed increase by using Bitmap.LockBits() and Bitmap.UnlockBits(). (In my tests, I could load the whole 9.44 sprite file in 35 seconds using Bitmap.SetPixels(), but I could load it in just 3.5 seconds! using Bitmap.LockBits()/Bitmap.UnlockBits().)

    Now, here's code used to read the sprite file that you can throw in to VB.Net and run. This uses the LockBits/UnlockBits method, and I have commented the code to help you understand what's going on. The sprite structure has been the same as far back as I can remember, so you shouldn't have a problem using this code for any client version. Also, this code reads and stores each sprite in an array, but you can modify it like the TibiaAPI code to only retrieve a specific sprite. If you have any questions feel free to ask:
    Code:
            static string 
    Public Class Form1
        Dim SpriteArray As Image()
    
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            Dim FileName As String = vbNullString
            MessageBox.Show("Please locate and open your Tibia.spr file", ".spr Reader")
            With OpenFileDialog1
                .Filter = "Spr file (*.spr)|*.spr|" & "All files|*.*"
                If .ShowDialog = Windows.Forms.DialogResult.OK Then
                    FileName = .FileName
                Else
                    Application.Exit()
                End If
            End With
    
            Using reader As New IO.BinaryReader(IO.File.OpenRead(FileName))
                Dim SpriteVersion As Integer = reader.ReadUInt32()
                Dim NumberOfSprites As Integer = reader.ReadUInt16()
                SpriteArray = Array.CreateInstance(GetType(Image), NumberOfSprites)
    
                For i As Integer = 0 To NumberOfSprites - 1 'even though sprite IDs start at 2, I started the loop at 0 for the array
                    Dim Sprite As Bitmap = New Bitmap(32, 32) 'this is the bitmap we'll be storing the sprite in, notice it's 32x32 pixels
                    Dim SpriteData As Drawing.Imaging.BitmapData = Sprite.LockBits(New Rectangle(0, 0, 32, 32), Imaging.ImageLockMode.ReadWrite, Imaging.PixelFormat.Format32bppArgb) 'here is where we lock the bits of the bitmap, since sprites are ARGB-based we have to make sure the format is the same
                    Dim ByteArray As Byte() = New Byte(4096) {} 'this is the array we'll store the pixel data in, even though a 32x32 pixel sprite has 1024 pixels we have to store 4 bits for each pixel (alpha, red, green blue)
                    Dim currentPixel As UShort = 0
                    Dim targetOffset As Long
    
                    'to begin, we have to seek to the sprite to get it's offset
                    'since I started the loop at 0 I have to add 1 because the sprites aren't 0-based (however, keep in mind sprite IDs begin at 2, not 1)
                    'each sprite is offset by 4 because the offset is stored as an integer (4 bytes)
                    'and the +6 is to skip past the SpriteVersion and NumberOfSprites
                    reader.BaseStream.Seek(6 + (i + 1) * 4, IO.SeekOrigin.Begin)
    
                    'now that we're at our desired sprite we have to read it's offset address (reader.ReadUInt32()), then we seek the returned value +3
                    'the +3 is to skip the transparent pixels' color, since we don't need it
                    reader.BaseStream.Seek(reader.ReadUInt32() + 3, IO.SeekOrigin.Begin)
    
                    'it seems as though sprite file for clients 7.40 and lower return a out-of-bounds position on the last sprite
                    'I didn't take the time to figure out why, but you can easily counter this problem with this line of code making sure the position
                    'of the stream is not beyond the file's size
                    If reader.BaseStream.Position >= reader.BaseStream.Length Then Continue For
    
                    targetOffset = reader.BaseStream.Position + reader.ReadUInt16() 'the ReadUInt16() returns the size of the sprite
                    If reader.BaseStream.Position < 9 Then Continue For 'if we encounter a blank sprite it actually sets the position of the stream at byte-5 and that's actually the beginning of the NumberOfSprites value, so we use Continue For to skip this sprite
    
                    While reader.BaseStream.Position < targetOffset
                        Dim transparentPixels As UShort = reader.ReadUInt16()
                        Dim coloredPixels As UShort = reader.ReadUInt16()
                        If (transparentPixels > 1024) OrElse (coloredPixels > 1024) Then Exit While 'as an extra padding of protection, I make sure the number of transparent and/or colored pixels doesn't exceed the size of a sprite
                        currentPixel += transparentPixels
                        For x As Integer = 0 To coloredPixels - 1
                            'here's where we get different from TibiaAPI's SetPixel()
                            'remember what I said about us storing all four pixel colors? here's where we use it
                            Dim CurrentOffset As Integer = (currentPixel * 4) 'this goes to the offset in ByteArray where the current pixel we're reading is going to be located
                            'pixel data is actually stored in reverse in bitmaps, so we write the Alpha value first (255) at the end of the pixel data, followed by Red before it, then Green before Red, and finally Blue at the beginning of the pixel data.
                            ByteArray(CurrentOffset + 3) = 255
                            ByteArray(CurrentOffset + 2) = reader.ReadByte()
                            ByteArray(CurrentOffset + 1) = reader.ReadByte()
                            ByteArray(CurrentOffset) = reader.ReadByte()
                            currentPixel += 1
                        Next
                    End While
    
                    Runtime.InteropServices.Marshal.Copy(ByteArray, 0, SpriteData.Scan0, 4096) 'here is where we copy the data in the byte array (where we stored the pixel data for the sprite) into our BitmapData
                    Sprite.UnlockBits(SpriteData) 'then we unlock the sprite so that we can use it
                    SpriteArray(i) = Sprite 'here I store the sprite in an Image array
                Next
            End Using
        End Sub
    End Class
    You can easily check each image by adding a PictureBox, TextBox, and Button to your form and using this code:
    Code:
            static string 
    'put this code in the button's click event
    If IsNumeric(TextBox1.Text) AndAlso Int(TextBox1.Text) >= 2 Then 'this makes sure the text in the text box is a number and that's not lower than 2 because sprite IDs start at 2 and our array is 0-based
        PictureBox1.Image = SpriteArray((Int(TextBox1.Text)) - 2) 'we use -2 here because of our array being 0-based and sprite IDs starting at 2
    End If
    Fixed quote tags incase anyone wants to see them. Also converted the code to C#, so that may be useful I guess:

    http://pastebin.com/Bmrvqs3L
    Last edited by XtrmJash; 06-29-2013 at 08:17 PM.

  5. #15
    Junior Member
    Join Date
    Jan 2015
    Posts
    4
    Hi, I was wondering if the structure of spr files remains the same for the latest version of Tibia (10.71), if not, what changed? Thanks in advance.

  6. #16
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723
    It should be the same. I'll take a look as soon as I can.

  7. #17
    Junior Member
    Join Date
    Jan 2015
    Posts
    4
    Thank you so much. Meanwhile, I'll be taking a look at the code of your Sprite Dump

  8. #18
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723
    Quote Originally Posted by Nano View Post
    Thank you so much. Meanwhile, I'll be taking a look at the code of your Sprite Dump
    The spr file structure is still the same. However, if you're using the code from the first post there are a couple of errors that need to be fixed. CipSoft changed the sprite count from an ushort (2-bytes) to an uint (4-bytes).

    So
    Code:
    [2] Number of sprites
    is now
    Code:
    [4] Number of sprites
    Then
    Code:
    Dim NumberOfSprites As Integer = reader.ReadUInt16()
    

    is now
    Code:
    Dim NumberOfSprites As UInteger = reader.ReadUInt32()
    

    And
    Code:
    reader.BaseStream.Seek(6 + (i + 1) * 4, IO.SeekOrigin.Begin)
    

    is now
    Code:
    reader.BaseStream.Seek(8 + (i + 1) * 4, IO.SeekOrigin.Begin)
    Here's the code I threw together to test it (C#):
    Code:
            private void ParseSpr()
            {
                string FileName = null;
                Microsoft.Win32.OpenFileDialog OpenFileDialog = new Microsoft.Win32.OpenFileDialog() { DefaultExt = ".spr", Filter = "Spr file (*.spr)|*.spr|" + "All files|*.*" };
                if ((Boolean)OpenFileDialog.ShowDialog())
                    FileName = OpenFileDialog.FileName;
                else
                    return;
    
    
                using (System.IO.BinaryReader BinaryReader = new System.IO.BinaryReader(System.IO.File.OpenRead(FileName)))
                {
                    uint SprVersion = BinaryReader.ReadUInt32();
                    uint SpriteCount = BinaryReader.ReadUInt32();
                    for (int i = 0; i < SpriteCount; i++)
                    {
                        BinaryReader.BaseStream.Seek(8 + (i + 1) * 4, System.IO.SeekOrigin.Begin);
                        BinaryReader.BaseStream.Seek(BinaryReader.ReadUInt32() + 3, System.IO.SeekOrigin.Begin);
                        long SpriteOffset = BinaryReader.BaseStream.Position + BinaryReader.ReadUInt16();
                        while (BinaryReader.BaseStream.Position < SpriteOffset)
                        {
                            ushort TransparentPixelCount = BinaryReader.ReadUInt16();
                            ushort ColoredPixelCount = BinaryReader.ReadUInt16();
                            for (int j = 0; j < ColoredPixelCount; j++)
                            {
                                BinaryReader.ReadBytes(3);
                            }
                        }
                    }
                }
            }
    
    Last edited by jo3bingham; 01-13-2015 at 07:24 PM.

  9. #19
    Junior Member
    Join Date
    Jan 2015
    Posts
    4
    Thank you so much for the quick answer!! The code worked perfectly I would like to ask one more question: Are there changes in the structure of the .dat file?
    Once again thanks in advance.

    EDIT: One extra question: Does BinaryReader.ReadUInt32() read the little-endian way?
    Last edited by Nano; 01-14-2015 at 12:34 AM. Reason: One extra question.

  10. #20
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723
    Quote Originally Posted by Nano View Post
    Thank you so much for the quick answer!! The code worked perfectly I would like to ask one more question: Are there changes in the structure of the .dat file?
    Once again thanks in advance.
    If you read through my thread you'll see I posted all the changes as they occurred, but there isn't a single post with all the changes. When I get home, and to my computer, I'll post an updated code on that thread.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •