예제 #1
0
        private static NgParameterRange DetectPattern(NgParameterRange parameter, NgBlock dbgBlock)
        {
            if (parameter.Kind != NgParameterKind.FixedEnumeration)
            {
                return(parameter);
            }

            // Parse the numbers inside the strings.
            var unmappedEnums = new List <Parsed>();

            foreach (var entry in parameter.FixedEnumeration)
            {
                unmappedEnums.Add(new Parsed {
                    Original = entry.Value, Substrings = SplitIntoNumbers(entry.Value.Name)
                });
            }
            unmappedEnums.Sort();

            var outputChoice = new List <NgParameterRange>();

            while (DetectLinearity(outputChoice, unmappedEnums, dbgBlock))
            {
            }
            return(new NgParameterRange(outputChoice));
        }
예제 #2
0
        private static NgParameterRange GetList(NgBlock block)
        {
            if (block.Items.Count == 1 && block.Items[0].StartsWith("#"))
            {
                // Special list
                var list = block.Items[0];
                // Dynamic list?
                if (list == "#ROOMS_255#")
                {
                    return(new NgParameterRange(NgParameterKind.Rooms255));
                }
                else if (list == "#SOUND_EFFECT_A#")
                {
                    return(new NgParameterRange(NgParameterKind.SoundEffectsA));
                }
                else if (list == "#SOUND_EFFECT_B#")
                {
                    return(new NgParameterRange(NgParameterKind.SoundEffectsB));
                }
                else if (list == "#SFX_1024#")
                {
                    return(new NgParameterRange(NgParameterKind.Sfx1024));
                }
                else if (list == "#NG_STRING_LIST_255#")
                {
                    return(new NgParameterRange(NgParameterKind.NgStringsList255));
                }
                else if (list == "#NG_STRING_LIST_ALL#")
                {
                    return(new NgParameterRange(NgParameterKind.NgStringsAll));
                }
                else if (list == "#PSX_STRING_LIST#")
                {
                    return(new NgParameterRange(NgParameterKind.PsxStringsList));
                }
                else if (list == "#PC_STRING_LIST#")
                {
                    return(new NgParameterRange(NgParameterKind.PcStringsList));
                }
                else if (list == "#STRING_LIST_255#")
                {
                    return(new NgParameterRange(NgParameterKind.StringsList255));
                }
                else if (list == "#MOVEABLES#")
                {
                    return(new NgParameterRange(NgParameterKind.MoveablesInLevel));
                }
                else if (list == "#SINK_LIST#")
                {
                    return(new NgParameterRange(NgParameterKind.SinksInLevel));
                }
                else if (list == "#STATIC_LIST#")
                {
                    return(new NgParameterRange(NgParameterKind.StaticsInLevel));
                }
                else if (list == "#FLYBY_LIST#")
                {
                    return(new NgParameterRange(NgParameterKind.FlybyCamerasInLevel));
                }
                else if (list == "#CAMERA_EFFECTS#")
                {
                    return(new NgParameterRange(NgParameterKind.CamerasInLevel));
                }
                else if (list == "#WAD-SLOTS#" || list == "#LARA_ANIM_SLOT#")
                {
                    return(new NgParameterRange(NgParameterKind.WadSlots));
                }
                else if (list == "#STATIC_SLOTS#")
                {
                    return(new NgParameterRange(NgParameterKind.StaticsSlots));
                }
                else if (list == "#LARA_POS_OCB#")
                {
                    return(new NgParameterRange(NgParameterKind.LaraStartPosOcb));
                }

                // Repeated strings
                if (list.StartsWith("#REPEAT#"))
                {
                    var tokens      = list.Replace("#REPEAT#", "").Split('#');
                    var radix       = tokens[0].Replace("\"", "");
                    var start       = int.Parse(tokens[1].Replace(",", ""));
                    var end         = int.Parse(tokens[2].Replace(",", ""));
                    var enumeration = new SortedList <ushort, TriggerParameterUshort>();
                    for (var i = start; i < end; i++)
                    {
                        enumeration.Add(ToU16(i), new TriggerParameterUshort((ushort)i, radix + i));
                    }
                    return(DetectPattern(new NgParameterRange(enumeration), block));
                }

                // TXT lists
                var listName = list.Replace("#", "");
                if (File.Exists("NG\\" + listName + ".txt"))
                {
                    return(DetectPattern(new NgParameterRange(GetListFromTxt(listName)), block));
                }
                else
                {
                    throw new Exception("Missing NG file");
                }
            }
            else
            {
                // Free list
                var enumeration = new SortedList <ushort, TriggerParameterUshort>();
                foreach (var item in block.Items)
                {
                    enumeration.Add(GetItemId(item), new TriggerParameterUshort(GetItemId(item), GetItemValue(item)));
                }
                return(DetectPattern(new NgParameterRange(enumeration), block));
            }
        }
예제 #3
0
        private static bool DetectLinearity(List <NgParameterRange> outChoice, List <Parsed> unmappedEnumsSorted, NgBlock dbgBlock)
        {
            if (unmappedEnumsSorted.Count <= 5) // It's not worth it if there are at most 5 elements
            {
                AddFixed(outChoice, unmappedEnumsSorted.Select(p => p.Original));
                unmappedEnumsSorted.Clear();
                return(false);
            }

            Parsed firstEnum = unmappedEnumsSorted.First();
            ushort idStart   = firstEnum.Original.Key;
            int    count     = 1;

            // Stop if there is a varying amount of numbers in the strings or if different values are fixed.
            for (int i = 1; i < unmappedEnumsSorted.Count; ++i)
            {
                Parsed entry = unmappedEnumsSorted[i];

                // Don't compress certain strings
                if (entry.Original.Name.StartsWith("PUZZLE_ITEM", StringComparison.InvariantCulture) ||
                    entry.Original.Name.StartsWith("KEY_ITEM", StringComparison.InvariantCulture) ||
                    entry.Original.Name.StartsWith("Send command for ", StringComparison.InvariantCulture) ||
                    entry.Original.Name.Length == 2 && entry.Original.Name.StartsWith("F", StringComparison.InvariantCulture) ||    // F keys
                    entry.Original.Name.Length == 7 && entry.Original.Name.StartsWith("Number", StringComparison.InvariantCulture)) // Number keys
                {
                    break;
                }

                if (entry.Substrings.Count != firstEnum.Substrings.Count)
                {
                    break;
                }
                for (int j = 0; j < entry.Substrings.Count; ++j)
                {
                    if (entry.Substrings[j].Value != null != (firstEnum.Substrings[j].Value != null))
                    {
                        goto ExitLoop0;
                    }
                }
                for (int j = 0; j < entry.Substrings.Count; ++j)
                {
                    if (entry.Substrings[j].Value == null)
                    {
                        if (entry.Substrings[j].String != firstEnum.Substrings[j].String)
                        {
                            goto ExitLoop0;
                        }
                    }
                }

                // Check if the run is linear
                // There can't be missing keys.
                if (idStart + count != entry.Original.Key)
                {
                    break;
                }
                ++count;
            }
ExitLoop0:
            if (count <= 5) // It's not worth it if there are at most 5 elements
            {
                AddFixed(outChoice, unmappedEnumsSorted.Take(count).Select(p => p.Original));
                unmappedEnumsSorted.RemoveRange(0, count);
                return(true);
            }

            // String format now is perfectly fine.
            // We just have to find some kind of linear pattern
            Parsed secondEnum = unmappedEnumsSorted[1];
            List <NgLinearParameter> linearParameters = new List <NgLinearParameter>(firstEnum.Substrings.Count);

            for (int i = 0; i < firstEnum.Substrings.Count; ++i)
            {
                if (firstEnum.Substrings[i].Value == null)
                {
                    linearParameters.Add(new NgLinearParameter {
                        FixedStr = firstEnum.Substrings[i].String
                    });
                }
                else
                {
                    decimal xDistance = secondEnum.Original.Key - firstEnum.Original.Key;
                    decimal yDistance = secondEnum.Substrings[i].Value.Value - firstEnum.Substrings[i].Value.Value;
                    decimal factor    = yDistance / xDistance;
                    decimal add       = firstEnum.Substrings[i].Value.Value - firstEnum.Original.Key * factor;
                    linearParameters.Add(new NgLinearParameter {
                        Factor = factor, Add = add
                    });
                }
            }

            // Eliminate unnecessary linear parameters
            // (i.e. linear with 0 slope)
            for (int i = 0; i < linearParameters.Count; ++i)
            {
                if (linearParameters[i].FixedStr != null)
                {
                    if (linearParameters[i].Factor == 0)
                    {
                        linearParameters[i] = new NgLinearParameter {
                            FixedStr = firstEnum.Substrings[i].String
                        }
                    }
                }
            }
            ;
            for (int i = linearParameters.Count - 1; i >= 1; --i)
            {
                if (linearParameters[i].FixedStr != null && linearParameters[i - 1].FixedStr != null)
                {
                    linearParameters[i - 1] = new NgLinearParameter {
                        FixedStr = linearParameters[i - 1].FixedStr + linearParameters[i].FixedStr
                    };
                    linearParameters.RemoveAt(i);
                    for (int j = 0; j < count; ++j)
                    {
                        unmappedEnumsSorted[j].Substrings.RemoveAt(i);
                    }
                }
            }

            // Check that the linear pattern is indeed a good fit
            for (int i = 0; i < count; ++i)
            {
                var @enum = unmappedEnumsSorted[i];
                for (int j = 0; j < linearParameters.Count; ++j)
                {
                    if (linearParameters[j].FixedStr == null)
                    {
                        // Calculate prediction with the linear model
                        decimal value = linearParameters[j].Factor * @enum.Original.Key + linearParameters[j].Add;

                        // Check prediction
                        if (@enum.Substrings[j].Value.Value != value)
                        {
                            count = i;
                            goto ExitLoop1;
                        }
                    }
                }
            }
ExitLoop1:
            if (count < 2 + linearParameters.Count * 2) // It's not worth it if there are at most 5 elements
            {
                AddFixed(outChoice, unmappedEnumsSorted.Take(count).Select(p => p.Original));
                unmappedEnumsSorted.RemoveRange(0, count);
                return(true);
            }

            outChoice.Add(new NgParameterRange(new NgLinearModel
            {
                Start        = idStart,
                EndInclusive = unchecked ((ushort)(idStart + count - 1)),
                Parameters   = linearParameters
            }));
            unmappedEnumsSorted.RemoveRange(0, count);
            return(true);
        }
예제 #4
0
        private static NgBlock ReadNgBlock(StreamReader reader)
        {
            string line = "";

            while (!reader.EndOfStream)
            {
                line = reader.ReadLine().Trim();
                if (line.StartsWith("<START"))
                {
                    break;
                }
            }

            if (reader.EndOfStream)
            {
                return(null);
            }

            var tokens = line.Split('_');
            var block  = new NgBlock();

            // Block type
            if (tokens[1] == "TRIGGERWHAT" || tokens[1] == "TRIGGERTYPE" || tokens[1] == "TEXTS")
            {
                block.Type = NgBlockType.Trigger;
            }
            else if (tokens[1] == "EFFECT")
            {
                block.Type = NgBlockType.FlipEffect;
            }
            else if (tokens[1] == "ACTION")
            {
                block.Type = NgBlockType.Action;
            }
            else if (tokens[1] == "CONDITION")
            {
                block.Type = NgBlockType.Condition;
            }
            else
            {
                throw new Exception("Unknown token[1]");
            }

            // Parameter type
            if (tokens[3].StartsWith("O"))
            {
                block.ParameterType = NgParameterType.Object;
            }
            else if (tokens[3].StartsWith("E"))
            {
                block.ParameterType = NgParameterType.Extra;
            }
            else if (tokens[3].StartsWith("T"))
            {
                block.ParameterType = NgParameterType.Timer;
            }
            else if (tokens[3].StartsWith("B"))
            {
                block.ParameterType = NgParameterType.Extra;
            }
            else
            {
                throw new Exception("Unknown token[3]");
            }

            block.Id = ToU16(int.Parse(tokens[2]));

            while (!reader.EndOfStream)
            {
                line = reader.ReadLine().Trim();
                if (line.StartsWith(";") || line == "")
                {
                    continue;
                }
                if (line.StartsWith("<END"))
                {
                    break;
                }
                block.Items.Add(AdjustCardinalDirections(line));
            }

            return(block);
        }