();
OrderedDictionary tool = new OrderedDictionary();
int toolIndex = 0;
foreach (string rowcol in rowcols)
{
string outval = "";
string newval = rowcol;
if (rowcol.StartsWith("|"))
{
newval = rowcol.Substring(1);
}
else
{
newval = rowcol;
}
if (!blacklist.Contains(rowcol))
{
// we have a column entry to parse
if (newval.StartsWith("}"))
{
// this is the end of the table, so skip it
continue;
}
else if (newval.Contains("["))
{
// this is a link of some kind
// extract all links matching regex
// if link has a pipe, split it
// if link is a file, use the image name as the "image" dictionary entry and discard anything after the first match
// if the link isn't a file, use the displayname (second match)
Match linkMts = Regex.Match(newval, @"(?<=\[\[).+(?=\]\])");
string tempval = "";
while (linkMts.Success)
{
// check for File: first
// then check for pipe without File:
string linktext = linkMts.Value;
if (linktext.Contains("File:"))
{
Match fileMts = Regex.Match(linktext, @"(?<=File:)[\w .]+");
tempval = newval.Replace(linktext, fileMts.Value);
}
else if (linkMts.Value.Contains("|"))
{
string[] linkProps = linkMts.Value.Split(new string[] { "|" }, StringSplitOptions.None);
if (linkProps[0].Contains("File"))
{
Match fileMts = Regex.Match(linkProps[0], @"(?<=File:).+");
// this is the image name
tempval = newval.Replace(linkProps[0], fileMts.Value);
}
else
{
tempval = newval.Replace(linkMts.Value, linkProps[1]);
}
}
linkMts = linkMts.NextMatch();
}
outval = tempval.Replace("[", "").Replace("]", "");
}
else if (newval.Contains("{"))
{
// there's data to be extracted here
// see if it's a template
Match tplMts = Regex.Match(newval, @"(?<=\{\{).+(?=\}\})");
while (tplMts.Success)
{
// split the value by pipe
string[] splitVals = tplMts.Value.Split(new string[] { "|" }, StringSplitOptions.None);
if (splitVals.Count() == 3)
{
// if array has three members, output like: Copper Bar (5)
outval = splitVals[1] + " (" + splitVals[2] + ")";
}
else if (splitVals.Length == 2)
{
// if the array has two members, ouput the second
outval = splitVals[1];
}
else
{
//this is wrong
}
tplMts = tplMts.NextMatch();
}
}
else
{
// use the value as-is
outval = newval;
}
}
else
{
// this is an empty column, use a space
outval = " ";
}
// clean up extraneous wikitext markup
outval = outval.Replace("'''", "").Replace("''", "").Replace("\n\n", "\n");
tool.Add(lstHeaders[toolIndex], outval);
toolIndex++;
newcols.Add(outval);
}
Console.WriteLine(tool["name"]);
tool.Dump();
items.Add(tool);
}
}
}
}
}
///
/// Single-line string parser
///
string parseString(string s)
{
// the default output is the input string
string result = s;
// replace all HTML entities with characters
result = HttpUtility.HtmlDecode(s);
// if the string contains wikitext markup, parse it
// otherwise, return propertyValue
if (new string[] { "[[", "{{" }.Any(str => s.Contains(str)))
{
if (s.Contains("[["))
{
// this string has one or more [[wikilinks]]
// extract all [[wikilinks]]
Match m = Regex.Match(s, @"\[\[(.+?)\]\]"); // .+? means lazy (ungreedy) matching
while (m.Success)
{
// check for File: first
// then check for pipe without File:
string linktext = m.Value;
if (linktext.Contains("File"))
{
// this link contains a file. do things.
if (linktext.Contains("Level"))
{
// [[File:Fishing Skill Icon.png|24px|link=]] [[Fishing]] Level 2
// Fishing]] Level 2
// retrieve whole wikilink and concatenate second and third values
Match mx = Regex.Match(linktext, @"\[\[File.+?\]\] \[\[(.+)\]\]( Level [0-9]+)", RegexOptions.IgnoreCase);
// for every match, perform string replacement
while (mx.Success)
{
// Fishing Level 2
string strOld = mx.Groups[1].Captures[0].Value;
string strNew = mx.Groups[2].Captures[0].Value;
result = result.Replace(strOld, strNew);
mx = mx.NextMatch();
}
}
else
{
// [[File:Shirt001.png|center]]
// [[File:Axe.png]]
// retrieve whole wikilink, second capture group is the filename
Match mx = Regex.Match(linktext, @"\[\[File:([\w -_.]+).*?\]\]", RegexOptions.IgnoreCase);
// for every match, perform string replacement
while (mx.Success)
{
// Shirt001.png
// Axe.png
string strOld = mx.Groups[0].Captures[0].Value;
string strNew = mx.Groups[1].Captures[0].Value;
result = result.Replace(strOld, strNew);
mx = mx.NextMatch();
}
}
}
else
{
// [[The Mines]]
// [[Random Events#Meteorite|meteorite]]
// this is a regular ol' link
if (linktext.Contains("|"))
{
string[] linkProps = m.Value.Split(new string[] { "|" }, StringSplitOptions.None);
result = result.Replace(m.Value, linkProps[1]);
}
else
{
result = result.Replace(m.Value, m.Value.Substring(2, m.Value.Length - 3));
}
}
m = m.NextMatch();
}
}
if (s.Contains("{{"))
{
// this string has one or more {{wikitext templates}}
// extract all {{wikitext templates}}
Match m = Regex.Match(s, @"\{\{(.+?)\}\}"); // .+? means lazy (ungreedy) matching
while (m.Success)
{
// this is the raw wikitext of the match
string wikitext = m.Value;
// this is the string minus the start and end brackets
string input = wikitext.Substring(2, wikitext.Length - 4);
// return this if all else fails
result = input;
// we can just split by the pipe for this - all {{wikitext templates}} use pipe as the delimiter
string[] segments = input.Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
// for case-insensitive string.Contains()
string templateName = segments[0].ToLower();
string littletext = wikitext.ToLower();
if (segments.Count() > 1)
{
if (templateName == "name")
{
if (segments.Count() == 2)
{
// return second segment as-is
result = segments[1];
}
else
{
// {{name|Mining|Level 9|class=inline}}
// everything past the third segment (e.g. 50) can be ignored
if (littletext.Contains("level"))
{
// {{name|Farming|Level 3|image=Farming Skill Icon.png}}
// Farming Level 3
// test with Equipment
result = segments[1] + " " + segments[2];
}
else if (littletext.Contains("+"))
{
// {{name|Defense|+3}
// +3 Defense
// test with Clothing and Weapons
result = segments[2] + " " + segments[1];
}
else
{
// {{name|Omni Geode|50|...}}
// Omni Geode {50)
result = segments[1] + " (" + segments[2] + ")";
}
}
}
else if (templateName == "npc")
{
// {{NPC|Jodi|Mother}}
// Jodi (Mother)
if (segments.Count() > 2)
result = segments[1] + " (" + segments[2] + ")";
else
result = segments[1];
}
else if (templateName == "price")
{
// {{Price|30|Currency}}
// JOPK, Qi, Star Token (if third parameter is empty, this is regular gold)
result = segments[1];
if (segments.Count() > 2)
{
switch (segments[2])
{
case "JOPK":
result += " tokens";
break;
case "Qi":
result += " Qui coins";
break;
case "Token":
result += " star tokens";
break;
}
}
else
{
result += "g";
}
}
else if (templateName == "description")
{
// {{Description|Wild Horseradish}}
// {{Description|Recipe|Lucky Lunch}}
// this returns a description string
// use this API call:
// https://stardewvalleywiki.com/mediawiki/api.php?action=expandtemplates&prop=wikitext&format=json&text=...
// result["expandtemplates"]["wikitext"]
result = Wikidata(wikitext, "wikitext");
}
else
{
// this is wrong
result += " ***BAD INPUT***";
}
m = m.NextMatch();
}
}
}
}
// clean tags from result
result = result.Replace("
", @"\n");
result = result.Replace("
", "");
return result;
}
///
/// String parser for delimited lists
///
static void parseSource(string strInput, string strStart, string strEnd, ref List lstSauce)
{
if (strInput.Contains("Abandoned House Icon.png") && !lstSauce.Contains("Hat Mouse"))
{
lstSauce.Add("Hat Mouse");
return;
}
// add blacklisted entries here
List avoid = new List() { };
// clean string of extraneous characters that break shit
strInput = strInput.Replace("{{!}}", "").Replace("''", "").Replace("'''", "");
while (strInput.IndexOf(strStart) > -1)
{
// trim any whitespace
strInput = strInput.Trim();
// find the first instance of the start characters
int start = strInput.IndexOf(strStart);
// find the first instance of the end characters
int end = strInput.IndexOf(strEnd) + 2;
// get the length of the string to be extracted
int length = end - start;
// extract the string
string sub = strInput.Substring(start, length);
// remove the start and end characters
sub = sub.Substring(2, (sub.Length - 4));
// remove the extracted string
strInput = strInput.Remove(start, length);
// clean up the substring
// don't include items that match the blacklist
sub = sub.Replace("''", "");
//"name|Fishing|Level 2|image=Fishing Skill Icon.png"
//"name|Bug Meat|1"
if (!avoid.Any(sub.Contains))
{
if (sub.Contains("|"))
{
string[] strSep = { "|" };
string[] subSplit = sub.Split(strSep, System.StringSplitOptions.RemoveEmptyEntries);
if (sub.Contains("Bundle"))
{
sub = subSplit[1] + " Bundle";
}
else if (strInput.Contains("ingredients"))
{
sub = subSplit[1];
sub += subSplit.Count() == 3 ? " (" + subSplit[2] + ")" : "";
}
else if (strInput.Contains("recipe"))
{
Console.Write(sub, " | ");
}
else
{
sub = subSplit[1];
}
}
lstSauce.Add(sub);
}
}
// if (lstSauce.Count() == 1)
// {
// lstSauce.Add(null);
// }
}
// function for parsing single line values
// this needs reworking
// bunch of regex all at once is fake & gay
string parseStringOld(string propertyValue)
{
if (propertyValue.Contains("Traveling Cart"))
{
//Util.Break(); // there's something funny
}
// {{name=Skill|Level #|imge=...}}
Match m_new = Regex.Match(propertyValue, @"(?<={{name\|)[\w\s']+\|Level [0-9]+(?=\|.+)");
// [[File:...]] [[Skill]] Level #
Match m_old = Regex.Match(propertyValue, @"(?!\[\[File.+\]\] \[\[)\w+\]\] Level [0-9]+", RegexOptions.IgnoreCase);
// {{NPC|...|...[[..]]}}
// {{NPC|...|...}}
Match m_npc = Regex.Match(propertyValue, @"(?<=\{\{NPC\|)[A-Za-z0-9 \-\+\|]+(?=\[\[|\}\})");
// {{description|...}}
Match m_desc = Regex.Match(propertyValue, @"(?<=\{\{description\|)[A-Za-z0-9 \-\+\|]+(?=|\}\})", RegexOptions.IgnoreCase);
// clean up wikilink syntax
Match m_link = Regex.Match(propertyValue, @"(?<=\[\[).+?(?=\]\])"); // .+? means lazy (ungreedy) matching
// remove wikilink brackets
Match m_bckt = Regex.Match(propertyValue, @"(?<=\[\[)[\w\s']+(?=\]\])");
// {{name|Defense|+3}}
Match m_stats = Regex.Match(propertyValue, @"(?<=\{\{name\|)[\w\s']+\|\+[0-9]+(?=\}\})");
// {{price|15000}}
Match m_price = Regex.Match(propertyValue, @"(?<=\{\{price\|)[\w\s']+(?=\}\})");
// {{name|Omni Geode|50}}
Match m_quantity = Regex.Match(propertyValue, @"(?<=\{\{name\|)[\w\s']+\|\+[0-9]+(?=\}\})");
// {{name|...}}
Match m_curly = Regex.Match(propertyValue, @"(?<=\{\{name\|)[\w\s']+(?=\||\})");
string result = propertyValue;
if (m_new.Success)
{
result = m_new.Value.Replace("|", " ");
}
else if (m_old.Success)
{
result = m_old.Value.Replace("]]", "");
}
else if (m_npc.Success)
{
result = m_npc.Value.Replace("|", " - ");
if (result.Contains("+"))
{
result += "hearts";
}
}
else if (m_desc.Success)
{
result = m_desc.Value;
}
else if (m_link.Success)
{
string tempval = propertyValue;
while (m_link.Success)
{
// check for File: first
// then check for pipe without File:
string linktext = m_link.Value;
if (linktext.Contains("File:"))
{
Match fileMts = Regex.Match(linktext, @"(?<=File:)[\w .]+");
tempval = tempval.Replace(linktext, fileMts.Value);
}
else if (linktext.Contains("|"))
{
string[] linkProps = m_link.Value.Split(new string[] { "|" }, StringSplitOptions.None);
tempval = tempval.Replace(m_link.Value, linkProps[1]);
}
m_link = m_link.NextMatch();
}
result = tempval.Replace("[", "").Replace("]", "");
}
else if (m_bckt.Success)
{
result = (propertyValue.Replace("[", "")).Replace("]", "");
}
else if (m_stats.Success)
{
List stats = new List();
// there might be more than one
while (m_stats.Success)
{
// split value by pipe
// display as val[1] + " " + val[0]
string[] props = m_stats.Value.Split(new string[] { "|" }, StringSplitOptions.None);
stats.Add((props[1] + " " + props[0]).Replace("{", "").Replace("}", ""));
m_stats = m_stats.NextMatch();
}
// output as multiline string
result = String.Join("\n", stats);
}
else if (m_price.Success)
{
string price = "{{price|" + m_price.Value + "}}";
result = propertyValue.Replace(price, m_price.Value);
}
else if (m_quantity.Success)
{
}
else if (m_curly.Success)
{
result = m_curly.Value;
}
return result;
}
///
/// global constants for different functions
///
public class Globals
{
///
/// list of blacklisted items by name - these will be skipped
///
public static List Avoid = new List
{
// wikitext characters
"<", "{", "|-", "|}",
// ignored items
"Brown Egg",
"Animals#",
"Stone Owl",
"Panda Hat",
// these tools are parsed separately
"Axes",
"Hoes",
"Pickaxes",
"Trash Cans",
"Watering Cans"
// these are broken and need to be fixed
"Coop",
"Barn",
"Haunted Skull",
"Jellies and Pickles",
"Shed",
"Slime"
};
///
/// list of categories for navbox wikitext parsing
///
public static string[] Categories =
{
"Animals", // 0
"Artifacts", // 1
"Artisan Goods", // 2
"Buildings", // 3
"Clothing", // 4
"Crop", // 5
"Decor", // 6
"Equipment", // 7
"Fish", // 8
"Foraging", // 9
"Furniture", // 10
"Ingredients", // 11
"Lighting", // 12
"Minerals", // 13
"Monsters", // 14
"Recipes", // 15
"Resources", // 16
"Seeds", // 17
"Tree", // 18
"Tools", // 19
"Villagers", // 20
"Warp Totems", // 21
"Weapons" // 22
};
///
/// Price multiplier for each Quality rating
///
public static OrderedDictionary Quality = new OrderedDictionary
{
{ "base", 1 },
{ "silver", 1.25 },
{ "gold", 1.5 },
{ "iridium", 2 }
};
public static OrderedDictionary BuffQuality = new OrderedDictionary
{
{ "base", 0 },
{ "silver", 1 },
{ "gold", 2 },
{ "iridium", 4 }
};
///
/// Price multiplier for each Profession
///
public static OrderedDictionary Professions = new OrderedDictionary
{
{ "artisan", 1.4 },
{ "rancher", 1.2 },
{ "gemologist", 1.3 },
{ "tiller", 1.1 },
{ "blacksmith", 1.5 },
{ "forester", 1.25 },
{ "fisher", 1.25 },
{ "angler", 1.5 }
};
///
/// Produce-to-ArtisanGood relationship
///
public static OrderedDictionary ArtisanGoods = new OrderedDictionary
{
{ "Hops", "Pale Ale" },
{ "Wheat", "Beer" },
{ "Honey", "Mead" },
{ "Milk", "Cheese" },
{ "Large Milk", "Cheese" },
{ "Goat Milk", "Goat Cheese" },
{ "Large Goat Milk", "Goat Cheese" },
{ "Coffee Bean", "Coffee" },
{ "Tea Leaves", "Green Tea" },
{ "Wool", "Cloth" },
{ "Egg", "Mayonnaise" },
{ "Large Egg", "Mayonnaise" },
{ "Void Egg", "Void Mayonnaise" },
{ "Dinosaur Egg", "Dinosaur Mayonnaise" },
{ "Truffle", "Truffle Oil" },
{ "Corn", "Oil" },
{ "Sunflower", "Oil" },
{ "Sunflower Seeds", "Oil" },
{ "Sturgeon Roe", "Caviar" },
{ "Roe", "Aged Roe" },
};
///
/// List of flowers for honey
///
public static List Flowers = new List
{
{ "Blue Jazz" },
{ "Fairy Rose" },
{ "Poppy" },
{ "Summer Spangle" },
{ "Sunflower" },
{ "Tulip" }
};
}