2023-08-22 10:00:21 -04:00
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 " +
2024-06-21 19:37:17 -05:00
" tlkdir Path to a directory containing dialog.tlk and prc8_consortium.tlk \ n " +
2023-08-22 10:00:21 -04:00
" \ 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 ) ;
}
}