Esempio n. 1
0
 public static string FollowLink(this IMEPackage pcc, int uIndex)
 {
     if (pcc.IsUExport(uIndex))
     {
         ExportEntry parent = pcc.GetUExport(uIndex);
         return($"{pcc.FollowLink(parent.idxLink)}{parent.ObjectName}.");
     }
     if (pcc.IsImport(uIndex))
     {
         ImportEntry parent = pcc.GetImport(uIndex);
         return($"{pcc.FollowLink(parent.idxLink)}{parent.ObjectName}.");
     }
     return("");
 }
Esempio n. 2
0
        /// <summary>
        /// Adds an import to the tree. This method is used to add new imports.
        /// </summary>
        /// <param name="importEntry"></param>
        public void AddImport(ImportEntry importEntry)
        {
            if (importEntry.FileRef != this)
            {
                throw new Exception("you cannot add a new import entry from another package file, it has invalid references!");
            }

            importEntry.Index            = imports.Count;
            importEntry.PropertyChanged += importChanged;
            importEntry.HeaderOffset     = 1; //This will make it so when setting idxLink it knows the import has been attached to the tree, even though this doesn't do anything. Find by offset may be confused by this. Updates on save
            imports.Add(importEntry);
            importEntry.EntryHasPendingChanges = true;
            ImportCount = imports.Count;

            updateTools(PackageChange.ImportAdd, importEntry.UIndex);
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImportCount)));
        }
Esempio n. 3
0
        //if neccessary, will fill in parents as Package Imports (if the import you need has non-Package parents, don't use this method)
        public static IEntry getEntryOrAddImport(this IMEPackage pcc, string fullPath, string className = "Class", string packageFile = "Core", int?objIdx = null)
        {
            if (string.IsNullOrEmpty(fullPath))
            {
                return(null);
            }

            //see if this import exists locally
            foreach (ImportEntry imp in pcc.Imports)
            {
                if (imp.FullPath == fullPath && (objIdx == null || objIdx == imp.ObjectName.Number))
                {
                    return(imp);
                }
            }

            //see if this is an export and exists locally
            foreach (ExportEntry exp in pcc.Exports)
            {
                if (exp.FullPath == fullPath && (objIdx == null || objIdx == exp.ObjectName.Number))
                {
                    return(exp);
                }
            }

            string[] pathParts = fullPath.Split('.');

            IEntry parent = pcc.getEntryOrAddImport(string.Join(".", pathParts.Take(pathParts.Length - 1)), "Package");

            var import = new ImportEntry(pcc)
            {
                idxLink     = parent?.UIndex ?? 0,
                ClassName   = className,
                ObjectName  = new NameReference(pathParts.Last(), objIdx ?? 0),
                PackageFile = packageFile
            };

            pcc.AddImport(import);
            return(import);
        }
Esempio n. 4
0
        /// <summary>S
        ///     UDKPackage class constructor. It also load namelist, importlist and exportinfo (not exportdata) from udk file
        /// </summary>
        /// <param name="filePath">full path + file name of desired udk file.</param>
        private UDKPackage(Stream fs, string filePath) : base(filePath != null ? Path.GetFullPath(filePath) : null)
        {
            #region Header

            uint magic = fs.ReadUInt32();
            if (magic != packageTagLittleEndian)
            {
                throw new FormatException("Not an Unreal package!");
            }
            ushort unrealVersion   = fs.ReadUInt16();
            ushort licenseeVersion = fs.ReadUInt16();
            FullHeaderSize = fs.ReadInt32();
            int foldernameStrLen = fs.ReadInt32();
            //always "None", so don't bother saving result
            if (foldernameStrLen > 0)
            {
                folderName = fs.ReadStringASCIINull(foldernameStrLen);
            }
            else
            {
                folderName = fs.ReadStringUnicodeNull(foldernameStrLen * -2);
            }

            Flags = (EPackageFlags)fs.ReadUInt32();

            //if (Flags.HasFlag(EPackageFlags.Compressed))
            //{
            //    throw new FormatException("Cannot read compressed UDK packages!");
            //}

            NameCount               = fs.ReadInt32();
            NameOffset              = fs.ReadInt32();
            ExportCount             = fs.ReadInt32();
            ExportOffset            = fs.ReadInt32();
            ImportCount             = fs.ReadInt32();
            ImportOffset            = fs.ReadInt32();
            DependencyTableOffset   = fs.ReadInt32();
            importExportGuidsOffset = fs.ReadInt32();
            importGuidsCount        = fs.ReadInt32();
            exportGuidsCount        = fs.ReadInt32();
            thumbnailTableOffset    = fs.ReadInt32();
            PackageGuid             = fs.ReadGuid();

            uint generationsTableCount = fs.ReadUInt32();
            if (generationsTableCount > 0)
            {
                generationsTableCount--;
                Gen0ExportCount          = fs.ReadInt32();
                Gen0NameCount            = fs.ReadInt32();
                Gen0NetworkedObjectCount = fs.ReadInt32();
            }
            //don't care about other gens, so skip them
            fs.Skip(generationsTableCount * 12);
            engineVersion        = fs.ReadInt32();
            cookedContentVersion = fs.ReadInt32();

            //skip compression type chunks. Decompressor will handle that
            long compressionInfoOffset = fs.Position;
            fs.SkipInt32();
            int numChunks = fs.ReadInt32();
            fs.Skip(numChunks * 16);

            packageSource = fs.ReadUInt32();
            //additional packages to cook, and texture allocation, but we don't care about those, so we won't read them in.

            #endregion
            Stream inStream = fs;
            if (IsCompressed && numChunks > 0)
            {
                inStream = CompressionHelper.DecompressPackage(new EndianReader(fs), compressionInfoOffset);
            }

            var reader = new EndianReader(inStream); //these will always be little endian so we don't actually use this except for passing
                                                     //through to methods that can use endianness

            inStream.JumpTo(NameOffset);
            for (int i = 0; i < NameCount; i++)
            {
                var name = inStream.ReadUnrealString();
                names.Add(name);
                nameLookupTable[name] = i;
                inStream.Skip(8);
            }

            inStream.JumpTo(ImportOffset);
            for (int i = 0; i < ImportCount; i++)
            {
                ImportEntry imp = new ImportEntry(this, reader)
                {
                    Index = i
                };
                imp.PropertyChanged += importChanged;
                imports.Add(imp);
            }

            //read exportTable (ExportEntry constructor reads export data)
            inStream.JumpTo(ExportOffset);
            for (int i = 0; i < ExportCount; i++)
            {
                ExportEntry e = new ExportEntry(this, reader)
                {
                    Index = i
                };
                e.PropertyChanged += exportChanged;
                exports.Add(e);
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Compares this package to another IMEPackage. The calling package must implement IMEPackage or this will throw an exception.
        /// </summary>
        /// <param name="compareFile"></param>
        /// <returns></returns>
        public List <EntryStringPair> CompareToPackage(IMEPackage compareFile)
        {
            if (this is IMEPackage thisPackage)
            {
                if (thisPackage.Game != compareFile.Game)
                {
                    throw new Exception("Can't compare files, they're for different games!");
                }

                if (thisPackage.Platform != compareFile.Platform)
                {
                    throw new Exception("Cannot compare packages across platforms!");
                }

                var changedImports = new List <EntryStringPair>();
                var changedNames   = new List <EntryStringPair>();
                var changedExports = new List <EntryStringPair>();

                #region Exports Comparison

                {
                    // these brackets are here to scope the variables so same named ones can be used in a later chunk
                    int numExportsToEnumerate = Math.Min(ExportCount, compareFile.ExportCount);

                    for (int i = 0; i < numExportsToEnumerate; i++)
                    {
                        ExportEntry exp1 = Exports[i];
                        ExportEntry exp2 = compareFile.Exports[i];

                        //make data offset and data size the same, as the exports could be the same even if it was appended later.
                        //The datasize being different is a data difference not a true header difference so we won't list it here.
                        byte[] header1 = exp1.Header.TypedClone();
                        byte[] header2 = exp2.Header.TypedClone();
                        Buffer.BlockCopy(BitConverter.GetBytes((long)0), 0, header1, 32, sizeof(long));
                        Buffer.BlockCopy(BitConverter.GetBytes((long)0), 0, header2, 32, sizeof(long));

                        if (!header1.SequenceEqual(header2))
                        {
                            changedExports.Add(new EntryStringPair(exp1,
                                                                   $"Export header has changed: {exp1.UIndex} {exp1.InstancedFullPath} ({exp1.ClassName})"));
                        }

                        if (!exp1.Data.SequenceEqual(exp2.Data))
                        {
                            changedExports.Add(new EntryStringPair(exp1,
                                                                   $"Export data has changed: {exp1.UIndex} {exp1.InstancedFullPath} ({exp1.ClassName})"));
                        }
                    }

                    IMEPackage enumerateExtras = thisPackage;
                    string     file            = "this file";
                    if (compareFile.ExportCount > numExportsToEnumerate)
                    {
                        file            = "other file";
                        enumerateExtras = compareFile;
                    }

                    for (int i = numExportsToEnumerate; i < enumerateExtras.ExportCount; i++)
                    {
                        Debug.WriteLine(
                            $"Export only exists in {file}: {i + 1} {enumerateExtras.Exports[i].InstancedFullPath}");
                        changedExports.Add(new EntryStringPair(
                                               enumerateExtras.Exports[i].FileRef == this ? enumerateExtras.Exports[i] : null,
                                               $"Export only exists in {file}: {i + 1} {enumerateExtras.Exports[i].InstancedFullPath}"));
                    }
                }

                #endregion

                #region Imports Comparison

                {
                    int numImportsToEnumerate = Math.Min(ImportCount, compareFile.ImportCount);

                    for (int i = 0; i < numImportsToEnumerate; i++)
                    {
                        ImportEntry imp1 = Imports[i];
                        ImportEntry imp2 = compareFile.Imports[i];
                        if (!imp1.Header.SequenceEqual(imp2.Header))
                        {
                            changedImports.Add(new EntryStringPair(imp1,
                                                                   $"Import header has changed: {imp1.UIndex} {imp1.InstancedFullPath}"));
                        }
                    }

                    IMEPackage enumerateExtras = thisPackage;
                    string     file            = "this file";
                    if (compareFile.ImportCount > numImportsToEnumerate)
                    {
                        file            = "other file";
                        enumerateExtras = compareFile;
                    }

                    for (int i = numImportsToEnumerate; i < enumerateExtras.ImportCount; i++)
                    {
                        Debug.WriteLine(
                            $"Import only exists in {file}: {-i - 1} {enumerateExtras.Imports[i].InstancedFullPath}");
                        changedImports.Add(new EntryStringPair(
                                               enumerateExtras.Imports[i].FileRef == this ? enumerateExtras.Imports[i] : null,
                                               $"Import only exists in {file}: {-i - 1} {enumerateExtras.Imports[i].InstancedFullPath}"));
                    }
                }

                #endregion

                #region Names Comparison

                {
                    int numNamesToEnumerate = Math.Min(NameCount, compareFile.NameCount);
                    for (int i = 0; i < numNamesToEnumerate; i++)
                    {
                        var name1 = Names[i];
                        var name2 = compareFile.Names[i];

                        //if (!StructuralComparisons.StructuralEqualityComparer.Equals(header1, header2))
                        if (!name1.Equals(name2, StringComparison.InvariantCultureIgnoreCase))

                        {
                            changedNames.Add(new EntryStringPair(null, $"Name {i} is different: {name1} |vs| {name2}"));
                        }
                    }

                    IMEPackage enumerateExtras = thisPackage;
                    string     file            = "this file";
                    if (compareFile.NameCount > numNamesToEnumerate)
                    {
                        file            = "other file";
                        enumerateExtras = compareFile;
                    }

                    for (int i = numNamesToEnumerate; i < enumerateExtras.NameCount; i++)
                    {
                        Debug.WriteLine($"Name only exists in {file}: {i} {enumerateExtras.Names[i]}");
                        changedNames.Add(new EntryStringPair(null,
                                                             $"Name only exists in {file}: {i} {enumerateExtras.Names[i]}"));
                    }
                }

                #endregion

                var fullList = new List <EntryStringPair>();
                fullList.AddRange(changedExports);
                fullList.AddRange(changedImports);
                fullList.AddRange(changedNames);
                return(fullList);
            }

            throw new Exception("Source object must implement IMEPackage to support package comparisons");
        }
Esempio n. 6
0
        public void RemoveTrailingTrash()
        {
            ExportEntry trashPackage = exports.FirstOrDefault(exp => exp.ObjectName == TrashPackageName);

            if (trashPackage == null)
            {
                return;
            }
            int trashPackageUIndex = trashPackage.UIndex;

            //make sure the first trashed export is the trashpackage
            foreach (ExportEntry exp in exports)
            {
                if (exp == trashPackage)
                {
                    //trashpackage is the first trashed export, so we're good
                    break;
                }
                if (exp.idxLink == trashPackageUIndex)
                {
                    //turn this into trashpackage, turn old trashpackage into regular Trash, and point all trash entries to the new trashpackage
                    exp.ObjectName  = TrashPackageName;
                    exp.idxLink     = 0;
                    exp.PackageGUID = TrashPackageGuid;

                    trashPackage.ObjectName  = "Trash";
                    trashPackage.idxLink     = exp.UIndex;
                    trashPackage.PackageGUID = Guid.Empty;

                    foreach (IEntry entry in trashPackage.GetChildren())
                    {
                        entry.idxLink = exp.UIndex;
                    }

                    trashPackage       = exp;
                    trashPackageUIndex = trashPackage.UIndex;
                    break;
                }
            }


            //remove imports
            for (int i = ImportCount - 1; i >= 0; i--)
            {
                ImportEntry lastImport = imports[i];
                if (lastImport.idxLink != trashPackageUIndex)
                {
                    //non-trash import, so stop removing
                    break;
                }

                lastImport.PropertyChanged -= importChanged;
                imports.RemoveAt(i);
                updateTools(PackageChange.ImportRemove, lastImport.UIndex);
            }
            if (ImportCount != imports.Count)
            {
                ImportCount = imports.Count;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ImportCount)));
            }

            //remove exports
            for (int i = ExportCount - 1; i >= 0; i--)
            {
                ExportEntry lastExport = exports[i];
                if (lastExport.idxLink != trashPackageUIndex)
                {
                    //non-trash export, so stop removing
                    break;
                }

                lastExport.PropertyChanged -= importChanged;
                exports.RemoveAt(i);
                updateTools(PackageChange.ExportRemove, lastExport.UIndex);
            }
            if (ExportCount != exports.Count)
            {
                ExportCount = exports.Count;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ExportCount)));
            }
            //if there are no more trashed imports or exports, and if the TrashPackage is the last export, remove it
            if (exports.LastOrDefault() is ExportEntry finalExport && finalExport == trashPackage && trashPackage.GetChildren().IsEmpty())
            {
                trashPackage.PropertyChanged -= importChanged;
                exports.Remove(trashPackage);
                updateTools(PackageChange.ExportRemove, trashPackage.UIndex);
            }
            if (ExportCount != exports.Count)
            {
                ExportCount = exports.Count;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ExportCount)));
            }
        }