2023-08-22 10:00:21 -04:00
|
|
|
package prc.autodoc;
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileWriter;
|
|
|
|
import java.io.IOException;
|
2024-06-21 19:37:17 -05:00
|
|
|
import java.nio.file.Paths;
|
2023-08-22 10:00:21 -04:00
|
|
|
import java.util.*;
|
|
|
|
import java.util.concurrent.CountDownLatch;
|
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
import static prc.Main.*;
|
|
|
|
import static prc.autodoc.EntryGeneration.*;
|
|
|
|
import static prc.autodoc.MenuGeneration.*;
|
|
|
|
import static prc.autodoc.PageGeneration.*;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The main purpose of this autodocumenter is to create parts of the manual for
|
|
|
|
* the PRC pack from 2da and TLK files. As a side effect of doing so, it finds
|
|
|
|
* many errors present in the 2das.
|
|
|
|
*/
|
|
|
|
public class Main {
|
2024-06-21 19:37:17 -05:00
|
|
|
public static ArrayList<String> getFoldersInFolder(String path) {
|
|
|
|
var result = new ArrayList<String>();
|
|
|
|
|
|
|
|
var folder = new File(path);
|
|
|
|
for (var fileEntry : folder.listFiles()) {
|
|
|
|
if (fileEntry.isDirectory()) {
|
|
|
|
result.add(fileEntry.getAbsolutePath().toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static ArrayList<String> getFilesInFolder(String path) {
|
|
|
|
var result = new ArrayList<String>();
|
|
|
|
|
|
|
|
var folder = new File(path);
|
|
|
|
for (var fileEntry : folder.listFiles()) {
|
|
|
|
if (!fileEntry.isDirectory()) {
|
|
|
|
result.add(fileEntry.getAbsolutePath().toString());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-08-22 10:00:21 -04:00
|
|
|
/**
|
|
|
|
* A small data structure class that gives access to both normal and custom
|
|
|
|
* TLK with the same method
|
|
|
|
*/
|
|
|
|
public static class TLKStore {
|
|
|
|
private final Data_TLK normal;
|
|
|
|
private final Data_TLK custom;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new TLKStore around the given two filenames. Equivalent to
|
|
|
|
* TLKStore(normalName, customName, "tlk").
|
|
|
|
*
|
|
|
|
* @param normalName dialog.tlk or equivalent for the given language
|
2024-06-21 19:37:17 -05:00
|
|
|
* @param customName prc8_consortium.tlk or equivalent for the given languag
|
2023-08-22 10:00:21 -04:00
|
|
|
* @throws TLKReadException if there are any problems reading either TLK
|
|
|
|
*/
|
|
|
|
public TLKStore(String normalName, String customName) {
|
2024-06-21 19:37:17 -05:00
|
|
|
var baseDirectory = "../../trunk"; // @TODO: Move to a Configuration File
|
|
|
|
this.normal = new Data_TLK(Paths.get(baseDirectory, "tlk", normalName).toAbsolutePath().toString());
|
|
|
|
this.custom = new Data_TLK(Paths.get(baseDirectory, "tlk", customName).toAbsolutePath().toString());
|
2023-08-22 10:00:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new TLKStore around the given two filenames.
|
|
|
|
*
|
|
|
|
* @param normalName dialog.tlk or equivalent for the given language
|
2024-06-21 19:37:17 -05:00
|
|
|
* @param customName prc8_consortium.tlk or equivalent for the given languag
|
2023-08-22 10:00:21 -04:00
|
|
|
* @param tlkDir Directory containing the two .tlk files
|
|
|
|
* @throws TLKReadException if there are any problems reading either TLK
|
|
|
|
*/
|
|
|
|
public TLKStore(String normalName, String customName, String tlkDir) {
|
2024-06-21 19:37:17 -05:00
|
|
|
var baseDirectory = "../../trunk"; // @TODO: Move to a Configuration File
|
|
|
|
this.normal = new Data_TLK(Paths.get(baseDirectory, tlkDir, normalName).toAbsolutePath().toString());
|
|
|
|
this.custom = new Data_TLK(Paths.get(baseDirectory, tlkDir, customName).toAbsolutePath().toString());
|
2023-08-22 10:00:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the TLK entry for the given StrRef. If there is nothing
|
|
|
|
* at the location, returns Bad StrRef. Automatically picks between
|
|
|
|
* normal and custom TLKs.
|
|
|
|
*
|
|
|
|
* @param num the line number in TLK
|
|
|
|
* @return the contents of the given TLK slot, or Bad StrRef
|
|
|
|
*/
|
|
|
|
public String get(int num) {
|
|
|
|
return num < 0x01000000 ? normal.getEntry(num) : custom.getEntry(num);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See above, except that this one automatically parses the string for
|
|
|
|
* the number.
|
|
|
|
*
|
|
|
|
* @param num the line number in TLK as string
|
|
|
|
* @return as above, except it returns Bad StrRef in case parsing failed
|
|
|
|
*/
|
|
|
|
public String get(String num) {
|
|
|
|
try {
|
|
|
|
return get(Integer.parseInt(num));
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
return Main.badStrRef;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Another data structure class. Stores 2das and handles loading them.
|
|
|
|
*/
|
|
|
|
public static class TwoDAStore {
|
|
|
|
private static class Loader implements Runnable {
|
|
|
|
private final String pathToLoad;
|
|
|
|
private final List<Data_2da> list;
|
|
|
|
private final CountDownLatch latch;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new Loader to load the given 2da file
|
|
|
|
*
|
|
|
|
* @param pathToLoad path of the 2da to load
|
|
|
|
* @param list list to store the loaded data into
|
|
|
|
* @param latch latch to countdown on once loading is complete
|
|
|
|
*/
|
|
|
|
public Loader(String pathToLoad, List<Data_2da> list, CountDownLatch latch) {
|
|
|
|
this.pathToLoad = pathToLoad;
|
|
|
|
this.list = list;
|
|
|
|
this.latch = latch;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @see java.lang.Runnable#run()
|
|
|
|
*/
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
Data_2da data = Data_2da.load2da(pathToLoad, true);
|
|
|
|
list.add(data);
|
|
|
|
latch.countDown();
|
|
|
|
} catch (Exception e) {
|
|
|
|
err_pr.println("Error: Failure while reading main 2das. Exception data:\n");
|
|
|
|
err_pr.printException(e);
|
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private final HashMap<String, Data_2da> data = new HashMap<String, Data_2da>();
|
|
|
|
private final String twoDAPath;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new TwoDAStore, without preloading anything.
|
|
|
|
*
|
|
|
|
* @param twoDAPath Path of the directory containing 2da files.
|
|
|
|
*/
|
|
|
|
public TwoDAStore(String twoDAPath) {
|
|
|
|
this.twoDAPath = twoDAPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a new TwoDAStore with all the main 2das preread in.
|
|
|
|
* On a read failure, kills program execution, since there's nothing
|
|
|
|
* that could be done anyway.
|
|
|
|
*/
|
|
|
|
public TwoDAStore() {
|
2024-06-21 19:37:17 -05:00
|
|
|
this("../../trunk/2das");
|
2023-08-22 10:00:21 -04:00
|
|
|
//long start = System.currentTimeMillis();
|
|
|
|
if (verbose) System.out.print("Loading main 2da files ");
|
2024-06-21 19:37:17 -05:00
|
|
|
CountDownLatch latch = new CountDownLatch(7);
|
|
|
|
List<Data_2da> list = Collections.synchronizedList(new ArrayList<Data_2da>());
|
|
|
|
ArrayList<Thread> threads = new ArrayList<>();
|
|
|
|
|
|
|
|
var baseDirectory = "../../trunk"; // @TODO: Move to a Configuration File
|
|
|
|
var folders = getFoldersInFolder(baseDirectory);
|
2024-06-23 21:55:37 -05:00
|
|
|
folders.add("base_nwn_files"); // this is where we store the base nwn 2da files
|
2024-06-21 19:37:17 -05:00
|
|
|
for (var folder : folders) {
|
|
|
|
if (folder.endsWith("2das")) {
|
|
|
|
var files = getFilesInFolder(folder);
|
|
|
|
for (var file : files) {
|
|
|
|
var thread = new Thread(new Loader(file, list, latch));
|
|
|
|
threads.add(thread);
|
|
|
|
thread.start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-22 10:00:21 -04:00
|
|
|
boolean oldVerbose = verbose;
|
|
|
|
verbose = false;
|
|
|
|
|
2024-06-21 19:37:17 -05:00
|
|
|
|
|
|
|
// // Read the main 2das
|
|
|
|
// new Thread(new Loader("2da" + fileSeparator + "classes.2da", list, latch)).start();
|
|
|
|
// new Thread(new Loader("2da" + fileSeparator + "domains.2da", list, latch)).start();
|
|
|
|
// new Thread(new Loader("2da" + fileSeparator + "feat.2da", list, latch)).start();
|
|
|
|
// new Thread(new Loader("2da" + fileSeparator + "masterfeats.2da", list, latch)).start();
|
|
|
|
// new Thread(new Loader("2da" + fileSeparator + "racialtypes.2da", list, latch)).start();
|
|
|
|
// new Thread(new Loader("2da" + fileSeparator + "skills.2da", list, latch)).start();
|
|
|
|
// new Thread(new Loader("2da" + fileSeparator + "spells.2da", list, latch)).start();
|
2023-08-22 10:00:21 -04:00
|
|
|
|
|
|
|
try {
|
2024-06-21 19:37:17 -05:00
|
|
|
for (Thread thread : threads) {
|
|
|
|
thread.join();
|
|
|
|
}
|
2023-08-22 10:00:21 -04:00
|
|
|
} catch (InterruptedException e) {
|
|
|
|
err_pr.println("Error: Interrupted while reading main 2das. Exception data:\n");
|
|
|
|
err_pr.printException(e);
|
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (Data_2da entry : list)
|
|
|
|
data.put(entry.getName(), entry);
|
|
|
|
verbose = oldVerbose;
|
|
|
|
if (verbose) System.out.println("- Done");
|
|
|
|
/*
|
|
|
|
try{
|
|
|
|
data.put("classes", new Data_2da("2da" + fileSeparator + "classes.2da"));
|
|
|
|
data.put("domains", new Data_2da("2da" + fileSeparator + "domains.2da"));
|
|
|
|
data.put("feat", new Data_2da("2da" + fileSeparator + "feat.2da"));
|
|
|
|
data.put("masterfeats", new Data_2da("2da" + fileSeparator + "masterfeats.2da"));
|
|
|
|
data.put("racialtypes", new Data_2da("2da" + fileSeparator + "racialtypes.2da"));
|
|
|
|
data.put("skills", new Data_2da("2da" + fileSeparator + "skills.2da"));
|
|
|
|
data.put("spells", new Data_2da("2da" + fileSeparator + "spells.2da"));
|
|
|
|
}catch(Exception e){
|
|
|
|
err_pr.println("Error: Failure while reading main 2das. Exception data:\n");
|
|
|
|
err_pr.printException(e);
|
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
//System.out.println("Time taken: " + (System.currentTimeMillis() - start));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a Data_2da structure wrapping the given 2da. If it hasn't been loaded
|
|
|
|
* yet, the loading is done now.
|
|
|
|
*
|
|
|
|
* @param name name of the 2da to get. Without the file end ".2da".
|
|
|
|
* @return a Data_2da structure
|
|
|
|
* @throws TwoDAReadException if any errors are encountered while reading
|
|
|
|
*/
|
|
|
|
public Data_2da get(String name) {
|
|
|
|
if (data.containsKey(name))
|
|
|
|
return data.get(name);
|
|
|
|
else {
|
|
|
|
Data_2da temp = null;
|
|
|
|
try {
|
2024-06-21 19:37:17 -05:00
|
|
|
var basePath = "../../trunk";
|
|
|
|
var potentialPaths = getFoldersInFolder(basePath);
|
2024-06-23 21:55:37 -05:00
|
|
|
potentialPaths.add("base_nwn_files"); // this is where we store the base nwn 2da files
|
2024-06-21 19:37:17 -05:00
|
|
|
for (var folder : potentialPaths) {
|
|
|
|
var file = new File(Paths.get(folder, name + ".2da").toAbsolutePath().toString());
|
|
|
|
if (file.exists()) {
|
|
|
|
temp = Data_2da.load2da(file.getAbsolutePath().toString(), true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (temp == null) {
|
|
|
|
throw new TwoDAReadException("File not found\n" + name);
|
|
|
|
}
|
|
|
|
|
2023-08-22 10:00:21 -04:00
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
throw new TwoDAReadException("Problem with filename when trying to read from 2da:\n" + e);
|
|
|
|
}
|
|
|
|
data.put(name, temp);
|
|
|
|
return temp;
|
|
|
|
}
|
|
|
|
}
|
2024-06-23 21:55:37 -05:00
|
|
|
|
|
|
|
public HashMap<String, Data_2da> findAll(String name) {
|
|
|
|
HashMap<String, Data_2da> copy = new HashMap<String, Data_2da>(data);
|
|
|
|
copy.keySet().removeIf(key -> !key.contains(name));
|
|
|
|
return copy;
|
|
|
|
}
|
2023-08-22 10:00:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A class for handling the settings file.
|
|
|
|
*/
|
|
|
|
public static class Settings {
|
|
|
|
/* Some pattern matchers for use when parsing the settings file */
|
|
|
|
private final Matcher mainMatch = Pattern.compile("\\S+:").matcher("");
|
|
|
|
private final Matcher paraMatch = Pattern.compile("\"[^\"]+\"").matcher("");
|
|
|
|
private final Matcher langMatch = Pattern.compile("\\w+=\"[^\"]+\"").matcher("");
|
|
|
|
|
|
|
|
/* An enumeration of the possible setting types */
|
|
|
|
private enum Modes {
|
|
|
|
/**
|
|
|
|
* The parser is currently working on lines specifying languages used.
|
|
|
|
*/
|
|
|
|
LANGUAGE,
|
|
|
|
/**
|
|
|
|
* The parser is currently working on lines containing string patterns that are
|
|
|
|
* used in differentiating between entries in spells.2da.
|
|
|
|
*/
|
|
|
|
SIGNATURE,
|
|
|
|
/**
|
|
|
|
* The parser is currently working on lines listing spells.2da entries that contain
|
|
|
|
* a significantly modified BW spell.
|
|
|
|
*/
|
|
|
|
MODIFIED_SPELL
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Settings data read in */
|
|
|
|
/**
|
|
|
|
* The settings for languages. An ArrayList of String[] containing setting for a specific language
|
|
|
|
*/
|
|
|
|
public ArrayList<String[]> languages = new ArrayList<String[]>();
|
|
|
|
/**
|
|
|
|
* An ArrayList of Integers. Indices to spells.2da of standard spells modified by the PRC
|
|
|
|
*/
|
|
|
|
public ArrayList<Integer> modifiedSpells = new ArrayList<Integer>();
|
|
|
|
/**
|
|
|
|
* A set of script name prefixes used to find epic spell entries in spells.2da
|
|
|
|
*/
|
|
|
|
public String[] epicspellSignatures = null;
|
|
|
|
/*/** A set of script name prefixes used to find psionic power entries in spells.2da *
|
|
|
|
public String[] psionicpowerSignatures = null;*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read the settings file in and store the data for later access.
|
|
|
|
* Terminates execution on any errors.
|
|
|
|
*/
|
|
|
|
public Settings() {
|
|
|
|
try {
|
|
|
|
// The settings file should be present in the directory this is run from
|
|
|
|
Scanner reader = new Scanner(new File("settings"));
|
|
|
|
String check;
|
|
|
|
Modes mode = null;
|
|
|
|
while (reader.hasNextLine()) {
|
|
|
|
check = reader.nextLine();
|
|
|
|
|
|
|
|
// Skip comments and blank lines
|
|
|
|
if (check.startsWith("#") || check.trim().equals("")) continue;
|
|
|
|
|
|
|
|
// Check if a new rule is starting
|
|
|
|
mainMatch.reset(check);
|
|
|
|
if (mainMatch.find()) {
|
|
|
|
if (mainMatch.group().equals("language:")) mode = Modes.LANGUAGE;
|
|
|
|
else if (mainMatch.group().equals("signature:")) mode = Modes.SIGNATURE;
|
|
|
|
else if (mainMatch.group().equals("modified_spell:")) mode = Modes.MODIFIED_SPELL;
|
|
|
|
else {
|
|
|
|
throw new Exception("Unrecognized setting detected");
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Take action based on current mode
|
|
|
|
if (mode == Modes.LANGUAGE) {
|
|
|
|
String[] temp = new String[LANGDATA_NUMENTRIES];
|
|
|
|
String result;
|
|
|
|
langMatch.reset(check);
|
|
|
|
// parse the language entry
|
|
|
|
for (int i = 0; i < LANGDATA_NUMENTRIES; i++) {
|
|
|
|
if (!langMatch.find())
|
|
|
|
throw new Exception("Missing language parameter");
|
|
|
|
result = langMatch.group();
|
|
|
|
|
|
|
|
if (result.startsWith("name=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_LANGNAME] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("base=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_BASETLK] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("prc=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_PRCTLK] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("feats=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_FEATSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("allfeats=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_ALLFEATSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("epicfeats=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_EPICFEATSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("allepicfeats=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_ALLEPICFEATSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("baseclasses=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_BASECLASSESTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("prestigeclasses=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_PRESTIGECLASSESTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("spells=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_SPELLSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("epicspells=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_EPICSPELLSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("psipowers=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_PSIONICPOWERSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("modspells=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_MODIFIEDSPELLSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("skills=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_SKILLSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("domains=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_DOMAINSTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("races=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_RACESTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("spellbook=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_SPELLBOOKTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("powers=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_POWERTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("truenameutterances=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_TRUENAMEUTTERANCETXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("invocations=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_INVOCATIONTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("maneuvers=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_MANEUVERTXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else if (result.startsWith("utterances=")) {
|
|
|
|
paraMatch.reset(result);
|
|
|
|
paraMatch.find();
|
|
|
|
temp[LANGDATA_UTTERANCETXT] = paraMatch.group().substring(1, paraMatch.group().length() - 1);
|
|
|
|
} else
|
|
|
|
throw new Exception("Unknown language parameter encountered\n" + check);
|
|
|
|
}
|
|
|
|
languages.add(temp);
|
|
|
|
}
|
|
|
|
// Parse the spell script name signatures
|
|
|
|
if (mode == Modes.SIGNATURE) {
|
|
|
|
String[] temp = check.trim().split("=");
|
|
|
|
if (temp[0].equals("epicspell")) {
|
|
|
|
epicspellSignatures = temp[1].replace("\"", "").split("\\|");
|
|
|
|
}/* Not needed anymore
|
|
|
|
else if(temp[0].equals("psionicpower")){
|
|
|
|
psionicpowerSignatures = temp[1].replace("\"", "").split("\\|");
|
|
|
|
}*/ else
|
|
|
|
throw new Exception("Unknown signature parameter encountered:\n" + check);
|
|
|
|
}
|
|
|
|
// Parse the spell modified spell indices
|
|
|
|
if (mode == Modes.MODIFIED_SPELL) {
|
|
|
|
modifiedSpells.add(Integer.parseInt(check.trim()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception e) {
|
|
|
|
err_pr.println("Error: Failed to read settings file:\n" + e + "\nAborting");
|
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A small enumeration for use in spell printing methods
|
|
|
|
*/
|
|
|
|
public enum SpellType {
|
|
|
|
/**
|
|
|
|
* The spell is not a real spell or psionic power, instead specifies some feat's spellscript.
|
|
|
|
*/
|
|
|
|
NONE,
|
|
|
|
/**
|
|
|
|
* The spell is a normal spell.
|
|
|
|
*/
|
|
|
|
NORMAL,
|
|
|
|
/**
|
|
|
|
* The spell is an epic spell.
|
|
|
|
*/
|
|
|
|
EPIC,
|
|
|
|
/**
|
|
|
|
* The spell is a psionic power.
|
|
|
|
*/
|
|
|
|
PSIONIC,
|
|
|
|
/**
|
|
|
|
* The spell is a truename utterance.
|
|
|
|
*/
|
|
|
|
UTTERANCE,
|
|
|
|
/**
|
|
|
|
* The spell is an invocation.
|
|
|
|
*/
|
|
|
|
INVOCATION,
|
|
|
|
/**
|
|
|
|
* The spell is a maneuver.
|
|
|
|
*/
|
|
|
|
MANEUVER
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A switche determinining how errors are handled
|
|
|
|
*/
|
|
|
|
public static boolean tolErr = true;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A boolean determining whether to print icons for the pages or not
|
|
|
|
*/
|
|
|
|
public static boolean icons = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A constant signifying Bad StrRef
|
|
|
|
*/
|
|
|
|
public static final String badStrRef = "Bad StrRef";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The container object for general configuration data read from file
|
|
|
|
*/
|
|
|
|
public static Settings settings;// = new Settings();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The file separator, given it's own constant for ease of use
|
|
|
|
*/
|
|
|
|
public static final String fileSeparator = System.getProperty("file.separator");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of the settings for currently used language. Index with the LANGDATA_ constants
|
|
|
|
*/
|
|
|
|
public static String[] curLanguageData = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Size of the curLanguageData array
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_NUMENTRIES = 22;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the language name
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_LANGNAME = 0;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the dialog.tlk equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_BASETLK = 1;
|
|
|
|
/**
|
2024-06-21 19:37:17 -05:00
|
|
|
* curLanguageData index of the name of the prc8_consortium.tlk equivalent for this language
|
2023-08-22 10:00:21 -04:00
|
|
|
*/
|
|
|
|
public static final int LANGDATA_PRCTLK = 2;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "All Feats" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_ALLFEATSTXT = 3;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "All Epic Feats" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_ALLEPICFEATSTXT = 4;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Feats" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_FEATSTXT = 5;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Epic Feats" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_EPICFEATSTXT = 6;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Base Classes" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_BASECLASSESTXT = 7;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Prestige Classes" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_PRESTIGECLASSESTXT = 8;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Spells" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_SPELLSTXT = 9;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Epic Spells" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_EPICSPELLSTXT = 10;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Psionic Powers" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_PSIONICPOWERSTXT = 11;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Modified Spells" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_MODIFIEDSPELLSTXT = 12;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Domains" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_DOMAINSTXT = 13;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Skills" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_SKILLSTXT = 14;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Races" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_RACESTXT = 15;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Spellbook" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_SPELLBOOKTXT = 16;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Powers" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_POWERTXT = 17;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Truename Utterances" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_TRUENAMEUTTERANCETXT = 18;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Invocations" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_INVOCATIONTXT = 19;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Maneuvers" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_MANEUVERTXT = 20;
|
|
|
|
/**
|
|
|
|
* curLanguageData index of the name of the "Utterances" string equivalent for this language
|
|
|
|
*/
|
|
|
|
public static final int LANGDATA_UTTERANCETXT = 21;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Current language name
|
|
|
|
*/
|
|
|
|
public static String curLanguage = null;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The base path. <code>"manual" + fileSeparator + curLanguage + fileSeparator</code>
|
|
|
|
*/
|
|
|
|
public static String mainPath = null;
|
|
|
|
/**
|
|
|
|
* The path to content directory. <code>mainPath + "content" + fileSeparator</code>
|
|
|
|
*/
|
|
|
|
public static String contentPath = null;
|
|
|
|
/**
|
|
|
|
* The path to menu directory. <code>mainPath + "mainPath" + fileSeparator</code>
|
|
|
|
*/
|
|
|
|
public static String menuPath = null;
|
|
|
|
/**
|
|
|
|
* The path to the image directory. <code>"manual" + fileSeparator + "images" + fileSeparator</code>
|
|
|
|
*/
|
|
|
|
public static String imagePath = "manual" + fileSeparator + "images" + fileSeparator;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data structures for accessing TLKs
|
|
|
|
*/
|
|
|
|
public static TwoDAStore twoDA;
|
|
|
|
/**
|
|
|
|
* Data structures for accessing TLKs
|
|
|
|
*/
|
|
|
|
public static TLKStore tlk;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The template files
|
|
|
|
*/
|
|
|
|
public static String babAndSavthrTableHeaderTemplate = null,
|
|
|
|
classTemplate = null,
|
|
|
|
classTablesEntryTemplate = null,
|
|
|
|
domainTemplate = null,
|
|
|
|
featTemplate = null,
|
|
|
|
mFeatTemplate = null,
|
|
|
|
menuTemplate = null,
|
|
|
|
menuItemTemplate = null,
|
|
|
|
prereqANDFeatHeaderTemplate = null,
|
|
|
|
prereqORFeatHeaderTemplate = null,
|
|
|
|
raceTemplate = null,
|
|
|
|
spellTemplate = null,
|
|
|
|
skillTableHeaderTemplate = null,
|
|
|
|
skillTemplate = null,
|
|
|
|
successorFeatHeaderTemplate = null,
|
|
|
|
iconTemplate = null,
|
|
|
|
listEntrySetTemplate = null,
|
|
|
|
listEntryTemplate = null,
|
|
|
|
alphaSortedListTemplate = null,
|
|
|
|
requiredForFeatHeaderTemplate = null,
|
|
|
|
pageLinkTemplate = null,
|
|
|
|
featMenuTemplate = null,
|
|
|
|
spellSubradialListTemplate = null,
|
|
|
|
spellSubradialListEntryTemplate = null,
|
|
|
|
classFeatTableTemplate = null,
|
|
|
|
classFeatTableEntryTemplate = null,
|
|
|
|
classMagicTableTemplate = null,
|
|
|
|
classMagicTableEntryTemplate = null,
|
|
|
|
craftTemplate = null;
|
|
|
|
|
|
|
|
|
|
|
|
/* Data structures to store generated entry data in */
|
|
|
|
public static HashMap<Integer, SpellEntry> spells;
|
|
|
|
public static HashMap<Integer, FeatEntry> masterFeats,
|
|
|
|
feats;
|
|
|
|
public static HashMap<Integer, ClassEntry> classes;
|
|
|
|
public static HashMap<Integer, DomainEntry> domains;
|
|
|
|
public static HashMap<Integer, RaceEntry> races;
|
|
|
|
public static HashMap<Integer, GenericEntry> skills;
|
|
|
|
|
|
|
|
public static HashMap<Integer, GenericEntry> craft_armour;
|
|
|
|
public static HashMap<Integer, GenericEntry> craft_weapon;
|
|
|
|
public static HashMap<Integer, GenericEntry> craft_ring;
|
|
|
|
public static HashMap<Integer, GenericEntry> craft_wondrous;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map of psionic power names to the indexes of the spells.2da entries chosen to represent the power in question
|
|
|
|
*/
|
|
|
|
public static HashMap<String, Integer> psiPowMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map of truenaming utterance names to the spells.2da indexes that contain utterance feat-linked entries
|
|
|
|
*/
|
|
|
|
public static HashMap<String, Integer> utterMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map of invocations to spells.2da
|
|
|
|
*/
|
|
|
|
public static HashMap<String, Integer> invMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map of maneuvers to spells.2da
|
|
|
|
*/
|
|
|
|
public static HashMap<String, Integer> maneuverMap;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ye olde maine methode
|
|
|
|
*
|
|
|
|
* @param args
|
|
|
|
*/
|
|
|
|
public static void main(String[] args) {
|
|
|
|
/* Argument parsing */
|
|
|
|
for (String opt : args) {
|
|
|
|
if (opt.equals("--help"))
|
|
|
|
readMe();
|
|
|
|
|
|
|
|
if (opt.startsWith("-")) {
|
|
|
|
if (opt.contains("a"))
|
|
|
|
tolErr = true;
|
|
|
|
if (opt.contains("q")) {
|
|
|
|
verbose = false;
|
|
|
|
spinner.disable();
|
|
|
|
}
|
|
|
|
if (opt.contains("i"))
|
|
|
|
icons = true;
|
|
|
|
if (opt.contains("s"))
|
|
|
|
spinner.disable();
|
|
|
|
if (opt.contains("?"))
|
|
|
|
readMe();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the settings
|
|
|
|
settings = new Settings();
|
|
|
|
|
|
|
|
// Initialize the 2da container data structure
|
|
|
|
twoDA = new TwoDAStore();
|
|
|
|
|
|
|
|
|
|
|
|
// Print the manual files for each language specified
|
|
|
|
for (int i = 0; i < settings.languages.size(); i++) {
|
|
|
|
// Set language, path and load TLKs
|
|
|
|
curLanguageData = settings.languages.get(i);
|
|
|
|
curLanguage = curLanguageData[LANGDATA_LANGNAME];
|
|
|
|
mainPath = "manual" + fileSeparator + curLanguage + fileSeparator;
|
|
|
|
contentPath = mainPath + "content" + fileSeparator;
|
|
|
|
menuPath = mainPath + "menus" + fileSeparator;
|
|
|
|
|
|
|
|
// If we fail on a language, skip to next one
|
|
|
|
try {
|
|
|
|
tlk = new TLKStore(curLanguageData[LANGDATA_BASETLK], curLanguageData[LANGDATA_PRCTLK]);
|
|
|
|
} catch (TLKReadException e) {
|
|
|
|
err_pr.println("Error: Failure while reading TLKs for language: " + curLanguage + ":\n" + e);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip to next if there is any problem with directories or templates
|
|
|
|
if (!(readTemplates() && buildDirectories())) continue;
|
|
|
|
|
|
|
|
// Do the actual work
|
2024-06-23 21:55:37 -05:00
|
|
|
createPages(twoDA);
|
2023-08-22 10:00:21 -04:00
|
|
|
createMenus();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for the image conversion to finish before exiting main
|
|
|
|
if (Icons.executor != null) {
|
|
|
|
Icons.executor.shutdown();
|
|
|
|
try {
|
|
|
|
Icons.executor.awaitTermination(120, TimeUnit.SECONDS);
|
|
|
|
} catch (InterruptedException e) {
|
|
|
|
err_pr.println("Error: Interrupted while waiting for image conversion to finish");
|
|
|
|
} finally {
|
|
|
|
System.exit(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prints the use instructions for this program and kills execution.
|
|
|
|
*/
|
|
|
|
private static void readMe() {
|
|
|
|
System.out.println("Usage:\n" +
|
|
|
|
" java prc/autodoc/Main [--help][-aiqs?]\n" +
|
|
|
|
"\n" +
|
|
|
|
"-a forces aborting printing on errors\n" +
|
|
|
|
"-i adds icons to pages\n" +
|
|
|
|
"-q quiet mode. Does not print any progress info, only failure messages\n" +
|
|
|
|
"-s disable the spinner. Useful when logging to file\n" +
|
|
|
|
"\n" +
|
|
|
|
"--help prints this info you are reading\n" +
|
|
|
|
"-? see --help\n"
|
|
|
|
);
|
|
|
|
System.exit(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads all the template files for the current language.
|
|
|
|
*
|
|
|
|
* @return <code>true</code> if all the reads succeeded, <code>false</code> otherwise
|
|
|
|
*/
|
|
|
|
private static boolean readTemplates() {
|
2024-06-21 19:37:17 -05:00
|
|
|
String templatePath = Paths.get("templates", curLanguage).toAbsolutePath().toString();
|
2023-08-22 10:00:21 -04:00
|
|
|
|
|
|
|
try {
|
2024-06-21 19:37:17 -05:00
|
|
|
babAndSavthrTableHeaderTemplate = readTemplate(Paths.get(templatePath, "babNsavthrtableheader.html").toAbsolutePath().toString());
|
|
|
|
classTablesEntryTemplate = readTemplate(Paths.get(templatePath, "classtablesentry.html").toAbsolutePath().toString());
|
|
|
|
classTemplate = readTemplate(Paths.get(templatePath, "class.html").toAbsolutePath().toString());
|
|
|
|
domainTemplate = readTemplate(Paths.get(templatePath, "domain.html").toAbsolutePath().toString());
|
|
|
|
featTemplate = readTemplate(Paths.get(templatePath, "feat.html").toAbsolutePath().toString());
|
|
|
|
mFeatTemplate = readTemplate(Paths.get(templatePath, "masterfeat.html").toAbsolutePath().toString());
|
|
|
|
menuTemplate = readTemplate(Paths.get(templatePath, "menu.html").toAbsolutePath().toString());
|
|
|
|
menuItemTemplate = readTemplate(Paths.get(templatePath, "menuitem.html").toAbsolutePath().toString());
|
|
|
|
prereqANDFeatHeaderTemplate = readTemplate(Paths.get(templatePath, "prerequisiteandfeatheader.html").toAbsolutePath().toString());
|
|
|
|
prereqORFeatHeaderTemplate = readTemplate(Paths.get(templatePath, "prerequisiteorfeatheader.html").toAbsolutePath().toString());
|
|
|
|
raceTemplate = readTemplate(Paths.get(templatePath, "race.html").toAbsolutePath().toString());
|
|
|
|
spellTemplate = readTemplate(Paths.get(templatePath, "spell.html").toAbsolutePath().toString());
|
|
|
|
skillTableHeaderTemplate = readTemplate(Paths.get(templatePath, "skilltableheader.html").toAbsolutePath().toString());
|
|
|
|
skillTemplate = readTemplate(Paths.get(templatePath, "skill.html").toAbsolutePath().toString());
|
|
|
|
successorFeatHeaderTemplate = readTemplate(Paths.get(templatePath, "successorfeatheader.html").toAbsolutePath().toString());
|
|
|
|
iconTemplate = readTemplate(Paths.get(templatePath, "icon.html").toAbsolutePath().toString());
|
|
|
|
listEntrySetTemplate = readTemplate(Paths.get(templatePath, "listpageentryset.html").toAbsolutePath().toString());
|
|
|
|
listEntryTemplate = readTemplate(Paths.get(templatePath, "listpageentry.html").toAbsolutePath().toString());
|
|
|
|
alphaSortedListTemplate = readTemplate(Paths.get(templatePath, "alphasorted_listpage.html").toAbsolutePath().toString());
|
|
|
|
requiredForFeatHeaderTemplate = readTemplate(Paths.get(templatePath, "reqforfeatheader.html").toAbsolutePath().toString());
|
|
|
|
pageLinkTemplate = readTemplate(Paths.get(templatePath, "pagelink.html").toAbsolutePath().toString());
|
|
|
|
featMenuTemplate = readTemplate(Paths.get(templatePath, "featmenu.html").toAbsolutePath().toString());
|
|
|
|
spellSubradialListTemplate = readTemplate(Paths.get(templatePath, "spellsubradials.html").toAbsolutePath().toString());
|
|
|
|
spellSubradialListEntryTemplate = readTemplate(Paths.get(templatePath, "spellsubradialsentry.html").toAbsolutePath().toString());
|
|
|
|
classFeatTableTemplate = readTemplate(Paths.get(templatePath, "classfeattable.html").toAbsolutePath().toString());
|
|
|
|
classFeatTableEntryTemplate = readTemplate(Paths.get(templatePath, "classfeattableentry.html").toAbsolutePath().toString());
|
|
|
|
classMagicTableTemplate = readTemplate(Paths.get(templatePath, "classmagictable.html").toAbsolutePath().toString());
|
|
|
|
classMagicTableEntryTemplate = readTemplate(Paths.get(templatePath, "classmagictableentry.html").toAbsolutePath().toString());
|
|
|
|
craftTemplate = readTemplate(Paths.get(templatePath, "craftprop.html").toAbsolutePath().toString());
|
2023-08-22 10:00:21 -04:00
|
|
|
} catch (IOException e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reads the template file given as parameter and returns a string with it's contents
|
|
|
|
* Kills execution if any operations fail.
|
|
|
|
*
|
|
|
|
* @param filePath string representing the path of the template file
|
|
|
|
* @return the contents of the template file as a string
|
|
|
|
* @throws IOException if the reading fails
|
|
|
|
*/
|
|
|
|
private static String readTemplate(String filePath) throws IOException {
|
|
|
|
try {
|
|
|
|
Scanner reader = new Scanner(new File(filePath));
|
|
|
|
StringBuffer temp = new StringBuffer();
|
|
|
|
|
|
|
|
while (reader.hasNextLine()) temp.append(reader.nextLine() + "\n");
|
|
|
|
|
|
|
|
return temp.toString();
|
|
|
|
} catch (Exception e) {
|
|
|
|
err_pr.println("Error: Failed to read template file:\n" + e);
|
|
|
|
throw new IOException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the directory structure for the current language
|
|
|
|
* being processed.
|
|
|
|
*
|
|
|
|
* @return <code>true</code> if all directories are successfully created,
|
|
|
|
* <code>false</code> otherwise
|
|
|
|
*/
|
|
|
|
private static boolean buildDirectories() {
|
|
|
|
String dirPath = mainPath + "content";
|
|
|
|
|
|
|
|
boolean toReturn = buildDir(dirPath);
|
|
|
|
dirPath += fileSeparator;
|
|
|
|
|
|
|
|
toReturn = toReturn
|
|
|
|
&& buildDir(dirPath + "base_classes")
|
|
|
|
&& buildDir(dirPath + "class_epic_feats")
|
|
|
|
&& buildDir(dirPath + "class_feats")
|
|
|
|
&& buildDir(dirPath + "domains")
|
|
|
|
&& buildDir(dirPath + "epic_feats")
|
|
|
|
&& buildDir(dirPath + "epic_spells")
|
|
|
|
&& buildDir(dirPath + "feats")
|
|
|
|
&& buildDir(dirPath + "itemcrafting")
|
|
|
|
&& buildDir(dirPath + "master_feats")
|
|
|
|
&& buildDir(dirPath + "prestige_classes")
|
|
|
|
&& buildDir(dirPath + "psionic_powers")
|
|
|
|
&& buildDir(dirPath + "races")
|
|
|
|
&& buildDir(dirPath + "skills")
|
|
|
|
&& buildDir(dirPath + "spells")
|
|
|
|
&& buildDir(dirPath + "utterances")
|
|
|
|
&& buildDir(dirPath + "invocations")
|
|
|
|
&& buildDir(dirPath + "maneuvers")
|
|
|
|
|
|
|
|
&& buildDir(mainPath + "menus");
|
|
|
|
|
|
|
|
System.gc();
|
|
|
|
|
|
|
|
return toReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Does the actual work of building the directories
|
|
|
|
*
|
|
|
|
* @param path the target directory to create
|
|
|
|
* @return <code>true</code> if the directory was already present or was successfully created,
|
|
|
|
* <code>false</code> otherwise
|
|
|
|
*/
|
|
|
|
private static boolean buildDir(String path) {
|
|
|
|
File builder = new File(path);
|
|
|
|
if (!builder.exists()) {
|
|
|
|
if (!builder.mkdirs()) {
|
|
|
|
err_pr.println("Error: Failure creating directory:\n" + builder.getPath());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!builder.isDirectory()) {
|
|
|
|
err_pr.println(builder.getPath() + " already exists as a file!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Replaces each line break in the given TLK entry with
|
|
|
|
* a line break followed by <code><br /></code>.
|
|
|
|
*
|
|
|
|
* @param toHTML tlk entry to convert
|
|
|
|
* @return the modified string
|
|
|
|
*/
|
|
|
|
public static String htmlizeTLK(String toHTML) {
|
|
|
|
return toHTML.replaceAll("\n", "\n<br />");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new file at the given <code>path</code>, erasing previous file if present.
|
|
|
|
* Prints the given <code>content</code> string into the file.
|
|
|
|
*
|
|
|
|
* @param path the path of the file to be created
|
|
|
|
* @param content the string to be printed into the file
|
|
|
|
* @throws PageGenerationException if one of the file operations fails
|
|
|
|
*/
|
|
|
|
public static void printPage(String path, String content) {
|
|
|
|
try {
|
|
|
|
File target = new File(path);
|
|
|
|
// Clean up old version if necessary
|
|
|
|
if (target.exists()) {
|
|
|
|
if (verbose) System.out.println("Deleting previous version of " + path);
|
|
|
|
target.delete();
|
|
|
|
}
|
|
|
|
target.createNewFile();
|
|
|
|
|
|
|
|
// Creater the writer and print
|
|
|
|
FileWriter writer = new FileWriter(target, false);
|
|
|
|
writer.write(content);
|
|
|
|
// Clean up
|
|
|
|
writer.flush();
|
|
|
|
writer.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
throw new PageGenerationException("IOException when printing " + path, e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Page creation. Calls all the specific functions for different page types
|
|
|
|
*/
|
2024-06-23 21:55:37 -05:00
|
|
|
private static void createPages(TwoDAStore twoDA) {
|
2023-08-22 10:00:21 -04:00
|
|
|
/* First, do the pages that do not require linking to other pages */
|
|
|
|
doSkills();
|
|
|
|
doCrafting();
|
2024-06-23 21:55:37 -05:00
|
|
|
listPsionicPowers(twoDA);
|
|
|
|
listTruenameUtterances(twoDA);
|
|
|
|
listInvocations(twoDA);
|
|
|
|
listManeuvers(twoDA);
|
2023-08-22 10:00:21 -04:00
|
|
|
doSpells();
|
|
|
|
|
|
|
|
/* Then, build the feats */
|
|
|
|
preliMasterFeats();
|
|
|
|
preliFeats();
|
|
|
|
linkFeats();
|
|
|
|
|
|
|
|
/* Last, domains, races and classes, which all link to the previous */
|
|
|
|
doDomains();
|
|
|
|
doRaces();
|
|
|
|
doClasses();
|
|
|
|
|
|
|
|
/* Then, print all of it */
|
|
|
|
printSkills();
|
|
|
|
printSpells();
|
|
|
|
printFeats();
|
|
|
|
printDomains();
|
|
|
|
printRaces();
|
|
|
|
printClasses();
|
|
|
|
printCrafting();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Menu creation. Calls the specific functions for different menu types
|
|
|
|
*/
|
|
|
|
private static void createMenus() {
|
|
|
|
/* First, the types that do not need any extra data beyond name & path
|
|
|
|
* and use GenericEntry
|
|
|
|
*/
|
|
|
|
doGenericMenu(skills, curLanguageData[LANGDATA_SKILLSTXT], "manual_menus_skills.html");
|
|
|
|
doGenericMenu(domains, curLanguageData[LANGDATA_DOMAINSTXT], "manual_menus_domains.html");
|
|
|
|
doGenericMenu(races, curLanguageData[LANGDATA_RACESTXT], "manual_menus_races.html");
|
|
|
|
doGenericMenu(craft_armour, curLanguageData[LANGDATA_SKILLSTXT], "manual_menus_craft_armour.html");
|
|
|
|
doGenericMenu(craft_weapon, curLanguageData[LANGDATA_SKILLSTXT], "manual_menus_craft_weapon.html");
|
|
|
|
doGenericMenu(craft_ring, curLanguageData[LANGDATA_SKILLSTXT], "manual_menus_craft_ring.html");
|
|
|
|
doGenericMenu(craft_wondrous, curLanguageData[LANGDATA_SKILLSTXT], "manual_menus_craft_wondrous.html");
|
|
|
|
/* Then the more specialised data where it needs to be split over several
|
|
|
|
* menu pages
|
|
|
|
*/
|
|
|
|
doSpellMenus();
|
|
|
|
doFeatMenus();
|
|
|
|
doClassMenus();
|
|
|
|
}
|
|
|
|
}
|