PRC8/nwn/nwnprc/DocGen/trunk/prc/utils/Validator.java
2024-06-21 19:37:17 -05:00

315 lines
17 KiB
Java

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<Integer, Tuple<Integer, Integer>> lowestIndex = new HashMap<Integer, Tuple<Integer, Integer>>(); // 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<Integer, Integer>(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<Integer> spellIDs = new TreeSet<Integer>();
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);
}
}