private static void TextPropertyDirtyHackContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            /* TextProperty pattern has to be `Size 0 Skip 1 SizeStart TPDH`
             * Tgt context should be TextProperty/0/X/Type/0
             * Pattern gets `Skip X Type`.
             */
            readingContext.pattern.TakeArg();

            if (readingContext.targetContext.Count > 0)
            {
                if (readingContext.targetContext[0] == "TextProperty")
                {
                    Int32 skipsLeft = Int32.Parse(readingContext.targetContext[1]);

                    if (skipsLeft == 0)
                    {
                        if (customRunDara.reportSearchSteps)
                        {
                            ExportParsingMachine.ReportExportContents($"Applying offset bruteforce for TextProperty at {readingContext.currentUexpOffset}");
                        }

                        readingContext.targetContext.TakeArg();
                        readingContext.targetContext.TakeArg();

                        // Proceed to needed offset
                        readingContext.pattern.Add(ExportParsingMachine.skipPatternElementName);
                        readingContext.pattern.Add(readingContext.targetContext.TakeArg());

                        // Value is there
                        readingContext.pattern.Add(readingContext.targetContext[0]);
                    }
                }
            }
        }
Example #2
0
        private static void MapGeneratorTypesPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            string tKey = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            string tVal = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            ExportParsingMachine.ReportExportContents($"<{tKey}, {tVal}>");

            if (Program.PatternExists($"{Program.PatternFolders.body}/{tKey}") && Program.PatternExists($"{Program.PatternFolders.body}/{tVal}"))
            {
                List <string> keyPattern = Program.GetPattern($"{Program.PatternFolders.body}/{tKey}");
                List <string> valPattern = Program.GetPattern($"{Program.PatternFolders.body}/{tVal}");

                if (keyPattern.TakeArg() == ExportParsingMachine.arrayRepeatPatternElementName &&
                    valPattern.TakeArg() == ExportParsingMachine.arrayRepeatPatternElementName)
                {
                    readingContext.pattern.Add(ExportParsingMachine.elementCountPatternElementName);
                    readingContext.pattern.Add(ExportParsingMachine.arrayRepeatPatternElementName);
                    readingContext.pattern.AddRange(keyPattern);
                    readingContext.pattern.Add(ExportParsingMachine.arrayRepeatEndPatternElementName);

                    readingContext.pattern.Add(ExportParsingMachine.elementCountPatternElementName);
                    readingContext.pattern.Add(ExportParsingMachine.arrayRepeatPatternElementName);
                    readingContext.pattern.AddRange(keyPattern);
                    readingContext.pattern.AddRange(valPattern);
                }
            }
        }
        private static void StructPropertyArrayTypeContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            string typeName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents($"Element structure type: {typeName}");
            }

            if (Program.PatternExists($"{Program.PatternFolders.structure}/{typeName}"))
            {
                readingContext.pattern.Add(ExportParsingMachine.arrayRepeatPatternElementName);
                readingContext.pattern.AddRange(Program.GetPattern($"{Program.PatternFolders.structure}/{typeName}"));
            }
            // Heuristics not allowed yet for replacement
            //else if (Program.config.enablePatternReadingHeuristica && readingContext.collectionElementCount != 0)
            //{
            //    readingContext.pattern.Add(structTypeHeuristicaPatternElementName);
            //    readingContext.pattern.Add(SkipIfPatternShorterThanPatternElemetnName);
            //    readingContext.pattern.Add("2");
            //    readingContext.pattern.Add(ExportParsingMachine.arrayRepeatPatternElementName);
            //}
        }
Example #4
0
        private static void ObjectIndexPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();
            string valueStr = ExportParsingMachine.ObjectByIndexFullNameString(uasset, uexp, readingContext);

            ExportParsingMachine.ReportExportContents($"Object: {valueStr}");
        }
Example #5
0
        private static void SizeStartPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            readingContext.declaredSizeStartOffset = readingContext.currentUexpOffset;

            ExportParsingMachine.ReportExportContents($"Size Start Offset: {readingContext.declaredSizeStartOffset}");
        }
Example #6
0
        private static void FloatPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            ExportParsingMachine.ReportExportContents($"Float Value: {BitConverter.ToSingle(uexp, readingContext.currentUexpOffset)}");

            readingContext.currentUexpOffset += 4;
        }
Example #7
0
        private static void SizePrefixedNullTermStringPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            string value = Program.SizePrefixedStringFromOffsetOffsetAdvance(uexp, ref readingContext.currentUexpOffset);

            ExportParsingMachine.ReportExportContents($"String: {value}");
        }
Example #8
0
        private static void NamePatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            ExportParsingMachine.ReportExportContents($"Name: {ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset)}");

            readingContext.currentUexpOffset += 8;
        }
Example #9
0
        private static void SizePatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.declaredSize       = BitConverter.ToInt32(uexp, readingContext.currentUexpOffset);
            readingContext.currentUexpOffset += 4;
            readingContext.pattern.TakeArg();

            ExportParsingMachine.ReportExportContents($"Size: {readingContext.declaredSize}");
        }
Example #10
0
        private static void BytePropPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            ExportParsingMachine.ReportExportContents($"Bytes Value: {BitConverter.ToString(uexp, readingContext.currentUexpOffset, readingContext.declaredSize)}");

            readingContext.currentUexpOffset += readingContext.declaredSize;
        }
        private static string ReplaceMode(List <string> args)
        {
            byte[] uasset = Program.runData.uasset;
            byte[] uexp   = Program.runData.uexp;

            Int32 exportIndex     = GetExportIndex(uasset, args).Value;
            Int32 exportDefOffset = BitConverter.ToInt32(uasset, OffsetConstants.exportOffsetOffset) + (exportIndex - 1) * OffsetConstants.exportDefSize;

            customRunDara.changedExportSerialOffset = BitConverter.ToInt32(uasset, exportDefOffset + OffsetConstants.exportSerialOffsetOffset);

            Int32 exportOffset = BitConverter.ToInt32(uasset, exportDefOffset + OffsetConstants.exportSerialOffsetOffset) -
                                 BitConverter.ToInt32(uasset, headerSizeOffset);
            Int32 exportSize = BitConverter.ToInt32(uasset, exportDefOffset + OffsetConstants.exportSerialSizeOffset);

            string targetContext = args.TakeArg();

            customRunDara.newValue = args.TakeArg();

            while (args.Count > 0)
            {
                replaceModeAdditionalKeys[args.TakeArg()](args);
            }

            ExportParsingMachine.ResetSLIString();

            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents(
                    $"Searching for {targetContext} in export at {exportOffset} to set new value {customRunDara.newValue}");
            }

            ExportParsingMachine.machineState = new Stack <ReadingContext>();
            ExportParsingMachine.machineState.Push(new ReadingContext()
            {
                currentUexpOffset       = exportOffset,
                declaredSize            = exportSize,
                declaredSizeStartOffset = exportOffset,
                collectionElementCount  = -1,

                pattern = new List <string>()
                {
                    "NTPL"
                },
                patternAlphabet = contextSearchProcessers,

                targetContext = new List <string>(targetContext.Split('/')),

                structCategory = ReadingContext.StructCategory.export,

                contextReturnProcesser = ContextReturnProcesser
            });

            ExportParsingMachine.StepsTilEndOfStruct(Program.runData.uasset, Program.runData.uexp);

            return($" -e {customRunDara.sizeChange} {customRunDara.changedExportSerialOffset}");
        }
Example #12
0
        private static void ArrayRepeatPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            Int32 scaledElementSize;

            // Some element types have no context-free size determination apart from assumed elements total size and count.
            // Also ignore it if we have 0 elements because it is pointless and causes exception.
            if (readingContext.pattern[0] == ExportParsingMachine.scaledArrayElementsPatternElementName &&
                readingContext.collectionElementCount != 0)
            {
                readingContext.pattern.TakeArg();
                scaledElementSize = (readingContext.declaredSizeStartOffset + readingContext.declaredSize -
                                     readingContext.currentUexpOffset) /
                                    (readingContext.collectionElementCount);
            }
            else
            {
                scaledElementSize = -1;
            }

            List <string> repeatedPattern = new List <string>();


            // Passing all the stuff to repeat in cycle which is all past ArrayRepeat and til ArrayRepeatEnd or end of pattern
            while (readingContext.pattern.Count > 0)
            {
                string element = readingContext.pattern.TakeArg();

                if (element == ExportParsingMachine.arrayRepeatEndPatternElementName)
                {
                    break;
                }

                repeatedPattern.Add(element);
            }

            for (int i = 0; i < readingContext.collectionElementCount; i++)
            {
                ExportParsingMachine.ReportExportContents($"Element {i}");

                ExportParsingMachine.machineState.Push(new ReadingContext()
                {
                    currentUexpOffset = readingContext.currentUexpOffset,

                    pattern         = new List <string>(repeatedPattern),
                    patternAlphabet = readingContext.patternAlphabet,

                    structCategory = ReadingContext.StructCategory.nonExport,

                    declaredSize = scaledElementSize
                });

                ExportParsingMachine.ExecutePushedReadingContext(uasset, uexp, readingContext);
            }
        }
        private static void SkipContextContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents("Skipping context");
            }

            readingContext.pattern.Clear();
            readingContext.currentUexpOffset = readingContext.declaredSizeStartOffset + readingContext.declaredSize;
        }
        private static void SizeStartContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            readingContext.declaredSizeStartOffset = readingContext.currentUexpOffset;
            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents($"Context Size start at {readingContext.currentUexpOffset}");
            }
        }
Example #15
0
        private static void UnknownBytesPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            Int32 count = Int32.Parse(readingContext.pattern.TakeArg());

            ExportParsingMachine.ReportExportContents($"Unknown Bytes: {BitConverter.ToString(uexp, readingContext.currentUexpOffset, count)}");

            readingContext.currentUexpOffset += count;
        }
Example #16
0
        private static void TextPropertyDirtyHackPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            //Epic Games probably like it when you have to f**k your brain with TexProperty having a body prefix which varies in SIZE between types.
            //I don't. I hope the author of that idea got a proper remedy.

            readingContext.currentUexpOffset = readingContext.declaredSizeStartOffset + readingContext.declaredSize;
            ExportParsingMachine.ReportExportContents("Text Property support is postponed. ETA depends on readability of UE shitcode.");
        }
Example #17
0
        private static void SkipIfEndPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            if (readingContext.pattern.Count == 0)
            {
                readingContext.currentUexpOffset = readingContext.declaredSizeStartOffset + readingContext.declaredSize;

                ExportParsingMachine.ReportExportContents("Skipping structure due to lack of pattern");
            }
        }
Example #18
0
        private static void NoneTerminatedPropListPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            string substructName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            if (substructName == ExportParsingMachine.endOfStructConfigName)
            {
                readingContext.pattern.TakeArg();
                return;
            }

            string typeName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            ExportParsingMachine.ReportExportContents("------------------------------");
            ExportParsingMachine.ReportExportContents($"{substructName} is {typeName}");

            List <string> propertyPattern;

            try
            {
                propertyPattern = Program.GetPattern($"{Program.PatternFolders.property}/{typeName}");
            }
            catch
            {
                ExportParsingMachine.ReportExportContents($"Failed to find a pattern for property type {typeName}");

                Int32 assumedSize = BitConverter.ToInt32(uexp, readingContext.currentUexpOffset);
                readingContext.currentUexpOffset += 8;

                ExportParsingMachine.ReportExportContents($"Assumed property size {assumedSize}");

                ExportParsingMachine.ReportExportContents($"Assumed property body {BitConverter.ToString(uexp, readingContext.currentUexpOffset + 1, assumedSize)}");

                throw;
            }

            ExportParsingMachine.machineState.Push(new ReadingContext()
            {
                currentUexpOffset       = readingContext.currentUexpOffset,
                declaredSize            = -1,
                declaredSizeStartOffset = -1,
                collectionElementCount  = -1,

                pattern         = propertyPattern,
                patternAlphabet = readingContext.patternAlphabet,

                structCategory = ReadingContext.StructCategory.nonExport
            });

            ExportParsingMachine.ExecutePushedReadingContext(uasset, uexp, readingContext);
        }
Example #19
0
        private static void ElementCountPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            Int32 elementCount = BitConverter.ToInt32(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 4;

            readingContext.collectionElementCount = elementCount;

            ExportParsingMachine.ReportExportContents($"Elements Count: {elementCount}");
        }
Example #20
0
        private static void SkipIfPatternShorterThanPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            Int32 minimalCountToProceed = Int32.Parse(readingContext.pattern.TakeArg());

            if (readingContext.pattern.Count < minimalCountToProceed)
            {
                readingContext.currentUexpOffset = readingContext.declaredSizeStartOffset + readingContext.declaredSize;
                readingContext.pattern.Clear();

                ExportParsingMachine.ReportExportContents("Skipping structure due to lack of pattern");
            }
        }
        private static void SizeContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            readingContext.declaredSize = BitConverter.ToInt32(uexp, readingContext.currentUexpOffset);
            readingContext.contextDeclaredSizeOffset = readingContext.currentUexpOffset;

            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents($"Size is {readingContext.declaredSize}, stored at {readingContext.contextDeclaredSizeOffset}");
            }

            readingContext.currentUexpOffset += 4;
        }
Example #22
0
        private static void ArrayElementTypeNameIndexPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            string typeName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            ExportParsingMachine.ReportExportContents($"Array Element Type: {typeName}");

            if (Program.PatternExists($"{Program.PatternFolders.body}/{typeName}"))
            {
                readingContext.pattern.AddRange(Program.GetPattern($"{Program.PatternFolders.body}/{typeName}"));
            }
        }
        private static void StructTypeNameIndexContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();
            string typeName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents($"Structure Type: {typeName}");
            }

            readingContext.currentUexpOffset += 8;

            if (Program.PatternExists($"{Program.PatternFolders.structure}/{typeName}"))
            {
                readingContext.pattern.AddRange(Program.GetPattern($"{Program.PatternFolders.structure}/{typeName}"));
            }
        }
Example #24
0
        private static void StructTypeNameIndexPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();
            string typeName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            ExportParsingMachine.ReportExportContents($"Structure Type: {typeName}");

            readingContext.currentUexpOffset += 8;

            if (Program.PatternExists($"{Program.PatternFolders.structure}/{typeName}"))
            {
                readingContext.pattern.AddRange(Program.GetPattern($"{Program.PatternFolders.structure}/{typeName}"));
            }
            else if (Program.config.enablePatternReadingHeuristica)
            {
                readingContext.pattern.Add(structTypeHeuristicaPatternElementName);
                readingContext.pattern.Add(ExportParsingMachine.skipIfPatternEndsPatternElementName);
            }
        }
        private static void ValueContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            string            primitiveTypeName = readingContext.pattern.TakeArg();
            PrimitiveTypeData primitiveType     = primitiveTypes[primitiveTypeName];


            if (readingContext.targetContext.Count == 2)
            {
                Int32 skipsLeft = Int32.Parse(readingContext.targetContext[1]);

                if (primitiveTypeName == readingContext.targetContext[0])
                {
                    if (skipsLeft == 0)
                    {
                        if (customRunDara.reportSearchSteps)
                        {
                            ExportParsingMachine.ReportExportContents($"Found replacement target at {readingContext.currentUexpOffset}");
                        }
                        readingContext.pattern.Clear();
                        readingContext.targetContext.Clear();

                        primitiveType.writer(ref readingContext.currentUexpOffset, customRunDara.newValue);

                        customRunDara.taskComplete = true;
                        return;
                    }
                    else
                    {
                        skipsLeft--;
                        readingContext.targetContext[1] = skipsLeft.ToString();
                    }
                }
            }

            primitiveType.skip(ref readingContext.currentUexpOffset);
            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents($"Skipping {primitiveTypeName} value");
            }
        }
Example #26
0
        private static void StructTypeHeurisitcaPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            readingContext.pattern.AddRange(PatternHeuristica.AssumedStructPattern(Program.runData, readingContext,
                                                                                   out PatternHeuristica.HeuristicaStatus heuristicaStatus));

            switch (heuristicaStatus)
            {
            case PatternHeuristica.HeuristicaStatus.Failure:
                ExportParsingMachine.ReportExportContents("Heuristica failed to give assumed structure pattern");
                break;

            case PatternHeuristica.HeuristicaStatus.NonCriticalFailure:
                ExportParsingMachine.ReportExportContents("Heuristica failed to find a meaningful pattern, boilerplate is provided");
                break;

            case PatternHeuristica.HeuristicaStatus.Success:
                ExportParsingMachine.ReportExportContents("Heuristica proposed a structure pattern, applying it");
                break;
            }
        }
Example #27
0
        private static void StructPropertyArrayTypePatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            string typeName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            ExportParsingMachine.ReportExportContents($"Element structure type: {typeName}");

            if (Program.PatternExists($"{Program.PatternFolders.structure}/{typeName}"))
            {
                readingContext.pattern.Add(ExportParsingMachine.arrayRepeatPatternElementName);
                readingContext.pattern.AddRange(Program.GetPattern($"{Program.PatternFolders.structure}/{typeName}"));
            }
            else if (Program.config.enablePatternReadingHeuristica && readingContext.collectionElementCount != 0)
            {
                readingContext.pattern.Add(structTypeHeuristicaPatternElementName);
                readingContext.pattern.Add(ExportParsingMachine.skipIfPatternShorterThanPatternElemetnName);
                readingContext.pattern.Add("2");
                readingContext.pattern.Add(ExportParsingMachine.arrayRepeatPatternElementName);
            }
        }
Example #28
0
 private static void GUIDPatternElementProcesser(byte[] uasset, byte[] uexp, ReadingContext readingContext)
 {
     readingContext.pattern.TakeArg();
     ExportParsingMachine.ReportExportContents(ExportParsingMachine.GUIDFromUexpOffsetToString(ref readingContext.currentUexpOffset));
 }
        private static void ArrayRepeatContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            readingContext.pattern.TakeArg();

            bool  thisArrayIsTarget = false;
            Int32 targetIndex       = -1;

            if (readingContext.targetContext.Count > 2)
            {
                if (readingContext.targetContext[0] == "Array")
                {
                    Int32 skipsLeft = Int32.Parse(readingContext.targetContext[1]);
                    if (skipsLeft == 0)
                    {
                        thisArrayIsTarget = true;

                        readingContext.targetContext.TakeArg();
                        readingContext.targetContext.TakeArg();

                        targetIndex = Int32.Parse(readingContext.targetContext.TakeArg());
                    }
                    else
                    {
                        skipsLeft--;
                        readingContext.targetContext[1] = skipsLeft.ToString();
                    }
                }
            }

            Int32 scaledElementSize;

            // Some element types have no context-free size determination apart from assumed elements total size and count.
            // Also ignore it if we have 0 elements because it is pointless and causes exception.
            if (readingContext.pattern[0] == ExportParsingMachine.scaledArrayElementsPatternElementName &&
                readingContext.collectionElementCount != 0)
            {
                readingContext.pattern.TakeArg();
                scaledElementSize = (readingContext.declaredSizeStartOffset + readingContext.declaredSize -
                                     readingContext.currentUexpOffset) /
                                    (readingContext.collectionElementCount);
            }
            else
            {
                scaledElementSize = -1;
            }

            List <string> repeatedPattern = new List <string>();


            // Passing all the stuff to repeat in cycle which is all past ArrayRepeat and til ArrayRepeatEnd or end of pattern
            while (readingContext.pattern.Count > 0)
            {
                string element = readingContext.pattern.TakeArg();

                if (element == ExportParsingMachine.arrayRepeatEndPatternElementName)
                {
                    break;
                }

                repeatedPattern.Add(element);
            }

            for (int i = 0; i < readingContext.collectionElementCount; i++)
            {
                if (customRunDara.reportSearchSteps)
                {
                    ExportParsingMachine.ReportExportContents($"Element {i}");
                }

                ExportParsingMachine.machineState.Push(new ReadingContext()
                {
                    currentUexpOffset = readingContext.currentUexpOffset,

                    pattern         = new List <string>(repeatedPattern),
                    patternAlphabet = readingContext.patternAlphabet,

                    targetContext = thisArrayIsTarget && (i == targetIndex) ? readingContext.targetContext : new List <string>()
                    {
                        "Pattern Blocker"
                    },

                    structCategory = ReadingContext.StructCategory.nonExport,

                    declaredSize = scaledElementSize,

                    contextReturnProcesser = ContextReturnProcesser
                });

                ExportParsingMachine.ExecutePushedReadingContext(uasset, uexp, readingContext);
            }
        }
        private static void NTPLContextSearcher(byte[] uasset, byte[] uexp, ReadingContext readingContext)
        {
            string targetPropertyName = readingContext.targetContext[0];

            string substructName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            if (substructName == ExportParsingMachine.endOfStructConfigName)
            {
                readingContext.pattern.TakeArg();
                return;
            }

            string typeName = ExportParsingMachine.FullNameString(uexp, readingContext.currentUexpOffset);

            readingContext.currentUexpOffset += 8;

            if (customRunDara.reportSearchSteps)
            {
                ExportParsingMachine.ReportExportContents("------------------------------");
                ExportParsingMachine.ReportExportContents($"{substructName} is {typeName}");
            }

            List <string> propertyPattern;

            try
            {
                propertyPattern = Program.GetPattern($"{Program.PatternFolders.property}/{typeName}");
            }
            catch
            {
                ExportParsingMachine.ReportExportContents($"Failed to find a pattern for property type {typeName}");

                Int32 assumedSize = BitConverter.ToInt32(uexp, readingContext.currentUexpOffset);
                readingContext.currentUexpOffset += 8;

                ExportParsingMachine.ReportExportContents($"Assumed property size {assumedSize}");

                ExportParsingMachine.ReportExportContents($"Assumed property body {BitConverter.ToString(uexp, readingContext.currentUexpOffset + 1, assumedSize)}");

                throw;
            }

            if (substructName != targetPropertyName)
            {
                propertyPattern.Insert(propertyPattern.IndexOf(ExportParsingMachine.sizeStartPatternElementName) + 1,
                                       skipContextPatternElementName);
            }

            List <string> targetSubContext = new List <string>(readingContext.targetContext);

            targetSubContext.RemoveAt(0);

            ExportParsingMachine.machineState.Push(new ReadingContext()
            {
                currentUexpOffset       = readingContext.currentUexpOffset,
                declaredSize            = -1,
                declaredSizeStartOffset = -1,
                collectionElementCount  = -1,

                targetContext = targetSubContext,

                pattern         = propertyPattern,
                patternAlphabet = readingContext.patternAlphabet,

                structCategory = ReadingContext.StructCategory.nonExport,

                contextReturnProcesser = ContextReturnProcesser
            });

            ExportParsingMachine.ExecutePushedReadingContext(uasset, uexp, readingContext);
        }