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 1 of 3 123 LastLast
Results 1 to 10 of 23

Thread: Tibia: Sprite File Structure

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

    Tibia: Sprite File Structure

    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=vb.net]
    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
    [/code]

    You can easily check each image by adding a PictureBox, TextBox, and Button to your form and using this code:
    [code=vb.net]
    '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
    [/code]
    Last edited by jo3bingham; 04-02-2013 at 04:49 PM.

  2. #2
    Senior Member
    Join Date
    Jan 2010
    Location
    Venezuela
    Posts
    366

    RE: Tibia: Sprite File Structure

    Amazing! Thank you so much Jo3, now I can try to read the sprite file using Qt, but I've read around that Qt doesn't supports ARGB32 so I'll have to do the transformation myself.

    So, I'm interested into writting the spr file.

    So we have the structure;

    Code:
    write 4 Bytes FileVersion
    write 2 Bytes SpriteCount
    write 3 Bytes (Sprite Offset?)
    
    -- Loop here through all the images array
    -- and write everything accordingly
    [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.----
    Dunno if this is correct.
    Okay reading your code, you say there's a 4 bytes (UInt32) sprite offset, where is this setup at?. What exactly is this random numbers?

  3. #3
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723

    RE: Tibia: Sprite File Structure

    No, reread my post closely. The 3 unnecessary bytes come before each sprite's data.

    Code:
    [4] File Version
    [2] Sprite Count
    ----Now we have sets of 4-bytes that contains the offset for each sprite.
    [4] Contains the offset of the first sprite
    [4] Contains the offset of the second sprite
    [4] Contains the offset of the third sprite
    ----This continues for each sprite. (The number in Sprite Count.)
    ----After all of the offsets we start the data for the first sprite.
    ----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.
    [1] 255
    [1] 0
    [1] 255
    ----Note that I only tested that on maybe 10 sprites. So you may have to do more research to verify this.
    ----Now we get into the actual pixels for this sprite. (This is just an example sprite.)
    [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
    ----Repeat for all transparent and colored pixels in the sprite. Once finished it will start at the next sprite, beginning with the 3 bytes I mentioned before.
    Hope that helps.

  4. #4
    Senior Member
    Join Date
    Nov 2009
    Posts
    320

    RE: Tibia: Sprite File Structure

    What the advantage of reading the sprites file? (not editors)
    Because the .dat have all sprites linked, didnt it?

    If I want to show all the sprites on my bot, I need to read both .spr and .dat ?

  5. #5
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723

    RE: Tibia: Sprite File Structure

    The Dat file just tells you what sprites are associated with that object. You have to use the sprite file to actually retrieve the sprite...

  6. #6
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723

    RE: Tibia: Sprite File Structure

    Updated my post to explain that the 3 bytes that begin each sprite's pixel data is just the color of it's transparent pixels. As far as I know, the Tibia client has always used magenta (red = 255, green = 0, blue = 255), so those bytes will always contain 255, 0, and 255 for it's RGB values, respectively.

  7. #7
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723

    RE: Tibia: Sprite File Structure

    I ran into an error last night with the sprite file for clients 7.40 and lower where the last sprite's offset returned a position out of the bounds of the file. I didn't take the time to find out why, but I added some code in the original post to show you how to counter this problem.

    Also, I started using WPF again, and it uses DirectX-based imaging. Therefore, System.Drawing.Bitmap is no longer usable and you can't use the Lock/Unlock-Bits method. However, you can easily accomplish this by using a BitmapResource, and from my tests it's almost twice as fast as Lock/Unlock-Bits (obviously, because you're not using DirectX-based imaging and not GDI).

  8. #8

    RE: Tibia: Sprite File Structure

    The first 10 bytes of spr of tibia 9.44

    Code:
    F6 31 31 4F = Spr version
    00 EC  = Number of sprite?
    06 B0 03 00 First offset?
    My old code the begin of sprite offset are:

    Code:
    F6 31 31 4F
    00 EC 06 B0 03
    00 0B B0 03 <- first offset pointer

  9. #9
    Administrator
    Join Date
    Mar 2007
    Posts
    1,723

    RE: Tibia: Sprite File Structure

    Quote Originally Posted by theafien
    The first 10 bytes of spr of tibia 9.44

    Code:
    F6 31 31 4F = Spr version
    00 EC  = Number of sprite?
    06 B0 03 00 First offset?
    My old code the begin of sprite offset are:

    Code:
    F6 31 31 4F
    00 EC 06 B0 03
    00 0B B0 03 <- first offset pointer
    You answered your own question. See the second line in your bytes (00 EC 06 B0 03)? The first two (00 EC) is the number of sprites and then it starts the first offset (06 B0 03 00), just like you showed in your first example.

  10. #10

    RE: Tibia: Sprite File Structure

    Quote Originally Posted by Jo3Bingham
    Quote Originally Posted by theafien
    The first 10 bytes of spr of tibia 9.44

    Code:
    F6 31 31 4F = Spr version
    00 EC  = Number of sprite?
    06 B0 03 00 First offset?
    My old code the begin of sprite offset are:

    Code:
    F6 31 31 4F
    00 EC 06 B0 03
    00 0B B0 03 <- first offset pointer
    You answered your own question. See the second line in your bytes (00 EC 06 B0 03)? The first two (00 EC) is the number of sprites and then it starts the first offset (06 B0 03 00), just like you showed in your first example.
    O God, i was reading as big endian.. sorry my stupidity.

    , cool. +rep

Posting Permissions

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