152 lines
5.2 KiB
Java
152 lines
5.2 KiB
Java
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|||
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||
|
|
// Magicbane Emulator Project © 2013 - 2026
|
||
|
|
// https://www.magicbane.com
|
||
|
|
|
||
|
|
package engine.parsers.wpak;
|
||
|
|
|
||
|
|
import engine.gameManager.ConfigManager;
|
||
|
|
|
||
|
|
import java.io.IOException;
|
||
|
|
import java.nio.charset.StandardCharsets;
|
||
|
|
import java.nio.file.Files;
|
||
|
|
import java.nio.file.Paths;
|
||
|
|
import java.util.ArrayDeque;
|
||
|
|
import java.util.ArrayList;
|
||
|
|
import java.util.List;
|
||
|
|
|
||
|
|
// Reads the client's wpak cfg files (an mbEditorPro export of Config.wpak) into
|
||
|
|
// tokenized lines and XxxBEGIN/XxxEND blocks. Each parser maps the tokens itself.
|
||
|
|
|
||
|
|
public class WpakFileReader {
|
||
|
|
|
||
|
|
public static final String wpakPath = ConfigManager.DEFAULT_DATA_DIR + "wpak/";
|
||
|
|
|
||
|
|
// Lines starting with '#' or '*' are comments (PowerActions_LoadCfg 0x578EC0).
|
||
|
|
// A '#' outside quotes ends the line, as in PowActionCostInfo.cfg.
|
||
|
|
|
||
|
|
public static ArrayList<WpakLine> readLines(String fileName) throws IOException {
|
||
|
|
|
||
|
|
ArrayList<WpakLine> lines = new ArrayList<>();
|
||
|
|
List<String> fileLines = Files.readAllLines(Paths.get(wpakPath + fileName), StandardCharsets.ISO_8859_1);
|
||
|
|
String text;
|
||
|
|
String trimmed;
|
||
|
|
ArrayList<String> tokens;
|
||
|
|
|
||
|
|
for (int i = 0; i < fileLines.size(); i++) {
|
||
|
|
text = fileLines.get(i);
|
||
|
|
trimmed = text.trim();
|
||
|
|
|
||
|
|
if (trimmed.isEmpty() || trimmed.charAt(0) == '#' || trimmed.charAt(0) == '*')
|
||
|
|
continue;
|
||
|
|
|
||
|
|
tokens = tokenize(fileName, i + 1, text);
|
||
|
|
|
||
|
|
if (!tokens.isEmpty())
|
||
|
|
lines.add(new WpakLine(fileName, i + 1, text, tokens));
|
||
|
|
}
|
||
|
|
|
||
|
|
return lines;
|
||
|
|
}
|
||
|
|
|
||
|
|
// A one-token line XxxBEGIN opens block "Xxx"; XxxEND closes the innermost one.
|
||
|
|
|
||
|
|
public static WpakBlock readBlocks(String fileName) throws IOException {
|
||
|
|
|
||
|
|
WpakBlock root = new WpakBlock(fileName, null);
|
||
|
|
ArrayDeque<WpakBlock> openBlocks = new ArrayDeque<>();
|
||
|
|
WpakBlock block;
|
||
|
|
String token;
|
||
|
|
|
||
|
|
openBlocks.push(root);
|
||
|
|
|
||
|
|
for (WpakLine line : readLines(fileName)) {
|
||
|
|
token = line.token(0);
|
||
|
|
|
||
|
|
if (line.size() == 1 && token.length() > 5 && token.endsWith("BEGIN")) {
|
||
|
|
block = new WpakBlock(token.substring(0, token.length() - 5), line);
|
||
|
|
openBlocks.peek().children.add(block);
|
||
|
|
openBlocks.push(block);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (line.size() == 1 && token.equals(openBlocks.peek().name + "END")) {
|
||
|
|
openBlocks.pop();
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (line.size() == 1 && closesOuterBlock(openBlocks, token))
|
||
|
|
throw line.error(token + " before " + openBlocks.peek().name + "END (opened at " + openBlocks.peek().location() + ")");
|
||
|
|
|
||
|
|
openBlocks.peek().children.add(line);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (openBlocks.size() > 1)
|
||
|
|
throw new WpakFormatException(openBlocks.peek().location() + ": " + openBlocks.peek().name + "BEGIN is never closed");
|
||
|
|
|
||
|
|
return root;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static boolean closesOuterBlock(ArrayDeque<WpakBlock> openBlocks, String token) {
|
||
|
|
|
||
|
|
for (WpakBlock block : openBlocks)
|
||
|
|
if (block.beginLine != null && token.equals(block.name + "END"))
|
||
|
|
return true;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Whitespace separates tokens; double quotes group one token (quotes removed, no escapes).
|
||
|
|
|
||
|
|
private static ArrayList<String> tokenize(String fileName, int lineNumber, String text) {
|
||
|
|
|
||
|
|
ArrayList<String> tokens = new ArrayList<>();
|
||
|
|
StringBuilder token = new StringBuilder();
|
||
|
|
boolean inToken = false;
|
||
|
|
boolean inQuotes = false;
|
||
|
|
char c;
|
||
|
|
|
||
|
|
for (int i = 0; i < text.length(); i++) {
|
||
|
|
c = text.charAt(i);
|
||
|
|
|
||
|
|
if (inQuotes) {
|
||
|
|
if (c == '"') {
|
||
|
|
inQuotes = false;
|
||
|
|
tokens.add(token.toString());
|
||
|
|
token.setLength(0);
|
||
|
|
} else
|
||
|
|
token.append(c);
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (c == '"' || c == '#' || Character.isWhitespace(c)) {
|
||
|
|
if (inToken) {
|
||
|
|
tokens.add(token.toString());
|
||
|
|
token.setLength(0);
|
||
|
|
inToken = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (c == '#')
|
||
|
|
break;
|
||
|
|
|
||
|
|
if (c == '"')
|
||
|
|
inQuotes = true;
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
token.append(c);
|
||
|
|
inToken = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (inQuotes)
|
||
|
|
throw new WpakFormatException(fileName + ":" + lineNumber + ": unclosed quote");
|
||
|
|
|
||
|
|
if (inToken)
|
||
|
|
tokens.add(token.toString());
|
||
|
|
|
||
|
|
return tokens;
|
||
|
|
}
|
||
|
|
}
|