299 lines
10 KiB
Java
299 lines
10 KiB
Java
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
|||
|
|
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||
|
|
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||
|
|
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||
|
|
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||
|
|
// Magicbane Emulator Project © 2013 - 2026
|
||
|
|
// https://www.magicbane.com
|
||
|
|
|
||
|
|
package engine.parsers.cache;
|
||
|
|
|
||
|
|
import engine.math.Vector3fImmutable;
|
||
|
|
import engine.parsers.objects.PowerGrant;
|
||
|
|
import engine.parsers.objects.UserPowerAction;
|
||
|
|
import org.json.JSONArray;
|
||
|
|
import org.json.JSONObject;
|
||
|
|
import org.pmw.tinylog.Logger;
|
||
|
|
|
||
|
|
import java.io.File;
|
||
|
|
import java.io.IOException;
|
||
|
|
import java.nio.charset.StandardCharsets;
|
||
|
|
import java.nio.file.Files;
|
||
|
|
import java.util.ArrayList;
|
||
|
|
import java.util.EnumMap;
|
||
|
|
import java.util.EnumSet;
|
||
|
|
import java.util.HashMap;
|
||
|
|
import java.util.LinkedHashMap;
|
||
|
|
|
||
|
|
// Shared JSON helpers for the COBJECTS cache parsers.
|
||
|
|
// Only generic reading lives here; each parser owns its own field mapping.
|
||
|
|
|
||
|
|
public class CacheParserUtil {
|
||
|
|
|
||
|
|
// Files are named <template id>.json
|
||
|
|
|
||
|
|
public static File[] listJsonFiles(String folderPath) {
|
||
|
|
|
||
|
|
File folder = new File(folderPath);
|
||
|
|
File[] jsonFiles = folder.listFiles((dir, name) -> name.endsWith(".json"));
|
||
|
|
|
||
|
|
if (jsonFiles == null) {
|
||
|
|
Logger.error("Cache folder not found: " + folder.getAbsolutePath());
|
||
|
|
return new File[0];
|
||
|
|
}
|
||
|
|
|
||
|
|
return jsonFiles;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static int templateIdFromFile(File jsonFile) {
|
||
|
|
|
||
|
|
String fileName = jsonFile.getName();
|
||
|
|
|
||
|
|
return Integer.parseInt(fileName.substring(0, fileName.length() - ".json".length()));
|
||
|
|
}
|
||
|
|
|
||
|
|
public static JSONObject readJsonFile(File jsonFile) throws IOException {
|
||
|
|
|
||
|
|
byte[] fileData = Files.readAllBytes(jsonFile.toPath());
|
||
|
|
|
||
|
|
return new JSONObject(new String(fileData, StandardCharsets.UTF_8));
|
||
|
|
}
|
||
|
|
|
||
|
|
// [x, y, z] -> vector. Returns null for an empty array.
|
||
|
|
|
||
|
|
public static Vector3fImmutable readVector3(JSONArray vectorJson) {
|
||
|
|
|
||
|
|
if (vectorJson.length() < 3)
|
||
|
|
return null;
|
||
|
|
|
||
|
|
return new Vector3fImmutable(vectorJson.getFloat(0), vectorJson.getFloat(1), vectorJson.getFloat(2));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Client strings carry spaces, hyphens and commas that enum constants
|
||
|
|
// can't ("Half-Giant", "Battle Magus").
|
||
|
|
|
||
|
|
public static String enumName(String value) {
|
||
|
|
|
||
|
|
String name = value;
|
||
|
|
|
||
|
|
name = name.replaceAll("\\s", "");
|
||
|
|
name = name.replaceAll("-", "");
|
||
|
|
name = name.replaceAll(",", "");
|
||
|
|
|
||
|
|
return name;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Exact match on the cleaned name first, then upper case (ResourceType).
|
||
|
|
// Unknown values are logged and returned as null so one bad value
|
||
|
|
// doesn't drop the whole template.
|
||
|
|
|
||
|
|
public static <E extends Enum<E>> E readEnum(Class<E> enumClass, String value, int templateId, String fieldName) {
|
||
|
|
|
||
|
|
String name = enumName(value);
|
||
|
|
|
||
|
|
try {
|
||
|
|
return Enum.valueOf(enumClass, name);
|
||
|
|
} catch (IllegalArgumentException e) {
|
||
|
|
// fall through to upper case
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
return Enum.valueOf(enumClass, name.toUpperCase());
|
||
|
|
} catch (IllegalArgumentException e) {
|
||
|
|
Logger.error(templateId + ": " + fieldName + " has no " + enumClass.getSimpleName() + " for '" + value + "'");
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public static <E extends Enum<E>> EnumSet<E> readEnumSet(Class<E> enumClass, JSONArray valuesJson, int templateId, String fieldName) {
|
||
|
|
|
||
|
|
EnumSet<E> enumSet = EnumSet.noneOf(enumClass);
|
||
|
|
E enumValue;
|
||
|
|
|
||
|
|
for (int i = 0; i < valuesJson.length(); i++) {
|
||
|
|
enumValue = readEnum(enumClass, valuesJson.getString(i), templateId, fieldName);
|
||
|
|
|
||
|
|
if (enumValue != null)
|
||
|
|
enumSet.add(enumValue);
|
||
|
|
}
|
||
|
|
|
||
|
|
return enumSet;
|
||
|
|
}
|
||
|
|
|
||
|
|
// [{keyName: "Strength", valueName: 40}, ...] -> enum map
|
||
|
|
// Negative values are stored unsigned in the JSON (-5 is 4294967291),
|
||
|
|
// so values are read as long and wrapped to int.
|
||
|
|
|
||
|
|
public static <E extends Enum<E>> EnumMap<E, Integer> readEnumValueMap(Class<E> enumClass, JSONArray entriesJson,
|
||
|
|
String keyName, String valueName,
|
||
|
|
int templateId, String fieldName) {
|
||
|
|
|
||
|
|
EnumMap<E, Integer> enumMap = new EnumMap<>(enumClass);
|
||
|
|
JSONObject entry;
|
||
|
|
E enumValue;
|
||
|
|
|
||
|
|
for (int i = 0; i < entriesJson.length(); i++) {
|
||
|
|
entry = entriesJson.getJSONObject(i);
|
||
|
|
enumValue = readEnum(enumClass, entry.getString(keyName), templateId, fieldName);
|
||
|
|
|
||
|
|
if (enumValue != null)
|
||
|
|
enumMap.put(enumValue, (int) entry.getLong(valueName));
|
||
|
|
}
|
||
|
|
|
||
|
|
return enumMap;
|
||
|
|
}
|
||
|
|
|
||
|
|
// [{keyName: "Sword", valueName: 20}, ...] -> map in file order
|
||
|
|
|
||
|
|
public static LinkedHashMap<String, Integer> readStringValueMap(JSONArray entriesJson, String keyName, String valueName) {
|
||
|
|
|
||
|
|
LinkedHashMap<String, Integer> valueMap = new LinkedHashMap<>();
|
||
|
|
JSONObject entry;
|
||
|
|
|
||
|
|
for (int i = 0; i < entriesJson.length(); i++) {
|
||
|
|
entry = entriesJson.getJSONObject(i);
|
||
|
|
valueMap.put(entry.getString(keyName), (int) entry.getLong(valueName));
|
||
|
|
}
|
||
|
|
|
||
|
|
return valueMap;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static LinkedHashMap<String, Float> readFloatMap(JSONObject mapJson) {
|
||
|
|
|
||
|
|
LinkedHashMap<String, Float> floatMap = new LinkedHashMap<>();
|
||
|
|
|
||
|
|
for (String key : mapJson.keySet())
|
||
|
|
floatMap.put(key, mapJson.getFloat(key));
|
||
|
|
|
||
|
|
return floatMap;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Values are read as long and wrapped to int so unsigned 32-bit hashes survive.
|
||
|
|
|
||
|
|
public static int[] readIntArray(JSONArray valuesJson) {
|
||
|
|
|
||
|
|
int[] values = new int[valuesJson.length()];
|
||
|
|
|
||
|
|
for (int i = 0; i < valuesJson.length(); i++)
|
||
|
|
values[i] = (int) valuesJson.getLong(i);
|
||
|
|
|
||
|
|
return values;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static float[] readFloatArray(JSONArray valuesJson) {
|
||
|
|
|
||
|
|
float[] values = new float[valuesJson.length()];
|
||
|
|
|
||
|
|
for (int i = 0; i < valuesJson.length(); i++)
|
||
|
|
values[i] = valuesJson.getFloat(i);
|
||
|
|
|
||
|
|
return values;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static ArrayList<Integer> readIntList(JSONArray valuesJson) {
|
||
|
|
|
||
|
|
ArrayList<Integer> values = new ArrayList<>();
|
||
|
|
|
||
|
|
for (int i = 0; i < valuesJson.length(); i++)
|
||
|
|
values.add((int) valuesJson.getLong(i));
|
||
|
|
|
||
|
|
return values;
|
||
|
|
}
|
||
|
|
|
||
|
|
// [[a, b], [a, b], ...] -> list of int[]
|
||
|
|
|
||
|
|
public static ArrayList<int[]> readIntArrays(JSONArray arraysJson) {
|
||
|
|
|
||
|
|
ArrayList<int[]> arrays = new ArrayList<>();
|
||
|
|
|
||
|
|
for (int i = 0; i < arraysJson.length(); i++)
|
||
|
|
arrays.add(readIntArray(arraysJson.getJSONArray(i)));
|
||
|
|
|
||
|
|
return arrays;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Sparse data values are mixed bool/int/string; stored as strings.
|
||
|
|
|
||
|
|
public static HashMap<String, String> readSparseData(JSONObject sparseJson) {
|
||
|
|
|
||
|
|
HashMap<String, String> sparseData = new HashMap<>();
|
||
|
|
|
||
|
|
for (String key : sparseJson.keySet())
|
||
|
|
sparseData.put(key, sparseJson.get(key).toString());
|
||
|
|
|
||
|
|
return sparseData;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Some fields are a name when set and the number 0 when not
|
||
|
|
// (obj_sound_table, item_skill_used). The number becomes "".
|
||
|
|
|
||
|
|
public static String readLabel(JSONObject json, String key) {
|
||
|
|
|
||
|
|
Object value = json.get(key);
|
||
|
|
|
||
|
|
if (value instanceof String)
|
||
|
|
return (String) value;
|
||
|
|
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
public static ArrayList<UserPowerAction> readUserPowerActions(JSONArray actionsJson) {
|
||
|
|
|
||
|
|
ArrayList<UserPowerAction> userPowerActions = new ArrayList<>();
|
||
|
|
JSONObject entry;
|
||
|
|
|
||
|
|
for (int i = 0; i < actionsJson.length(); i++) {
|
||
|
|
entry = actionsJson.getJSONObject(i);
|
||
|
|
userPowerActions.add(new UserPowerAction(entry.getString("power"), readIntArray(entry.getJSONArray("arguments"))));
|
||
|
|
}
|
||
|
|
|
||
|
|
return userPowerActions;
|
||
|
|
}
|
||
|
|
|
||
|
|
public static ArrayList<PowerGrant> readPowerGrants(JSONArray grantsJson) {
|
||
|
|
|
||
|
|
ArrayList<PowerGrant> powerGrants = new ArrayList<>();
|
||
|
|
JSONObject entry;
|
||
|
|
PowerGrant powerGrant;
|
||
|
|
|
||
|
|
for (int i = 0; i < grantsJson.length(); i++) {
|
||
|
|
entry = grantsJson.getJSONObject(i);
|
||
|
|
powerGrant = new PowerGrant();
|
||
|
|
|
||
|
|
powerGrant.power_type = entry.getString("power_type");
|
||
|
|
powerGrant.power_value = entry.getInt("power_value");
|
||
|
|
powerGrant.power_granted_attrs = entry.getJSONArray("power_granted_attrs");
|
||
|
|
powerGrant.power_granted_skills.addAll(readIntArrays(entry.getJSONArray("power_granted_skills")));
|
||
|
|
powerGrant.power_granted_powers.addAll(readIntArrays(entry.getJSONArray("power_granted_powers")));
|
||
|
|
powerGrant.power_monster_types = entry.getJSONArray("power_monster_types");
|
||
|
|
|
||
|
|
powerGrants.add(powerGrant);
|
||
|
|
}
|
||
|
|
|
||
|
|
return powerGrants;
|
||
|
|
}
|
||
|
|
|
||
|
|
// [{power_type: "X", power_actions: [{power_arguments: [a, b]}, ...]}, ...]
|
||
|
|
// -> power_type -> list of argument arrays (one per power_actions entry)
|
||
|
|
|
||
|
|
public static LinkedHashMap<String, ArrayList<int[]>> readPowerActions(JSONArray actionsJson) {
|
||
|
|
|
||
|
|
LinkedHashMap<String, ArrayList<int[]>> powerActions = new LinkedHashMap<>();
|
||
|
|
JSONObject entry;
|
||
|
|
JSONArray argumentsJson;
|
||
|
|
ArrayList<int[]> arguments;
|
||
|
|
|
||
|
|
for (int i = 0; i < actionsJson.length(); i++) {
|
||
|
|
entry = actionsJson.getJSONObject(i);
|
||
|
|
argumentsJson = entry.getJSONArray("power_actions");
|
||
|
|
arguments = new ArrayList<>();
|
||
|
|
|
||
|
|
for (int j = 0; j < argumentsJson.length(); j++)
|
||
|
|
arguments.add(readIntArray(argumentsJson.getJSONObject(j).getJSONArray("power_arguments")));
|
||
|
|
|
||
|
|
powerActions.put(entry.getString("power_type"), arguments);
|
||
|
|
}
|
||
|
|
|
||
|
|
return powerActions;
|
||
|
|
}
|
||
|
|
}
|