Add event trigger dumper, names via hash brute force

This commit is contained in:
Michael Scire 2022-02-07 00:24:58 -08:00
parent dd87159997
commit e5f1724637
8 changed files with 388 additions and 20 deletions

View File

@ -22,35 +22,38 @@ public class TriggerTable8a : IFlatBufferArchive<Trigger8a>
[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))]
public class Trigger8a
{
[FlatBufferItem(00)] public Trigger8a_F00 Field_00 { get; set; } = new();
[FlatBufferItem(01)] public Trigger8a_F01[] Field_01 { get; set; } = Array.Empty<Trigger8a_F01>();
[FlatBufferItem(02)] public Trigger8a_F02[] Field_02 { get; set; } = Array.Empty<Trigger8a_F02>();
[FlatBufferItem(00)] public TriggerMeta8a Meta { get; set; } = new();
[FlatBufferItem(01)] public TriggerCondition8a[] Conditions { get; set; } = Array.Empty<TriggerCondition8a>();
[FlatBufferItem(02)] public TriggerCommand8a[] Commands { get; set; } = Array.Empty<TriggerCommand8a>();
}
[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))]
public class Trigger8a_F00
public class TriggerMeta8a
{
[FlatBufferItem(00)] public ulong Field_00 { get; set; }
[FlatBufferItem(01)] public ulong Field_01 { get; set; }
[FlatBufferItem(02)] public string Field_02 { get; set; } = string.Empty;
[FlatBufferItem(03)] public string Field_03 { get; set; } = string.Empty;
[FlatBufferItem(04)] public string Field_04 { get; set; } = string.Empty; // assumed
[FlatBufferItem(00)] public TriggerType8a TriggerTypeID { get; set; }
[FlatBufferItem(01)] public ulong Unused_01 { get; set; }
[FlatBufferItem(02)] public string TriggerMetaArg1 { get; set; } = string.Empty;
[FlatBufferItem(03)] public string TriggerMetaArg2 { get; set; } = string.Empty;
[FlatBufferItem(04)] public string TriggerMetaArg3 { get; set; } = string.Empty;
}
[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))]
public class Trigger8a_F01
public class TriggerCondition8a : IHasCondition8a
{
[FlatBufferItem(00)] public ulong Field_00 { get; set; }
[FlatBufferItem(01)] public byte Field_01 { get; set; } // unk
[FlatBufferItem(02)] public string Field_02 { get; set; } = string.Empty;
[FlatBufferItem(03)] public string Field_03 { get; set; } = string.Empty; // assumed
[FlatBufferItem(04)] public string Field_04 { get; set; } = string.Empty; // assumed
[FlatBufferItem(0)] public ConditionType8a ConditionTypeID { get; set; }
[FlatBufferItem(1)] public Condition8a ConditionID { get; set; }
[FlatBufferItem(2)] public string ConditionArg1 { get; set; } = string.Empty;
[FlatBufferItem(3)] public string ConditionArg2 { get; set; } = string.Empty;
[FlatBufferItem(4)] public string ConditionArg3 { get; set; } = string.Empty;
// For interface compat
public string ConditionArg4 { get; set; } = string.Empty;
public string ConditionArg5 { get; set; } = string.Empty;
}
[FlatBufferTable, TypeConverter(typeof(ExpandableObjectConverter))]
public class Trigger8a_F02
public class TriggerCommand8a
{
[FlatBufferItem(00)] public ulong Field_00 { get; set; }
[FlatBufferItem(01)] public string[] Field_01 { get; set; } = Array.Empty<string>();
[FlatBufferItem(02)] public string[] Field_02 { get; set; } = Array.Empty<string>();
[FlatBufferItem(00)] public TriggerCommandType8a CommandTypeID { get; set; }
[FlatBufferItem(01)] public string[] Arguments { get; set; } = Array.Empty<string>();
}

View File

@ -0,0 +1,69 @@
using FlatSharp.Attributes;
// ReSharper disable UnusedMember.Global
#pragma warning disable RCS1154 // Sort enum members.
namespace pkNX.Structures.FlatBuffers;
[FlatBufferEnum(typeof(ulong))]
public enum TriggerCommandType8a : ulong
{
AllPokeRecover = 0x35AE11B202B33FAB, // "all_poke_recover"
AutoSaveNg = 0x396A9D10B0C06938, // "auto_save_ng"
AutoSaveNgReset = 0x5A23A70C90B2E3C0, // "auto_save_ng_reset"
BagLostCall = 0x8CF3D91B280B82C9, // "bag_lost_call"
BoutiqueCall = 0x8212CBCB399C7F54, // "boutique_call"
CallBattle = 0xB54D19CA9F2B27A8, // "call_battle"
CallEvent = 0x466C5DFFFA6047DE, // "call_event"
CallRealTimeEvent = 0x1608695374913BA9, // "call_real_time_event"
CallTrainerBattle = 0x81FE18C0B68BFC12, // "call_trainer_battle"
CallTriggerBoot = 0xA6C73C7E028A667B, // "call_trigger_boot"
DemoPlay = 0xC68C92A921029B81, // "demo_play"
EndNushiBattle = 0xF4F2BE33D270D1F7, // "end_nushi_battle"
FinishRealTimeEvent = 0x6C0A8D2940D8F34A, // "finish_real_time_event"
FlagReset = 0x861AC906050FBC07, // "flag_reset"
FlagSet = 0x2719CB6B0BCC8398, // "flag_set"
ForceReport = 0x949ED0E99A38A7AF, // "force_report"
HugeOutbreakClear = 0x5B3F92D4849AB3AE, // "huge_outbreak_clear"
HugeOutbreakLottery = 0x1D29BC9A7BA7D242, // "huge_outbreak_lottery"
ItemAdd = 0x6D35E2EAB45C0F6E, // "item_add"
ItemBoxCall = 0x2D01C6B3EE7EC58D, // "item_box_call"
ItemDelete = 0xC59A0CC763D2A7A8, // "item_delete"
ItemSub = 0xD20CF4EA5CB0BEEF, // "item_sub"
LeaveWildPokemon = 0x635B9BF9C7B6BEA3, // "leave_wild_pokemon"
MapChange = 0x1223758E1F82495E, // "map_change"
MapChangeGameOver = 0xA93595533A427B98, // "map_change_game_over"
MoneyAdd = 0x5195731EE2BCA693, // "money_add"
PlayerAnimationStateReset = 0x3221105676A499B3, // "player_animation_state_reset"
PokeAdd = 0x82E032DE25CE78E0, // "poke_add"
PokeBoxCall = 0x1F24A4FCF859527B, // "poke_box_call"
PokePassEvolution = 0x36C4EA8E0C07226A, // "poke_pass_evolution"
PokeSelectMenuCall = 0xE75986350917856A, // "poke_select_menu_call"
QuestAdvance = 0x1F9C392CEFBB1B58, // "quest_advance"
QuestEnd = 0x7F22B0CAFA5DFD63, // "quest_end"
QuestProgressSet = 0x8F4ECBA903BE3CD2, // "quest_progress_set"
QuestStart = 0xACDCFB9B749A3D26, // "quest_start"
ResetWormhole = 0xEEE8243BDF3948B0, // "reset_wormhole"
ResidentWorkClear = 0x908FC6969AB5250D, // "resident_work_clear"
SelectAreaCall = 0xDA71107BF9807C04, // "select_area_call"
SetDressupItem = 0x2D7A8BD9982C8840, // "set_dressup_item"
SetFocusItem = 0x3F669C91AF847B32, // "set_focus_item"
ShopCall = 0x9E3A5EB597D7F7A6, // "shop_call"
ShowExpResult = 0x6C9C2C4BF7EC3138, // "show_exp_result"
SimpleMessageChara = 0xEA08F03627CA535F, // "simple_message_chara"
SimpleMessageSystem = 0x388689E19B68D6F9, // "simple_message_system"
SleepWormhole = 0x6C50ACC5A3018C28, // "sleep_wormhole"
SoundEventPost = 0x53D11252A6F42576, // "sound_event_post"
SoundSituationMixer = 0x82EBFDB54C8107A5, // "sound_situation_mixer"
SpawnWildPokemon = 0x39A6CB761A05967B, // "spawn_wild_pokemon"
StartNushiBattle = 0x081BF500D5709C9C, // "start_nushi_battle"
StartWormhole = 0x2156F9E8ED44730D, // "start_wormhole"
TimeChange = 0x48D69D8788B2FE61, // "time_change"
TimeLock = 0xF7911B293C7F980E, // "time_lock"
TimeRelease = 0x6A28CB95A4BBFF62, // "time_release"
TriggerCommandPreset = 0x8CD4CD78EA36EF55, // "trigger_command_preset"
WorkAdd = 0xB956CAA67E5EB58C, // "work_add"
WorkSet = 0x555FCEA6D6C83ADB, // "work_set"
None = 0xCBF29CE484222645, // ""
}

View File

@ -0,0 +1,44 @@
using FlatSharp.Attributes;
// ReSharper disable UnusedMember.Global
#pragma warning disable RCS1154 // Sort enum members.
namespace pkNX.Structures.FlatBuffers;
[FlatBufferEnum(typeof(ulong))]
public enum TriggerType8a : ulong
{
AreaChange = 0x7E23ECD43CE201A1, // "area_change"
BattleLose = 0x054B378194CDEB91, // "battle_lose"
BattleWin = 0xB2EE750DBCD8FFD6, // "battle_win"
BgEvent = 0x64224581923358E9, // "bg_event"
BootUrankUpgrade = 0x77E1D0135FEDD0E0, // "boot_urank_upgrade"
CaptureFailedPoke = 0xC09BBA82558CCB11, // "capture_failed_poke"
CapturedEventPoke = 0x82DCDEC11E4B902C, // "captured_event_poke"
CapturedPoke = 0xF9DCAFF7B9A4A1B5, // "captured_poke"
DigHit = 0xDEDAFE9CD0903841, // "dig_hit"
DoorEvent = 0xF8026805B0390A1E, // "door_event"
EventBattleLose = 0x956AB57BCBE24AC2, // "event_battle_lose"
EventBattleWin = 0x78891EA1EB1719B7, // "event_battle_win"
FinishEventKisekaeHome = 0xEC5C94E0F6AF0025, // "finish_event_kisekae_home"
FinishEventScript = 0x2E632ED350D2BC4D, // "finish_event_script"
FinishNushiBattle = 0x3B78EA5ACF12B68F, // "finish_nushi_battle"
FinishRideMiniGame = 0x43264D2AEB95F420, // "finish_ride_mini_game"
FinishSelectArea = 0xAB29E81B22E196F1, // "finish_select_area"
FinishThrowMiniGame = 0x474D107BF9969EDC, // "finish_throw_mini_game"
GameOver = 0x57BA8020944AD222, // "game_over"
GameOverAfterward = 0x5F2F50327A48756D, // "game_over_afterward"
GetMkrg = 0x9FE3F0D2163599C7, // "get_mkrg"
NpcTalk = 0xAB00E1A6F5D27687, // "npc_talk"
PosEnter = 0xBD5314EC693575D4, // "pos_enter"
PosExit = 0xC01E67CA2AAD4DFA, // "pos_exit"
PosStay = 0xDB75191D739E26DF, // "pos_stay"
ThrowNg = 0x63CFDE902FA005E7, // "throw_ng"
TrainerBanditLose = 0xA4E0055DFAB6E157, // "trainer_bandit_lose"
TrainerBanditWin = 0x2E11B56C5A55E9E8, // "trainer_bandit_win"
TrainerBattleLose = 0x7DACDFC0F3964597, // "trainer_battle_lose"
TrainerBattleWin = 0xB7942BC60882A228, // "trainer_battle_win"
WildPokeEscape = 0xE22F524AAE64337F, // "wild_poke_escape"
None = 0xCBF29CE484222645, // ""
}

View File

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace pkNX.Structures.FlatBuffers
{
public static class Trigger8aUtil
{
public static IEnumerable<string> GetTriggerTableSummary(TriggerTable8a tab)
{
foreach (var trg in tab.Table)
{
yield return "Trigger:";
foreach (var line in GetTriggerSummary(trg))
yield return $"\t{line}";
}
}
public static IEnumerable<string> GetTriggerSummary(Trigger8a trg)
{
yield return "Meta:";
yield return $"\t{GetTriggerMetaSummary(trg.Meta)}";
yield return "Conditions:";
foreach (var line in GetTriggerConditionsSummary(trg.Conditions))
yield return $"\t{line}";
yield return "Commands:";
foreach (var line in GetTriggerCommandsSummary(trg.Commands))
yield return $"\t{line}";
}
public static string GetTriggerMetaSummary(TriggerMeta8a meta)
{
if (meta.Unused_01 != 0)
throw new ArgumentException("TriggerMeta has unused field set?");
var argsSummary = GetTriggerArgsSummary(meta.TriggerMetaArg1, meta.TriggerMetaArg2, meta.TriggerMetaArg3);
if (Enum.IsDefined(typeof(TriggerType8a), meta.TriggerTypeID))
return $"{meta.TriggerTypeID}({argsSummary})";
else
return $"0x{(ulong)meta.TriggerTypeID:X16}({argsSummary})";
}
public static IEnumerable<string> GetTriggerConditionsSummary(IEnumerable<TriggerCondition8a> conds)
{
foreach (var cond in conds)
yield return $"{Condition8aUtil.GetConditionTypeSummary(cond)}: {Condition8aUtil.GetConditionSummary(cond)}";
}
public static IEnumerable<string> GetTriggerCommandsSummary(IEnumerable<TriggerCommand8a> cmds)
{
foreach (var cmd in cmds)
yield return GetTriggerCommandSummary(cmd);
}
public static string GetTriggerCommandSummary(TriggerCommand8a cmd)
{
var argsSummary = GetTriggerArgsSummary(cmd.Arguments);
if (Enum.IsDefined(typeof(TriggerCommandType8a), cmd.CommandTypeID))
return $"{cmd.CommandTypeID}({argsSummary})";
else
return $"0x{(ulong)cmd.CommandTypeID:X16}({argsSummary})";
}
public static string GetTriggerArgsSummary(params string[] args)
{
var firstEmpty = -1;
for (var i = 0; i < args.Length; i++)
{
if (firstEmpty >= 0 && !string.IsNullOrEmpty(args[i]))
throw new ArgumentException($"Invalid TriggerArg at index {i}!");
else if (firstEmpty < 0 && string.IsNullOrEmpty(args[i]))
firstEmpty = i;
}
return string.Join(", ", args.Select(s => $"\"{s}\"").Take(firstEmpty >= 0 ? firstEmpty : args.Length));
}
}
}

View File

@ -59,6 +59,7 @@ private void InitializeComponent()
this.Tab_Future = new System.Windows.Forms.TabPage();
this.flowLayoutPanel4 = new System.Windows.Forms.FlowLayoutPanel();
this.B_OpenFolder = new System.Windows.Forms.Button();
this.B_EventTriggers = new System.Windows.Forms.Button();
this.TC_Options.SuspendLayout();
this.Tab_General.SuspendLayout();
this.flowLayoutPanel1.SuspendLayout();
@ -294,6 +295,7 @@ private void InitializeComponent()
this.flowLayoutPanel3.Controls.Add(this.B_Outbreak);
this.flowLayoutPanel3.Controls.Add(this.B_MoveShop);
this.flowLayoutPanel3.Controls.Add(this.B_ScriptCommandNames);
this.flowLayoutPanel3.Controls.Add(this.B_EventTriggers);
this.flowLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel3.Location = new System.Drawing.Point(3, 3);
this.flowLayoutPanel3.Name = "flowLayoutPanel3";
@ -399,6 +401,16 @@ private void InitializeComponent()
this.B_OpenFolder.UseVisualStyleBackColor = true;
this.B_OpenFolder.Click += new System.EventHandler(this.B_OpenFolder_Click);
//
// B_EventTriggers
//
this.B_EventTriggers.Location = new System.Drawing.Point(132, 125);
this.B_EventTriggers.Name = "B_EventTriggers";
this.B_EventTriggers.Size = new System.Drawing.Size(123, 55);
this.B_EventTriggers.TabIndex = 22;
this.B_EventTriggers.Text = "Event Triggers";
this.B_EventTriggers.UseVisualStyleBackColor = true;
this.B_EventTriggers.Click += new System.EventHandler(this.B_EventTriggers_Click);
//
// DumperPLA
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -455,5 +467,6 @@ private void InitializeComponent()
private System.Windows.Forms.Button B_Placement;
private System.Windows.Forms.Button B_Resident;
private System.Windows.Forms.Button B_DumpDrops;
private System.Windows.Forms.Button B_EventTriggers;
}
}

View File

@ -117,6 +117,12 @@ private void B_DumpScriptCommands(object sender, EventArgs e)
System.Media.SystemSounds.Asterisk.Play();
}
private void B_EventTriggers_Click(object sender, EventArgs e)
{
Dumper.DumpEventTriggers();
System.Media.SystemSounds.Asterisk.Play();
}
private void B_GetDex_Click(object sender, EventArgs e)
{
Dumper.DumpDex();
@ -147,6 +153,5 @@ private void B_EggMove_Click(object sender, EventArgs e)
System.Media.SystemSounds.Asterisk.Play();
}
#endregion
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -1079,6 +1079,39 @@ public void DumpScriptID()
File.WriteAllText(path, text);
}
public void DumpEventTriggers()
{
var eventTriggerDir = Path.Combine(ROM.PathRomFS, "bin", "event", "event_progress", "trigger");
var eventTriggerFiles = Directory.EnumerateFiles(eventTriggerDir, "*", SearchOption.AllDirectories).Where(p => Path.GetExtension(p) == ".bin");
const string outFolder = "event_trigger";
Directory.CreateDirectory(GetPath(outFolder));
var allLines = new List<string>();
foreach (var f in eventTriggerFiles)
{
if (Path.GetFileName(f) == "trigger_preset.bin")
continue;
var table = FlatBufferConverter.DeserializeFrom<TriggerTable8a>(f);
var curLines = new List<string>();
curLines.Add($"File: {Path.GetFileName(f)}");
foreach (var line in Trigger8aUtil.GetTriggerTableSummary(table))
curLines.Add($"\t{line}");
File.WriteAllLines(GetPath(outFolder, $"trigger_{Path.GetFileNameWithoutExtension(f).Replace("trigger_", string.Empty)}.txt"), curLines);
allLines.AddRange(curLines);
allLines.Add(string.Empty);
}
File.WriteAllLines(GetPath(outFolder, "triggerAll.txt"), allLines);
}
public void DumpMoveShop()
{
var file = ROM.GetFile(GameFile.MoveShop).FilePath;