Skip to content

Instantly share code, notes, and snippets.

@amoeba
Created March 16, 2025 20:57
Show Gist options
  • Save amoeba/425e709e8dc38a6a0c6ba991f822e728 to your computer and use it in GitHub Desktop.
Save amoeba/425e709e8dc38a6a0c6ba991f822e728 to your computer and use it in GitHub Desktop.
using ACE.DatLoader.FileTypes;
using ACE.Entity.Enum;
using SkiaSharp;
List<int> GetImageColorArray(Texture texture)
{
List<int> colors = new List<int>();
if (texture.Length == 0) return colors;
switch (texture.Format)
{
case SurfacePixelFormat.PFID_R8G8B8: // RGB
using (BinaryReader reader = new BinaryReader(new MemoryStream(texture.SourceData)))
{
for (uint i = 0; i < texture.Height; i++)
for (uint j = 0; j < texture.Width; j++)
{
byte b = reader.ReadByte();
byte g = reader.ReadByte();
byte r = reader.ReadByte();
int color = (r << 16) | (g << 8) | b;
colors.Add(color);
}
}
break;
case SurfacePixelFormat.PFID_A8R8G8B8: // ARGB format. Most UI textures fall into this category
using (BinaryReader reader = new BinaryReader(new MemoryStream(texture.SourceData)))
{
for (uint i = 0; i < texture.Height; i++)
for (uint j = 0; j < texture.Width; j++)
colors.Add(reader.ReadInt32());
}
break;
}
return colors;
}
SKBitmap GetBitmap(Texture texture, List<int> colorArray)
{
SKBitmap image = new SKBitmap(texture.Width, texture.Height);
switch (texture.Format)
{
case SurfacePixelFormat.PFID_R8G8B8:
case SurfacePixelFormat.PFID_CUSTOM_LSCAPE_R8G8B8:
for (int i = 0; i < texture.Height; i++)
for (int j = 0; j < texture.Width; j++)
{
int idx = (i * texture.Width) + j;
byte r = (byte)((colorArray[idx] & 0xFF0000) >> 16);
byte g = (byte)((colorArray[idx] & 0xFF00) >> 8);
byte b = (byte)(colorArray[idx] & 0xFF);
image.SetPixel(j, i, new SKColor(r, g, b));
}
break;
case SurfacePixelFormat.PFID_A8R8G8B8:
for (int i = 0; i < texture.Height; i++)
for (int j = 0; j < texture.Width; j++)
{
int idx = (i * texture.Width) + j;
byte a = (byte)((int)((colorArray[idx] & 0xFF000000) >> 24));
byte r = (byte)((colorArray[idx] & 0xFF0000) >> 16);
byte g = (byte)((colorArray[idx] & 0xFF00) >> 8);
byte b = (byte)(colorArray[idx] & 0xFF);
image.SetPixel(j, i, new SKColor(r, g, b, a));
}
break;
}
return image;
}
string filePath = "../ACEmulator/ACE/Dats/client_portal.dat";
long offset = 885193728 + 4; // Set your desired offset here
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (BinaryReader reader = new BinaryReader(fs))
{
fs.Seek(offset, SeekOrigin.Begin);
// Texture
var texture = new Texture();
texture.Unpack(reader);
Console.WriteLine($"Data at offset {offset}. Format is {texture.Format}; {texture.Width}x{texture.Height}; Length={texture.Length}");
// Get image data
List<int> colors = GetImageColorArray(texture);
var bitmap = GetBitmap(texture, colors);
// Save the bitmap to a file
string outputPath = "texture_output.png";
using (var image = SKImage.FromBitmap(bitmap))
using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
using (var stream = File.OpenWrite(outputPath))
{
data.SaveTo(stream);
}
Console.WriteLine($"Image saved to {Path.GetFullPath(outputPath)}");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment