Пример #1
0
        private static void ApplyFixes()
        {
            DisplayMessage("Applying fixes...");
#if !DEBUG
            try
            {
#endif
            var fixes = XDocument.Load("fixes.xml");
            if (fixes.Root == null)
            {
                throw new InvalidDataException("fixes.xml file corrupted.");
            }

            foreach (var fixset in fixes.Root.Elements())
            {
                if (!model.ContainsKey(fixset.Name.LocalName))
                {
                    model[fixset.Name.LocalName] = new List <dynamic>();
                }
                var data = (List <dynamic>)model[fixset.Name.LocalName];

                var update = fixset.Element("update");
                if (update != null)
                {
                    foreach (var fix in update.Elements())
                    {
                        var elements = data.Where(e => e.id == Convert.ToInt32(fix.Attribute("id").Value, CultureInfo.InvariantCulture)).ToList();

                        if (!elements.Any())
                        {
                            dynamic el = new ModelObject();

                            foreach (var attr in fix.Attributes())
                            {
                                el[attr.Name.LocalName] = attr.Parse();
                            }

                            data.Add(el);
                            continue;
                        }

                        var element = elements.Single();
                        foreach (var attr in fix.Attributes())
                        {
                            element[attr.Name.LocalName] = attr.Parse();
                        }
                    }
                }

                var remove = fixset.Element("remove");
                if (remove == null)
                {
                    continue;
                }

                foreach (var fix in remove.Elements())
                {
                    data.RemoveAll(x => x.id == Convert.ToInt32(fix.Attribute("id").Value, CultureInfo.InvariantCulture));
                }
            }
#if !DEBUG
        }

        catch
        {
            DisplayError();
            throw;
        }
#endif

            DisplaySuccess();
        }
Пример #2
0
        internal static dynamic[] Parse(Stream stream, IDictionary <int, string> fields)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            var header = stream.Read <Header>(0);

            if (header.Format != 0x00000067736D5F64)
            {
                throw new InvalidOperationException("Invalid data format.");
            }

            if (header.Version != 0x0000000300000003)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, "Invalid format version. [{0:X8}]", header.Version));
            }

            long[] table;
            if (header.TableSize != 0)
            {
                if (header.TableSize != header.Count * 8 || header.EntrySize != 0)
                {
                    throw new InvalidOperationException("Data is corrupt.");
                }

                table = stream.ReadArray <long>(header.Count, header.HeaderSize);
            }
            else
            {
                if (header.DataSize != header.Count * header.EntrySize)
                {
                    throw new InvalidOperationException("Data is corrupt.");
                }

                table = new long[] { };
            }

            stream.Position = header.HeaderSize + header.TableSize;
            byte[] data = new byte[header.DataSize];
            stream.Read(data, 0, data.Length);

            if (header.Encrypted)
            {
                for (int i = 0; i < table.Length; i++)
                {
                    table[i] = ~table[i];
                }

                for (int i = 0; i < data.Length; i++)
                {
                    data[i] = (byte)~data[i];
                }
            }

            dynamic objects = new ModelObject[header.Count];

            int length = (int)header.EntrySize;

            for (int i = 0; i < objects.Length; i++)
            {
                int offset;
                if (header.TableSize != 0)
                {
                    length = (int)(table[i] >> 32);
                    offset = (int)table[i];
                }
                else
                {
                    offset = i * (int)header.EntrySize;
                }

                int count = BitConverter.ToInt32(data, offset);

                dynamic resource = new ModelObject();

                for (int j = 0; j < count; j++)
                {
                    if (!fields.ContainsKey(j))
                    {
                        continue;
                    }

                    int entryoffset = BitConverter.ToInt32(data, offset + j * 8 + 4) + offset;
                    int entrytype   = BitConverter.ToInt32(data, offset + j * 8 + 8);

                    dynamic value;
                    switch (entrytype)
                    {
                    case 0:
                        entryoffset += 0x1C;

                        int stringlength = 0;
                        for (int k = 0; k < length; k++)
                        {
                            if (data[entryoffset + k] == 0)
                            {
                                break;
                            }

                            stringlength++;
                        }

                        value = ShiftJISFF11Encoding.ShiftJISFF11.GetString(data, entryoffset, stringlength);
                        break;

                    case 1:
                        value = BitConverter.ToInt32(data, entryoffset);
                        break;

                    default:
                        value = null;
                        break;
                    }

                    resource[fields[j]] = value;
                }

                objects[i] = resource;
            }

            return(objects);
        }
Пример #3
0
        private static void PostProcess()
        {
            Console.WriteLine("Post-processing parsed data...");

            var success = false;

            try
            {
                // Add log names for non-english languages
                foreach (var buff in model.buffs)
                {
                    if (buff.ContainsKey("ja"))
                    {
                        buff.jal = buff.ja;
                    }
                }

                // Populate ability recast table with proper names
                foreach (var recast in model.ability_recasts)
                {
                    foreach (var action in model.actions)
                    {
                        if (recast.id != action.recast_id)
                        {
                            continue;
                        }

                        recast.en = action.en;
                        recast.ja = action.ja;
                        break;
                    }
                }

                // Add categories to key items
                var category = "";
                for (var i = model.key_items.Count - 1; i >= 0; --i)
                {
                    dynamic ki = model.key_items[i];
                    if (ki.en.StartsWith("-"))
                    {
                        category = ki.en.Substring(1);
                        model.key_items.Remove(ki);
                    }
                    else
                    {
                        ki.category = category;
                    }
                }

                // Move item descriptions into separate table
                //TODO: Remove when shared resources are implemented
                model.item_descriptions = new List <dynamic>();
                foreach (var item in model.items)
                {
                    dynamic item_description = new ModelObject();
                    item_description.id = item.id;
                    item_description.en = item.endesc;
                    item_description.ja = item.jadesc;

                    item.endesc = null;
                    item.jadesc = null;

                    model.item_descriptions.Add(item_description);
                }

                // Fill in linked auto-translate names
                foreach (var at in model.auto_translates)
                {
                    if (!at.en.StartsWith("@"))
                    {
                        continue;
                    }

                    int id = int.Parse(at.en.Substring(2), NumberStyles.HexNumber);

                    string key;
                    switch ((char)at.en[1])
                    {
                    case 'A':
                        key = "zones";
                        break;

                    case 'C':
                        key = "spells";
                        break;

                    case 'J':
                        key = "jobs";
                        break;

                    case 'Y':
                        key = "actions";
                        break;

                    default:
                        throw new InvalidDataException(string.Format("Unknown auto-translate code: {0}", at.en));
                    }

                    dynamic item = null;
                    foreach (var i in model[key])
                    {
                        if (i.id != id)
                        {
                            continue;
                        }

                        item = i;
                        break;
                    }

                    if (item != null)
                    {
                        at.en = item.en;
                        at.ja = item.ja;
                    }
                    else
                    {
                        //throw new InvalidDataException(string.Format("Unknown auto-translate ID for {0}: {1}", key, id));
                    }
                }

                // Split abilities into categories
                foreach (var action in model.actions)
                {
                    // Weapon skill
                    if (action.id >= 0x0000 && action.id < 0x0200)
                    {
                        action.monster_level = null;
                        action.mp_cost       = null;
                        action.recast_id     = null;
                        action.tp_cost       = null;
                        action.type          = null;

                        model.weapon_skills.Add(action);
                    }
                    // Job ability
                    else if (action.id >= 0x0200 && action.id < 0x0600)
                    {
                        action.id -= 0x0200;

                        action.monster_level = null;

                        model.job_abilities.Add(action);
                    }
                    // Job traits
                    else if (action.id >= 0x0600 && action.id < 0x0700)
                    {
                        action.id -= 0x0600;

                        action.monster_level = null;
                        action.mp_cost       = null;
                        action.prefix        = null;
                        action.recast_id     = null;
                        action.tp_cost       = null;
                        action.type          = null;

                        model.job_traits.Add(action);
                    }
                    // Monstrosity
                    else if (action.id >= 0x0700)
                    {
                        action.id -= 0x0700;

                        action.mp_cost   = null;
                        action.recast_id = null;
                        action.type      = null;

                        // Remove names, as they are parsed separately
                        action.en = null;
                        action.ja = null;

                        if (action.id < model.monster_abilities.Count)
                        {
                            model.monster_abilities[action.id].Merge(action);
                        }
                    }
                }
                model.actions = null;


                // Shift monster abilities up by 0x100
                foreach (var monster_ability in model.monster_abilities)
                {
                    monster_ability.id += 0x100;
                }

                // Split merit point names/descriptions and filter garbage values
                foreach (var merit_point in model.merit_points)
                {
                    // The first 64 entries contain the category names
                    if (merit_point.id < 0x40 || merit_point.en.StartsWith("Meripo"))
                    {
                        merit_point.id = 0;
                        continue;
                    }

                    // Uneven entries contain the descriptions for the previous entry
                    if (merit_point.id % 2 != 1)
                    {
                        continue;
                    }

                    model.merit_points[merit_point.id - 1].endesc = merit_point.en;
                    model.merit_points[merit_point.id - 1].jadesc = merit_point.ja;

                    merit_point.id = 0;
                }

                ((List <dynamic>)model.merit_points).RemoveAll(merit_point => merit_point.id == 0);

                // Split job point names/descriptions and filter garbage values
                foreach (var job_point in model.job_points)
                {
                    // The first 64 entries contain the category names
                    if (job_point.id < 0x40 || job_point.en == "カテゴリー名" || job_point.en == "ヘルプ文")
                    {
                        job_point.id = 0;
                        continue;
                    }

                    // Uneven entries contain the descriptions for the previous entry
                    if (job_point.id % 2 != 1)
                    {
                        continue;
                    }

                    model.job_points[job_point.id - 1].endesc = job_point.en;
                    model.job_points[job_point.id - 1].jadesc = job_point.ja;

                    job_point.id = 0;
                }

                ((List <dynamic>)model.job_points).RemoveAll(job_point => job_point.id == 0);

                foreach (var mount in model.mounts)
                {
                    mount.prefix = "/mount";
                }

                success = true;
            }
            finally
            {
                DisplayResult(success);
            }
        }
        private static ModelObject ParseSpell(BinaryReader reader)
        {
            dynamic spell = new ModelObject();

            spell.id = reader.ReadInt16();
            if (spell.id == 0)
            {
                return(null);
            }

            spell.type    = (MagicType)reader.ReadInt16();
            spell.element = reader.ReadByte();
            var padding = reader.ReadByte();                 // Unknown, possibly just padding or element being a short. Always 0x00

            Debug.Assert(padding == 0);
            spell.targets   = reader.ReadUInt16();
            spell.skill     = reader.ReadInt16();
            spell.mp_cost   = reader.ReadInt16();
            spell.cast_time = reader.ReadByte() / 4.0;
            spell.recast    = reader.ReadByte() / 4.0;
            spell.levels    = new Dictionary <int, int>();
            for (var job = 0; job < 0x18; ++job)
            {
                var level = reader.ReadInt16();
                if (level != -1)
                {
                    spell.levels[job] = level;
                }
            }

            // Currently the last job slot occasionally mirrors the lowest level that any job learns the spell.
            // May be NPC-related information. This may be removed at some point, if they expand on jobs more
            spell.levels.Remove(0x17);

            // SE changed spell recast times in memory to be indexed by spell ID, not recast ID
            spell._oldrecast = reader.ReadInt16(); // old spell.recast_id
            spell.recast_id  = spell.id;

            spell.icon_id_nq   = reader.ReadInt16();
            spell.icon_id      = reader.ReadInt16();
            spell.requirements = reader.ReadByte();
            var range = reader.ReadByte();

            spell.range = range == 15 ? 0 : range;

            // AoE information?
            spell._aoe_range       = reader.ReadByte(); // spell.aoe_range : 11 for uncastable AOE enfeebling. 15 for self target spells
            spell._target_shape    = reader.ReadByte(); // spell.target_shape : Target type. 1 for centered on target. 2 for conal. 3 for
            spell._cursor_behavior = reader.ReadByte(); // spell.cursor_behavior : 1 for self-target non-AoE, 2 for self-target AoE, 5 for self-target AoE that needs an enemy in range, 6 for Geomancy, 8 for Enemy, etc.
            reader.ReadBytes(0x03);

            // Unknown bytes
            spell._unknown12 = reader.ReadByte(); // The first and eighth bits are used. 0, 1, 128, and 129 are observed. They're systematic but I can't see what the covariate is.
            spell._unknown13 = reader.ReadByte(); //

            // Another requirements field?
            spell._unknown14 = reader.ReadByte(); // 128 if the spell can be stacked with Accession. Seems redundant with the "requirements" field. Takes no other values.
            spell._unknown15 = reader.ReadByte(); // 1 for Manifestation, 2 for Enlightenment, 4 for Embrava, 8 for Meteor, 16 for Geo-spells, 32 for -ra spells, 64 for spells that take all MP (Full Cure and Death)

            //Unknown section
            //reader.ReadBytes(0x0A);
            spell._unknown16 = reader.ReadByte();
            spell._unknown17 = reader.ReadByte();
            spell._unknown18 = reader.ReadByte();
            spell._unknown19 = reader.ReadByte();
            spell._unknown20 = reader.ReadByte();
            spell._unknown21 = reader.ReadByte();
            spell._unknown22 = reader.ReadByte();
            spell._unknown23 = reader.ReadByte();
            spell._unknown24 = reader.ReadByte();
            spell._unknown25 = reader.ReadByte();
            spell._unknown26 = reader.ReadUInt16(); // Always 0x0000
            Debug.Assert((Boolean)(spell._unknown26 == 0));

            // These next 3 bytes indicate whether a spell is accessible through Gifts for specific jobs.
            reader.ReadByte();                 // spells.gift_spell_flags : 0x08 WHM, 0x10 BLM, 0x20 RDM, 0x80 PLD
            reader.ReadByte();                 // spells.gift_spell_flags : 0x01 DRK, 0x04 BRD, 0x20 NIN
            reader.ReadByte();                 // spells.gift_spell_flags : 0x10 SCH, 0x20 GEO, 0x40 RUN

            var trail = reader.ReadBytes(0x4); // 0x00000000

            foreach (var b in trail)
            {
                Debug.Assert(b == 0);
            }
            Debug.Assert(reader.ReadSByte() == -1); // 0xFF, last byte

            // Derived data
            spell.prefix = ((MagicType)spell.type).Prefix();

            return(spell);
        }