Jo3Bingham

SpriteDump

Nov 22nd, 2013
408
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 21.63 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace SpriteDump
  7. {
  8.     class Program
  9.     {
  10.         // String variable to hold the filename of the .date file.
  11.         static string DatFileName = string.Empty;
  12.  
  13.         // String variable to hold the filename of the .spr file.
  14.         static string SprFileName = string.Empty;
  15.  
  16.         // Flags in the .dat file each object could have.
  17.         static byte Flag_Bank = 0;
  18.         static byte Flag_Clip = 1;
  19.         static byte Flag_Bottom = 2;
  20.         static byte Flag_Top = 3;
  21.         static byte Flag_Container = 4;
  22.         static byte Flag_Cumulative = 5;
  23.         static byte Flag_ForceUse = 6;
  24.         static byte Flag_MultiUse = 7;
  25.         static byte Flag_Write = 8;
  26.         static byte Flag_WriteOnce = 9;
  27.         static byte Flag_LiquidContainer = 10;
  28.         static byte Flag_LiquidPool = 11;
  29.         static byte Flag_Unpass = 12;
  30.         static byte Flag_Unmove = 13;
  31.         static byte Flag_Unsight = 14;
  32.         static byte Flag_Avoid = 15;
  33.         static byte Flag_NoMovementAnimation = 16;
  34.         static byte Flag_Take = 17;
  35.         static byte Flag_Hang = 18;
  36.         static byte Flag_HookSouth = 19;
  37.         static byte Flag_HookEast = 20;
  38.         static byte Flag_Rotate = 21;
  39.         static byte Flag_Light = 22;
  40.         static byte Flag_DontHide = 23;
  41.         static byte Flag_Translucent = 24;
  42.         static byte Flag_Shift = 25;
  43.         static byte Flag_Height = 26;
  44.         static byte Flag_LyingObject = 27;
  45.         static byte Flag_AnimateAlways = 28;
  46.         static byte Flag_Automap = 29;
  47.         static byte Flag_LensHelp = 30;
  48.         static byte Flag_FullBank = 31;
  49.         static byte Flag_IgnoreLook = 32;
  50.         static byte Flag_Clothes = 33;
  51.         static byte Flag_Market = 34;
  52.         static byte Flag_Default_Action = 35;
  53.         static byte Flag_Usable = 254;
  54.  
  55.         static void ParseDatFile()
  56.         {
  57.             // Use a BinaryReader object to read the .dat file in bytes.
  58.             // "using" automatically closes the file when exited.
  59.             using (System.IO.BinaryReader reader = new System.IO.BinaryReader(System.IO.File.OpenRead(DatFileName)))
  60.             {
  61.                 // Unique value for each .dat file.
  62.                 UInt32 DatVersion = reader.ReadUInt32();
  63.                 // Number of items.
  64.                 UInt16 ItemCount = reader.ReadUInt16();
  65.                 // Number of outfits.
  66.                 UInt16 OutfitCount = reader.ReadUInt16();
  67.                 // Number of effects.
  68.                 UInt16 EffectCount = reader.ReadUInt16();
  69.                 // Number of projectiles.
  70.                 UInt16 ProjectileCount = reader.ReadUInt16();
  71.  
  72.                 // Add number of items, outfits, effects, and projectiles together so we know how many objects to loop through.
  73.                 int maxID = ItemCount + OutfitCount + EffectCount + ProjectileCount;
  74.                 // Set ID equal to 100 because the first object ID is 100.
  75.                 // This is helpful if you want to store all the information for each object in an array, and you'll automatically know the ID of that item.
  76.                 // Eg., when we are on ID: 3031 we are on the information for gold coin.
  77.                 int ID = 100;
  78.  
  79.                 // Loop through each object.
  80.                 while ((ID < maxID))
  81.                 {
  82.                     // Read first flag.
  83.                     byte flag = reader.ReadByte();
  84.                     // Loop until flag equals 255.
  85.                     while (flag != 255)
  86.                     {
  87.                         // Is ground object. (Grass)
  88.                         if (flag == Flag_Bank)
  89.                         {
  90.                             // Speed at which a creature can move when on it.
  91.                             reader.ReadUInt16();
  92.                         }
  93.                         // Overlay order. (Door)
  94.                         else if (flag == Flag_Clip) { }
  95.                         // Overlay order. (Sewer Grate)
  96.                         else if (flag == Flag_Bottom) { }
  97.                         // Overlay order. (Roof)
  98.                         else if (flag == Flag_Top) { }
  99.                         // Container object. (Backpack)
  100.                         else if (flag == Flag_Container) { }
  101.                         // Can be stacked. (Gold)
  102.                         else if (flag == Flag_Cumulative) { }
  103.                         // Can only be used, cannot be used with. (Food)
  104.                         else if (flag == Flag_ForceUse) { }
  105.                         // Can be used and used with. (Potion)
  106.                         else if (flag == Flag_MultiUse) { }
  107.                         // Can be written on, and read. (Letter)
  108.                         else if (flag == Flag_Write)
  109.                         {
  110.                             // Max length of text that can be written.
  111.                             reader.ReadUInt16();
  112.                         }
  113.                         // Can only be read. (Blackboard)
  114.                         else if (flag == Flag_WriteOnce)
  115.                         {
  116.                             // Max length of text that can be on it.
  117.                             reader.ReadUInt16();
  118.                         }
  119.                         // Holds liquid. (Vial)
  120.                         else if (flag == Flag_LiquidContainer) { }
  121.                         // Liquid on ground. (Blood)
  122.                         else if (flag == Flag_LiquidPool) { }
  123.                         // Cannot be walked on. (Tree)
  124.                         else if (flag == Flag_Unpass) { }
  125.                         // Cannot be moved. (Statue)
  126.                         else if (flag == Flag_Unmove) { }
  127.                         // Blocks projectiles. (Wall)
  128.                         else if (flag == Flag_Unsight) { }
  129.                         // Character attempts to avoid when auto-walking. (Fire Field)
  130.                         else if (flag == Flag_Avoid) { }
  131.                         // I got nothing. (?)
  132.                         else if (flag == Flag_NoMovementAnimation) { }
  133.                         // Can be picked up. (Sword)
  134.                         else if (flag == Flag_Take) { }
  135.                         // Can be hung on a wall. (Tapestry)
  136.                         else if (flag == Flag_Hang) { }
  137.                         // Hangs facing South. (Dragon Lord Trophy)
  138.                         else if (flag == Flag_HookSouth) { }
  139.                         // Hangs facing East. (Behemoh Trophy)
  140.                         else if (flag == Flag_HookEast) { }
  141.                         // Can be rotated. (Chair)
  142.                         else if (flag == Flag_Rotate) { }
  143.                         // Gives off light. (Torch)
  144.                         else if (flag == Flag_Light)
  145.                         {
  146.                             // Brightness.
  147.                             reader.ReadUInt16();
  148.                             // Color.
  149.                             reader.ReadUInt16();
  150.                         }
  151.                         // Who knows. (?)
  152.                         else if (flag == Flag_DontHide) { }
  153.                         // Can be seen through? (?)
  154.                         else if (flag == Flag_Translucent) { }
  155.                         // Is shifted when drawn. (?)
  156.                         else if (flag == Flag_Shift)
  157.                         {
  158.                             // Shift on x-axis.
  159.                             reader.ReadUInt16();
  160.                             // Shift on y-axis.
  161.                             reader.ReadUInt16();
  162.                         }
  163.                         // Height? (?)
  164.                         else if (flag == Flag_Height)
  165.                         {
  166.                             // Elevation?
  167.                             reader.ReadUInt16();
  168.                         }
  169.                         // White lies? (?)
  170.                         else if (flag == Flag_LyingObject) { }
  171.                         // Is always animated. (Energy Field)
  172.                         else if (flag == Flag_AnimateAlways) { }
  173.                         // Is a pixel on the automap. (Mountain)
  174.                         else if (flag == Flag_Automap)
  175.                         {
  176.                             // Color. (Mountain = Gray)
  177.                             reader.ReadUInt16();
  178.                         }
  179.                         // Has extra "help" information (eg. IsLadder, IsSewer, IsDoor, etc.)
  180.                         else if (flag == Flag_LensHelp)
  181.                         {
  182.                             // Help value. (IsLadder = 0x44C; Could be different now.)
  183.                             reader.ReadUInt16();
  184.                         }
  185.                         // Has to do with ground objects. (?)
  186.                         else if (flag == Flag_FullBank) { }
  187.                         // I'm assuming it's ignored when you look at it. (?)
  188.                         else if (flag == Flag_IgnoreLook) { }
  189.                         // Can be placed in an inventory slot. (Helmet, Armor, Legs, etc.)
  190.                         else if (flag == Flag_Clothes)
  191.                         {
  192.                             // Inventory slot.
  193.                             reader.ReadUInt16();
  194.                         }
  195.                         // Can be bought/sold in the Market. (Demon Shield)
  196.                         else if (flag == Flag_Market)
  197.                         {
  198.                             // Market category. (Tools, Valuables, Premium Scrolls, etc.)
  199.                             reader.ReadUInt16();
  200.                             // ID of item for which sprite is displayed for trade.
  201.                             reader.ReadUInt16();
  202.                             // ID of item for which sprite is displayed when shown.
  203.                             reader.ReadUInt16();
  204.                             // Name length.
  205.                             UInt16 MarketNameLength = reader.ReadUInt16();
  206.                             // Name. Encoded with "iso-8859-1".
  207.                             reader.ReadBytes(MarketNameLength);
  208.                             /* Replace the line above (reader.ReadBytes(MarketNameLength);) with System.Text.Encoding.GetEncoding("iso-8859-1").GetString(reader.ReadBytes(MarketNameLength));
  209.                             to get the actual string. */
  210.                             // Profession object is restricted to. (eg., Knight)
  211.                             reader.ReadUInt16();
  212.                             // Level object is restricted to. (eg., Level 100)
  213.                             reader.ReadUInt16();
  214.                         }
  215.                         // Has a default action when clicked on (Smart-Click).
  216.                         else if (flag == Flag_Default_Action)
  217.                         {
  218.                             // Default action.
  219.                             reader.ReadUInt16();
  220.                         }
  221.                         // Is usable. My guess is this relates to the Smart-Click feature.
  222.                         else if (flag == Flag_Usable) { }
  223.                         // Invalid  flag!
  224.                         else { }
  225.  
  226.                         // Read next flag.
  227.                         flag = reader.ReadByte();
  228.                     }
  229.  
  230.                     // Width.
  231.                     byte Width = reader.ReadByte();
  232.                     // Height.
  233.                     byte Height = reader.ReadByte();
  234.  
  235.                     // If more than 1 sprite wide, or high.
  236.                     if (Width > 1 | Height > 1)
  237.                     {
  238.                         // Exact size.
  239.                         reader.ReadByte();
  240.                     }
  241.                     //else { exact size is 32 }
  242.  
  243.                     // Layers.
  244.                     byte Layers = reader.ReadByte();
  245.                     // Pattern width.
  246.                     byte PatternWidth = reader.ReadByte();
  247.                     // Pattern height.
  248.                     byte PatternHeight = reader.ReadByte();
  249.                     // Pattern depth.
  250.                     byte PatternDepth = reader.ReadByte();
  251.                     // Animation phases.
  252.                     byte Phases = reader.ReadByte();
  253.  
  254.                     // Multiply width, height, layers, pattern width, pattern height, pattern depth, and phases to get number of sprites.
  255.                     Int32 NumberOfSprites = Width * Height * Layers * PatternWidth * PatternHeight * PatternDepth * Phases;
  256.  
  257.                     // Loop to get each sprite.
  258.                     for (Int32 i = 0; i < NumberOfSprites; i++)
  259.                     {
  260.                         // Since the 9.60 client, sprite IDs are stored in 4 bytes.
  261.                         // Before 9.60, sprite IDs were stored in 2 bytes.
  262.                        
  263.                         // Sprite ID.
  264.                         UInt32 SpriteID = reader.ReadUInt32();
  265.  
  266.                         // Create empty bitmap for sprite.
  267.                         System.Drawing.Bitmap Sprite = null;
  268.  
  269.                         // Make sure the sprites ID is not 0 before calling the function to get its bitmap.
  270.                         if (SpriteID > 0)
  271.                         {
  272.                             // Call function to get the bitmap for the sprite.
  273.                             Sprite = GetSpriteBitmap(SpriteID);
  274.  
  275.                             // Make sure a folder for the sprites exists in the directory you launched the executable from.
  276.                             if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "/Sprites/"))
  277.                             {
  278.                                 // If not, create it.
  279.                                 System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "/Sprites/");
  280.                             }
  281.  
  282.                             // Save the sprite to the sprites folder in the format: "ItemID (SpriteID).png"
  283.                             Sprite.Save("Sprites/" + ID.ToString() + " (" + SpriteID.ToString() + ").png", System.Drawing.Imaging.ImageFormat.Png);
  284.                         }
  285.                     }
  286.  
  287.                     // Increase ID by 1 to continue the loop.
  288.                     ID += 1;
  289.                 }
  290.             }
  291.         }
  292.  
  293.         static System.Drawing.Bitmap GetSpriteBitmap(UInt32 SpriteID)
  294.         {
  295.             // Use a BinaryReader object to read the .spr file in bytes.
  296.             // "using" automatically closes the file when exited.
  297.             using (System.IO.BinaryReader reader = new System.IO.BinaryReader(System.IO.File.OpenRead(SprFileName)))
  298.             {
  299.                 // Create an empty 32 pixel by 32 pixel bitmap for the sprite.
  300.                 System.Drawing.Bitmap Sprite = new System.Drawing.Bitmap(32, 32);
  301.                 // Lock the sprite's bits. This makes creating bitmap images 100x more efficient than SetPixel().
  302.                 System.Drawing.Imaging.BitmapData SpriteData = Sprite.LockBits(new System.Drawing.Rectangle(0, 0, 32, 32), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
  303.                 // Create byte array to store data for each pixel. There are 1024 pixels in a 32x32 pixel image, and each pixel holds 4 bytes of data (4096 bytes of data).
  304.                 byte[] PixelData = new byte[4096];
  305.  
  306.                 // Seek ahead to the SpriteID. 8 is the number of bytes of the information at the beginning of the .spr file (SprVersion 4-bytes, NumberOfSprites 4-bytes).
  307.                 // Sprite IDs start at 2 so we have to subtract one to get the offset. Multiply by 4 because offsets are stored in 4-bytes.
  308.                 // Eg., after the first 8 bytes it starts the 4-byte offsets for each sprite ID. The first 4 bytes are empty (if I remember correctly).
  309.                 reader.BaseStream.Seek(8 + (SpriteID - 1) * 4, System.IO.SeekOrigin.Begin);
  310.                 // Reading 4-bytes will tell us the offset of our sprite ID. Skip 3 bytes because those hold the transparent pixel information for that sprite ID (this is usually/always magenta).
  311.                 reader.BaseStream.Seek(reader.ReadUInt32() + 3, System.IO.SeekOrigin.Begin);
  312.  
  313.                 // The next 2 bytes hold the size of the sprite in pixels. I add it to the stream position to make my while loop easier to maintain.
  314.                 long SpriteSize = reader.BaseStream.Position + reader.ReadUInt16();
  315.  
  316.                 // Create current pixel counter and set it to 0.
  317.                 UInt16 CurrentPixel = 0;
  318.  
  319.                 // Loop through each pixel.
  320.                 while (reader.BaseStream.Position < SpriteSize)
  321.                 {
  322.                     // 2 bytes hold the number of transparent pixels before there's a colored pixel.
  323.                     UInt16 TransparentPixels = reader.ReadUInt16();
  324.                     // 2 bytes hold the number of colored pixels before a transparent pixel.
  325.                     UInt16 ColoredPixels = reader.ReadUInt16();
  326.  
  327.                     // Skip past the transparent pixels because we only need the colored pixels.
  328.                     CurrentPixel += TransparentPixels;
  329.  
  330.                     // Loop through the colored pixels.
  331.                     for (int i = 0; i < ColoredPixels; i++)
  332.                     {
  333.                         // This is just the offset in our PixelData array that makes it easier to store each byte for each pixel's color.
  334.                         Int32 CurrentOffset = CurrentPixel * 4;
  335.  
  336.                         // Last byte in the current offset is the Alpha channel and it's always transparent.
  337.                         PixelData[CurrentOffset + 3] = 255;
  338.                         // Third byte in the current offset is the Red channel, and the first byte we read for this pixel.
  339.                         PixelData[CurrentOffset + 2] = reader.ReadByte();
  340.                         // Second byte in the current offset is the Blue channel, and the second byte we read for this pixel.
  341.                         PixelData[CurrentOffset + 1] = reader.ReadByte();
  342.                         // First byte in the current offset is the Green channel, and the last byte we read for this pixel.
  343.                         PixelData[CurrentOffset] = reader.ReadByte();
  344.  
  345.                         // Increase the CurrentPixel counter by 1.
  346.                         CurrentPixel += 1;
  347.                     }
  348.                 }
  349.  
  350.                 // Copy the bytes from our PixelData array to the BitmapData object we created when locking the Sprite bitmap's bits.
  351.                 System.Runtime.InteropServices.Marshal.Copy(PixelData, 0, SpriteData.Scan0, 4096);
  352.                 // Unlock Sprite bitmap's bits.
  353.                 Sprite.UnlockBits(SpriteData);
  354.                 // Return the Sprite ID's bitmap.
  355.                 return Sprite;
  356.             }
  357.         }
  358.  
  359.         [STAThread]
  360.         static void Main(string[] args)
  361.         {
  362.             // Ask the user to press the Enter key then locate the .dat file.
  363.             Console.WriteLine("Press Enter then locate the .dat file.");
  364.             // Wait for user to press a key.
  365.             Console.ReadKey();
  366.  
  367.             // Create an OpenFileDialog object to easily locate the .dat and .spr files.
  368.             System.Windows.Forms.OpenFileDialog OpenFileDialog = new System.Windows.Forms.OpenFileDialog
  369.             {
  370.                 // Set the filter to .dat files.
  371.                 Filter = "Dat file (*.dat)|*.dat|All files (*.*)|*.*"
  372.             };
  373.  
  374.             // Check which button the user pressed on the OpenFileDialog window.
  375.             if (OpenFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  376.             {
  377.                 // If 'OK', set our empty dat filename string to the filename of the selected file.
  378.                 DatFileName = OpenFileDialog.FileName;
  379.             }
  380.             else
  381.             {
  382.                 // Otherwise, leave the function. Which will close the program.
  383.                 return;
  384.             }
  385.  
  386.             // Ask the user to press the Enter key then locate the .spr file.
  387.             Console.WriteLine("Press Enter then locate the .spr file.");
  388.             // Wait for user to press a key.
  389.             Console.ReadKey();
  390.  
  391.             // Set the filter to .spr files so we can get the .spr file next.
  392.             OpenFileDialog.Filter = "Spr File (*.spr)|*.spr|All files (*.*)|*.*";
  393.  
  394.             if (OpenFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  395.             {
  396.                 // If 'OK', set our empty spr filename string to the filename of the selected file.
  397.                 SprFileName = OpenFileDialog.FileName;
  398.             }
  399.             else
  400.             {
  401.                 // Otherwise, leave the function. Which will close the program.
  402.                 return;
  403.             }
  404.  
  405.             // Tell user to wait patiently for dump to finish, we will let them know when it is finished.
  406.             Console.WriteLine("Please wait while sprites are dumped. You will be notified when this operation is complete.");
  407.  
  408.             // I'm going to create a stopwatch object to calculate how long it takes to dump all the sprites.
  409.             System.Diagnostics.Stopwatch Stopwatch = System.Diagnostics.Stopwatch.StartNew();
  410.  
  411.             // Call our function to parse the .dat file.
  412.             ParseDatFile();
  413.  
  414.             // Tell user how long it took to dump all the sprites.
  415.             Console.WriteLine("It took " + Stopwatch.ElapsedMilliseconds.ToString() + " milliseconds to dump sprites.");
  416.            
  417.             // Ask user to press the Enter key to close the application.
  418.             Console.WriteLine("Press Enter to close the application.");
  419.             // Wait for user to press a key before closing application.
  420.             Console.ReadKey();
  421.         }
  422.     }
  423. }
Add Comment
Please, Sign In to add comment