using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Packer{ class Program{ static readonly List extensions = new List( new Extensions[] { new Extensions { Source = ".mmm", Destination = ".mad" }, new Extensions { Source = ".mtm", Destination = ".mtd" }, new Extensions { Source = ".pmm", Destination = ".pmg" }, new Extensions { Source = ".pom", Destination = ".pog" }, new Extensions { Source = ".ptm", Destination = ".ptg" } }); static byte[] Decompress(byte[] source) { int offset = 0; List outBuffer = new List(); while (source[offset] != 0) { byte blockType = source[offset]; } while (source[offset] != 0) { byte blockType = source[offset]; if (blockType < 0x40) { outBuffer.AddRange(source.AsSpan(offset + 1, blockType).ToArray()); offset += 1 + blockType; } else if (blockType < 0x50) { var count = (blockType - 0x40) + 3; byte v0 = outBuffer[outBuffer.Count - 2]; byte v1 = outBuffer[outBuffer.Count - 1]; //byte v0 = source[offset - 2]; //byte v1 = source[offset - 1]; v0 = (byte)(v1 - v0); do { v1 += v0; outBuffer.Add(v1); --count; } while (count != 0); offset += 1; } else if (blockType < 0x60) { var count = (blockType - 0x50) + 2; short v0 = (short)(outBuffer[outBuffer.Count - 4] | ((short)outBuffer[outBuffer.Count - 3] << 8)); short v1 = (short)(outBuffer[outBuffer.Count - 2] | ((short)outBuffer[outBuffer.Count - 1] << 8)); //short v0 = (short)(source[offset - 4] | ((short)source[offset - 3] << 8)); //short v1 = (short)(source[offset - 2] | ((short)source[offset - 1] << 8)); v0 = (short)(v1 - v0); do { v1 += v0; outBuffer.Add((byte)(v1 & 0xff)); outBuffer.Add((byte)((v1 >> 8) & 0xff)); --count; } while (count != 0); offset += 1; } else if (blockType < 0x70) { //Copy last written byte (base 3) var b = outBuffer[outBuffer.Count - 1]; var copyCount = (blockType - 0x60) + 3; for (int i = 0; i < copyCount; ++i) { outBuffer.Add(b); } offset += 1; } else if (blockType < 0x80) { var b0 = outBuffer[outBuffer.Count - 2]; var b1 = outBuffer[outBuffer.Count - 1]; var copyCount = (blockType - 0x70) + 2; for (int i = 0; i < copyCount; ++i) { outBuffer.Add(b0); outBuffer.Add(b1); } offset += 1; } else if (blockType < 0xC0) { //Copy 3 bytes var backOffset = (blockType - 0x80) + 3; var readOffset = (outBuffer.Count) - backOffset; outBuffer.Add(outBuffer[readOffset + 0]); outBuffer.Add(outBuffer[readOffset + 1]); outBuffer.Add(outBuffer[readOffset + 2]); offset += 1; } else if (blockType < 0xE0) { var copyCount = ((blockType - 0xC0) >> 2) + 4; var backOffset = ((blockType - 0xC0) & 3) << 8; backOffset += (source[offset + 1] + 3); var readStart = (outBuffer.Count) - backOffset; var block = outBuffer.ToArray().AsSpan(readStart, Math.Min(copyCount, backOffset)); for(int i = 0; i < copyCount; ++i) { outBuffer.Add(block[i % block.Length]); } offset += 2; } else { var backOffset = ((blockType - 0xE0) * 256) + (source[offset + 1] + 3); var bytesCount = source[offset + 2] + 5; var readStart = (outBuffer.Count) - backOffset; for (int i = 0; i < bytesCount; ++i) { outBuffer.Add(outBuffer[readStart + (i % backOffset)]); } offset += 3; } } return outBuffer.ToArray(); } static void DecompressFile(string input, string output) { var bytes = File.ReadAllBytes(input); var decompressed = Decompress(bytes); File.WriteAllBytes(output, decompressed); } struct Extensions { public string Source; public string Destination; } static void DecompressDirectory(string directory) { var files = Directory.GetFiles(directory); files = files.Where(v => extensions.Any( g=> g.Source == Path.GetExtension(v).ToLower())).ToArray(); foreach(var file in files) { var referenceFilePath = Path.GetDirectoryName(file); referenceFilePath = Path.Combine(referenceFilePath, "Decompressed", Path.GetFileNameWithoutExtension(file) + extensions.First(v => v.Source == Path.GetExtension(file).ToLower()).Destination); if (!File.Exists(referenceFilePath)) { continue; } var compressedData = File.ReadAllBytes(file); var referenceData = File.ReadAllBytes(referenceFilePath); var decompressedData = Decompress(compressedData); var equal = decompressedData.SequenceEqual(referenceData); if (!equal) { throw new Exception("Invalid bytes :("); } } Console.WriteLine(); } static void Main(string[] args) { string input = @"C:\Users\l-pro\Downloads\!HOW\COLD1.PMM"; string output = @"C:\Users\l-pro\Downloads\!HOW\COLD1.PMM_UNK"; DecompressFile(input, output); DecompressDirectory(@"C:\Users\l-pro\Downloads\!HOW\Test"); Console.WriteLine("Done"); } } }