/*
* RomRaider Open-Source Tuning, Logging and Reflashing
* Copyright (C) 2006-2016 RomRaider.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
//DOM XML parser for ROMs
package com.romraider.xml;
import static com.romraider.xml.DOMHelper.unmarshallAttribute;
import static com.romraider.xml.DOMHelper.unmarshallText;
import static org.w3c.dom.Node.ELEMENT_NODE;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.modelmbean.XMLParseException;
import javax.swing.JOptionPane;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.romraider.Settings;
import com.romraider.editor.ecu.ECUEditorManager;
import com.romraider.maps.DataCell;
import com.romraider.maps.Rom;
import com.romraider.maps.RomID;
import com.romraider.maps.Scale;
import com.romraider.maps.Table;
import com.romraider.maps.Table1D;
import com.romraider.maps.Table2D;
import com.romraider.maps.Table3D;
import com.romraider.maps.TableSwitch;
import com.romraider.swing.DebugPanel;
import com.romraider.swing.JProgressPane;
import com.romraider.util.ObjectCloner;
import com.romraider.util.SettingsManager;
public final class DOMRomUnmarshaller {
private static final Logger LOGGER = Logger
.getLogger(DOMRomUnmarshaller.class);
private JProgressPane progress = null;
private final List<Scale> scales = new ArrayList<Scale>();
private String memModelEndian = null;
private final Scale rawScale = new Scale();
private final Map<String, Integer> tableNames = new HashMap<String, Integer>();
public DOMRomUnmarshaller() {
}
public Rom unmarshallXMLDefinition(Node rootNode, byte[] input,
JProgressPane progress) throws RomNotFoundException,
XMLParseException, StackOverflowError, Exception {
this.progress = progress;
Node n;
NodeList nodes = rootNode.getChildNodes();
// unmarshall scales first
for (int i = 0; i < nodes.getLength(); i++) {
n = nodes.item(i);
if (n.getNodeType() == ELEMENT_NODE
&& n.getNodeName().equalsIgnoreCase("scalingbase")) {
scales.add(unmarshallScale(n, new Scale()));
}
}
// now unmarshall roms
for (int i = 0; i < nodes.getLength(); i++) {
n = nodes.item(i);
if (n.getNodeType() == ELEMENT_NODE
&& n.getNodeName().equalsIgnoreCase("rom")) {
Node n2;
NodeList nodes2 = n.getChildNodes();
for (int z = 0; z < nodes2.getLength(); z++) {
n2 = nodes2.item(z);
if (n2.getNodeType() == ELEMENT_NODE
&& n2.getNodeName().equalsIgnoreCase("romid")) {
RomID romID = unmarshallRomID(n2, new RomID());
if (romID.getInternalIdString().length() > 0
&& foundMatch(romID, input)) {
Rom output = unmarshallRom(n, new Rom());
// set ram offset
output.getRomID().setRamOffset(
output.getRomID().getFileSize()
- input.length);
return output;
}
}
}
}
}
throw new RomNotFoundException();
}
public static boolean foundMatch(RomID romID, byte[] file) {
String idString = romID.getInternalIdString();
// romid is hex string
if (idString.length() > 2
&& idString.substring(0, 2).equalsIgnoreCase("0x")) {
try {
// put romid in to byte array to check for match
idString = idString.substring(2); // remove "0x"
int[] romIDBytes = new int[idString.length() / 2];
for (int i = 0; i < romIDBytes.length; i++) {
// check to see if each byte matches
if ((file[romID.getInternalIdAddress() + i] & 0xff) != Integer
.parseInt(idString.substring(i * 2, i * 2 + 2), 16)) {
return false;
}
}
// if no mismatched bytes found, return true
return true;
} catch (Exception ex) {
// if any exception is encountered, names do not match
LOGGER.warn("Error finding match", ex);
return false;
}
// else romid is NOT hex string
} else {
try {
String ecuID = new String(file, romID.getInternalIdAddress(),
romID.getInternalIdString().length());
return foundMatchByString(romID, ecuID);
} catch (Exception ex) {
// if any exception is encountered, names do not match
return false;
}
}
}
public static boolean foundMatchByString(RomID romID, String ecuID) {
try {
if (ecuID.equalsIgnoreCase(romID.getInternalIdString())) {
return true;
} else {
return false;
}
} catch (Exception ex) {
// if any exception is encountered, names do not match
return false;
}
}
public Rom unmarshallRom(Node rootNode, Rom rom) throws XMLParseException,
RomNotFoundException, StackOverflowError, Exception {
Node n;
NodeList nodes = rootNode.getChildNodes();
filterFoundRomTables(nodes);
progress.update("Creating " + rom.getRomID().getXmlid() + " tables...", 0);
if (!unmarshallAttribute(rootNode, "base", "none").equalsIgnoreCase(
"none")) {
rom = getBaseRom(rootNode.getParentNode(),
unmarshallAttribute(rootNode, "base", "none"), rom);
rom.getRomID().setObsolete(false);
}
for (int i = 0; i < nodes.getLength(); i++) {
n = nodes.item(i);
// update progress
int currProgress = (int) (i / (double) nodes.getLength() * 100);
progress.update("Creating " + rom.getRomID().getXmlid() + " tables...", currProgress);
if (n.getNodeType() == ELEMENT_NODE) {
if (n.getNodeName().equalsIgnoreCase("romid")) {
rom.setRomID(unmarshallRomID(n, rom.getRomID()));
} else if (n.getNodeName().equalsIgnoreCase("table")) {
Table table = null;
try {
table = rom.getTableByName(unmarshallAttribute(n, "name",
null));
} catch (TableNotFoundException e) {
/*
* table does not
* already exist (do
* nothing)
*/
} catch (InvalidTableNameException iex) {
// Table name is null or empty. Do nothing.
}
try {
table = unmarshallTable(n, table, rom);
//rom.addTableByName(table);
if (table != null) {
//rom.removeTableByName(table);
rom.addTable(table);
}
} catch (TableIsOmittedException ex) {
// table is not supported in inherited def (skip)
if (table != null) {
//rom.removeTableByName(table);
rom.removeTable(table);
}
} catch (XMLParseException ex) {
LOGGER.error("Error unmarshalling rom", ex);
}
} else { /* unexpected element in Rom (skip) */
}
} else { /* unexpected node-type in Rom (skip) */
}
}
return rom;
}
public Rom getBaseRom(Node rootNode, String xmlID, Rom rom)
throws XMLParseException, RomNotFoundException, StackOverflowError,
Exception {
Node n;
NodeList nodes = rootNode.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
n = nodes.item(i);
if (n.getNodeType() == ELEMENT_NODE
&& n.getNodeName().equalsIgnoreCase("rom")) {
Node n2;
NodeList nodes2 = n.getChildNodes();
for (int z = 0; z < nodes2.getLength(); z++) {
n2 = nodes2.item(z);
if (n2.getNodeType() == ELEMENT_NODE
&& n2.getNodeName().equalsIgnoreCase("romid")) {
RomID romID = unmarshallRomID(n2, new RomID());
if (romID.getXmlid().equalsIgnoreCase(xmlID)) {
Rom returnrom = unmarshallRom(n, rom);
returnrom.getRomID().setObsolete(false);
return returnrom;
}
}
}
}
}
throw new RomNotFoundException();
}
public RomID unmarshallRomID(Node romIDNode, RomID romID) {
Node n;
NodeList nodes = romIDNode.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
n = nodes.item(i);
if (n.getNodeType() == ELEMENT_NODE) {
if (n.getNodeName().equalsIgnoreCase("xmlid")) {
romID.setXmlid(unmarshallText(n));
} else if (n.getNodeName()
.equalsIgnoreCase("internalidaddress")) {
romID.setInternalIdAddress(RomAttributeParser
.parseHexString(unmarshallText(n)));
} else if (n.getNodeName().equalsIgnoreCase("internalidstring")) {
romID.setInternalIdString(unmarshallText(n));
if (romID.getInternalIdString() == null) {
romID.setInternalIdString("");
}
} else if (n.getNodeName().equalsIgnoreCase("caseid")) {
romID.setCaseId(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("ecuid")) {
romID.setEcuId(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("make")) {
romID.setMake(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("market")) {
romID.setMarket(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("model")) {
romID.setModel(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("submodel")) {
romID.setSubModel(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("transmission")) {
romID.setTransmission(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("year")) {
romID.setYear(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("flashmethod")) {
romID.setFlashMethod(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("memmodel")) {
romID.setMemModel(unmarshallText(n));
memModelEndian = unmarshallAttribute(n, "endian", null);
} else if (n.getNodeName().equalsIgnoreCase("filesize")) {
romID.setFileSize(RomAttributeParser
.parseFileSize(unmarshallText(n)));
} else if (n.getNodeName().equalsIgnoreCase("obsolete")) {
romID.setObsolete(Boolean.parseBoolean(unmarshallText(n)));
} else { /* unexpected element in RomID (skip) */
}
} else { /* unexpected node-type in RomID (skip) */
}
}
return romID;
}
private Table unmarshallTable(Node tableNode, Table table, Rom rom)
throws XMLParseException, TableIsOmittedException, Exception {
if (unmarshallAttribute(tableNode, "omit", "false").equalsIgnoreCase(
"true")) { // remove table if omitted
throw new TableIsOmittedException();
}
if (!unmarshallAttribute(tableNode, "base", "none").equalsIgnoreCase(
"none")) { // copy base table for inheritance
try {
table = (Table) ObjectCloner
.deepCopy(rom.getTableByName(unmarshallAttribute(tableNode,
"base", "none")));
} catch (TableNotFoundException ex) { /* table not found, do nothing */
} catch (InvalidTableNameException ex) { // Table name is invalid, do nothing.
} catch (NullPointerException ex) {
JOptionPane.showMessageDialog(ECUEditorManager.getECUEditor(),
new DebugPanel(ex, SettingsManager.getSettings().getSupportURL()), "Exception",
JOptionPane.ERROR_MESSAGE);
}
}
try {
if (table.getType() < 1) {
}
} catch (NullPointerException ex) { // if type is null or less than 0,
// create new instance (otherwise it
// is inherited)
final String tn = unmarshallAttribute(tableNode, "name", "unknown");
final String type = unmarshallAttribute(tableNode, "type", "unknown");
if (tableNames.containsKey(tn) || type.contains("xis")) {
if (unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("3D")) {
table = new Table3D();
table.getScales().add(rawScale);
((Table3D) table).getXAxis().getScales().add(rawScale);
((Table3D) table).getYAxis().getScales().add(rawScale);
} else if (unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("2D")) {
table = new Table2D();
table.getScales().add(rawScale);
((Table2D) table).getAxis().getScales().add(rawScale);
} else if (unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("1D")) {
table = new Table1D();
} else if (unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("X Axis")
|| unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("Y Axis")) {
table = new Table1D();
} else if (unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("Static Y Axis")
|| unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("Static X Axis")) {
table = new Table1D();
} else if (unmarshallAttribute(tableNode, "type", "unknown")
.equalsIgnoreCase("Switch")) {
table = new TableSwitch();
} else {
throw new XMLParseException("Error loading table, "
+ tableNode.getAttributes().getNamedItem("name"));
}
}
else {
return table;
}
}
// unmarshall table attributes
final String tn = unmarshallAttribute(tableNode, "name", table.getName());
final int type = RomAttributeParser.parseTableType(unmarshallAttribute(
tableNode, "type", String.valueOf(table.getType())));
table.setName(tn);
table.setType(type);
if (unmarshallAttribute(tableNode, "beforeram", "false")
.equalsIgnoreCase("true")) {
table.setBeforeRam(true);
}
table.setCategory(unmarshallAttribute(tableNode, "category",
table.getCategory()));
if (table.getStorageType() < 1) {
table.setSignedData(RomAttributeParser
.parseStorageDataSign(unmarshallAttribute(tableNode,
"storagetype",
String.valueOf(table.getStorageType()))));
}
table.setStorageType(RomAttributeParser
.parseStorageType(unmarshallAttribute(tableNode, "storagetype",
String.valueOf(table.getStorageType()))));
if (memModelEndian == null) {
table.setEndian(RomAttributeParser.parseEndian(unmarshallAttribute(
tableNode, "endian", String.valueOf(table.getEndian()))));
}
else {
final int endian = memModelEndian.equalsIgnoreCase("little") ? 1 : 2;
table.setMemModelEndian(endian);
table.setEndian(endian);
}
if (tableNames.containsKey(tn)) {
table.setStorageAddress(tableNames.get(tn));
}
else {
table.setStorageAddress(RomAttributeParser
.parseHexString(unmarshallAttribute(tableNode,
"storageaddress",
String.valueOf(table.getStorageAddress()))));
}
table.setDescription(unmarshallAttribute(tableNode, "description",
table.getDescription()));
table.setDataSize(unmarshallAttribute(tableNode, "sizey",
unmarshallAttribute(tableNode, "sizex", table.getDataSize())));
table.setFlip(unmarshallAttribute(tableNode, "flipy",
unmarshallAttribute(tableNode, "flipx", table.getFlip())));
table.setUserLevel(unmarshallAttribute(tableNode, "userlevel",
table.getUserLevel()));
table.setLocked(unmarshallAttribute(tableNode, "locked",
table.isLocked()));
table.setLogParam(unmarshallAttribute(tableNode, "logparam",
table.getLogParam()));
if (table.getType() == Settings.TABLE_3D) {
((Table3D) table).setSwapXY(unmarshallAttribute(tableNode,
"swapxy", ((Table3D) table).getSwapXY()));
((Table3D) table).setFlipX(unmarshallAttribute(tableNode, "flipx",
((Table3D) table).getFlipX()));
((Table3D) table).setFlipY(unmarshallAttribute(tableNode, "flipy",
((Table3D) table).getFlipY()));
((Table3D) table).setSizeX(unmarshallAttribute(tableNode, "sizex",
((Table3D) table).getSizeX()));
((Table3D) table).setSizeY(unmarshallAttribute(tableNode, "sizey",
((Table3D) table).getSizeY()));
}
Node n;
NodeList nodes = tableNode.getChildNodes();
for (int i = 0; i < nodes.getLength(); i++) {
n = nodes.item(i);
if (n.getNodeType() == ELEMENT_NODE) {
if (n.getNodeName().equalsIgnoreCase("table")) {
if (table.getType() == Settings.TABLE_2D) { // if table is 2D,
// parse axis
if (RomAttributeParser
.parseTableType(unmarshallAttribute(n, "type",
"unknown")) == Settings.TABLE_Y_AXIS
|| RomAttributeParser
.parseTableType(unmarshallAttribute(n,
"type", "unknown")) == Settings.TABLE_X_AXIS) {
Table1D tempTable = (Table1D) unmarshallTable(n,
((Table2D) table).getAxis(), rom);
if (tempTable.getDataSize() != table.getDataSize()) {
tempTable.setDataSize(table.getDataSize());
}
tempTable.setData(((Table2D) table).getAxis()
.getData());
((Table2D) table).setAxis(tempTable);
}
} else if (table.getType() == Settings.TABLE_3D) { // if table
// is 3D,
// populate
// xAxis
if (RomAttributeParser
.parseTableType(unmarshallAttribute(n, "type",
"unknown")) == Settings.TABLE_X_AXIS) {
Table1D tempTable = (Table1D) unmarshallTable(n,
((Table3D) table).getXAxis(), rom);
if (tempTable.getDataSize() != ((Table3D) table)
.getSizeX()) {
tempTable.setDataSize(((Table3D) table)
.getSizeX());
}
tempTable.setData(((Table3D) table).getXAxis()
.getData());
((Table3D) table).setXAxis(tempTable);
} else if (RomAttributeParser
.parseTableType(unmarshallAttribute(n, "type",
"unknown")) == Settings.TABLE_Y_AXIS) {
Table1D tempTable = (Table1D) unmarshallTable(n,
((Table3D) table).getYAxis(), rom);
if (tempTable.getDataSize() != ((Table3D) table)
.getSizeY()) {
tempTable.setDataSize(((Table3D) table)
.getSizeY());
}
tempTable.setData(((Table3D) table).getYAxis()
.getData());
((Table3D) table).setYAxis(tempTable);
}
}
} else if (n.getNodeName().equalsIgnoreCase("scaling")) {
// check whether scale already exists. if so, modify, else
// use new instance
Scale baseScale = table.getScale(unmarshallAttribute(n,"name", "Default"));
table.addScale(unmarshallScale(n, baseScale));
} else if (n.getNodeName().equalsIgnoreCase("data")) {
// parse and add data to table
DataCell dataCell = new DataCell(table, unmarshallText(n));
if(table instanceof Table1D) {
((Table1D)table).addStaticDataCell(dataCell);
} else {
// Why would this happen. Static should only be for axis.
LOGGER.error("Error adding static data cell.");
}
} else if (n.getNodeName().equalsIgnoreCase("description")) {
table.setDescription(unmarshallText(n));
} else if (n.getNodeName().equalsIgnoreCase("state")) {
((TableSwitch) table).setValues(
unmarshallAttribute(n, "name", ""),
unmarshallAttribute(n, "data", "0.0"));
} else { /* unexpected element in Table (skip) */
}
} else { /* unexpected node-type in Table (skip) */
}
}
return table;
}
private Scale unmarshallScale(Node scaleNode, Scale scale) {
// look for base scale first
String base = unmarshallAttribute(scaleNode, "base", "none");
if (!base.equalsIgnoreCase("none")) {
for (Scale scaleItem : scales) {
// check whether name matches base and set scale if so
if (scaleItem.getName().equalsIgnoreCase(base)) {
try {
scale = (Scale) ObjectCloner.deepCopy(scaleItem);
} catch (Exception ex) {
JOptionPane.showMessageDialog(
ECUEditorManager.getECUEditor(),
new DebugPanel(ex, SettingsManager.getSettings()
.getSupportURL()), "Exception",
JOptionPane.ERROR_MESSAGE);
}
}
}
}
// set remaining attributes
scale.setName(unmarshallAttribute(scaleNode, "name", "Default"));
scale.setUnit(unmarshallAttribute(scaleNode, "units", scale.getUnit()));
scale.setExpression(unmarshallAttribute(scaleNode, "expression",
scale.getExpression()));
scale.setByteExpression(unmarshallAttribute(scaleNode, "to_byte",
scale.getByteExpression()));
scale.setFormat(unmarshallAttribute(scaleNode, "format", "#"));
scale.setMax(unmarshallAttribute(scaleNode, "max", 0.0));
scale.setMin(unmarshallAttribute(scaleNode, "min", 0.0));
// get coarse increment with new attribute name (coarseincrement), else
// look for old (increment)
scale.setCoarseIncrement(unmarshallAttribute(
scaleNode,
"coarseincrement",
unmarshallAttribute(scaleNode, "increment",
scale.getCoarseIncrement())));
scale.setFineIncrement(unmarshallAttribute(scaleNode, "fineincrement",
scale.getFineIncrement()));
for (Scale s : scales) {
if (s.equals(scale)) {
return s;
}
}
scales.add(scale);
return scale;
}
/**
* Create a list of table names to be used as a filter on the inherited
* tables to reduce unnecessary table object creation.
* @param nodes - the NodeList to filter
* @throws XMLParseException
* @throws TableIsOmittedException
* @throws Exception
*/
private void filterFoundRomTables (NodeList nodes) {
Node n;
for (int i = 0; i < nodes.getLength(); i++) {
n = nodes.item(i);
if (n.getNodeType() == ELEMENT_NODE
&& n.getNodeName().equalsIgnoreCase("table")) {
final String name = unmarshallAttribute(n, "name", "unknown");
final int address = RomAttributeParser
.parseHexString(unmarshallAttribute(n,
"storageaddress", "-1"));
if (unmarshallAttribute(n, "omit", "false").equalsIgnoreCase(
"true")) {
return;
}
if (!tableNames.containsKey(name) && address > 0) {
tableNames.put(name, address);
}
else if (tableNames.containsKey(name)) {
if (tableNames.get(name) < 1 && address > 0) {
tableNames.put(name, address);
}
}
}
}
}
}