public string GetDataAsHtml(T8BTEMGP.T8BTEMGP enemyGroups, T8BTEMST.T8BTEMST enemies, Dictionary <uint, TSS.TSSEntry> inGameIdDict, GameVersion version, string versionPostfix, GameLocale locale, WebsiteLanguage websiteLanguage) { StringBuilder sb = new StringBuilder(); sb.Append("<tr><td>"); sb.Append(RefString); sb.Append("<br>"); sb.Append(inGameIdDict[StringDicID].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage)); for (int i = 0; i < EnemyGroupIDs.Length; ++i) { if (EnemyGroupIDs[i] == 0xFFFFFFFFu) { continue; } var group = enemyGroups.EnemyGroupIdDict[EnemyGroupIDs[i]]; foreach (int enemyId in group.EnemyIDs) { if (enemyId < 0) { continue; } var enemy = enemies.EnemyIdDict[(uint)enemyId]; sb.Append(inGameIdDict[enemy.NameStringDicID].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage)); sb.Append("<br>"); } } sb.Append("</td></tr>"); return(sb.ToString()); }
public string GetDataAsHtml(string stratum, int floor, T8BTEMST.T8BTEMST Enemies, T8BTEMGP.T8BTEMGP EnemyGroups, T8BTEMEG.T8BTEMEG EncounterGroups, GameVersion version, string versionPostfix, GameLocale locale, WebsiteLanguage websiteLanguage, T8BTXTMT treasures, ItemDat.ItemDat items, Dictionary <uint, TSS.TSSEntry> inGameIdDict, bool surroundingTable = true, bool phpLinks = false) { StringBuilder sb = new StringBuilder(); if (surroundingTable) { sb.Append("<div id=\"" + stratum + floor + "\">"); sb.Append("<table class=\"necropolisfloor\">"); sb.Append("<tr>"); sb.Append("<th colspan=\"6\">"); sb.Append("<div class=\"itemname\" style=\"text-align: center;\">"); sb.Append(stratum + "-" + floor); sb.Append("</div>"); sb.Append("</td>"); sb.Append("</tr>"); } for (int y = 0; y < VerticalTiles; y++) { sb.Append("<tr>"); for (int x = 0; x < HorizontalTiles; x++) { sb.Append(TileList[(int)(y * HorizontalTiles + x)].GetDataAsHtml(stratum, floor, Enemies, EnemyGroups, EncounterGroups, version, versionPostfix, locale, websiteLanguage, treasures, items, inGameIdDict, phpLinks: phpLinks)); } sb.Append("</tr>"); //sb.Append( "<tr><td colspan=\"" + HorizontalTiles + "\"><hr></td></tr>" ); } if (surroundingTable) { sb.Append("</table>"); sb.Append("</div>"); } return(sb.ToString()); }
public ItemForm( GameVersion version, ItemDat itemDat, TSSFile TSS, T8BTSK.T8BTSK skills, T8BTEMST.T8BTEMST enemies, COOKDAT.COOKDAT cookdat, WRLDDAT.WRLDDAT locations ) { InitializeComponent(); this.Version = version; this.itemDat = itemDat; this.TSS = TSS; this.Skills = skills; this.Enemies = enemies; this.Recipes = cookdat; this.Locations = locations; this.InGameIdDict = TSS.GenerateInGameIdDictionary(); labels = new List<Label>(); textboxes = new List<TextBox>(); for ( int i = 0; i < ItemDatSingle.size / 4; ++i ) { Label l = new Label(); l.Text = ""; l.Size = new System.Drawing.Size( 100, 20 ); TextBox b = new TextBox(); b.Size = new System.Drawing.Size( 50, 20 ); b.Text = ""; labels.Add( l ); textboxes.Add( b ); FlowLayoutPanel p = new FlowLayoutPanel(); p.Size = new System.Drawing.Size( 200, 20 ); p.FlowDirection = FlowDirection.LeftToRight; p.Controls.Add( l ); p.Controls.Add( b ); switch ( (ItemData)i ) { case ItemData.NamePointer: case ItemData.DescriptionPointer: case ItemData.UnknownTextPointer: case ItemData.TextIDPart1: case ItemData.TextIDPart2: case ItemData.TextIDPart3: case ItemData.TextIDPart4: case ItemData.TextIDPart5: case ItemData.TextIDPart6: case ItemData.TextIDPart7: case ItemData.TextIDPart8: break; default: flowLayoutPanel1.Controls.Add( p ); break; } } foreach ( ItemDatSingle i in itemDat.items ) { var entry = GetEntry( i.Data[(int)ItemData.NamePointer] ); listBox1.Items.Add( entry.StringEngOrJpn ); } }
public static int Execute(List <string> args) { if (args.Count < 7) { Console.WriteLine("Usage: [360/PS3] ITEM.DAT STRING_DIC.SO T8BTSK T8BTEMST COOKDAT WRLDDAT"); return(-1); } GameVersion version = GameVersion.None; switch (args[0].ToUpperInvariant()) { case "360": version = GameVersion.X360; break; case "PS3": version = GameVersion.PS3; break; } if (version == GameVersion.None) { Console.WriteLine("First parameter must indicate game version!"); return(-1); } ItemDat items = new ItemDat(args[1]); TSSFile TSS; try { TSS = new TSSFile(System.IO.File.ReadAllBytes(args[2])); } catch (System.IO.FileNotFoundException) { Console.WriteLine("Could not open STRING_DIC.SO, exiting."); return(-1); } T8BTSK.T8BTSK skills = new T8BTSK.T8BTSK(args[3]); T8BTEMST.T8BTEMST enemies = new T8BTEMST.T8BTEMST(args[4]); COOKDAT.COOKDAT cookdat = new COOKDAT.COOKDAT(args[5]); WRLDDAT.WRLDDAT locations = new WRLDDAT.WRLDDAT(args[6]); Console.WriteLine("Initializing GUI..."); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); ItemForm itemForm = new ItemForm(version, items, TSS, skills, enemies, cookdat, locations); Application.Run(itemForm); return(0); }
public string GetDataAsHtml(T8BTEMST.T8BTEMST enemies, Dictionary <uint, TSS.TSSEntry> inGameIdDict, GameVersion version, string versionPostfix, GameLocale locale, WebsiteLanguage websiteLanguage) { var sb = new StringBuilder(); sb.Append("<tr id=\"egroup" + InGameID + "\">"); sb.Append("<td>"); sb.Append(RefString); sb.Append("<br>"); sb.Append(inGameIdDict[StringDicID].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage)); sb.Append("<br>"); sb.Append("ID: "); sb.Append(ID); sb.Append("<br>"); sb.Append("InGameId: "); sb.Append(InGameID); sb.Append("<br>"); sb.Append("Flag: "); sb.Append(SomeFlag); sb.Append("</td>"); for (int i = 0; i < EnemyIDs.Length; ++i) { sb.Append("<td>"); if (EnemyIDs[i] >= 0) { sb.Append(inGameIdDict[enemies.EnemyIdDict[(uint)EnemyIDs[i]].NameStringDicID].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage)); sb.Append("<br>"); sb.Append("~1: "); sb.Append(UnknownFloats[i]); sb.Append("<br>"); sb.Append("X Pos: "); sb.Append(PosX[i]); sb.Append("<br>"); sb.Append("Y Pos: "); sb.Append(PosY[i]); sb.Append("<br>"); sb.Append("Scale: "); sb.Append(Scale[i]); sb.Append("<br>"); sb.Append("~5: "); sb.Append(UnknownInts[i]); } sb.Append("</td>"); } sb.Append("</tr>"); return(sb.ToString()); }
public static int Execute( List<string> args ) { if ( args.Count < 7 ) { Console.WriteLine( "Usage: [360/PS3] ITEM.DAT STRING_DIC.SO T8BTSK T8BTEMST COOKDAT WRLDDAT" ); return -1; } GameVersion version = GameVersion.None; switch ( args[0].ToUpperInvariant() ) { case "360": version = GameVersion.X360; break; case "PS3": version = GameVersion.PS3; break; } if ( version == GameVersion.None ) { Console.WriteLine( "First parameter must indicate game version!" ); return -1; } ItemDat items = new ItemDat( args[1] ); TSSFile TSS; try { TSS = new TSSFile( System.IO.File.ReadAllBytes( args[2] ) ); } catch ( System.IO.FileNotFoundException ) { Console.WriteLine( "Could not open STRING_DIC.SO, exiting." ); return -1; } T8BTSK.T8BTSK skills = new T8BTSK.T8BTSK( args[3] ); T8BTEMST.T8BTEMST enemies = new T8BTEMST.T8BTEMST( args[4] ); COOKDAT.COOKDAT cookdat = new COOKDAT.COOKDAT( args[5] ); WRLDDAT.WRLDDAT locations = new WRLDDAT.WRLDDAT( args[6] ); Console.WriteLine( "Initializing GUI..." ); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault( false ); ItemForm itemForm = new ItemForm( version, items, TSS, skills, enemies, cookdat, locations ); Application.Run( itemForm ); return 0; }
public void PrintData(EndianUtils.Endianness endian, Dictionary <uint, TSS.TSSEntry> inGameDic, List <ItemDat.ItemDatSingle> itemDataSorted, T8BTEMST.T8BTEMST enemies) { using (DuplicatableStream stream = Stream.Duplicate()) { stream.ReadUInt32().FromEndian(endian); // ? stream.ReadUInt32().FromEndian(endian); // ? stream.ReadUInt32Array(9, endian); stream.ReadUInt32().FromEndian(endian); // play time in frames, assuming 60 frames = 1 second stream.ReadUInt32().FromEndian(endian); // gald stream.DiscardBytes(4); // ? uint[] itemCounts = stream.ReadUInt32Array(3072, endian); uint[] itemBookBitfields = stream.ReadUInt32Array(3072 / 32, endian); stream.DiscardBytes(4); // ? stream.ReadUInt32Array(4, endian); // control modes for the four active party slots stream.ReadUInt32Array(3, endian); // strategies assigned to dpad directions stream.DiscardBytes(0x40); // ?? for (int i = 0; i < 8; ++i) { // custom strategy names // game seems to read these till null byte so this could totally be abused to buffer overflow... stream.ReadAscii(0x40); } stream.DiscardBytes(0xA84D0 - 0xA7360); // ? uint[] monsterBookBitfieldsScanned = stream.ReadUInt32Array(0x48 / 4, endian); stream.DiscardBytes(0xA8680 - 0xA8518); // ? uint[] monsterBookBitfieldsSeen = stream.ReadUInt32Array(0x48 / 4, endian); stream.DiscardBytes(0xA8928 - 0xA86C8); // ? uint collectorsBookIndex = 0; foreach (var item in itemDataSorted) { uint i = item.Data[(int)ItemDat.ItemData.ID]; if (item.Data[(int)ItemDat.ItemData.InCollectorsBook] > 0) { bool haveItem = ((itemBookBitfields[i / 32] >> (int)(i % 32)) & 1) > 0; Console.WriteLine((haveItem ? "Y" : "N") + (collectorsBookIndex) + ": " + inGameDic[item.NamePointer].StringEngOrJpn); ++collectorsBookIndex; } } uint monsterBookIndex = 0; foreach (var enemy in enemies.EnemyList) { uint i = enemy.InGameID; if (enemy.InMonsterBook > 0) { bool haveSeen = ((monsterBookBitfieldsSeen[i / 32] >> (int)(i % 32)) & 1) > 0; bool haveScanned = ((monsterBookBitfieldsScanned[i / 32] >> (int)(i % 32)) & 1) > 0; Console.WriteLine((haveSeen ? "Y" : "N") + (haveScanned ? "Y" : "N") + (monsterBookIndex) + ": " + inGameDic[enemy.NameStringDicID].StringEngOrJpn); ++monsterBookIndex; } } } }
public string GetDataAsHtml(string stratum, int floor, T8BTEMST.T8BTEMST Enemies, T8BTEMGP.T8BTEMGP EnemyGroups, T8BTEMEG.T8BTEMEG EncounterGroups, GameVersion version, string versionPostfix, GameLocale locale, WebsiteLanguage websiteLanguage, T8BTXTMT treasures, ItemDat.ItemDat items, Dictionary <uint, TSS.TSSEntry> inGameIdDict, bool phpLinks = false) { StringBuilder sb = new StringBuilder(); bool printEnemies = Enemies != null && EnemyGroups != null && EncounterGroups != null; sb.Append("<td class=\"necropolistile" + RoomType + "\">"); if (RoomType != 0) { sb.Append("<div class=\"necropolis-arrow-up\">"); if (MoveUpAllowed > 0) { sb.Append("<img src=\"etc/up.png\" width=\"16\" height=\"16\">"); } sb.Append("</div>"); sb.Append("<div class=\"necropolis-arrow-side\">"); if (MoveLeftAllowed > 0) { sb.Append("<img src=\"etc/left.png\" width=\"16\" height=\"16\">"); } sb.Append("</div>"); sb.Append("<div class=\"necropolis-data\">"); if (printEnemies) { foreach (uint groupId in EncounterGroups.EncounterGroupIdDict[EnemyGroup].EnemyGroupIDs) { if (groupId == 0xFFFFFFFFu) { continue; } foreach (int enemyId in EnemyGroups.EnemyGroupIdDict[groupId].EnemyIDs) { if (enemyId < 0) { continue; } var enemy = Enemies.EnemyIdDict[(uint)enemyId]; sb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Enemy, version, versionPostfix, locale, websiteLanguage, phpLinks, category: (int)enemy.Category, id: (int)enemy.InGameID) + "\">"); sb.Append("<img src=\"monster-icons/46px/monster-" + enemy.IconID.ToString("D3") + ".png\" title=\"" + inGameIdDict[enemy.NameStringDicID].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage) + "\" width=\"23\" height=\"23\">"); sb.Append("</a>"); } sb.Append("<br>"); } } else { sb.Append("<img src=\"item-icons/ICON60.png\" width=\"16\" height=\"16\"> " + (FramesToMove / 60) + " sec<br>"); switch (RoomType) { case 1: sb.Append("Entrance<br>"); break; case 2: case 5: { int targetFloor; string targetLinkId; string targetStratum; if (RoomType == 5) { targetFloor = (floor + FloorExitDiff); } else { targetFloor = (floor + 1); } if (targetFloor == 11) { targetStratum = ((char)(((int)stratum[0]) + 1)).ToString(); targetLinkId = targetStratum + "1"; } else { targetStratum = stratum; targetLinkId = targetStratum + targetFloor; } string targetHumanReadable; if (targetFloor == 11) { targetHumanReadable = stratum + " Bottom"; } else { targetHumanReadable = stratum + "-" + targetFloor; } string nextHumanReadable = ""; if (targetFloor == 11) { nextHumanReadable = targetStratum + "-1"; } string linkStart = "<a href=\"" + (phpLinks ? Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.NecropolisMap, version, versionPostfix, locale, websiteLanguage, true, extra: targetLinkId) : ("#" + targetLinkId)) + "\">"; string linkEnd = "</a>"; string finalText; if (targetFloor == 11) { if (stratum == "F") { finalText = $"Exit to {targetHumanReadable}"; } else { finalText = $"Exit to {targetHumanReadable}<br>(Go to {linkStart}{nextHumanReadable}{linkEnd})"; } } else { finalText = $"Exit to {linkStart}{targetHumanReadable}{linkStart}"; } sb.Append(finalText); sb.Append("<br>"); break; } case 3: //sb.Append( "Regular Room<br>" ); break; case 4: //sb.Append( "Treasure Room<br>" ); break; } if (RegularTreasure > 0) { // not a generic solution, but the unmodified game has all four slots identical for regular treasures var treasureInfo = treasures.TreasureInfoList[(int)RegularTreasure]; sb.Append("<table>"); sb.Append("<tr>"); for (int i = 0; i < 3; ++i) { var item = items.itemIdDict[treasureInfo.Items[i]]; sb.Append("<td>"); sb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Item, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)item.Data[(int)ItemData.ID], icon: (int)item.Data[(int)ItemData.Icon]) + "\">"); sb.Append("<img src=\"items/U_" + item.ItemString.TrimNull() + ".png\" height=\"32\" width=\"32\" title=\"" + inGameIdDict[item.NamePointer].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage) + "\">"); sb.Append("</a>"); sb.Append("</td>"); } sb.Append("</tr>"); sb.Append("<tr>"); for (int i = 0; i < 3; ++i) { sb.Append("<td>"); sb.Append(treasureInfo.Chances[i] + "%"); sb.Append("</td>"); } sb.Append("</tr>"); sb.Append("</table>"); } if (SpecialTreasure > 0) { // unmodified game always has special treasures as one in the first slot with 100% chance var treasureInfo = treasures.TreasureInfoList[(int)SpecialTreasure]; var item = items.itemIdDict[treasureInfo.Items[0]]; sb.Append("<img src=\"item-icons/ICON" + item.Data[(int)ItemDat.ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Item, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)item.Data[(int)ItemData.ID], icon: (int)item.Data[(int)ItemData.Icon]) + "\">"); sb.Append(inGameIdDict[item.NamePointer].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage) + "</a><br>"); } } sb.Append("</div>"); sb.Append("<div class=\"necropolis-arrow-side\">"); if (MoveRightAllowed > 0) { sb.Append("<img src=\"etc/right.png\" width=\"16\" height=\"16\">"); } sb.Append("</div>"); sb.Append("<div class=\"necropolis-arrow-down\">"); if (MoveDownAllowed > 0) { sb.Append("<img src=\"etc/down.png\" width=\"16\" height=\"16\">"); } sb.Append("</div>"); } sb.Append("</td>"); return(sb.ToString()); }
public string GetDataAsHtml(GameVersion version, string versionPostfix, GameLocale locale, WebsiteLanguage websiteLanguage, TSS.TSSFile stringDic, Dictionary <uint, TSS.TSSEntry> inGameIdDict, T8BTEMEG.T8BTEMEG encounterGroups, T8BTEMGP.T8BTEMGP enemyGroups, T8BTEMST.T8BTEMST enemies, ShopData.ShopData shops, bool phpLinks = false) { StringBuilder sb = new StringBuilder(); StringBuilder shopEnemySb = new StringBuilder(); List <uint> alreadyPrinted = new List <uint>(); for (int i = 0; i < ShopsOrEnemyGroups.Length; ++i) { if (ShopsOrEnemyGroups[i] == 0) { continue; } if (Category == 1) { // references to shops var shop = shops.ShopDictionary[ShopsOrEnemyGroups[i]]; shopEnemySb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Shop, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)shop.InGameID) + "\">"); shopEnemySb.Append(inGameIdDict[shop.StringDicID].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage)); shopEnemySb.Append("</a>"); } else { // references to encounter groups foreach (uint groupId in encounterGroups.EncounterGroupIdDict[ShopsOrEnemyGroups[i]].EnemyGroupIDs) { if (groupId == 0xFFFFFFFFu) { continue; } foreach (uint id in enemyGroups.EnemyGroupIdDict[groupId].EnemyIDs) { if (id == 0xFFFFFFFFu) { continue; } if (alreadyPrinted.Contains(id)) { continue; } var enemy = enemies.EnemyIdDict[id]; shopEnemySb.Append("<img src=\"monster-icons/44px/monster-" + enemy.IconID.ToString("D3") + ".png\" height=\"22\" width=\"22\"> "); shopEnemySb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Enemy, version, versionPostfix, locale, websiteLanguage, phpLinks, category: (int)enemy.Category, id: (int)enemy.InGameID) + "\">"); shopEnemySb.Append(inGameIdDict[enemy.NameStringDicID].StringEngOrJpnHtml(version, inGameIdDict, websiteLanguage) + "</a>"); shopEnemySb.Append("<br>"); alreadyPrinted.Add(id); } } } } int variantCount = 0; for (int i = 0; i < 4; ++i) { if (i >= 1 && ChangeEventTriggers[i] == 0) { continue; } var name = inGameIdDict[NameStringDicIDs[i]]; var desc = inGameIdDict[DescStringDicIDs[i]]; if (i >= 1 && name.StringJpn == inGameIdDict[NameStringDicIDs[i - 1]].StringJpn && name.StringEng == inGameIdDict[NameStringDicIDs[i - 1]].StringEng && desc.StringJpn == inGameIdDict[DescStringDicIDs[i - 1]].StringJpn && desc.StringEng == inGameIdDict[DescStringDicIDs[i - 1]].StringEng && RefStrings[i] == RefStrings[i - 1] ) { continue; } variantCount++; } for (int i = 0; i < 4; ++i) { if (i >= 1 && ChangeEventTriggers[i] == 0) { continue; } var name = inGameIdDict[NameStringDicIDs[i]]; var desc = inGameIdDict[DescStringDicIDs[i]]; if (i >= 1 && name.StringJpn == inGameIdDict[NameStringDicIDs[i - 1]].StringJpn && name.StringEng == inGameIdDict[NameStringDicIDs[i - 1]].StringEng && desc.StringJpn == inGameIdDict[DescStringDicIDs[i - 1]].StringJpn && desc.StringEng == inGameIdDict[DescStringDicIDs[i - 1]].StringEng && RefStrings[i] == RefStrings[i - 1] ) { continue; } if (i == 0) { sb.Append("<tr id=\"location" + LocationID + "\">"); } else { sb.Append("<tr>"); } sb.Append("<td>"); if (RefStrings[i] != "") { sb.Append("<img src=\"worldmap/U_" + RefStrings[i] + ".png\">"); } sb.Append("</td>"); sb.Append("<td>"); if (websiteLanguage.WantsJp()) { sb.Append("<span class=\"itemname\">"); sb.Append(name.StringJpnHtml(version, inGameIdDict)); sb.Append("</span>"); sb.Append("<br>"); sb.Append(desc.StringJpnHtml(version, inGameIdDict)); } if (websiteLanguage.WantsBoth()) { sb.Append("<br>"); sb.Append("<br>"); } if (websiteLanguage.WantsEn()) { sb.Append("<span class=\"itemname\">"); sb.Append(name.StringEngHtml(version, inGameIdDict)); sb.Append("</span>"); sb.Append("<br>"); sb.Append(desc.StringEngHtml(version, inGameIdDict)); } if (RefStrings[i] == "") { sb.Append("<br>"); } sb.Append("</td>"); if (i == 0) { sb.Append("<td rowspan=\"" + variantCount + "\">"); sb.Append(shopEnemySb); sb.Append("</td>"); } sb.Append("</tr>"); } return(sb.ToString()); }
public static string GetItemDataAsText(GameVersion version, ItemDat items, ItemDatSingle item, T8BTSK.T8BTSK skills, T8BTEMST.T8BTEMST enemies, COOKDAT.COOKDAT Recipes, WRLDDAT.WRLDDAT Locations, TSS.TSSFile tss, Dictionary <uint, TSS.TSSEntry> dict = null) { if (dict == null) { dict = tss.GenerateInGameIdDictionary(); } var sb = new StringBuilder(); sb.AppendLine("[" + item.ItemString.TrimNull() + "]"); sb.Append("[Icon" + item.Data[(int)ItemData.Icon] + "] "); var nameEntry = dict[item.NamePointer]; sb.AppendLine(nameEntry.StringEngOrJpn); var descEntry = dict[item.DescriptionPointer]; sb.AppendLine(descEntry.StringEngOrJpn); switch (item.Data[(int)ItemData.Category]) { case 2: sb.AppendLine("<Tools>"); break; case 3: sb.AppendLine("<Main>"); break; case 4: sb.AppendLine("<Sub>"); break; case 5: sb.AppendLine("<Head>"); break; case 6: sb.AppendLine("<Body>"); break; case 7: sb.AppendLine("<Accessories>"); break; case 8: sb.AppendLine("<Ingredients>"); break; case 9: sb.AppendLine("<Synthesis Materials>"); break; case 10: sb.AppendLine("<Valuables>"); break; case 11: sb.AppendLine("<DLC>"); break; default: sb.AppendLine("<UNKNOWN>"); break; } sb.AppendLine("Price in shops: " + item.Data[(int)ItemData.ShopPrice] + " Gald"); if (item.Data[(int)ItemData.BuyableIn1] > 0 || item.Data[(int)ItemData.BuyableIn2] > 0 || item.Data[(int)ItemData.BuyableIn3] > 0) { sb.Append("Available at shops in: "); if (item.Data[(int)ItemData.BuyableIn1] > 0) { sb.Append(Locations.LocationIdDict[item.Data[(int)ItemData.BuyableIn1]].GetLastValidName(dict).StringEngOrJpn); } if (item.Data[(int)ItemData.BuyableIn2] > 0) { sb.Append("; " + Locations.LocationIdDict[item.Data[(int)ItemData.BuyableIn2]].GetLastValidName(dict).StringEngOrJpn); } if (item.Data[(int)ItemData.BuyableIn3] > 0) { sb.Append("; " + Locations.LocationIdDict[item.Data[(int)ItemData.BuyableIn3]].GetLastValidName(dict).StringEngOrJpn); } sb.AppendLine(); } uint equip = item.Data[(int)ItemData.EquippableByBitfield]; if (equip > 0) { sb.Append("Equippable by: "); if ((equip & 1) == 1) { sb.Append("[YUR]"); } if ((equip & 2) == 2) { sb.Append("[EST]"); } if ((equip & 4) == 4) { sb.Append("[KAR]"); } if ((equip & 8) == 8) { sb.Append("[RIT]"); } if ((equip & 16) == 16) { sb.Append("[RAV]"); } if ((equip & 32) == 32) { sb.Append("[JUD]"); } if ((equip & 64) == 64) { sb.Append("[RAP]"); } if ((equip & 128) == 128) { sb.Append("[FRE]"); } if (version.HasPS3Content() && (equip & 256) == 256) { sb.Append("[PAT]"); } sb.AppendLine(); } uint synthCount = item.Data[(int)ItemData.SynthRecipeCount]; switch (synthCount) { case 0: sb.AppendLine("Can't be synthesized."); break; case 1: sb.AppendLine("Can be synthesized in 1 way."); break; default: sb.AppendLine("Can be synthesized in " + synthCount + " ways."); break; } for (int j = 0; j < synthCount; ++j) { uint synthItemCount = item.Data[(int)ItemData.Synth1ItemSlotCount + j * 16]; sb.AppendLine("Synthesis method #" + (j + 1)); sb.AppendLine(" Required Synthesis Level: " + item.Data[(int)ItemData._Synth1Level + j * 16]); sb.AppendLine(" Price: " + item.Data[(int)ItemData.Synth1Price + j * 16] + " Gald"); sb.AppendLine(" Requires " + synthItemCount + " items:"); for (int i = 0; i < synthItemCount; ++i) { var otherItem = items.itemIdDict[item.Data[(int)ItemData.Synth1Item1Type + i * 2 + j * 16]]; var otherItemNameEntry = dict[otherItem.NamePointer]; string otherItemName = otherItemNameEntry.StringEngOrJpn; sb.AppendLine(" Item " + (i + 1) + ": " + otherItemName + " x" + item.Data[(int)ItemData.Synth1Item1Count + i * 2 + j * 16]); } } switch (item.Data[(int)ItemData.Category]) { case 2: default: // seems to be some kind of singletarget/multitarget flag maybe? //sb.AppendLine( "~19: " + item.Data[(int)ItemData.PATK] ); // seems to be a bitfield regarding what stuff it heals, 1 == death, 2 = magical ailment, 4 == physical ailment // this is already covered below so don't print it //sb.AppendLine( "~20: " + item.Data[(int)ItemData.MATK] ); if (item.Data[(int)ItemData.MDEF_or_HPHealPercent] > 0) { sb.AppendLine("HP Heal %: " + item.Data[(int)ItemData.MDEF_or_HPHealPercent]); } if (item.Data[(int)ItemData.AGL_TPHealPercent] > 0) { sb.AppendLine("TP Heal %: " + item.Data[(int)ItemData.AGL_TPHealPercent]); } // why is this here twice? uint physAilAlt = item.Data[(int)ItemData.PDEF]; uint physAil = item.Data[(int)ItemData._LUCK]; if (physAil != physAilAlt) { throw new Exception(); } if (physAil > 0) { sb.Append("Cures physical ailments: "); if ((physAil & 1) == 1) { sb.Append("Death "); } if ((physAil & 2) == 2) { sb.Append("Poison "); } if ((physAil & 4) == 4) { sb.Append("Paralysis "); } if ((physAil & 8) == 8) { sb.Append("Petrification "); } if ((physAil & 16) == 16) { sb.Append("Weak "); } if ((physAil & 32) == 32) { sb.Append("SealedArtes "); } if ((physAil & 64) == 64) { sb.Append("SealedSkills "); } if ((physAil & 128) == 128) { sb.Append("Contamination "); } sb.AppendLine(); } if (item.Data[(int)ItemData._AGL_Again] > 0) { sb.AppendLine("Cures magical ailments"); } if (item.Data[26] > 0) { sb.AppendLine("Permanent PATK increase: " + item.Data[26]); } if (item.Data[27] > 0) { sb.AppendLine("Permanent PDEF increase: " + item.Data[27]); } if (item.Data[(int)ItemData.AttrFire] > 0) { sb.AppendLine("Permanent MATK increase: " + item.Data[(int)ItemData.AttrFire]); } if (item.Data[(int)ItemData.AttrWater] > 0) { sb.AppendLine("Permanent MDEF increase: " + item.Data[(int)ItemData.AttrWater]); } if (item.Data[(int)ItemData.AttrWind] > 0) { sb.AppendLine("Permanent AGL increase: " + item.Data[(int)ItemData.AttrWind]); } if (item.Data[(int)ItemData.Skill1] > 0) { sb.AppendLine("Max HP increase: " + item.Data[(int)ItemData.Skill1]); } if (item.Data[(int)ItemData.Skill1Metadata] > 0) { sb.AppendLine("Max TP increase: " + item.Data[(int)ItemData.Skill1Metadata]); } break; case 3: case 4: case 5: case 6: case 7: if ((int)item.Data[(int)ItemData.PATK] > 0) { sb.AppendLine("PATK: " + (int)item.Data[(int)ItemData.PATK]); } if ((int)item.Data[(int)ItemData.MATK] > 0) { sb.AppendLine("MATK: " + (int)item.Data[(int)ItemData.MATK]); } if ((int)item.Data[(int)ItemData.PDEF] > 0) { sb.AppendLine("PDEF: " + (int)item.Data[(int)ItemData.PDEF]); } if ((int)item.Data[(int)ItemData.MDEF_or_HPHealPercent] > 0) { sb.AppendLine("MDEF: " + (int)item.Data[(int)ItemData.MDEF_or_HPHealPercent]); } int agl1 = (int)item.Data[(int)ItemData.AGL_TPHealPercent]; int agl2 = (int)item.Data[(int)ItemData._AGL_Again]; if (agl1 != agl2) { sb.AppendLine("!!! AGL1: " + agl1 + " / AGL2: " + agl2); } else { if (agl1 > 0) { sb.AppendLine("AGL: " + agl1); } } if ((int)item.Data[(int)ItemData._LUCK] > 0) { sb.AppendLine("LUCK: " + (int)item.Data[(int)ItemData._LUCK]); } if ((int)item.Data[(int)ItemData.AttrFire] != 0) { sb.AppendLine("Attribute Fire: " + (int)item.Data[(int)ItemData.AttrFire]); } if ((int)item.Data[(int)ItemData.AttrWater] != 0) { sb.AppendLine("Attribute Water: " + (int)item.Data[(int)ItemData.AttrWater]); } if ((int)item.Data[(int)ItemData.AttrWind] != 0) { sb.AppendLine("Attribute Wind: " + (int)item.Data[(int)ItemData.AttrWind]); } if ((int)item.Data[(int)ItemData.AttrEarth] != 0) { sb.AppendLine("Attribute Earth: " + (int)item.Data[(int)ItemData.AttrEarth]); } if ((int)item.Data[(int)ItemData.AttrLight] != 0) { sb.AppendLine("Attribute Light: " + (int)item.Data[(int)ItemData.AttrLight]); } if ((int)item.Data[(int)ItemData.AttrDark] != 0) { sb.AppendLine("Attribute Darkness: " + (int)item.Data[(int)ItemData.AttrDark]); } for (int i = 0; i < 3; ++i) { uint skillId = item.Data[(int)ItemData.Skill1 + i * 2]; if (skillId != 0) { var skill = skills.SkillIdDict[skillId]; var skillNameEntry = dict[skill.NameStringDicID]; string skillName = skillNameEntry.StringEngOrJpn; sb.AppendLine("Skill #" + (i + 1) + " Name: " + skillName); sb.AppendLine("Skill #" + (i + 1) + " Metadata: " + item.Data[(int)ItemData.Skill1Metadata + i * 2]); } } break; } for (int j = 0; j < 2; ++j) { for (int i = 0; i < 16; ++i) { uint enemyId = item.Data[(int)ItemData.Drop1Enemy + i + j * 32]; if (enemyId != 0) { var enemy = enemies.EnemyIdDict[enemyId]; var enemyNameEntry = dict[enemy.NameStringDicID]; string enemyName = enemyNameEntry.StringEngOrJpn; sb.AppendLine("Enemy " + (j == 0 ? "Drop" : "Steal") + " #" + (i + 1) + ": " + enemyName + ", " + item.Data[(int)ItemData.Drop1Chance + i + j * 32] + "%"); } } } for (int i = 0; i < 8; ++i) { if (item.Data[(int)ItemData.UsedInRecipe1 + i] != 0) { sb.AppendLine("Used in Recipe #" + (i + 1) + ": " + item.Data[(int)ItemData.UsedInRecipe1 + i]); } } //sb.AppendLine( "~3: " + item.Data[3] ); //sb.AppendLine( "~5: " + item.Data[5] ); //sb.AppendLine( "~18: " + item.Data[18] ); /* all of these values make no sense to me, probably useless for the reader * sb.AppendLine( "~169: " + item.Data[169] ); * sb.AppendLine( "~170: " + item.Data[170] ); * sb.AppendLine( "~171: " + item.Data[171] ); * sb.AppendLine( "~172: " + item.Data[172] ); * sb.AppendLine( "~173: " + item.Data[173] ); * sb.AppendLine( "~174: " + item.Data[174] ); */ // no idea, maybe related to what shows up on the character model? //sb.AppendLine( "~175: " + (int)item.Data[175] ); // seems to be some sort of ID, useless for the reader //sb.AppendLine( "~176: " + item.Data[176] ); if (item.Data[(int)ItemData.UsableInBattle] > 0) { sb.AppendLine("Usable in battle"); } ; if (item.Data[(int)ItemData.InCollectorsBook] == 0) { sb.AppendLine("Not in Collector's Book"); } return(sb.ToString()); }
public static string GetItemDataAsHtml(GameVersion version, string versionPostfix, GameLocale locale, WebsiteLanguage websiteLanguage, ItemDat items, ItemDatSingle item, T8BTSK.T8BTSK skills, T8BTEMST.T8BTEMST enemies, COOKDAT.COOKDAT Recipes, WRLDDAT.WRLDDAT Locations, ShopData.ShopData shopData, TOVSEAF.TOVSEAF searchPoints, TSS.TSSFile tss, Dictionary <uint, TSS.TSSEntry> dict, bool phpLinks = false) { if (dict == null) { dict = tss.GenerateInGameIdDictionary(); } bool trustItemBookForShops = true; // true to use the values of the item book for shop locations; false to parse it out from the actual data bool trustItemBookForEnemies = false; // true to use the values of the item book for enemy drop/steal lists; false to parse it out from the actual data var sb = new StringBuilder(); sb.Append("<tr id=\"item" + item.Data[(int)ItemData.ID] + "\">"); sb.Append("<td rowspan=\"3\">"); sb.Append("<img src=\"items/U_" + item.ItemString.TrimNull() + ".png\" height=\"128\" width=\"128\">"); sb.Append("</td><td colspan=\"2\">"); uint equip = item.Data[(int)ItemData.EquippableByBitfield]; if (equip > 0) { sb.Append("<span class=\"equip\">"); Website.WebsiteGenerator.AppendCharacterBitfieldAsImageString(sb, dict, version, equip, websiteLanguage.MainJp()); sb.Append("</span>"); } var nameEntry = dict[item.NamePointer]; var descEntry = dict[item.DescriptionPointer]; if (websiteLanguage.WantsJp()) { sb.Append("<img src=\"item-icons/ICON" + item.Data[(int)ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<span class=\"itemname\">"); sb.Append(nameEntry.StringJpnHtml(version, dict)); sb.Append("</span>"); sb.Append("<br>"); sb.Append("<span class=\"itemdesc\">"); sb.Append(descEntry.StringJpnHtml(version, dict)); sb.Append("</span>"); } if (websiteLanguage.WantsBoth()) { sb.Append("<br>"); sb.Append("<br>"); } if (websiteLanguage.WantsEn()) { sb.Append("<img src=\"item-icons/ICON" + item.Data[(int)ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<span class=\"itemname\">"); sb.Append(nameEntry.StringEngHtml(version, dict)); sb.Append("</span>"); sb.Append("<br>"); sb.Append("<span class=\"itemdesc\">"); sb.Append(descEntry.StringEngHtml(version, dict)); sb.Append("</span>"); } sb.Append("<span class=\"special\">"); if (item.Data[(int)ItemData.UsableInBattle] > 0) { sb.Append("Usable in battle"); } ; if (item.Data[(int)ItemData.InCollectorsBook] == 0) { sb.Append("Not in Collector's Book"); } sb.Append("</span>"); sb.Append("</td>"); uint synthCount = item.Data[(int)ItemData.SynthRecipeCount]; switch (synthCount) { case 0: break; case 1: sb.Append("<td colspan=\"2\">"); break; default: sb.Append("<td>"); break; } for (int j = 0; j < synthCount; ++j) { uint synthItemCount = item.Data[(int)ItemData.Synth1ItemSlotCount + j * 16]; sb.Append("Synthesis Level: " + item.Data[(int)ItemData._Synth1Level + j * 16]); sb.Append("<br>"); sb.Append("Price: " + item.Data[(int)ItemData.Synth1Price + j * 16] + " Gald"); for (int i = 0; i < synthItemCount; ++i) { sb.Append("<br>"); var otherItem = items.itemIdDict[item.Data[(int)ItemData.Synth1Item1Type + i * 2 + j * 16]]; var otherItemNameEntry = dict[otherItem.NamePointer]; string otherItemName = otherItemNameEntry.StringEngOrJpnHtml(version, dict, websiteLanguage); sb.Append("<img src=\"item-icons/ICON" + otherItem.Data[(int)ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Item, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)otherItem.Data[(int)ItemData.ID], icon: (int)otherItem.Data[(int)ItemData.Icon]) + "\">"); sb.Append(otherItemName + "</a> x" + item.Data[(int)ItemData.Synth1Item1Count + i * 2 + j * 16]); } if (synthCount > 1 && j == 0) { sb.Append("</td><td>"); } } sb.Append("</td></tr><tr>"); uint category = item.Data[(int)ItemData.Category]; switch (category) { case 2: default: sb.Append("<td colspan=\"2\">"); if (item.Data[(int)ItemData.MDEF_or_HPHealPercent] > 0) { sb.Append("HP Heal %: " + item.Data[(int)ItemData.MDEF_or_HPHealPercent] + "<br>"); } if (item.Data[(int)ItemData.AGL_TPHealPercent] > 0) { sb.Append("TP Heal %: " + item.Data[(int)ItemData.AGL_TPHealPercent] + "<br>"); } // why is this here twice? uint physAilAlt = item.Data[(int)ItemData.PDEF]; uint physAil = item.Data[(int)ItemData._LUCK]; if (physAil != physAilAlt) { throw new Exception(); } if (physAil > 0) { sb.Append("Cures physical ailments: "); if ((physAil & 1) == 1) { sb.Append("<img src=\"text-icons/icon-status-13.png\" height=\"32\" width=\"32\">"); } if ((physAil & 2) == 2) { sb.Append("<img src=\"text-icons/icon-status-01.png\" height=\"32\" width=\"32\">"); } if ((physAil & 4) == 4) { sb.Append("<img src=\"text-icons/icon-status-02.png\" height=\"32\" width=\"32\">"); } if ((physAil & 8) == 8) { sb.Append("<img src=\"text-icons/icon-status-03.png\" height=\"32\" width=\"32\">"); } if ((physAil & 16) == 16) { sb.Append("<img src=\"text-icons/icon-status-04.png\" height=\"32\" width=\"32\">"); } if ((physAil & 32) == 32) { sb.Append("<img src=\"text-icons/icon-status-05.png\" height=\"32\" width=\"32\">"); } if ((physAil & 64) == 64) { sb.Append("<img src=\"text-icons/icon-status-06.png\" height=\"32\" width=\"32\">"); } if ((physAil & 128) == 128) { sb.Append("<img src=\"text-icons/icon-status-07.png\" height=\"32\" width=\"32\">"); } sb.Append("<br>"); } if (item.Data[(int)ItemData._AGL_Again] > 0) { sb.Append("Cures magical ailments<br>"); } if (item.Data[(int)ItemData.PermanentPAtkIncrease] > 0) { sb.Append("Permanent PATK increase: " + item.Data[(int)ItemData.PermanentPAtkIncrease] + "<br>"); } if (item.Data[(int)ItemData.PermanentPDefIncrease] > 0) { sb.Append("Permanent PDEF increase: " + item.Data[(int)ItemData.PermanentPDefIncrease] + "<br>"); } if (item.Data[(int)ItemData.AttrFire] > 0) { sb.Append("Permanent MATK increase: " + item.Data[(int)ItemData.AttrFire] + "<br>"); } if (item.Data[(int)ItemData.AttrWater] > 0) { sb.Append("Permanent MDEF increase: " + item.Data[(int)ItemData.AttrWater] + "<br>"); } if (item.Data[(int)ItemData.AttrWind] > 0) { sb.Append("Permanent AGL increase: " + item.Data[(int)ItemData.AttrWind] + "<br>"); } if (item.Data[(int)ItemData.Skill1] > 0) { sb.Append("Max HP increase: " + item.Data[(int)ItemData.Skill1] + "<br>"); } if (item.Data[(int)ItemData.Skill1Metadata] > 0) { sb.Append("Max TP increase: " + item.Data[(int)ItemData.Skill1Metadata] + "<br>"); } for (int i = 0; i < 8; ++i) { int recipeId = (int)item.Data[(int)ItemData.UsedInRecipe1 + i]; if (recipeId != 0) { var recipe = Recipes.RecipeList[recipeId]; var recipeNameEntry = dict[recipe.NameStringDicID]; sb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Recipe, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)recipe.ID) + "\">" + recipeNameEntry.StringEngOrJpnHtml(version, dict, websiteLanguage) + "</a><br>"); } } sb.Append("</td>"); break; case 3: case 4: case 5: case 6: case 7: sb.Append("<td>"); if ((int)item.Data[(int)ItemData.PATK] > 0) { sb.Append("PATK: " + (int)item.Data[(int)ItemData.PATK] + "<br>"); } if ((int)item.Data[(int)ItemData.MATK] > 0) { sb.Append("MATK: " + (int)item.Data[(int)ItemData.MATK] + "<br>"); } if ((int)item.Data[(int)ItemData.PDEF] > 0) { sb.Append("PDEF: " + (int)item.Data[(int)ItemData.PDEF] + "<br>"); } if ((int)item.Data[(int)ItemData.MDEF_or_HPHealPercent] > 0) { sb.Append("MDEF: " + (int)item.Data[(int)ItemData.MDEF_or_HPHealPercent] + "<br>"); } int agl1 = (int)item.Data[(int)ItemData.AGL_TPHealPercent]; int agl2 = (int)item.Data[(int)ItemData._AGL_Again]; if (agl2 > 0) { sb.Append("AGL: " + agl2 + "<br>"); } if ((int)item.Data[(int)ItemData._LUCK] > 0) { sb.Append("LUCK: " + (int)item.Data[(int)ItemData._LUCK] + "<br>"); } int attackElementCount = 0; int defenseElementCount = 0; for (int i = 0; i < 6; ++i) { if ((int)item.Data[(int)ItemData.AttrFire + i] > 0) { attackElementCount++; } if ((int)item.Data[(int)ItemData.AttrFire + i] < 0) { defenseElementCount++; } } if (attackElementCount > 0 || defenseElementCount > 0) { int fire = (int)item.Data[(int)ItemData.AttrFire]; int watr = (int)item.Data[(int)ItemData.AttrWater]; int wind = (int)item.Data[(int)ItemData.AttrWind]; int eart = (int)item.Data[(int)ItemData.AttrEarth]; int lght = (int)item.Data[(int)ItemData.AttrLight]; int dark = (int)item.Data[(int)ItemData.AttrDark]; if (defenseElementCount > 0) { sb.Append("<table class=\"element\"><tr>"); sb.Append("<td colspan=\"" + defenseElementCount + "\">Resistance</td>"); sb.Append("</tr><tr>"); if (fire < 0) { sb.Append("<td><img src=\"text-icons/icon-element-02.png\"></td>"); } if (eart < 0) { sb.Append("<td><img src=\"text-icons/icon-element-04.png\"></td>"); } if (wind < 0) { sb.Append("<td><img src=\"text-icons/icon-element-01.png\"></td>"); } if (watr < 0) { sb.Append("<td><img src=\"text-icons/icon-element-05.png\"></td>"); } if (lght < 0) { sb.Append("<td><img src=\"text-icons/icon-element-03.png\"></td>"); } if (dark < 0) { sb.Append("<td><img src=\"text-icons/icon-element-06.png\"></td>"); } sb.Append("</tr><tr>"); if (fire < 0) { sb.Append("<td>" + -fire + "%</td>"); } if (eart < 0) { sb.Append("<td>" + -eart + "%</td>"); } if (wind < 0) { sb.Append("<td>" + -wind + "%</td>"); } if (watr < 0) { sb.Append("<td>" + -watr + "%</td>"); } if (lght < 0) { sb.Append("<td>" + -lght + "%</td>"); } if (dark < 0) { sb.Append("<td>" + -dark + "%</td>"); } sb.Append("</tr></table>"); } if (attackElementCount > 0) { sb.Append("<table class=\"element\"><tr>"); if (category == 3 || category == 4) { // weapons and sub-weapons add elemental attributes to your attack sb.Append("<td colspan=\"" + attackElementCount + "\">Attack Element</td>"); } else { // defensive equipment instead uses this field as a weak/resist, with weak as positive and resist as negative values sb.Append("<td colspan=\"" + attackElementCount + "\">Weakness</td>"); } sb.Append("</tr><tr>"); if (fire > 0) { sb.Append("<td><img src=\"text-icons/icon-element-02.png\"></td>"); } if (eart > 0) { sb.Append("<td><img src=\"text-icons/icon-element-04.png\"></td>"); } if (wind > 0) { sb.Append("<td><img src=\"text-icons/icon-element-01.png\"></td>"); } if (watr > 0) { sb.Append("<td><img src=\"text-icons/icon-element-05.png\"></td>"); } if (lght > 0) { sb.Append("<td><img src=\"text-icons/icon-element-03.png\"></td>"); } if (dark > 0) { sb.Append("<td><img src=\"text-icons/icon-element-06.png\"></td>"); } if (!(category == 3 || category == 4)) { // weapons always have a "1" here, don't print that, it's not useful sb.Append("</tr><tr>"); if (fire > 0) { sb.Append("<td>" + fire + "%</td>"); } if (eart > 0) { sb.Append("<td>" + eart + "%</td>"); } if (wind > 0) { sb.Append("<td>" + wind + "%</td>"); } if (watr > 0) { sb.Append("<td>" + watr + "%</td>"); } if (lght > 0) { sb.Append("<td>" + lght + "%</td>"); } if (dark > 0) { sb.Append("<td>" + dark + "%</td>"); } } sb.Append("</tr></table>"); } } sb.Append("</td><td>"); for (int i = 0; i < 3; ++i) { uint skillId = item.Data[(int)ItemData.Skill1 + i * 2]; if (skillId != 0) { var skill = skills.SkillIdDict[skillId]; var skillNameEntry = dict[skill.NameStringDicID]; string skillName = skillNameEntry.StringEngOrJpnHtml(version, dict, websiteLanguage); string skillCat = "<img src=\"skill-icons/category-" + skill.Category.ToString() + ".png\" height=\"16\" width=\"16\">"; sb.Append(skillCat); sb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Skill, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)skill.InGameID) + "\">"); sb.Append(skillName); sb.Append("</a>, " + item.Data[(int)ItemData.Skill1Metadata + i * 2] + "<br>"); } } sb.Append("</td>"); break; } sb.Append("<td>"); sb.Append(item.Data[(int)ItemData.ShopPrice] + " Gald"); if (trustItemBookForShops) { if (item.Data[(int)ItemData.BuyableIn1] > 0 || item.Data[(int)ItemData.BuyableIn2] > 0 || item.Data[(int)ItemData.BuyableIn3] > 0) { //sb.Append( "<br>Available at shops in:" ); for (int i = 0; i < 3; ++i) { if (item.Data[(int)ItemData.BuyableIn1 + i] > 0) { var loc = Locations.LocationIdDict[item.Data[(int)ItemData.BuyableIn1 + i]]; sb.Append("<br><a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Location, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)loc.LocationID) + "\">"); sb.Append(loc.GetLastValidName(dict).StringEngOrJpnHtml(version, dict, websiteLanguage) + "</a>"); } } } } else { List <ShopData.ShopDefinition> shops = new List <ShopData.ShopDefinition>(); foreach (var kvp in shopData.ShopDictionary) { ShopData.ShopDefinition shop = kvp.Value; foreach (var shopItem in shop.ShopItems) { if (shopItem.ItemID == item.Data[(int)ItemData.ID]) { shops.Add(shop); break; } } } bool printShopsDirectly = false; if (printShopsDirectly) { foreach (var shop in shops) { sb.Append("<br><a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Shop, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)shop.InGameID) + "\">"); sb.Append(dict[shop.StringDicID].StringEngOrJpnHtml(version, dict, websiteLanguage) + "</a>"); } } else { foreach (var shop in shops) { WRLDDAT.Location loc = FindLocationOfShop(shop, shopData, Locations); if (loc != null) { sb.Append("<br><a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Location, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)loc.LocationID) + "\">"); sb.Append(loc.GetLastValidName(dict).StringEngOrJpnHtml(version, dict, websiteLanguage) + "</a>"); } else { sb.Append("<br><a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Shop, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)shop.InGameID) + "\">"); sb.Append(dict[shop.StringDicID].StringEngOrJpnHtml(version, dict, websiteLanguage) + "</a>"); } } } } sb.Append("</td><td>"); List <uint> itemsRequireSynth = FindItemsThatRequireForSynth(item.Data[(int)ItemData.ID], items); if (itemsRequireSynth.Count > 0) { sb.Append("Can be used to synthesize"); foreach (uint otherItemId in itemsRequireSynth) { ItemDatSingle otherItem = items.itemIdDict[otherItemId]; sb.Append("<br>"); var otherItemNameEntry = dict[otherItem.NamePointer]; string otherItemName = otherItemNameEntry.StringEngOrJpnHtml(version, dict, websiteLanguage); sb.Append("<img src=\"item-icons/ICON" + otherItem.Data[(int)ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<a href=\"" + Website.WebsiteGenerator.GetUrl(Website.WebsiteSection.Item, version, versionPostfix, locale, websiteLanguage, phpLinks, id: (int)otherItem.Data[(int)ItemData.ID], icon: (int)otherItem.Data[(int)ItemData.Icon]) + "\">"); sb.Append(otherItemName + "</a>"); } } sb.Append("</td></tr><tr>"); // drops and steals List <(uint enemyId, uint dropChance, uint?fatalType, uint?fatalIncrease)>[] enemyIds = new List <(uint enemyId, uint dropChance, uint?fatalType, uint?fatalIncrease)> [3];
public string GetDataAsHtml(GameVersion version, Dictionary <uint, Arte> arteIdDict, T8BTEMST.T8BTEMST enemies, T8BTSK.T8BTSK skills, TSS.TSSFile stringDic, Dictionary <uint, TSS.TSSEntry> inGameIdDict, bool phpLinks = false) { StringBuilder sb = new StringBuilder(); sb.Append("<tr id=\"arte" + InGameID + "\">"); sb.Append("<td style=\"text-align: right;\">"); //sb.Append( RefString + "<br>" ); if (Character > 0 && Character <= 9) { Website.GenerateWebsite.AppendCharacterBitfieldAsImageString(sb, version, 1u << (int)(Character - 1)); } if (Character > 9) { var enemy = enemies.EnemyIdDict[Character]; sb.Append("<a href=\"" + Website.GenerateWebsite.GetUrl(Website.WebsiteSection.Enemy, version, phpLinks, category: (int)enemy.Category, id: (int)enemy.InGameID) + "\">"); sb.Append("<img src=\"monster-icons/48px/monster-" + enemy.IconID.ToString("D3") + ".png\" width=\"32\" height=\"32\" title=\"" + inGameIdDict[enemy.NameStringDicID].StringEngOrJpnHtml(version) + "\">"); sb.Append("</a>"); } sb.Append("<img src=\"menu-icons/artes-"); switch (Type) { case ArteType.Base: sb.Append("00"); break; case ArteType.Arcane: sb.Append("01"); break; case ArteType.Altered: sb.Append("12"); break; case ArteType.AlteredSpell: sb.Append("12"); break; case ArteType.NoviceSpell: sb.Append("04"); break; case ArteType.IntermediateSpell: sb.Append("05"); break; case ArteType.AdvancedSpell: sb.Append("06"); break; case ArteType.Burst: sb.Append("02"); break; case ArteType.BurstSpell: sb.Append("02"); break; case ArteType.AlteredBurst: sb.Append("02"); break; case ArteType.AlteredBurstSpell: sb.Append("02"); break; case ArteType.Mystic: sb.Append("02"); break; } sb.Append(".png\" width=\"32\" height=\"32\">"); sb.Append("</td>"); sb.Append("<td>"); sb.Append("<span class=\"itemname\">"); sb.Append(inGameIdDict[NameStringDicId].StringJpnHtml(version) + "</span><br>"); sb.Append(inGameIdDict[DescStringDicId].StringJpnHtml(version)); sb.Append("</td>"); sb.Append("<td>"); sb.Append("<span class=\"itemname\">"); sb.Append(inGameIdDict[NameStringDicId].StringEngHtml(version) + "</span><br>"); sb.Append(inGameIdDict[DescStringDicId].StringEngHtml(version)); sb.Append("</td>"); sb.Append("<td>"); //sb.Append( Type + "<br>" ); bool iconsInserted = false; if (Character <= 9) { switch (FatalStrikeType) { case 0: sb.Append("<img src=\"menu-icons/artes-13.png\" width=\"16\" height=\"16\">"); iconsInserted = true; break; case 1: sb.Append("<img src=\"menu-icons/artes-15.png\" width=\"16\" height=\"16\">"); iconsInserted = true; break; case 2: sb.Append("<img src=\"menu-icons/artes-14.png\" width=\"16\" height=\"16\">"); iconsInserted = true; break; } } if (ElementFire > 0) { sb.Append("<img src=\"text-icons/icon-element-02.png\" width=\"16\" height=\"16\">"); iconsInserted = true; } if (ElementEarth > 0) { sb.Append("<img src=\"text-icons/icon-element-04.png\" width=\"16\" height=\"16\">"); iconsInserted = true; } if (ElementWind > 0) { sb.Append("<img src=\"text-icons/icon-element-01.png\" width=\"16\" height=\"16\">"); iconsInserted = true; } if (ElementWater > 0) { sb.Append("<img src=\"text-icons/icon-element-05.png\" width=\"16\" height=\"16\">"); iconsInserted = true; } if (ElementLight > 0) { sb.Append("<img src=\"text-icons/icon-element-03.png\" width=\"16\" height=\"16\">"); iconsInserted = true; } if (ElementDarkness > 0) { sb.Append("<img src=\"text-icons/icon-element-06.png\" width=\"16\" height=\"16\">"); iconsInserted = true; } if (iconsInserted) { sb.Append("<br>"); } if (TPUsage > 0) { sb.Append("TP usage: " + TPUsage + "<br>"); } for (int i = 0; i < LearnRequirementsOtherArtesType.Length; ++i) { if (LearnRequirementsOtherArtesType[i] > 0) { switch (LearnRequirementsOtherArtesType[i]) { case 1: // Level if (LearnRequirementsOtherArtesId[i] == 300) { sb.Append("Learn via Event"); } else { sb.Append("Level " + LearnRequirementsOtherArtesId[i]); } break; case 2: // Other Arte var otherArte = arteIdDict[LearnRequirementsOtherArtesId[i]]; if (otherArte.ID != this.ID) { sb.Append("<img src=\"menu-icons/artes-" + otherArte.GetIconNumber() + ".png\" width=\"16\" height=\"16\">"); sb.Append("<a href=\"#arte" + otherArte.InGameID + "\">" + inGameIdDict[otherArte.NameStringDicId].StringEngOrJpnHtml(version)); sb.Append("</a>, "); } else { sb.Append("Learn with "); } sb.Append(LearnRequirementsOtherArtesUsageCount[i] + " uses"); break; case 3: // appears on Rita and Repede's Burst Artes in 360 only, appears to be unused break; default: sb.Append("##Unknown Learn Type: " + LearnRequirementsOtherArtesType[i] + "<br>"); sb.Append("##Value 1: " + LearnRequirementsOtherArtesId[i] + "<br>"); sb.Append("##Value 2: " + LearnRequirementsOtherArtesUsageCount[i] + "<br>"); break; } sb.Append("<br>"); } } for (int i = 0; i < AlteredArteRequirementType.Length; ++i) { if (AlteredArteRequirementType[i] > 0) { switch (AlteredArteRequirementType[i]) { case 1: // original arte var otherArte = arteIdDict[AlteredArteRequirementId[i]]; sb.Append("Alters from "); sb.Append("<img src=\"menu-icons/artes-" + otherArte.GetIconNumber() + ".png\" width=\"16\" height=\"16\">"); sb.Append("<a href=\"#arte" + otherArte.InGameID + "\">" + inGameIdDict[otherArte.NameStringDicId].StringEngOrJpnHtml(version) + "</a>"); break; case 3: // skill var skill = skills.SkillIdDict[AlteredArteRequirementId[i]]; sb.Append("<img src=\"skill-icons/category-" + skill.Category + ".png\" width=\"16\" height=\"16\">"); sb.Append("<a href=\"" + Website.GenerateWebsite.GetUrl(Website.WebsiteSection.Skill, version, phpLinks, id: (int)skill.InGameID) + "\">" + inGameIdDict[skill.NameStringDicID].StringEngOrJpnHtml(version) + "</a>"); break; default: sb.Append("##Unknown Altered Type: " + AlteredArteRequirementType[i] + "<br>"); sb.Append("##Value: " + AlteredArteRequirementId[i] + "<br>"); break; } sb.Append("<br>"); } } if (UsableInMenu > 0) { sb.Append("Usable outside of battle"); } sb.Append("</td>"); /* * sb.Append( "<td>" ); * sb.AppendLine(); * sb.AppendLine( "~" + 9 + ": " + Data[9] + "<br>" ); * for ( int i = 16; i < 22; ++i ) { * sb.AppendLine( "~" + i + ": " + Data[i] + "<br>" ); * } * for ( int i = 40; i < 69; ++i ) { * if ( i == 44 ) continue; * if ( i == 42 || i == 66 ) { * sb.AppendLine( "~" + i + ": " + Data[i].UIntToFloat() + "<br>" ); * } else { * sb.AppendLine( "~" + i + ": " + Data[i] + "<br>" ); * } * } * for ( int i = 79; i < 95; ++i ) { * if ( i == 83 ) continue; * sb.AppendLine( "~" + i + ": " + Data[i] + "<br>" ); * } * sb.Append( "</td>" ); * //*/ sb.Append("</tr>"); return(sb.ToString()); }
public static int Parse(List <string> args) { if (args.Count < 2) { Console.WriteLine("Usage: SaveDataParser savefile gamedatapath"); Console.WriteLine("Save must be decrypted."); Console.WriteLine("Game data path should point to a directory or other container with the game files, which is needed to parse things like item, title, and enemy data correctly."); return(-1); } IContainer gameContainer = Website.GenerateWebsite.ContainerFromPath(args[1]); if (gameContainer == null) { Console.WriteLine("Invalid game data path given."); return(-1); } GameVersion?maybeVersion = Website.GenerateWebsite.GuessGameVersionFromContainer(gameContainer); if (!maybeVersion.HasValue) { Console.WriteLine("Failed to determine game version from given data path."); return(-1); } GameVersion version = maybeVersion.Value; IContainer gameDir = Website.GenerateWebsite.FindGameDataDirectory(gameContainer, version); if (gameDir == null) { Console.WriteLine("Failed to find correct file container -- is your game dump incomplete?"); return(-1); } GameLocale locale = VesperiaUtil.GetValidLocales(version).First(); EndianUtils.Endianness endian = VesperiaUtil.GetEndian(version); TextUtils.GameTextEncoding encoding = VesperiaUtil.GetEncoding(version); BitUtils.Bitness bits = VesperiaUtil.GetBitness(version); TSS.TSSFile stringDic = new TSS.TSSFile(Website.GenerateWebsite.TryGetStringDic(gameDir, locale, version), encoding, endian); Dictionary <uint, TSS.TSSEntry> inGameDic = stringDic.GenerateInGameIdDictionary(); ItemDat.ItemDat itemData = new ItemDat.ItemDat(Website.GenerateWebsite.TryGetItemDat(gameDir, locale, version), Website.GenerateWebsite.TryGetItemSortDat(gameDir, locale, version), EndianUtils.Endianness.BigEndian); List <ItemDat.ItemDatSingle> itemDataSorted = itemData.GetSortedByInGameSorting(); FAMEDAT.FAMEDAT titles = new FAMEDAT.FAMEDAT(Website.GenerateWebsite.TryGetTitles(gameDir, locale, version), endian); T8BTEMST.T8BTEMST enemies = new T8BTEMST.T8BTEMST(Website.GenerateWebsite.TryGetEnemies(gameDir, locale, version), endian, bits); using (DuplicatableFileStream file = new DuplicatableFileStream(args[0])) { var savedata = new SaveData(file, endian); savedata.SavePoint.PrintData(); savedata.PartyData.PrintData(endian, inGameDic, itemDataSorted, enemies); foreach (var characterBlock in savedata.CharacterData) { characterBlock.PrintData(endian, version, inGameDic, titles); Console.WriteLine("====="); } //savedata.ExportTo( args[0] + ".ext" ); } return(0); }
public static int Parse(List <string> args) { if (args.Count < 1) { Console.WriteLine("Usage: SaveDataParser SAVE"); Console.WriteLine("Save must be decrypted."); return(-1); } Util.Endianness endian = Util.Endianness.BigEndian; var stringDic = new TSS.TSSFile(@"c:\Dropbox\ToV\PS3\mod\string.svo.ext\STRING_DIC.SO", Util.GameTextEncoding.ShiftJIS, Util.Endianness.BigEndian); var inGameDic = stringDic.GenerateInGameIdDictionary(); var itemData = new ItemDat.ItemDat(@"c:\Dropbox\ToV\PS3\orig\item.svo.ext\ITEM.DAT", endian); var itemDataSorted = itemData.GetSortedByInGameSorting(); var titles = new FAMEDAT.FAMEDAT(@"c:\Dropbox\ToV\PS3\orig\menu.svo.ext\FAMEDATA.BIN", endian); var enemies = new T8BTEMST.T8BTEMST(@"c:\Dropbox\ToV\PS3\orig\btl.svo.ext\BTL_PACK.DAT.ext\0005.ext\ALL.0000", endian, Util.Bitness.B32); using (Stream file = new FileStream(args[0], FileMode.Open, FileAccess.Read)) { file.DiscardBytes(0x228); // short header, used for save menu on 360 version to display basic info about save string magic = file.ReadAscii(8); if (magic != "TO8SAVE\0") { throw new Exception("Invalid magic byte sequence for ToV save: " + magic); } uint saveFileSize = file.ReadUInt32().FromEndian(endian); if (saveFileSize != 0xCCAA0) { throw new Exception("Unexpected filesize for ToV save: " + saveFileSize); } file.DiscardBytes(0x3AC8 - 0x234); // no idea what all this is // save point flags, one byte each, 0x00 not visted 0x01 visited byte[] savePointFlags = file.ReadUInt8Array(0x59); { PrintSavePoint(savePointFlags, 0x00, "Fiertia Deck (Docked at Atherum)"); PrintSavePoint(savePointFlags, 0x01, "Atherum"); PrintSavePoint(savePointFlags, 0x02, "Fiertia Hold"); // same flag for both opportunities? PrintSavePoint(savePointFlags, 0x03, "Keiv Moc (Middle)"); PrintSavePoint(savePointFlags, 0x04, "Keiv Moc (Boss)"); PrintSavePoint(savePointFlags, 0x05, "Zaphias (Lower Quarter)"); PrintSavePoint(savePointFlags, 0x06, "Zaphias (Royal Quarter)"); PrintSavePoint(savePointFlags, 0x07, "Zaphias Castle (Prison)"); PrintSavePoint(savePointFlags, 0x08, "Zaphias Castle (Kitchen)"); // 2nd visit only PrintSavePoint(savePointFlags, 0x09, "Zaphias Castle (Hallways)"); // before zagi fight PrintSavePoint(savePointFlags, 0x0A, "Zaphias Castle (Sword Stair)"); PrintSavePoint(savePointFlags, 0x0B, "Zaphias Castle (Big Hall)"); // 2nd visit only, that big room that leads to the sword stair PrintSavePoint(savePointFlags, 0x0C, "Weasand of Cados (Middle)"); PrintSavePoint(savePointFlags, 0x0D, "Weasand of Cados (Exit)"); PrintSavePoint(savePointFlags, 0x0E, "Halure (Inn)"); PrintSavePoint(savePointFlags, 0x0F, "Ghasfarost (Bottom)"); PrintSavePoint(savePointFlags, 0x10, "Ghasfarost (Top)"); PrintSavePoint(savePointFlags, 0x11, "Myorzo (Vacant House)"); PrintSavePoint(savePointFlags, 0x12, "Mt. Temza (Middle)"); PrintSavePoint(savePointFlags, 0x13, "Mt. Temza (Boss)"); PrintSavePoint(savePointFlags, 0x14, "Deidon Hold"); PrintSavePoint(savePointFlags, 0x15, "Northeastern Hypionia"); // aurnion before it's built PrintSavePoint(savePointFlags, 0x16, "Aurnion (Developing)"); PrintSavePoint(savePointFlags, 0x17, "Aurnion (Developed)"); PrintSavePoint(savePointFlags, 0x18, "Caer Bocram"); PrintSavePoint(savePointFlags, 0x19, "Quoi Woods"); PrintSavePoint(savePointFlags, 0x1A, "Dahngrest (Inn)"); PrintSavePoint(savePointFlags, 0x1B, "Ehmead Hill"); PrintSavePoint(savePointFlags, 0x1C, "Erealumen (Middle)"); PrintSavePoint(savePointFlags, 0x1D, "Erealumen (Boss)"); PrintSavePoint(savePointFlags, 0x1E, "Heracles (Near Engine Room)"); PrintSavePoint(savePointFlags, 0x1F, "Heracles (Near Control Room)"); // zagi fight PrintSavePoint(savePointFlags, 0x20, "Zopheir (Boss)"); // 1st visit only PrintSavePoint(savePointFlags, 0x21, "Zopheir (Near Aer Krene)"); // 2nd visit only PrintSavePoint(savePointFlags, 0x22, "Manor of the Wicked"); PrintSavePoint(savePointFlags, 0x23, "Tarqaron (Middle)"); PrintSavePoint(savePointFlags, 0x24, "Tarqaron (Top)"); PrintSavePoint(savePointFlags, 0x25, "Baction B1F"); PrintSavePoint(savePointFlags, 0x26, "Baction B2F"); // both save points on B2F share this flag...? PrintSavePoint(savePointFlags, 0x27, "Mantaic (Inn)"); PrintSavePoint(savePointFlags, 0x28, "Relewiese (Middle)"); PrintSavePoint(savePointFlags, 0x29, "Relewiese (Boss)"); PrintSavePoint(savePointFlags, 0x2A, "Capua Nor (Outside Ragou's Mansion)"); PrintSavePoint(savePointFlags, 0x2B, "Capua Nor (Inn)"); PrintSavePoint(savePointFlags, 0x2C, "Capua Torim (Inn)"); PrintSavePoint(savePointFlags, 0x2D, "Shaikos Ruins"); PrintSavePoint(savePointFlags, 0x2E, "Zaude (Side Entrance)"); PrintSavePoint(savePointFlags, 0x2F, "Zaude (Alexei)"); PrintSavePoint(savePointFlags, 0x30, "Zaude (Yeager)"); PrintSavePoint(savePointFlags, 0x31, "Aspio (Inn)"); PrintSavePoint(savePointFlags, 0x32, "Nordopolica (Inn)"); PrintSavePoint(savePointFlags, 0x33, "Heliord (Inn)"); PrintSavePoint(savePointFlags, 0x34, "Yormgen (Inn)"); PrintSavePoint(savePointFlags, 0x35, "Weasand of Kogorh (Oasis)"); PrintSavePoint(savePointFlags, 0x36, "Weasand of Kogorh (Exit)"); PrintSavePoint(savePointFlags, 0x37, "Egothor Forest"); PrintSavePoint(savePointFlags, 0x38, "Dahngrest Underpass (Oath)"); PrintSavePoint(savePointFlags, 0x39, "Ragou's Mansion"); // basement dungeon midpoint PrintSavePoint(savePointFlags, 0x3A, "Dahngrest Underpass (Exit)"); PrintSavePoint(savePointFlags, 0x3B, "Abysmal Hollow (Aer Krene near Yumanju)"); PrintSavePoint(savePointFlags, 0x3C, "? Abysmal Hollow (Aer Krene near Zaphias)"); PrintSavePoint(savePointFlags, 0x3D, "Abysmal Hollow (Aer Krene near Heliord)"); PrintSavePoint(savePointFlags, 0x3E, "Abysmal Hollow (Aer Krene near Nordopolica)"); PrintSavePoint(savePointFlags, 0x3F, "? Abysmal Hollow (Center)"); PrintSavePoint(savePointFlags, 0x40, "City of the Waning Moon"); PrintSavePoint(savePointFlags, 0x41, "Necropolis of Nostalgia A3"); PrintSavePoint(savePointFlags, 0x42, "Necropolis of Nostalgia A6"); PrintSavePoint(savePointFlags, 0x43, "Necropolis of Nostalgia A9"); PrintSavePoint(savePointFlags, 0x44, "Necropolis of Nostalgia A Bottom"); PrintSavePoint(savePointFlags, 0x45, "Necropolis of Nostalgia B2"); PrintSavePoint(savePointFlags, 0x46, "Necropolis of Nostalgia B5"); PrintSavePoint(savePointFlags, 0x47, "Necropolis of Nostalgia B8"); PrintSavePoint(savePointFlags, 0x48, "Necropolis of Nostalgia B Bottom"); PrintSavePoint(savePointFlags, 0x49, "Necropolis of Nostalgia C3"); PrintSavePoint(savePointFlags, 0x4A, "Necropolis of Nostalgia C6"); PrintSavePoint(savePointFlags, 0x4B, "Necropolis of Nostalgia C9"); PrintSavePoint(savePointFlags, 0x4C, "Necropolis of Nostalgia C Bottom"); PrintSavePoint(savePointFlags, 0x4D, "Necropolis of Nostalgia D3"); PrintSavePoint(savePointFlags, 0x4E, "Necropolis of Nostalgia D6"); PrintSavePoint(savePointFlags, 0x4F, "Necropolis of Nostalgia D9"); PrintSavePoint(savePointFlags, 0x50, "Necropolis of Nostalgia D Bottom"); PrintSavePoint(savePointFlags, 0x51, "Necropolis of Nostalgia E3"); PrintSavePoint(savePointFlags, 0x52, "Necropolis of Nostalgia E6"); PrintSavePoint(savePointFlags, 0x53, "Necropolis of Nostalgia E9"); PrintSavePoint(savePointFlags, 0x54, "Necropolis of Nostalgia E Bottom"); PrintSavePoint(savePointFlags, 0x55, "Necropolis of Nostalgia F3"); PrintSavePoint(savePointFlags, 0x56, "Necropolis of Nostalgia F6"); PrintSavePoint(savePointFlags, 0x57, "Necropolis of Nostalgia F9"); PrintSavePoint(savePointFlags, 0x58, "Necropolis of Nostalgia F Bottom"); } file.DiscardBytes(0xA3F48 - 0x3B21); // no idea what all this is file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32Array(9, endian); file.ReadUInt32().FromEndian(endian); // play time in frames, assuming 60 frames = 1 second file.ReadUInt32().FromEndian(endian); // gald file.DiscardBytes(4); // ? uint[] itemCounts = file.ReadUInt32Array(3072, endian); uint[] itemBookBitfields = file.ReadUInt32Array(3072 / 32, endian); file.DiscardBytes(4); // ? file.ReadUInt32Array(4, endian); // control modes for the four active party slots file.ReadUInt32Array(3, endian); // strategies assigned to dpad directions file.DiscardBytes(0x40); // ?? for (int i = 0; i < 8; ++i) { // custom strategy names // game seems to read these till null byte so this could totally be abused to buffer overflow... file.ReadAscii(0x40); } file.DiscardBytes(0xA84D0 - 0xA7360); // ? uint[] monsterBookBitfieldsScanned = file.ReadUInt32Array(0x48 / 4, endian); file.DiscardBytes(0xA8680 - 0xA8518); // ? uint[] monsterBookBitfieldsSeen = file.ReadUInt32Array(0x48 / 4, endian); file.DiscardBytes(0xA8928 - 0xA86C8); // ? // 9 character blocks, each 0x4010 bytes for (int character = 0; character < 9; ++character) { file.ReadUInt32().FromEndian(endian); // ? file.ReadAscii(0x40); // custom character name file.ReadUInt32().FromEndian(endian); // character ID file.ReadUInt32().FromEndian(endian); // level file.ReadUInt32().FromEndian(endian); // current HP file.ReadUInt32().FromEndian(endian); // current TP file.ReadUInt32().FromEndian(endian); // max HP file.ReadUInt32().FromEndian(endian); // max TP file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // EXP file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // base attack file.ReadUInt32().FromEndian(endian); // base magic attack file.ReadUInt32().FromEndian(endian); // base def file.ReadUInt32().FromEndian(endian); // base mdef file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // base agility file.ReadUInt32().FromEndian(endian); // luck file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // base attack attribute fire file.ReadUInt32().FromEndian(endian); // base attack attribute earth file.ReadUInt32().FromEndian(endian); // base attack attribute wind file.ReadUInt32().FromEndian(endian); // base attack attribute water file.ReadUInt32().FromEndian(endian); // base attack attribute light file.ReadUInt32().FromEndian(endian); // base attack attribute dark file.ReadUInt32().FromEndian(endian); // base attack attribute physical...? file.ReadUInt32().FromEndian(endian); // base damage multiplier fire file.ReadUInt32().FromEndian(endian); // base damage multiplier earth file.ReadUInt32().FromEndian(endian); // base damage multiplier wind file.ReadUInt32().FromEndian(endian); // base damage multiplier water file.ReadUInt32().FromEndian(endian); // base damage multiplier light file.ReadUInt32().FromEndian(endian); // base damage multiplier dark file.ReadUInt32().FromEndian(endian); // base damage multiplier physical? file.DiscardBytes(0xA8A60 - 0xA89F0); // ? file.ReadUInt32().FromEndian(endian); // modified attack (base + from equipment) file.ReadUInt32().FromEndian(endian); // mod def file.ReadUInt32().FromEndian(endian); // mod matk file.ReadUInt32().FromEndian(endian); // mod mdef file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // mod agility file.ReadUInt32().FromEndian(endian); // mod luck file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // ? file.ReadUInt32().FromEndian(endian); // mod attack attribute fire file.ReadUInt32().FromEndian(endian); // mod attack attribute earth file.ReadUInt32().FromEndian(endian); // mod attack attribute wind file.ReadUInt32().FromEndian(endian); // mod attack attribute water file.ReadUInt32().FromEndian(endian); // mod attack attribute light file.ReadUInt32().FromEndian(endian); // mod attack attribute dark file.ReadUInt32().FromEndian(endian); // mod attack attribute physical...? file.ReadUInt32().FromEndian(endian); // mod damage multiplier fire file.ReadUInt32().FromEndian(endian); // mod damage multiplier earth file.ReadUInt32().FromEndian(endian); // mod damage multiplier wind file.ReadUInt32().FromEndian(endian); // mod damage multiplier water file.ReadUInt32().FromEndian(endian); // mod damage multiplier light file.ReadUInt32().FromEndian(endian); // mod damage multiplier dark file.ReadUInt32().FromEndian(endian); // mod damage multiplier physical? file.DiscardBytes(0xA8E04 - 0xA8ABC); // ? file.ReadUInt32().FromEndian(endian); // enemy kill counter (?) file.DiscardBytes(0xAAE28 - 0xA8E08); // ? // skill equipment is stored around here file.DiscardBytes(0xAC5B8 - 0xAAE28); // ? uint[] titlesUnlockedBitfield = file.ReadUInt32Array(15, endian); foreach (var title in titles.TitleList) { bool haveTitle = ((titlesUnlockedBitfield[title.ID / 32] >> (int)(title.ID % 32)) & 1) > 0; if (haveTitle || ((title.BunnyGuildPointsMaybe > 0 || title.ID == 67) && title.Character == (character + 1))) { Console.WriteLine((haveTitle ? "Y" : "N") + ": " + inGameDic[title.NameStringDicID].StringEngOrJpn); } } Console.WriteLine("==="); file.DiscardBytes(0xAC938 - 0xAC5F4); // ? } uint collectorsBookIndex = 0; foreach (var item in itemDataSorted) { uint i = item.Data[(int)ItemDat.ItemData.ID]; if (item.Data[(int)ItemDat.ItemData.InCollectorsBook] > 0) { bool haveItem = ((itemBookBitfields[i / 32] >> (int)(i % 32)) & 1) > 0; Console.WriteLine((haveItem ? "Y" : "N") + (collectorsBookIndex) + ": " + inGameDic[item.NamePointer].StringEngOrJpn); ++collectorsBookIndex; } } uint monsterBookIndex = 0; foreach (var enemy in enemies.EnemyList) { uint i = enemy.InGameID; if (enemy.InMonsterBook > 0) { bool haveSeen = ((monsterBookBitfieldsSeen[i / 32] >> (int)(i % 32)) & 1) > 0; bool haveScanned = ((monsterBookBitfieldsScanned[i / 32] >> (int)(i % 32)) & 1) > 0; Console.WriteLine((haveSeen ? "Y" : "N") + (haveScanned ? "Y" : "N") + (monsterBookIndex) + ": " + inGameDic[enemy.NameStringDicID].StringEngOrJpn); ++monsterBookIndex; } } } return(0); }
public ItemForm(GameVersion version, ItemDat itemDat, TSSFile TSS, T8BTSK.T8BTSK skills, T8BTEMST.T8BTEMST enemies, COOKDAT.COOKDAT cookdat, WRLDDAT.WRLDDAT locations) { InitializeComponent(); this.Version = version; this.itemDat = itemDat; this.TSS = TSS; this.Skills = skills; this.Enemies = enemies; this.Recipes = cookdat; this.Locations = locations; this.InGameIdDict = TSS.GenerateInGameIdDictionary(); labels = new List <Label>(); textboxes = new List <TextBox>(); for (int i = 0; i < ItemDatSingle.size / 4; ++i) { Label l = new Label(); l.Text = ""; l.Size = new System.Drawing.Size(100, 20); TextBox b = new TextBox(); b.Size = new System.Drawing.Size(50, 20); b.Text = ""; labels.Add(l); textboxes.Add(b); FlowLayoutPanel p = new FlowLayoutPanel(); p.Size = new System.Drawing.Size(200, 20); p.FlowDirection = FlowDirection.LeftToRight; p.Controls.Add(l); p.Controls.Add(b); switch ((ItemData)i) { case ItemData.NamePointer: case ItemData.DescriptionPointer: case ItemData.UnknownTextPointer: case ItemData.TextIDPart1: case ItemData.TextIDPart2: case ItemData.TextIDPart3: case ItemData.TextIDPart4: case ItemData.TextIDPart5: case ItemData.TextIDPart6: case ItemData.TextIDPart7: case ItemData.TextIDPart8: break; default: flowLayoutPanel1.Controls.Add(p); break; } } foreach (ItemDatSingle i in itemDat.items) { var entry = GetEntry(i.Data[(int)ItemData.NamePointer]); listBox1.Items.Add(entry.StringEngOrJpn); } }
public static string GetItemDataAsHtml(GameVersion version, ItemDat items, ItemDatSingle item, T8BTSK.T8BTSK skills, T8BTEMST.T8BTEMST enemies, COOKDAT.COOKDAT Recipes, WRLDDAT.WRLDDAT Locations, TSS.TSSFile tss, Dictionary <uint, TSS.TSSEntry> dict = null, bool phpLinks = false) { if (dict == null) { dict = tss.GenerateInGameIdDictionary(); } var sb = new StringBuilder(); sb.Append("<tr id=\"item" + item.Data[(int)ItemData.ID] + "\">"); sb.Append("<td rowspan=\"3\">"); sb.Append("<img src=\"items/U_" + item.ItemString.TrimNull() + ".png\" height=\"128\" width=\"128\">"); sb.Append("</td><td colspan=\"2\">"); uint equip = item.Data[(int)ItemData.EquippableByBitfield]; if (equip > 0) { sb.Append("<span class=\"equip\">"); Website.GenerateWebsite.AppendCharacterBitfieldAsImageString(sb, version, equip); sb.Append("</span>"); } var nameEntry = dict[item.NamePointer]; var descEntry = dict[item.DescriptionPointer]; sb.Append("<img src=\"item-icons/ICON" + item.Data[(int)ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<span class=\"itemname\">"); sb.Append(nameEntry.StringJpnHtml(version)); sb.Append("</span>"); sb.Append("<br>"); sb.Append("<span class=\"itemdesc\">"); sb.Append(descEntry.StringJpnHtml(version)); sb.Append("</span>"); sb.Append("<br>"); sb.Append("<br>"); sb.Append("<img src=\"item-icons/ICON" + item.Data[(int)ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<span class=\"itemname\">"); sb.Append(nameEntry.StringEngHtml(version)); sb.Append("</span>"); sb.Append("<br>"); sb.Append("<span class=\"itemdesc\">"); sb.Append(descEntry.StringEngHtml(version)); sb.Append("</span>"); sb.Append("<span class=\"special\">"); if (item.Data[(int)ItemData.UsableInBattle] > 0) { sb.Append("Usable in battle"); } ; if (item.Data[(int)ItemData.InCollectorsBook] == 0) { sb.Append("Not in Collector's Book"); } sb.Append("</span>"); sb.Append("</td>"); uint synthCount = item.Data[(int)ItemData.SynthRecipeCount]; switch (synthCount) { case 0: break; case 1: sb.Append("<td colspan=\"2\">"); break; default: sb.Append("<td>"); break; } for (int j = 0; j < synthCount; ++j) { uint synthItemCount = item.Data[(int)ItemData.Synth1ItemSlotCount + j * 16]; sb.Append("Synthesis Level: " + item.Data[(int)ItemData._Synth1Level + j * 16]); sb.Append("<br>"); sb.Append("Price: " + item.Data[(int)ItemData.Synth1Price + j * 16] + " Gald"); for (int i = 0; i < synthItemCount; ++i) { sb.Append("<br>"); var otherItem = items.itemIdDict[item.Data[(int)ItemData.Synth1Item1Type + i * 2 + j * 16]]; var otherItemNameEntry = dict[otherItem.NamePointer]; string otherItemName = otherItemNameEntry.StringEngOrJpnHtml(version); sb.Append("<img src=\"item-icons/ICON" + otherItem.Data[(int)ItemData.Icon] + ".png\" height=\"16\" width=\"16\"> "); sb.Append("<a href=\"" + Website.GenerateWebsite.GetUrl(Website.WebsiteSection.Item, version, phpLinks, id: (int)otherItem.Data[(int)ItemData.ID], icon: (int)otherItem.Data[(int)ItemData.Icon]) + "\">"); sb.Append(otherItemName + "</a> x" + item.Data[(int)ItemData.Synth1Item1Count + i * 2 + j * 16]); } if (synthCount > 1 && j == 0) { sb.Append("</td><td>"); } } sb.Append("</td></tr><tr>"); uint category = item.Data[(int)ItemData.Category]; switch (category) { case 2: default: sb.Append("<td colspan=\"2\">"); if (item.Data[(int)ItemData.MDEF_or_HPHealPercent] > 0) { sb.Append("HP Heal %: " + item.Data[(int)ItemData.MDEF_or_HPHealPercent] + "<br>"); } if (item.Data[(int)ItemData.AGL_TPHealPercent] > 0) { sb.Append("TP Heal %: " + item.Data[(int)ItemData.AGL_TPHealPercent] + "<br>"); } // why is this here twice? uint physAilAlt = item.Data[(int)ItemData.PDEF]; uint physAil = item.Data[(int)ItemData._LUCK]; if (physAil != physAilAlt) { throw new Exception(); } if (physAil > 0) { sb.Append("Cures physical ailments: "); if ((physAil & 1) == 1) { sb.Append("<img src=\"text-icons/icon-status-13.png\" height=\"32\" width=\"32\">"); } if ((physAil & 2) == 2) { sb.Append("<img src=\"text-icons/icon-status-01.png\" height=\"32\" width=\"32\">"); } if ((physAil & 4) == 4) { sb.Append("<img src=\"text-icons/icon-status-02.png\" height=\"32\" width=\"32\">"); } if ((physAil & 8) == 8) { sb.Append("<img src=\"text-icons/icon-status-03.png\" height=\"32\" width=\"32\">"); } if ((physAil & 16) == 16) { sb.Append("<img src=\"text-icons/icon-status-04.png\" height=\"32\" width=\"32\">"); } if ((physAil & 32) == 32) { sb.Append("<img src=\"text-icons/icon-status-05.png\" height=\"32\" width=\"32\">"); } if ((physAil & 64) == 64) { sb.Append("<img src=\"text-icons/icon-status-06.png\" height=\"32\" width=\"32\">"); } if ((physAil & 128) == 128) { sb.Append("<img src=\"text-icons/icon-status-07.png\" height=\"32\" width=\"32\">"); } sb.Append("<br>"); } if (item.Data[(int)ItemData._AGL_Again] > 0) { sb.Append("Cures magical ailments<br>"); } if (item.Data[(int)ItemData.PermanentPAtkIncrease] > 0) { sb.Append("Permanent PATK increase: " + item.Data[(int)ItemData.PermanentPAtkIncrease] + "<br>"); } if (item.Data[(int)ItemData.PermanentPDefIncrease] > 0) { sb.Append("Permanent PDEF increase: " + item.Data[(int)ItemData.PermanentPDefIncrease] + "<br>"); } if (item.Data[(int)ItemData.AttrFire] > 0) { sb.Append("Permanent MATK increase: " + item.Data[(int)ItemData.AttrFire] + "<br>"); } if (item.Data[(int)ItemData.AttrWater] > 0) { sb.Append("Permanent MDEF increase: " + item.Data[(int)ItemData.AttrWater] + "<br>"); } if (item.Data[(int)ItemData.AttrWind] > 0) { sb.Append("Permanent AGL increase: " + item.Data[(int)ItemData.AttrWind] + "<br>"); } if (item.Data[(int)ItemData.Skill1] > 0) { sb.Append("Max HP increase: " + item.Data[(int)ItemData.Skill1] + "<br>"); } if (item.Data[(int)ItemData.Skill1Metadata] > 0) { sb.Append("Max TP increase: " + item.Data[(int)ItemData.Skill1Metadata] + "<br>"); } for (int i = 0; i < 8; ++i) { int recipeId = (int)item.Data[(int)ItemData.UsedInRecipe1 + i]; if (recipeId != 0) { var recipe = Recipes.RecipeList[recipeId]; var recipeNameEntry = dict[recipe.NameStringDicID]; sb.Append("<a href=\"" + Website.GenerateWebsite.GetUrl(Website.WebsiteSection.Recipe, version, phpLinks, id: (int)recipe.ID) + "\">" + recipeNameEntry.StringEngOrJpnHtml(version) + "</a><br>"); } } sb.Append("</td>"); break; case 3: case 4: case 5: case 6: case 7: sb.Append("<td>"); if ((int)item.Data[(int)ItemData.PATK] > 0) { sb.Append("PATK: " + (int)item.Data[(int)ItemData.PATK] + "<br>"); } if ((int)item.Data[(int)ItemData.MATK] > 0) { sb.Append("MATK: " + (int)item.Data[(int)ItemData.MATK] + "<br>"); } if ((int)item.Data[(int)ItemData.PDEF] > 0) { sb.Append("PDEF: " + (int)item.Data[(int)ItemData.PDEF] + "<br>"); } if ((int)item.Data[(int)ItemData.MDEF_or_HPHealPercent] > 0) { sb.Append("MDEF: " + (int)item.Data[(int)ItemData.MDEF_or_HPHealPercent] + "<br>"); } int agl1 = (int)item.Data[(int)ItemData.AGL_TPHealPercent]; int agl2 = (int)item.Data[(int)ItemData._AGL_Again]; if (agl2 > 0) { sb.Append("AGL: " + agl2 + "<br>"); } if ((int)item.Data[(int)ItemData._LUCK] > 0) { sb.Append("LUCK: " + (int)item.Data[(int)ItemData._LUCK] + "<br>"); } int attackElementCount = 0; int defenseElementCount = 0; for (int i = 0; i < 6; ++i) { if ((int)item.Data[(int)ItemData.AttrFire + i] > 0) { attackElementCount++; } if ((int)item.Data[(int)ItemData.AttrFire + i] < 0) { defenseElementCount++; } } if (attackElementCount > 0 || defenseElementCount > 0) { int fire = (int)item.Data[(int)ItemData.AttrFire]; int watr = (int)item.Data[(int)ItemData.AttrWater]; int wind = (int)item.Data[(int)ItemData.AttrWind]; int eart = (int)item.Data[(int)ItemData.AttrEarth]; int lght = (int)item.Data[(int)ItemData.AttrLight]; int dark = (int)item.Data[(int)ItemData.AttrDark]; if (defenseElementCount > 0) { sb.Append("<table class=\"element\"><tr>"); sb.Append("<td colspan=\"" + defenseElementCount + "\">Resistance</td>"); sb.Append("</tr><tr>"); if (fire < 0) { sb.Append("<td><img src=\"text-icons/icon-element-02.png\"></td>"); } if (eart < 0) { sb.Append("<td><img src=\"text-icons/icon-element-04.png\"></td>"); } if (wind < 0) { sb.Append("<td><img src=\"text-icons/icon-element-01.png\"></td>"); } if (watr < 0) { sb.Append("<td><img src=\"text-icons/icon-element-05.png\"></td>"); } if (lght < 0) { sb.Append("<td><img src=\"text-icons/icon-element-03.png\"></td>"); } if (dark < 0) { sb.Append("<td><img src=\"text-icons/icon-element-06.png\"></td>"); } sb.Append("</tr><tr>"); if (fire < 0) { sb.Append("<td>" + -fire + "%</td>"); } if (eart < 0) { sb.Append("<td>" + -eart + "%</td>"); } if (wind < 0) { sb.Append("<td>" + -wind + "%</td>"); } if (watr < 0) { sb.Append("<td>" + -watr + "%</td>"); } if (lght < 0) { sb.Append("<td>" + -lght + "%</td>"); } if (dark < 0) { sb.Append("<td>" + -dark + "%</td>"); } sb.Append("</tr></table>"); } if (attackElementCount > 0) { sb.Append("<table class=\"element\"><tr>"); if (category == 3 || category == 4) { // weapons and sub-weapons add elemental attributes to your attack sb.Append("<td colspan=\"" + attackElementCount + "\">Attack Element</td>"); } else { // defensive equipment instead uses this field as a weak/resist, with weak as positive and resist as negative values sb.Append("<td colspan=\"" + attackElementCount + "\">Weakness</td>"); } sb.Append("</tr><tr>"); if (fire > 0) { sb.Append("<td><img src=\"text-icons/icon-element-02.png\"></td>"); } if (eart > 0) { sb.Append("<td><img src=\"text-icons/icon-element-04.png\"></td>"); } if (wind > 0) { sb.Append("<td><img src=\"text-icons/icon-element-01.png\"></td>"); } if (watr > 0) { sb.Append("<td><img src=\"text-icons/icon-element-05.png\"></td>"); } if (lght > 0) { sb.Append("<td><img src=\"text-icons/icon-element-03.png\"></td>"); } if (dark > 0) { sb.Append("<td><img src=\"text-icons/icon-element-06.png\"></td>"); } if (!(category == 3 || category == 4)) { // weapons always have a "1" here, don't print that, it's not useful sb.Append("</tr><tr>"); if (fire > 0) { sb.Append("<td>" + fire + "%</td>"); } if (eart > 0) { sb.Append("<td>" + eart + "%</td>"); } if (wind > 0) { sb.Append("<td>" + wind + "%</td>"); } if (watr > 0) { sb.Append("<td>" + watr + "%</td>"); } if (lght > 0) { sb.Append("<td>" + lght + "%</td>"); } if (dark > 0) { sb.Append("<td>" + dark + "%</td>"); } } sb.Append("</tr></table>"); } } sb.Append("</td><td>"); for (int i = 0; i < 3; ++i) { uint skillId = item.Data[(int)ItemData.Skill1 + i * 2]; if (skillId != 0) { var skill = skills.SkillIdDict[skillId]; var skillNameEntry = dict[skill.NameStringDicID]; string skillName = skillNameEntry.StringEngOrJpnHtml(version); string skillCat = "<img src=\"skill-icons/category-" + skill.Category.ToString() + ".png\" height=\"16\" width=\"16\">"; sb.Append(skillCat); sb.Append("<a href=\"" + Website.GenerateWebsite.GetUrl(Website.WebsiteSection.Skill, version, phpLinks, id: (int)skill.InGameID) + "\">"); sb.Append(skillName); sb.Append("</a>, " + item.Data[(int)ItemData.Skill1Metadata + i * 2] + "<br>"); } } sb.Append("</td>"); break; } sb.Append("<td colspan=\"2\">"); sb.Append(item.Data[(int)ItemData.ShopPrice] + " Gald"); if (item.Data[(int)ItemData.BuyableIn1] > 0 || item.Data[(int)ItemData.BuyableIn2] > 0 || item.Data[(int)ItemData.BuyableIn3] > 0) { //sb.Append( "<br>Available at shops in:" ); for (int i = 0; i < 3; ++i) { if (item.Data[(int)ItemData.BuyableIn1 + i] > 0) { var loc = Locations.LocationIdDict[item.Data[(int)ItemData.BuyableIn1 + i]]; sb.Append("<br><a href=\"" + Website.GenerateWebsite.GetUrl(Website.WebsiteSection.Location, version, phpLinks, id: (int)loc.LocationID) + "\">"); sb.Append(loc.GetLastValidName(dict).StringEngOrJpnHtml(version) + "</a>"); } } sb.AppendLine(); } sb.Append("</td></tr><tr>"); // read how many drops and steals this item lists int[] dropStealCount = new int[2]; for (int j = 0; j < 2; ++j) { dropStealCount[j] = 0; for (int i = 0; i < 16; ++i) { uint enemyId = item.Data[(int)ItemData.Drop1Enemy + i + j * 32]; if (enemyId != 0) { dropStealCount[j]++; } } } for (int j = 0; j < 2; ++j) { sb.Append("<td colspan=\"2\">"); if (dropStealCount[j] > 0) { int colCount = Math.Min(4, dropStealCount[j]); int rowCount = (dropStealCount[j] - 1) / 4 + 1; sb.Append("<table class=\"element\">"); sb.Append("<tr><td colspan=\"" + colCount + "\">"); sb.Append(j == 0 ? "Drop" : "Steal"); sb.Append("</td></tr>"); int cellCount = 0; for (int i = 0; i < 16; ++i) { uint enemyId = item.Data[(int)ItemData.Drop1Enemy + i + j * 32]; if (enemyId != 0) { if (cellCount % 4 == 0) { sb.Append("<tr>"); } sb.Append("<td>"); var enemy = enemies.EnemyIdDict[enemyId]; var enemyNameEntry = dict[enemy.NameStringDicID]; string enemyName = enemyNameEntry.StringEngOrJpnHtml(version); sb.Append("<img src=\"monster-icons/44px/monster-" + enemy.IconID.ToString("D3") + ".png\"><br>"); sb.Append("<a href=\"" + Website.GenerateWebsite.GetUrl(Website.WebsiteSection.Enemy, version, phpLinks, category: (int)enemy.Category, id: (int)enemy.InGameID) + "\">"); sb.Append(enemyName + "</a><br>" + item.Data[(int)ItemData.Drop1Chance + i + j * 32] + "%"); sb.Append("</td>"); if (cellCount % 4 == 3) { sb.Append("</tr>"); } cellCount++; } } if (cellCount % 4 != 0) { if (cellCount > 4) { for (int i = cellCount % 4; i < 4; ++i) { sb.Append("<td></td>"); } } sb.Append("</tr>"); } sb.Append("</table>"); } sb.Append("</td>"); } sb.Append("</tr>"); return(sb.ToString()); }