Skip to content

Instantly share code, notes, and snippets.

@ogarvey
Created September 2, 2025 10:33
Show Gist options
  • Select an option

  • Save ogarvey/b1e72672dca0611b3d40cf5ee2456897 to your computer and use it in GitHub Desktop.

Select an option

Save ogarvey/b1e72672dca0611b3d40cf5ee2456897 to your computer and use it in GitHub Desktop.
I.M Meen/Chill Manor Cmp Decoding Method
public static Image<Rgba32> DecodeCmp(byte[] cmpData, byte[] originalPalette)
{
var width = BitConverter.ToUInt16(cmpData.Take(2).ToArray(), 0);
var height = BitConverter.ToUInt16(cmpData.Skip(2).Take(2).ToArray(), 0);
var firstNonTransparentRow = BitConverter.ToUInt16(cmpData.Skip(4).Take(2).ToArray(), 0);
var lastNonTransparentRow = BitConverter.ToUInt16(cmpData.Skip(6).Take(2).ToArray(), 0);
var firstNonTransparentColumn = BitConverter.ToUInt16(cmpData.Skip(8).Take(2).ToArray(), 0);
var lastNonTransparentColumn = BitConverter.ToUInt16(cmpData.Skip(0xa).Take(2).ToArray(), 0);
var colourCount = cmpData[0xc];
var colourBytes = cmpData.Skip(0xd).Take(colourCount).ToArray();
var cmpPaletteBytes = new byte[colourCount * 3];
for (int i = 0; i < colourCount; i++)
{
var colour = colourBytes[i];
var palIndex = colour * 3;
cmpPaletteBytes[i * 3] = originalPalette[palIndex];
cmpPaletteBytes[i * 3 + 1] = originalPalette[palIndex + 1];
cmpPaletteBytes[i * 3 + 2] = originalPalette[palIndex + 2];
}
var offsetDataOffset = 0xd + colourCount;
var offsetList = new List<ushort>();
var offsetData = cmpData.Skip(offsetDataOffset).ToArray();
var offset = BitConverter.ToUInt16(offsetData.Take(2).ToArray(), 0);
offsetList.Add(offset);
var currentPos = offsetDataOffset + 2;
while (currentPos < offsetList[0])
{
offset = BitConverter.ToUInt16(cmpData.Skip(currentPos).Take(2).ToArray(), 0);
offsetList.Add(offset);
currentPos += 2;
}
var imageDataLines = new List<byte[]>();
for (int i = 0; i < offsetList.Count; i++)
{
var start = offsetList[i];
var end = i == offsetList.Count - 1 ? cmpData.Length : offsetList[i + 1];
var line = cmpData.Skip(start).Take(end - start).ToArray();
imageDataLines.Add(line);
}
var imageLines = new List<byte[]>();
if (firstNonTransparentRow > 0)
{
for (int i = 0; i < firstNonTransparentRow; i++)
{
var lineData = Enumerable.Repeat((byte)0x0, width).ToArray();
imageLines.Add(lineData);
}
}
foreach (var line in imageDataLines)
{
var lineData = new byte[width];
var lineIndex = 0;
for (int i = 0; i < line.Length; i++)
{
if (lineIndex >= lineData.Length - 1)
{
break;
}
var b = line[i];
if ((b & 0x80) == 0x80)
{
var count = b & 0x7F;
for (int j = 0; j < count && lineIndex < lineData.Length; j++)
{
lineData[lineIndex] = 0x0;
lineIndex++;
}
}
else
{
lineData[lineIndex] = b;
lineIndex++;
}
}
imageLines.Add(lineData);
}
if (lastNonTransparentRow < height - 1)
{
for (int i = lastNonTransparentRow; i < height - 1; i++)
{
var lineData = Enumerable.Repeat((byte)0x0, width).ToArray();
imageLines.Add(lineData);
}
}
var imageData = imageLines.SelectMany(x => x).ToArray();
var image = new Image<Rgba32>(width, height);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
var i = y * width + x;
var paletteIndex = imageData[i];
Color color;
if (paletteIndex < cmpPaletteBytes.Length / 3)
{
color = Color.FromRgb(cmpPaletteBytes[paletteIndex * 3], cmpPaletteBytes[paletteIndex * 3 + 1], cmpPaletteBytes[paletteIndex * 3 + 2]);
}
else
{
color = Color.Transparent;
}
image[x, y] = color;
}
}
return image;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment