private static void Convert(string pegName) { if (File.Exists(pegName) == false) { Console.WriteLine("{0} does not exist.", pegName); return; } string gpegName = Path.ChangeExtension(pegName, ".g_peg_pc"); if (File.Exists(gpegName) == false) { Console.WriteLine("{0} does not exist.", gpegName); return; } Stream header = File.OpenRead(pegName); Stream data = File.OpenRead(gpegName); FileFormats.PegFile peg = new FileFormats.PegFile(); peg.Read(header); foreach (FileFormats.PegEntry entry in peg.Entries) { int index = 0; foreach (FileFormats.PegFrame frame in entry.Frames) { data.Seek(frame.Offset, SeekOrigin.Begin); byte[] compressed = new byte[frame.Size]; data.Read(compressed, 0, (int)frame.Size); FileFormats.PegFormat format = (FileFormats.PegFormat)frame.Format; Bitmap bitmap; // DXT if (format == FileFormats.PegFormat.DXT1 || format == FileFormats.PegFormat.DXT3 || format == FileFormats.PegFormat.DXT5) { Squish.Flags flags = 0; if (format == FileFormats.PegFormat.DXT1) { flags |= Squish.Flags.DXT1; } else if (format == FileFormats.PegFormat.DXT3) { flags |= Squish.Flags.DXT3; } else if (format == FileFormats.PegFormat.DXT5) { flags |= Squish.Flags.DXT5; } byte[] decompressed = new byte[frame.Width * frame.Height * 4]; Squish.Decompress(decompressed, frame.Width, frame.Height, compressed, (int)flags); bitmap = MakeBitmapFromDXT(frame.Width, frame.Height, decompressed, true); } // R5G6B5 else if (format == FileFormats.PegFormat.R5G6B5) { bitmap = MakeBitmapFromR5G6B5(frame.Width, frame.Height, compressed); } // A8R8G8B8 else if (format == FileFormats.PegFormat.A8R8G8B8) { bitmap = MakeBitmapFromA8R8G8B8(frame.Width, frame.Height, compressed); } else { throw new Exception("unhandled format " + frame.Format.ToString()); } string prefix = Path.ChangeExtension(pegName, null) + " "; if (entry.Frames.Count == 1) { bitmap.Save(prefix + Path.ChangeExtension(entry.Name, ".png"), ImageFormat.Png); } else { string name = Path.GetFileNameWithoutExtension(entry.Name); name += " (frame " + index.ToString() + ")"; bitmap.Save(prefix + Path.ChangeExtension(name, ".png"), ImageFormat.Png); } index++; } } }
public static void Main(string[] args) { string decalId = "12"; bool verbose = false; bool showHelp = false; var options = new OptionSet() { { "i|index=", "set decal index", v => decalId = v }, { "v|verbose", "be verbose", v => verbose = v != null }, { "h|help", "show this message and exit", v => showHelp = v != null }, }; List <string> extras; try { extras = options.Parse(args); } catch (OptionException e) { Console.Write("{0}: ", GetExecutableName()); Console.WriteLine(e.Message); Console.WriteLine("Try `{0} --help' for more information.", GetExecutableName()); return; } if (extras.Count < 1 || extras.Count > 2 || showHelp == true) { Console.WriteLine("Usage: {0} [OPTIONS]+ input_png [output_pak]", GetExecutableName()); Console.WriteLine("Unpack specified archive."); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } int decalIndex; if (int.TryParse(decalId, out decalIndex) == false) { Console.WriteLine($"Could not parse decal index '{decalId}'."); return; } if (decalIndex < 0 || decalIndex > 999) { Console.WriteLine($"Invalid decal index '{decalIndex}."); return; } var inputPath = Path.GetFullPath(extras[0]); string outputPath; if (extras.Count > 1) { outputPath = extras[1]; } else { var outputName = Path.GetFileNameWithoutExtension(inputPath); var outputParentPath = Path.GetDirectoryName(inputPath); outputPath = Path.Combine(outputParentPath, $"DXM-WindowsNoEditor_Decal_{outputName}_P.pak"); } const Squish.Flags squishFlags = Squish.Flags.DXT5 | Squish.Flags.SourceBGRA; int dxtTotalSize = 0; for (int mipIndex = 10; mipIndex >= 0; mipIndex--) { var mipSize = 1 << mipIndex; dxtTotalSize += Squish.GetStorageRequirements(mipSize, mipSize, squishFlags); } if (dxtTotalSize != 1398128) { throw new InvalidOperationException(); } var dxtBytes = new byte[dxtTotalSize]; using (var image = Image.FromFile(inputPath)) { if (image.Width != 1024 || image.Height != 1024) { Console.WriteLine("[Warning] Original image not 1024x1024, will resize (probably badly)."); } int dxtOffset = 0; for (int mipIndex = 10; mipIndex >= 0; mipIndex--) { var mipSize = 1 << mipIndex; var rgbaStride = mipSize * 4; var rgbaBytes = new byte[rgbaStride * mipSize]; using (var bitmap = MaybeResize(image, mipSize, mipSize)) { var area = new Rectangle(0, 0, mipSize, mipSize); var data = bitmap.LockBits(area, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var scan = data.Scan0; for (int y = 0, o = 0; y < mipSize; y++, scan += data.Stride, o += rgbaStride) { Marshal.Copy(scan, rgbaBytes, o, rgbaStride); } bitmap.UnlockBits(data); } var dxtSize = Squish.GetStorageRequirements(mipSize, mipSize, squishFlags); Squish.CompressImage(rgbaBytes, 0, mipSize, mipSize, dxtBytes, dxtOffset, squishFlags); dxtOffset += dxtSize; } } var decalIndexBytes = Encoding.ASCII.GetBytes($"{decalIndex:D3}"); var filenameBytes = Encoding.UTF8.GetBytes(Path.GetFileNameWithoutExtension(outputPath)); var filenameHash = ComputeMD5(filenameBytes, 0, filenameBytes.Length); const int pakEntryHeaderSize = 53; const int pakEntryHeaderHashOffset = 28; var outputLength = 0; var uassetOffset = outputLength; outputLength += Resources.UAsset.Length; var ubulkHeaderOffset = outputLength; outputLength += Resources.UBulkHeader.Length; var ubulkDataOffset = outputLength; outputLength += dxtBytes.Length; var uexpOffset = outputLength; outputLength += Resources.UExp.Length; var pakIndexOffset = outputLength; outputLength += Resources.PakIndex.Length; var outputBytes = new byte[outputLength]; Array.Copy(Resources.UAsset, 0, outputBytes, uassetOffset, Resources.UAsset.Length); Array.Copy(Resources.UBulkHeader, 0, outputBytes, ubulkDataOffset, Resources.UBulkHeader.Length); Array.Copy(dxtBytes, 0, outputBytes, ubulkDataOffset, dxtBytes.Length); Array.Copy(Resources.UExp, 0, outputBytes, uexpOffset, Resources.UExp.Length); Array.Copy(Resources.PakIndex, 0, outputBytes, pakIndexOffset, Resources.PakIndex.Length); Array.Copy(filenameHash, 0, outputBytes, uassetOffset + pakEntryHeaderSize + 93, filenameHash.Length); Array.Copy(decalIndexBytes, 0, outputBytes, uassetOffset + pakEntryHeaderSize + 243, decalIndexBytes.Length); Array.Copy(decalIndexBytes, 0, outputBytes, uassetOffset + pakEntryHeaderSize + 349, decalIndexBytes.Length); var uassetHash = ComputeSHA1(outputBytes, uassetOffset + pakEntryHeaderSize, Resources.UAsset.Length - pakEntryHeaderSize); var ubulkHash = ComputeSHA1(outputBytes, ubulkDataOffset, dxtBytes.Length); var uexpHash = ComputeSHA1(outputBytes, uexpOffset + pakEntryHeaderSize, Resources.UExp.Length - pakEntryHeaderSize); Array.Copy(uassetHash, 0, outputBytes, uassetOffset + pakEntryHeaderHashOffset, uassetHash.Length); Array.Copy(ubulkHash, 0, outputBytes, ubulkHeaderOffset + pakEntryHeaderHashOffset, ubulkHash.Length); Array.Copy(uexpHash, 0, outputBytes, uexpOffset + pakEntryHeaderHashOffset, uexpHash.Length); Array.Copy(decalIndexBytes, 0, outputBytes, pakIndexOffset + 74, decalIndexBytes.Length); Array.Copy(uassetHash, 0, outputBytes, pakIndexOffset + 113, uassetHash.Length); Array.Copy(decalIndexBytes, 0, outputBytes, pakIndexOffset + 151, decalIndexBytes.Length); Array.Copy(ubulkHash, 0, outputBytes, pakIndexOffset + 189, ubulkHash.Length); Array.Copy(decalIndexBytes, 0, outputBytes, pakIndexOffset + 227, decalIndexBytes.Length); Array.Copy(uexpHash, 0, outputBytes, pakIndexOffset + 264, uexpHash.Length); var pakIndexHash = ComputeSHA1(outputBytes, pakIndexOffset, 289); Array.Copy(pakIndexHash, 0, outputBytes, pakIndexOffset + 314, pakIndexHash.Length); File.WriteAllBytes(outputPath, outputBytes); }