private static IResource GetResource( ExportInfo[] exportInfos, ImportInfo[] importInfos, int index) { if (index == 0) { return null; } if (index < 0 && -index <= importInfos.Length) { return importInfos[-index - 1]; } if (index > 0 && index <= exportInfos.Length) { return exportInfos[index - 1]; } throw new InvalidOperationException(); }
public static void Main(string[] args) { var showHelp = false; string classFilter = null; var options = new OptionSet() { { "c|class=", "only unpack exports that are instances of specified class, ie Core.GFxUI.GFxMovieInfo", v => classFilter = v }, { "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]+ -j input_pcc [output_pcc]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPath = extras[0]; var outputPath = extras.Count >= 2 ? extras[1] : Path.ChangeExtension(inputPath, null); using (var input = File.OpenRead(inputPath)) { var magic = input.ReadValueU32(Endian.Little); if (magic != 0x9E2A83C1 && magic.Swap() != 0x9E2A83C1) { throw new FormatException("not a package"); } var endian = magic == 0x9E2A83C1 ? Endian.Little : Endian.Big; var encoding = endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode; var versionLo = input.ReadValueU16(endian); var versionHi = input.ReadValueU16(endian); if (versionLo != 684 && versionHi != 194) { throw new FormatException("unsupported version"); } input.Seek(4, SeekOrigin.Current); var folderNameLength = input.ReadValueS32(endian); var folderNameByteLength = folderNameLength >= 0 ? folderNameLength : (-folderNameLength * 2); input.Seek(folderNameByteLength, SeekOrigin.Current); /*var packageFlagsOffset = input.Position;*/ var packageFlags = input.ReadValueU32(endian); if ((packageFlags & 8) != 0) { input.Seek(4, SeekOrigin.Current); } var nameCount = input.ReadValueU32(endian); var namesOffset = input.ReadValueU32(endian); var exportCount = input.ReadValueU32(endian); var exportInfosOffset = input.ReadValueU32(endian); var importCount = input.ReadValueU32(endian); var importInfosOffset = input.ReadValueU32(endian); Stream data; if ((packageFlags & 0x02000000) == 0) { data = input; } else { input.Seek(36, SeekOrigin.Current); var generationsCount = input.ReadValueU32(endian); input.Seek(generationsCount * 12, SeekOrigin.Current); input.Seek(20, SeekOrigin.Current); var blockCount = input.ReadValueU32(endian); var blockStream = new BlockStream(input); for (int i = 0; i < blockCount; i++) { var uncompressedOffset = input.ReadValueU32(endian); var uncompressedSize = input.ReadValueU32(endian); var compressedOffset = input.ReadValueU32(endian); var compressedSize = input.ReadValueU32(endian); blockStream.AddBlock( uncompressedOffset, uncompressedSize, compressedOffset, compressedSize); } data = blockStream; } var names = new string[nameCount]; var exportInfos = new ExportInfo[exportCount]; for (uint i = 0; i < exportCount; i++) { exportInfos[i] = new ExportInfo(); } var importInfos = new ImportInfo[importCount]; for (uint i = 0; i < importCount; i++) { importInfos[i] = new ImportInfo(); } data.Seek(namesOffset, SeekOrigin.Begin); for (uint i = 0; i < nameCount; i++) { var nameLength = data.ReadValueS32(endian); if (nameLength >= 0) { names[i] = data.ReadString(nameLength, true, Encoding.UTF8); } else { names[i] = data.ReadString(-nameLength * 2, true, encoding); } } data.Seek(importInfosOffset, SeekOrigin.Begin); for (uint i = 0; i < importCount; i++) { var importInfo = importInfos[i]; var packageNameIndex = data.ReadValueS32(endian); importInfo.PackageName = names[packageNameIndex]; data.Seek(12, SeekOrigin.Current); var outerIndex = data.ReadValueS32(endian); importInfo.Outer = GetResource(exportInfos, importInfos, outerIndex); var objectNameIndex = data.ReadValueS32(endian); importInfo.ObjectName = names[objectNameIndex]; data.Seek(4, SeekOrigin.Current); } data.Seek(exportInfosOffset, SeekOrigin.Begin); for (uint i = 0; i < exportCount; i++) { var exportInfo = exportInfos[i]; exportInfo.PackageName = Path.GetFileNameWithoutExtension(inputPath); var classIndex = data.ReadValueS32(endian); exportInfo.Class = GetResource(exportInfos, importInfos, classIndex); data.Seek(4, SeekOrigin.Current); var outerIndex = data.ReadValueS32(endian); exportInfo.Outer = GetResource(exportInfos, importInfos, outerIndex); var objectNameIndex = data.ReadValueS32(endian); exportInfo.ObjectName = names[objectNameIndex]; data.Seek(16, SeekOrigin.Current); exportInfo.DataSize = data.ReadValueU32(endian); exportInfo.DataOffset = data.ReadValueU32(endian); data.Seek(4, SeekOrigin.Current); var count = data.ReadValueU32(endian); data.Seek(count * 4, SeekOrigin.Current); data.Seek(20, SeekOrigin.Current); } for (int i = 0; i < exportInfos.Length; i++) { var exportInfo = exportInfos[i]; if (classFilter != null) { if (exportInfo.Class == null) { continue; } if (exportInfo.Class.FullName.ToLowerInvariant() != classFilter) { continue; } } if (exportInfo.Class == null) { Console.WriteLine("{0}", exportInfo.FullName); } else { Console.WriteLine("({0}) {1}", exportInfo.Class.FullName, exportInfo.FullName); } var fullPath = exportInfo.FullPath; fullPath = fullPath.Replace(":", "_"); var exportPath = Path.Combine(outputPath, fullPath + " [export#" + i.ToString(CultureInfo.InvariantCulture) + "].bin"); if (File.Exists(exportPath) == true) { throw new InvalidOperationException(); } Directory.CreateDirectory(Path.GetDirectoryName(exportPath)); using (var output = File.Create(exportPath)) { data.Seek(exportInfo.DataOffset, SeekOrigin.Begin); output.WriteFromStream(data, exportInfo.DataSize); } } } }
public static void Main(string[] args) { var showHelp = false; string classFilter = null; var options = new OptionSet() { { "c|class=", "only unpack exports that are instances of specified class, ie Core.GFxUI.GFxMovieInfo", v => classFilter = v }, { "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]+ -j input_pcc [output_pcc]", GetExecutableName()); Console.WriteLine(); Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); return; } var inputPath = extras[0]; var outputPath = extras.Count >= 2 ? extras[1] : Path.ChangeExtension(inputPath, null); using (var input = File.OpenRead(inputPath)) { var magic = input.ReadValueU32(Endian.Little); if (magic != 0x9E2A83C1 && magic.Swap() != 0x9E2A83C1) { throw new FormatException("not a package"); } var endian = magic == 0x9E2A83C1 ? Endian.Little : Endian.Big; var encoding = endian == Endian.Little ? Encoding.Unicode : Encoding.BigEndianUnicode; var versionLo = input.ReadValueU16(endian); var versionHi = input.ReadValueU16(endian); if (versionLo != 684 && versionHi != 194) { throw new FormatException("unsupported version"); } input.Seek(4, SeekOrigin.Current); var folderNameLength = input.ReadValueS32(endian); var folderNameByteLength = folderNameLength >= 0 ? folderNameLength : (-folderNameLength * 2); input.Seek(folderNameByteLength, SeekOrigin.Current); /*var packageFlagsOffset = input.Position;*/ var packageFlags = input.ReadValueU32(endian); if ((packageFlags & 8) != 0) { input.Seek(4, SeekOrigin.Current); } var nameCount = input.ReadValueU32(endian); var namesOffset = input.ReadValueU32(endian); var exportCount = input.ReadValueU32(endian); var exportInfosOffset = input.ReadValueU32(endian); var importCount = input.ReadValueU32(endian); var importInfosOffset = input.ReadValueU32(endian); Stream data; if ((packageFlags & 0x02000000) == 0) { data = input; } else { input.Seek(36, SeekOrigin.Current); var generationsCount = input.ReadValueU32(endian); input.Seek(generationsCount * 12, SeekOrigin.Current); input.Seek(20, SeekOrigin.Current); var blockCount = input.ReadValueU32(endian); var blockStream = new BlockStream(input); for (int i = 0; i < blockCount; i++) { var uncompressedOffset = input.ReadValueU32(endian); var uncompressedSize = input.ReadValueU32(endian); var compressedOffset = input.ReadValueU32(endian); var compressedSize = input.ReadValueU32(endian); blockStream.AddBlock( uncompressedOffset, uncompressedSize, compressedOffset, compressedSize); } data = blockStream; } var names = new string[nameCount]; var exportInfos = new ExportInfo[exportCount]; for (uint i = 0; i < exportCount; i++) { exportInfos[i] = new ExportInfo(); } var importInfos = new ImportInfo[importCount]; for (uint i = 0; i < importCount; i++) { importInfos[i] = new ImportInfo(); } data.Seek(namesOffset, SeekOrigin.Begin); for (uint i = 0; i < nameCount; i++) { var nameLength = data.ReadValueS32(endian); if (nameLength >= 0) { names[i] = data.ReadString(nameLength, true, Encoding.UTF8); } else { names[i] = data.ReadString(-nameLength * 2, true, encoding); } } data.Seek(importInfosOffset, SeekOrigin.Begin); for (uint i = 0; i < importCount; i++) { var importInfo = importInfos[i]; var packageNameIndex = data.ReadValueS32(endian); importInfo.PackageName = names[packageNameIndex]; data.Seek(12, SeekOrigin.Current); var outerIndex = data.ReadValueS32(endian); importInfo.Outer = GetResource(exportInfos, importInfos, outerIndex); var objectNameIndex = data.ReadValueS32(endian); importInfo.ObjectName = names[objectNameIndex]; data.Seek(4, SeekOrigin.Current); } data.Seek(exportInfosOffset, SeekOrigin.Begin); for (uint i = 0; i < exportCount; i++) { var exportInfo = exportInfos[i]; exportInfo.PackageName = Path.GetFileNameWithoutExtension(inputPath); var classIndex = data.ReadValueS32(endian); exportInfo.Class = GetResource(exportInfos, importInfos, classIndex); data.Seek(4, SeekOrigin.Current); var outerIndex = data.ReadValueS32(endian); exportInfo.Outer = GetResource(exportInfos, importInfos, outerIndex); var objectNameIndex = data.ReadValueS32(endian); exportInfo.ObjectName = names[objectNameIndex]; data.Seek(16, SeekOrigin.Current); exportInfo.DataSize = data.ReadValueU32(endian); exportInfo.DataOffset = data.ReadValueU32(endian); data.Seek(4, SeekOrigin.Current); var count = data.ReadValueU32(endian); data.Seek(count * 4, SeekOrigin.Current); data.Seek(20, SeekOrigin.Current); } for (int i = 0; i < exportInfos.Length; i++) { var exportInfo = exportInfos[i]; if (classFilter != null) { if (exportInfo.Class == null) { continue; } if (exportInfo.Class.FullName.ToLowerInvariant() != classFilter) { continue; } } if (exportInfo.Class == null) { Console.WriteLine("{0}", exportInfo.FullName); } else { Console.WriteLine("({0}) {1}", exportInfo.Class.FullName, exportInfo.FullName); } var fullPath = exportInfo.FullPath; fullPath = fullPath.Replace(":", "_"); var exportPath = Path.Combine(outputPath, fullPath + " [export#" + i.ToString(CultureInfo.InvariantCulture) + "].bin"); if (File.Exists(exportPath) == true) { throw new InvalidOperationException(); } Directory.CreateDirectory(Path.GetDirectoryName(exportPath)); using (var output = File.Create(exportPath)) { data.Seek(exportInfo.DataOffset, SeekOrigin.Begin); output.WriteFromStream(data, exportInfo.DataSize); } } } }