From b2883fd79fb10da9c0e79f0842c0cc3da2f53100 Mon Sep 17 00:00:00 2001
From: Zin <62830952+Zintixx@users.noreply.github.com>
Date: Thu, 28 May 2026 22:04:28 -0700
Subject: [PATCH] maid tables
---
Maple2.File.Parser/Maple2.File.Parser.csproj | 2 +-
Maple2.File.Parser/ServerTableParser.cs | 28 ++++++++
Maple2.File.Parser/TableParser.cs | 65 +++++++++++++++++++
Maple2.File.Parser/Xml/Table/MaidExp.cs | 14 ++++
Maple2.File.Parser/Xml/Table/MaidProperty.cs | 15 +++++
Maple2.File.Parser/Xml/Table/MaidRecipe.cs | 30 +++++++++
.../Xml/Table/MaidRecipeGroup.cs | 16 +++++
Maple2.File.Parser/Xml/Table/MaidSalary.cs | 15 +++++
.../Xml/Table/Server/MaidGradeInfo.cs | 17 +++++
.../Xml/Table/Server/MaidRecipeSvr.cs | 36 ++++++++++
Maple2.File.Tests/ServerTableParserTest.cs | 18 +++++
Maple2.File.Tests/TableParserTest.cs | 19 ++++++
12 files changed, 274 insertions(+), 1 deletion(-)
create mode 100644 Maple2.File.Parser/Xml/Table/MaidExp.cs
create mode 100644 Maple2.File.Parser/Xml/Table/MaidProperty.cs
create mode 100644 Maple2.File.Parser/Xml/Table/MaidRecipe.cs
create mode 100644 Maple2.File.Parser/Xml/Table/MaidRecipeGroup.cs
create mode 100644 Maple2.File.Parser/Xml/Table/MaidSalary.cs
create mode 100644 Maple2.File.Parser/Xml/Table/Server/MaidGradeInfo.cs
create mode 100644 Maple2.File.Parser/Xml/Table/Server/MaidRecipeSvr.cs
diff --git a/Maple2.File.Parser/Maple2.File.Parser.csproj b/Maple2.File.Parser/Maple2.File.Parser.csproj
index 86883bf..dc10057 100644
--- a/Maple2.File.Parser/Maple2.File.Parser.csproj
+++ b/Maple2.File.Parser/Maple2.File.Parser.csproj
@@ -13,7 +13,7 @@
MapleStory2, File, Parser, m2d, xml
true
- 2.4.15
+ 2.4.16
net8.0
README.md
enable
diff --git a/Maple2.File.Parser/ServerTableParser.cs b/Maple2.File.Parser/ServerTableParser.cs
index 12569fd..1369b9d 100644
--- a/Maple2.File.Parser/ServerTableParser.cs
+++ b/Maple2.File.Parser/ServerTableParser.cs
@@ -15,6 +15,8 @@
using ItemMergeOptionRoot = Maple2.File.Parser.Xml.Table.Server.ItemMergeOptionRoot;
using ItemOptionVariation = Maple2.File.Parser.Xml.Table.Server.ItemOptionVariation;
using MergeOption = Maple2.File.Parser.Xml.Table.Server.MergeOption;
+using MaidRecipe = Maple2.File.Parser.Xml.Table.Server.MaidRecipe;
+using MaidRecipeRoot = Maple2.File.Parser.Xml.Table.Server.MaidRecipeRoot;
namespace Maple2.File.Parser;
@@ -61,6 +63,8 @@ public class ServerTableParser {
private readonly XmlSerializer constantsSerializer;
private readonly XmlSerializer npcStatFactorByPlayerCountSerializer;
private readonly XmlSerializer npcStatFactorByLevelSerializer;
+ private readonly XmlSerializer maidGradeInfoSerializer;
+ private readonly XmlSerializer maidRecipeSerializer;
public ServerTableParser(M2dReader xmlReader) {
this.xmlReader = xmlReader;
@@ -105,6 +109,8 @@ public ServerTableParser(M2dReader xmlReader) {
constantsSerializer = new XmlSerializer(typeof(Constants));
npcStatFactorByPlayerCountSerializer = new XmlSerializer(typeof(NpcStatFactorByPlayerCountRoot));
npcStatFactorByLevelSerializer = new XmlSerializer(typeof(NpcStatFactorByLevelRoot));
+ maidGradeInfoSerializer = new XmlSerializer(typeof(MaidGradeInfoRoot));
+ maidRecipeSerializer = new XmlSerializer(typeof(MaidRecipeRoot));
// var seen = new HashSet();
// this.bankTypeSerializer.UnknownAttribute += (sender, args) => {
@@ -728,4 +734,26 @@ public IEnumerable ParseNpcStatFactorByLevel() {
yield return entry;
}
}
+
+ public IEnumerable<(int Grade, MaidGradeInfo GradeInfo)> ParseMaidGradeInfo() {
+ string xml = Sanitizer.RemoveEmpty(xmlReader.GetString(xmlReader.GetEntry("table/Server/MaidGradeInfo.xml")));
+ var reader = XmlReader.Create(new StringReader(xml));
+ var data = maidGradeInfoSerializer.Deserialize(reader) as MaidGradeInfoRoot;
+ Debug.Assert(data != null);
+
+ foreach (MaidGradeInfo entry in data.Grade) {
+ yield return (entry.Grade, entry);
+ }
+ }
+
+ public IEnumerable<(int Id, MaidRecipe Recipe)> ParseMaidRecipe() {
+ string xml = Sanitizer.RemoveEmpty(xmlReader.GetString(xmlReader.GetEntry("table/Server/MaidRecipeSvr.xml")));
+ var reader = XmlReader.Create(new StringReader(xml));
+ var data = maidRecipeSerializer.Deserialize(reader) as MaidRecipeRoot;
+ Debug.Assert(data != null);
+
+ foreach (MaidRecipe entry in data.recipe) {
+ yield return (entry.Id, entry);
+ }
+ }
}
diff --git a/Maple2.File.Parser/TableParser.cs b/Maple2.File.Parser/TableParser.cs
index 255b4d1..29ea37e 100644
--- a/Maple2.File.Parser/TableParser.cs
+++ b/Maple2.File.Parser/TableParser.cs
@@ -117,6 +117,11 @@ public class TableParser {
private readonly XmlSerializer fameLogSerializer;
private readonly XmlSerializer famePickMethodSerializer;
private readonly XmlSerializer characterAbilitySerializer;
+ private readonly XmlSerializer maidExpSerializer;
+ private readonly XmlSerializer maidPropertySerializer;
+ private readonly XmlSerializer maidRecipeSerializer;
+ private readonly XmlSerializer maidRecipeGroupSerializer;
+ private readonly XmlSerializer maidSalarySerializer;
private readonly string locale;
private readonly string language;
@@ -228,6 +233,11 @@ public TableParser(M2dReader xmlReader, string language) {
fameLogSerializer = new XmlSerializer(typeof(FameLogRoot));
famePickMethodSerializer = new XmlSerializer(typeof(FamePickMethod));
characterAbilitySerializer = new XmlSerializer(typeof(CharacterAbilityRoot));
+ maidExpSerializer = new XmlSerializer(typeof(MaidExpRoot));
+ maidPropertySerializer = new XmlSerializer(typeof(MaidPropertyRoot));
+ maidRecipeSerializer = new XmlSerializer(typeof(MaidRecipeRoot));
+ maidRecipeGroupSerializer = new XmlSerializer(typeof(MaidRecipeGroupRoot));
+ maidSalarySerializer = new XmlSerializer(typeof(MaidSalaryRoot));
locale = FeatureLocaleFilter.Locale.ToLower();
this.language = language;
@@ -1746,4 +1756,59 @@ public IEnumerable ParseJobTableNew() {
yield return (entry.id, entry);
}
}
+
+ public IEnumerable<(int Level, MaidExp Exp)> ParseMaidExp() {
+ string xml = Sanitizer.RemoveEmpty(xmlReader.GetString(xmlReader.GetEntry($"table/maidexp.xml")));
+ var reader = XmlReader.Create(new StringReader(xml));
+ var data = maidExpSerializer.Deserialize(reader) as MaidExpRoot;
+ Debug.Assert(data != null);
+
+ foreach (MaidExp entry in data.Exp) {
+ yield return (entry.Level, entry);
+ }
+ }
+
+ public IEnumerable<(int MaidId, MaidProperty Property)> ParseMaidProperty() {
+ string xml = Sanitizer.RemoveEmpty(xmlReader.GetString(xmlReader.GetEntry($"table/maidproperty.xml")));
+ var reader = XmlReader.Create(new StringReader(xml));
+ var data = maidPropertySerializer.Deserialize(reader) as MaidPropertyRoot;
+ Debug.Assert(data != null);
+
+ foreach (MaidProperty entry in data.Property) {
+ yield return (entry.MaidID, entry);
+ }
+ }
+
+ public IEnumerable<(int Id, MaidRecipe Recipe)> ParseMaidRecipe() {
+ string xml = Sanitizer.RemoveEmpty(xmlReader.GetString(xmlReader.GetEntry($"table/maidrecipe.xml")));
+ var reader = XmlReader.Create(new StringReader(xml));
+ var data = maidRecipeSerializer.Deserialize(reader) as MaidRecipeRoot;
+ Debug.Assert(data != null);
+
+ foreach (MaidRecipe entry in data.recipe) {
+ yield return (entry.Id, entry);
+ }
+ }
+
+ public IEnumerable<(int GroupId, MaidRecipeGroup Group)> ParseMaidRecipeGroup() {
+ string xml = Sanitizer.RemoveEmpty(xmlReader.GetString(xmlReader.GetEntry($"table/maidrecipegroup.xml")));
+ var reader = XmlReader.Create(new StringReader(xml));
+ var data = maidRecipeGroupSerializer.Deserialize(reader) as MaidRecipeGroupRoot;
+ Debug.Assert(data != null);
+
+ foreach (MaidRecipeGroup entry in data.group) {
+ yield return (entry.GroupID, entry);
+ }
+ }
+
+ public IEnumerable<(int Id, MaidSalary Salary)> ParseMaidSalary() {
+ string xml = Sanitizer.RemoveEmpty(xmlReader.GetString(xmlReader.GetEntry($"table/{locale}/maidsalary.xml")));
+ var reader = XmlReader.Create(new StringReader(xml));
+ var data = maidSalarySerializer.Deserialize(reader) as MaidSalaryRoot;
+ Debug.Assert(data != null);
+
+ foreach (MaidSalary entry in data.key) {
+ yield return (entry.id, entry);
+ }
+ }
}
diff --git a/Maple2.File.Parser/Xml/Table/MaidExp.cs b/Maple2.File.Parser/Xml/Table/MaidExp.cs
new file mode 100644
index 0000000..34890f0
--- /dev/null
+++ b/Maple2.File.Parser/Xml/Table/MaidExp.cs
@@ -0,0 +1,14 @@
+using System.Xml.Serialization;
+
+namespace Maple2.File.Parser.Xml.Table;
+
+// ./data/xml/table/maidexp.xml
+[XmlRoot("ms2")]
+public class MaidExpRoot {
+ [XmlElement] public List Exp;
+}
+
+public class MaidExp {
+ [XmlAttribute] public int Level;
+ [XmlAttribute] public int Exp;
+}
diff --git a/Maple2.File.Parser/Xml/Table/MaidProperty.cs b/Maple2.File.Parser/Xml/Table/MaidProperty.cs
new file mode 100644
index 0000000..102bb6a
--- /dev/null
+++ b/Maple2.File.Parser/Xml/Table/MaidProperty.cs
@@ -0,0 +1,15 @@
+using System.Xml.Serialization;
+
+namespace Maple2.File.Parser.Xml.Table;
+
+// ./data/xml/table/maidproperty.xml
+[XmlRoot("ms2")]
+public class MaidPropertyRoot {
+ [XmlElement] public List Property;
+}
+
+public class MaidProperty {
+ [XmlAttribute] public int MaidID;
+ [XmlAttribute] public int RecipeGroupID;
+ [XmlAttribute] public int NPCID;
+}
diff --git a/Maple2.File.Parser/Xml/Table/MaidRecipe.cs b/Maple2.File.Parser/Xml/Table/MaidRecipe.cs
new file mode 100644
index 0000000..68309f1
--- /dev/null
+++ b/Maple2.File.Parser/Xml/Table/MaidRecipe.cs
@@ -0,0 +1,30 @@
+using System.Xml.Serialization;
+using M2dXmlGenerator;
+
+namespace Maple2.File.Parser.Xml.Table;
+
+// ./data/xml/table/maidrecipe.xml
+[XmlRoot("ms2")]
+public partial class MaidRecipeRoot {
+ [M2dFeatureLocale(Selector = "Id")] private IList _recipe;
+}
+
+public partial class MaidRecipe : IFeatureLocale {
+ [XmlAttribute] public int Id;
+ [XmlAttribute] public string FirstIngredientItemID = string.Empty;
+ [XmlAttribute] public int FirstIngredientCount;
+ [XmlAttribute] public string SecondIngredientItemID = string.Empty;
+ [XmlAttribute] public int SecondIngredientCount;
+ [XmlAttribute] public string ThirdIngredientItemID = string.Empty;
+ [XmlAttribute] public int ThirdIngredientCount;
+ [XmlAttribute] public int WorkbenchType;
+ [XmlAttribute] public int LeadTimeNormal;
+ [XmlAttribute] public int LeadTimeGood;
+ [XmlAttribute] public int LeadTimeVeryGood;
+ [XmlAttribute] public int MaidExp;
+ [XmlAttribute] public int MaidMood;
+ [XmlAttribute] public int ProductItemID;
+ [XmlAttribute] public int ProductsocketDataID;
+ [XmlAttribute] public int ProductCount;
+ [XmlAttribute] public int ProductRank;
+}
diff --git a/Maple2.File.Parser/Xml/Table/MaidRecipeGroup.cs b/Maple2.File.Parser/Xml/Table/MaidRecipeGroup.cs
new file mode 100644
index 0000000..4f8fb50
--- /dev/null
+++ b/Maple2.File.Parser/Xml/Table/MaidRecipeGroup.cs
@@ -0,0 +1,16 @@
+using System.Xml.Serialization;
+using M2dXmlGenerator;
+
+namespace Maple2.File.Parser.Xml.Table;
+
+// ./data/xml/table/maidrecipegroup.xml
+[XmlRoot("ms2")]
+public class MaidRecipeGroupRoot {
+ [XmlElement] public List group;
+}
+
+public partial class MaidRecipeGroup {
+ [XmlAttribute] public int GroupID;
+ [M2dArray] public int[] RecipeIDs;
+ [M2dArray] public int[] RequireLevels;
+}
diff --git a/Maple2.File.Parser/Xml/Table/MaidSalary.cs b/Maple2.File.Parser/Xml/Table/MaidSalary.cs
new file mode 100644
index 0000000..811589b
--- /dev/null
+++ b/Maple2.File.Parser/Xml/Table/MaidSalary.cs
@@ -0,0 +1,15 @@
+using System.Xml.Serialization;
+
+namespace Maple2.File.Parser.Xml.Table;
+
+// ./data/xml/table/na/maidsalary.xml
+[XmlRoot("ms2")]
+public class MaidSalaryRoot {
+ [XmlElement] public List key;
+}
+
+public partial class MaidSalary {
+ [XmlAttribute] public int id;
+ [XmlAttribute] public int SalaryType;
+ [XmlAttribute] public int SalaryAmount;
+}
diff --git a/Maple2.File.Parser/Xml/Table/Server/MaidGradeInfo.cs b/Maple2.File.Parser/Xml/Table/Server/MaidGradeInfo.cs
new file mode 100644
index 0000000..230658b
--- /dev/null
+++ b/Maple2.File.Parser/Xml/Table/Server/MaidGradeInfo.cs
@@ -0,0 +1,17 @@
+using System.Xml.Serialization;
+
+namespace Maple2.File.Parser.Xml.Table.Server;
+
+// ./data/server/table/Server/MaidGradeInfo.xml
+[XmlRoot("ms2")]
+public class MaidGradeInfoRoot {
+ [XmlElement] public List Grade;
+}
+
+public class MaidGradeInfo {
+ [XmlAttribute] public int Grade;
+ [XmlAttribute] public float JackpotRate;
+ [XmlAttribute] public float FeelNormalRate;
+ [XmlAttribute] public float FeelGoodRate;
+ [XmlAttribute] public float FeelVeryGoodRate;
+}
diff --git a/Maple2.File.Parser/Xml/Table/Server/MaidRecipeSvr.cs b/Maple2.File.Parser/Xml/Table/Server/MaidRecipeSvr.cs
new file mode 100644
index 0000000..7904103
--- /dev/null
+++ b/Maple2.File.Parser/Xml/Table/Server/MaidRecipeSvr.cs
@@ -0,0 +1,36 @@
+using System.Xml.Serialization;
+using M2dXmlGenerator;
+
+namespace Maple2.File.Parser.Xml.Table.Server;
+
+// ./data/server/table/Server/MaidRecipeSvr.xml
+[XmlRoot("ms2")]
+public partial class MaidRecipeRoot {
+ [M2dFeatureLocale(Selector = "Id")] private IList _recipe;
+}
+
+public partial class MaidRecipe : IFeatureLocale {
+ [XmlAttribute] public int Id;
+ [XmlAttribute] public string FirstIngredientItemID = string.Empty;
+ [XmlAttribute] public int FirstIngredientCount;
+ [XmlAttribute] public string SecondIngredientItemID = string.Empty;
+ [XmlAttribute] public int SecondIngredientCount;
+ [XmlAttribute] public string ThirdIngredientItemID = string.Empty;
+ [XmlAttribute] public int ThirdIngredientCount;
+ [XmlAttribute] public int WorkbenchType;
+ [XmlAttribute] public int LeadTimeNormal;
+ [XmlAttribute] public int LeadTimeGood;
+ [XmlAttribute] public int LeadTimeVeryGood;
+ [XmlAttribute] public int MaidExp;
+ [XmlAttribute] public int ProductItemID;
+ [XmlAttribute] public int ProductsocketDataID;
+ [XmlAttribute] public int ProductCount;
+ [XmlAttribute] public int ProductRank;
+ [XmlAttribute] public int JackpotItemID;
+ [XmlAttribute] public int JackpotsocketDataID;
+ [XmlAttribute] public int JackpotCount;
+ [XmlAttribute] public int JackpotRank;
+ [XmlAttribute] public float JackpotRate;
+ [XmlAttribute] public int JackpotMood;
+ [XmlAttribute] public int ImmediatelyCompleteFee;
+}
diff --git a/Maple2.File.Tests/ServerTableParserTest.cs b/Maple2.File.Tests/ServerTableParserTest.cs
index 3692d91..efcbb27 100644
--- a/Maple2.File.Tests/ServerTableParserTest.cs
+++ b/Maple2.File.Tests/ServerTableParserTest.cs
@@ -464,5 +464,23 @@ public void TestNpcStatFactorByLevel() {
continue;
}
}
+
+ [TestMethod]
+ public void TestMaidGradeInfo() {
+ var parser = new ServerTableParser(TestUtils.ServerReader);
+
+ foreach ((_, _) in parser.ParseMaidGradeInfo()) {
+ continue;
+ }
+ }
+
+ [TestMethod]
+ public void TestMaidRecipe() {
+ var parser = new ServerTableParser(TestUtils.ServerReader);
+
+ foreach ((_, _) in parser.ParseMaidRecipe()) {
+ continue;
+ }
+ }
}
diff --git a/Maple2.File.Tests/TableParserTest.cs b/Maple2.File.Tests/TableParserTest.cs
index 38ab84a..2576faa 100644
--- a/Maple2.File.Tests/TableParserTest.cs
+++ b/Maple2.File.Tests/TableParserTest.cs
@@ -843,4 +843,23 @@ public void TestCharacterAbility() {
continue;
}
}
+
+ [TestMethod]
+ public void TestMaid() {
+ foreach ((_, _) in _parser.ParseMaidExp()) {
+ continue;
+ }
+ foreach ((_, _) in _parser.ParseMaidProperty()) {
+ continue;
+ }
+ foreach ((_, _) in _parser.ParseMaidRecipe()) {
+ continue;
+ }
+ foreach ((_, _) in _parser.ParseMaidRecipeGroup()) {
+ continue;
+ }
+ foreach ((_, _) in _parser.ParseMaidSalary()) {
+ continue;
+ }
+ }
}