Files
Server/src/engine/parsers/wpak/WpakFileReader.java
T

152 lines
5.2 KiB
Java
Raw Normal View History

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// 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;
}
}