package prc.utils; import prc.autodoc.*; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; import static prc.Main.err_pr; import static prc.autodoc.Main.TwoDAStore; /** * Performs a bunch of cross-reference tests on 2das. * * @author Ornedan */ public class Validator { private static TwoDAStore twoDA; private static boolean pedantic = false; /** * Ye olde maine methode. * * @param args The arguments */ public static void main(String[] args) { if (args.length == 0) readMe(); String twoDAPath = null; String tlkPath = null; // parse args for (String param : args) {//2dadir tlkdir | [--help] // Parameter parseage if (param.startsWith("-")) { if (param.equals("--help")) readMe(); else { for (char c : param.substring(1).toCharArray()) { switch (c) { case 'p': pedantic = true; default: System.out.println("Unknown parameter: " + c); readMe(); } } } } else { // It's a pathname if (twoDAPath == null) twoDAPath = param; else if (tlkPath == null) tlkPath = param; else { System.out.println("Unknown parameter: " + param); readMe(); } } } twoDA = new TwoDAStore(twoDAPath); doSpellsAndDes2dasTest(twoDAPath); doSpellsAndIprpSpellsTest(twoDAPath); doDesAndIprpTest(twoDAPath); } private static void doDesAndIprpTest(String twoDAPath) { Data_2da iprp_spells = twoDA.get("iprp_spells"), des_crft_spells = twoDA.get("des_crft_spells"); // For each des_crft_spells entry, see if it points at the lowest CasterLvl entry Map> lowestIndex = new HashMap>(); // Map of spells.2da index -> (iprp_spells.2da index, CasterLvl value) int spellID, casterLvl; for (int i = 0; i < iprp_spells.getEntryCount(); i++) { if (!iprp_spells.getEntry("SpellIndex", i).equals("****")) { // Only lines that are connected to spells.2da are scanned try { spellID = Integer.parseInt(iprp_spells.getEntry("SpellIndex", i)); } catch (NumberFormatException e) { // Logged already, just skip continue; } try { casterLvl = Integer.parseInt(iprp_spells.getEntry("CasterLvl", i)); } catch (NumberFormatException e) { err_pr.println("Error: Error: Non-number value in iprp_spells.2da CasterLvl column on line " + i + ": " + iprp_spells.getEntry("CasterLvl", i)); continue; } if (lowestIndex.get(spellID) == null || lowestIndex.get(spellID).e2 > casterLvl) lowestIndex.put(spellID, new Tuple(i, casterLvl)); } } for (int i = 0; i < des_crft_spells.getEntryCount(); i++) { if (!des_crft_spells.getEntry("IPRP_SpellIndex", i).equals("****")) { if (lowestIndex.get(i) == null) err_pr.println("Error: Error: des_crft_spells.2da IPRP_SpellIndex defined for spell " + i + ", but no matching iprp_spells.2da entries exist"); try { if (lowestIndex.get(i).e1 != Integer.parseInt(des_crft_spells.getEntry("IPRP_SpellIndex", i))) err_pr.println("Error: Warning: des_crft_spells.2da IPRP_SpellIndex entry on line " + i + " points at iprp_spells.2da entry with non-lowest CasterLvl value: " + des_crft_spells.getEntry("IPRP_SpellIndex", i) + " (lowest is on line: " + lowestIndex.get(i).e1 + ")"); } catch (NumberFormatException e) { err_pr.println("Error: Error: Non-number value in des_crft_spells.2da IPRP_SpellIndex column on line " + i + ": " + iprp_spells.getEntry("SpellIndex", i)); } } else if (lowestIndex.get(i) != null) err_pr.println("Error: Error: iprp_spells.2da entry defined for spell " + i + ", but des_crft_spells.2da IPRP_SpellIndex is not"); } } private static void doSpellsAndIprpSpellsTest(String twoDAPath) { Data_2da spells = twoDA.get("spells"), iprp_spells = twoDA.get("iprp_spells"); // For each iprp_spells entry, make sure InnateLevel matches spells Innate for (int i = 0; i < iprp_spells.getEntryCount(); i++) { if (!iprp_spells.getEntry("SpellIndex", i).equals("****")) { // Only lines that are connected to spells.2da are scanned try { if (!iprp_spells.getEntry("InnateLvl", i).equals(spells.getEntry("Innate", Integer.parseInt(iprp_spells.getEntry("SpellIndex", i)))) && !(spells.getEntry("Innate", Integer.parseInt(iprp_spells.getEntry("SpellIndex", i))).equals("0") && iprp_spells.getEntry("InnateLvl", i).equals("0.5") ) ) err_pr.println("Error: Warning: Differing Innate and InnateLvl among spells.2da and iprp_spells.2da on iprp_spells.2da line " + i + ": " + "(" + spells.getEntry("Innate", Integer.parseInt(iprp_spells.getEntry("SpellIndex", i))) + "," + iprp_spells.getEntry("InnateLvl", i) + ")"); } catch (NumberFormatException e) { err_pr.println("Error: Error: Non-number value in iprp_spells.2da SpellIndex column on line " + i + ": " + iprp_spells.getEntry("SpellIndex", i)); } } } // For each iprp_spells entry, if GeneralUse is 1, check whether PotionUse and WandUse obey the standard level constraints if (pedantic) { int level; boolean targetSelf; for (int i = 0; i < iprp_spells.getEntryCount(); i++) { if (!iprp_spells.getEntry("SpellIndex", i).equals("****")) { // Only lines that are connected to spells.2da are scanned if (!iprp_spells.getEntry("GeneralUse", i).equals("****") && !iprp_spells.getEntry("GeneralUse", i).equals("0")) { try { level = Integer.parseInt(iprp_spells.getEntry("InnateLvl", i)); } catch (NumberFormatException e) { if (iprp_spells.getEntry("InnateLvl", i).equals("0.5")) level = 0; else { err_pr.println("Error: Error: Non-number value in iprp_spells.2da InnateLvl column on line " + i + ": " + iprp_spells.getEntry("InnateLvl", i)); continue; } } try { targetSelf = (Integer.parseInt(spells.getEntry("TargetType", Integer.parseInt(iprp_spells.getEntry("SpellIndex", i))) .substring(2), 16) & 0x1) == 1; } catch (NumberFormatException e) { err_pr.println("Error: Error: Non-number value among iprp_spells.2da SpellIndex and spells.2da TargetType on iprp_spells.2da line " + i); continue; } if (iprp_spells.getEntry("PotionUse", i).equals("0") && targetSelf && level <= 3) err_pr.println("Error: Warning: PotionUse 0 in iprp_spells.2da when spell of 3rd level or less and self-targetable on line " + i); if (iprp_spells.getEntry("PotionUse", i).equals("1") && (!targetSelf || level > 3)) err_pr.println("Error: Warning: PotionUse 1 in iprp_spells.2da when spell of level higher than 3rd or not self-targetable on line " + i); if (iprp_spells.getEntry("WandUse", i).equals("0") && level <= 4) err_pr.println("Error: Warning: WandUse 0 in iprp_spells.2da when spell of 4th level or less on line " + i); if (iprp_spells.getEntry("WandUse", i).equals("1") && level > 4) err_pr.println("Error: Warning: WandUse 1 in iprp_spells.2da when spell of level higher than 4th on line " + i); } } } } // For each actual spell in spells.2da, make sure there is at least one iprp_spells.2da entry Set spellIDs = new TreeSet(); for (int i = 0; i < spells.getEntryCount(); i++) { if (!spells.getEntry("Bard", i).equals("****") || !spells.getEntry("Cleric", i).equals("****") || !spells.getEntry("Druid", i).equals("****") || !spells.getEntry("Paladin", i).equals("****") || !spells.getEntry("Ranger", i).equals("****") || !spells.getEntry("Wiz_Sorc", i).equals("****")) spellIDs.add(i); } for (int i = 0; i < iprp_spells.getEntryCount(); i++) { if (!iprp_spells.getEntry("SpellIndex", i).equals("****")) { try { spellIDs.remove(Integer.parseInt(iprp_spells.getEntry("SpellIndex", i))); } catch (NumberFormatException e) { err_pr.println("Error: Error: Non-number value in iprp_spells.2da SpellIndex column on line " + i + ": " + iprp_spells.getEntry("SpellIndex", i)); } } } for (int spellID : spellIDs) err_pr.println("Error: Error: Spell " + spellID + " does not have any iprp_spells.2da entries"); } private static void doSpellsAndDes2dasTest(String twoDAPath) { Data_2da spells = twoDA.get("spells"), des_crft_scroll = twoDA.get("des_crft_scroll"), des_crft_spells = twoDA.get("des_crft_spells"); // First, whine about differing lengths on spells and des_crft_spells if (spells.getEntryCount() != des_crft_spells.getEntryCount()) err_pr.println("Error: Warning: spells.2da and des_crft_spells.2da have different number of entries"); int maxCommon = Math.min(Math.min(spells.getEntryCount(), des_crft_spells.getEntryCount()), des_crft_scroll.getEntryCount()); // First, check labels up to the common max for (int i = 0; i < maxCommon; i++) { if (!(spells.getEntry("Label", i).equals(des_crft_spells.getEntry("Label", i)) && spells.getEntry("Label", i).equals(des_crft_scroll.getEntry("Label", i)) && des_crft_spells.getEntry("Label", i).equals(des_crft_scroll.getEntry("Label", i)))) err_pr.println("Error: Warning: Differing Label among spells.2da, des_crft_scroll.2da and des_crft_spells.2da on line: " + i + "('" + spells.getEntry("Label", i) + "','" + des_crft_scroll.getEntry("Label", i) + "','" + des_crft_spells.getEntry("Label", i) + "')"); } // Then, check spells.2da and des_crft_spells.2da int a = 0, b = 0; boolean spellsNum, desNum; for (int i = 0; i < Math.min(spells.getEntryCount(), des_crft_spells.getEntryCount()); i++) { spellsNum = desNum = true; // Here, we are only interested in lines that contain something if (!(spells.getEntry("Label", i).startsWith("**") || spells.getEntry("Label", i).equals("ReservedForISCAndESS")) ) { if (!spells.getEntry("Label", i).equals(des_crft_spells.getEntry("Label", i))) err_pr.println("Error: Warning: Differing Label among spells.2da and des_crft_spells.2da on line: " + i + "('" + spells.getEntry("Label", i) + "','" + des_crft_spells.getEntry("Label", i) + "')"); try { a = Integer.parseInt(spells.getEntry("Innate", i)); } catch (NumberFormatException e) { spellsNum = false; } try { b = Integer.parseInt(des_crft_spells.getEntry("Level", i)); } catch (NumberFormatException e) { desNum = false; } // If both are numeric, compare if (spellsNum && desNum) { if (a != b) err_pr.println("Error: Error: Differing Innate and Level values among spells.2da and des_crft_spells.2da on line " + i + ": " + "(" + a + "," + b + ")"); } // Otherwise, erroneous cases are those where only one value is non-numeric else if (!spellsNum && desNum) err_pr.println("Error: Error: Non-number value in spells.2da Innate column on line " + i + ": " + spells.getEntry("Innate", i)); else if (spellsNum && !desNum) err_pr.println("Error: Error: Non-number value in des_crft_spells.2da Level column on line " + i + ": " + des_crft_spells.getEntry("Level", i)); // Or where the non-numericity is not just **** else { if (!spells.getEntry("Innate", i).equals("****")) err_pr.println("Error: Error: Non-number value in spells.2da Innate column on line " + i + ": " + spells.getEntry("Innate", i)); if (!des_crft_spells.getEntry("Level", i).equals("****")) err_pr.println("Error: Error: Non-number value in des_crft_spells.2da Level column on line " + i + ": " + des_crft_spells.getEntry("Level", i)); } } } // Then check that all spells that have a scroll is are NoScroll 0 for (int i = 0; i < des_crft_scroll.getEntryCount(); i++) { if ((!des_crft_scroll.getEntry("Wiz_Sorc", i).equals("****") || !des_crft_scroll.getEntry("Cleric", i).equals("****") || !des_crft_scroll.getEntry("Paladin", i).equals("****") || !des_crft_scroll.getEntry("Druid", i).equals("****") || !des_crft_scroll.getEntry("Ranger", i).equals("****") || !des_crft_scroll.getEntry("Bard", i).equals("****") ) && des_crft_spells.getEntry("NoScroll", i).equals("1") ) err_pr.println("Error: Error: NoScroll 1 in des_crft_spells.2da when a scroll entry has been defined in des_crft_scroll.2da on line: " + i); } // Then check that all spells that should have a scroll do have a scroll for (int i = 0; i < spells.getEntryCount(); i++) { checkScrollsPresence(spells, des_crft_scroll, "Bard", i); checkScrollsPresence(spells, des_crft_scroll, "Cleric", i); checkScrollsPresence(spells, des_crft_scroll, "Druid", i); checkScrollsPresence(spells, des_crft_scroll, "Paladin", i); checkScrollsPresence(spells, des_crft_scroll, "Ranger", i); checkScrollsPresence(spells, des_crft_scroll, "Wiz_Sorc", i); } } private static void checkScrollsPresence(Data_2da spells, Data_2da des_crft_scroll, String column, int i) { if (!spells.getEntry(column, i).equals("****")) { if (i >= des_crft_scroll.getEntryCount() || des_crft_scroll.getEntry(column, i).equals("****")) { err_pr.println("Error: Error: No " + column + " scroll defined in des_crft_scroll when " + column + " level is defined in spells on line: " + i); } } } /** * Prints the use instructions for this program and kills execution. */ private static void readMe() { // 0 1 2 3 4 5 6 7 8 // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 System.out.println("Usage:\n" + " java -jar prc.jar validate 2dadir tlkdir | [--help]\n" + "\n" + "2dadir Path to a directory containing 2da files\n" + "tlkdir Path to a directory containing dialog.tlk and prc8_consortium.tlk\n" + "\n" + "-p pedantic mode. Makes extra checks\n" + "\n" + "--help prints this info you are reading\n" + "\n" + "\n" + "Performs a set of validation operations on 2da files.\n" ); System.exit(0); } }