/* * 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. */ package com.romraider.maps; import static com.romraider.maps.RomChecksum.calculateRomChecksum; import static com.romraider.util.HexUtil.asBytes; import static com.romraider.util.HexUtil.asHex; import static javax.swing.JOptionPane.DEFAULT_OPTION; import static javax.swing.JOptionPane.QUESTION_MESSAGE; import static javax.swing.JOptionPane.showOptionDialog; import java.beans.PropertyVetoException; import java.io.File; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Vector; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.tree.DefaultMutableTreeNode; import org.apache.log4j.Logger; import com.romraider.Settings; import com.romraider.logger.ecu.ui.handler.table.TableUpdateHandler; import com.romraider.swing.CategoryTreeNode; import com.romraider.swing.JProgressPane; import com.romraider.swing.TableFrame; import com.romraider.swing.TableTreeNode; import com.romraider.util.SettingsManager; import com.romraider.xml.InvalidTableNameException; import com.romraider.xml.TableNotFoundException; public class Rom extends DefaultMutableTreeNode implements Serializable { private static final long serialVersionUID = 7865405179738828128L; private static final Logger LOGGER = Logger.getLogger(Rom.class); private RomID romID = new RomID(); private String fileName = ""; private File fullFileName = new File("."); private final Vector<TableTreeNode> tableNodes = new Vector<TableTreeNode>(); private byte[] binData; private boolean isAbstract = false; public Rom() { tableNodes.clear(); } public void refreshDisplayedTables() { // Remove all nodes from the ROM tree node. super.removeAllChildren(); Settings settings = SettingsManager.getSettings(); // Add nodes to ROM tree. for (TableTreeNode tableTreeNode : tableNodes) { TableFrame tableFrame = tableTreeNode.getFrame(); Table table = tableFrame.getTable(); if (settings.isDisplayHighTables() || settings.getUserLevel() >= table.getUserLevel()) { boolean categoryExists = false; for (int j = 0; j < getChildCount(); j++) { if (getChildAt(j).toString().equals(table.getCategory())) { // add to appropriate category getChildAt(j).add(tableTreeNode); categoryExists = true; break; } } if (!categoryExists) { // if category does not already exist, create it CategoryTreeNode categoryNode = new CategoryTreeNode(table.getCategory()); categoryNode.add(tableTreeNode); this.add(categoryNode); } } } } public void addTable(Table table) { boolean found = false; String frameTitle = this.getFileName()+" - "+table.getName(); for (int i = 0; i < tableNodes.size(); i++) { if (tableNodes.get(i).getTable().equals(table)) { tableNodes.remove(i); tableNodes.add(i, new TableTreeNode(new TableFrame(frameTitle, table))); found = true; break; } } if (!found) { tableNodes.add(new TableTreeNode(new TableFrame(frameTitle, table))); } } public void addTableByName(Table table) { boolean found = false; String frameTitle = this.getFileName()+" - "+table.getName(); for (int i = 0; i < tableNodes.size(); i++) { if (tableNodes.get(i).getTable().getName().equalsIgnoreCase(table.getName())) { tableNodes.remove(i); tableNodes.add(i, new TableTreeNode(new TableFrame(frameTitle, table))); found = true; break; } } if (!found) { tableNodes.add(new TableTreeNode(new TableFrame(frameTitle, table))); } } public void removeTable(Table table) { for(int i = 0; i < tableNodes.size(); i++) { if(tableNodes.get(i).getTable().equals(table)) { tableNodes.remove(i); return; } } } public void removeTableByName(Table table) { for(int i = 0; i < tableNodes.size(); i++) { if(tableNodes.get(i).getTable().getName().equalsIgnoreCase(table.getName())) { tableNodes.remove(i); return; } } } public Table getTableByName(String tableName) throws TableNotFoundException, InvalidTableNameException { if(null == tableName || tableName.isEmpty()) { throw new InvalidTableNameException(); } for (TableTreeNode tableNode : tableNodes) { if (tableNode.getTable().getName().equalsIgnoreCase(tableName)) { return tableNode.getTable(); } } throw new TableNotFoundException(); } public List<Table> findTables(String regex) { List<Table> result = new ArrayList<Table>(); for (TableTreeNode tableNode : tableNodes) { String name = tableNode.getTable().getName(); if (name.matches(regex)) result.add(tableNode.getTable()); } return result; } public void populateTables(byte[] binData, JProgressPane progress) { this.binData = binData; for (int i = 0; i < tableNodes.size(); i++) { // update progress int currProgress = (int) (i / (double) tableNodes.size() * 100); progress.update("Populating tables...", currProgress); Table table = tableNodes.get(i).getTable(); try { // if storageaddress has not been set (or is set to 0) omit table if (table.getStorageAddress() != 0) { try { table.populateTable(binData, this.getRomID().getRamOffset()); TableUpdateHandler.getInstance().registerTable(table); if (null != table.getName() && table.getName().equalsIgnoreCase("Checksum Fix")){ setEditStamp(binData, table.getStorageAddress()); } } catch (ArrayIndexOutOfBoundsException ex) { LOGGER.error(table.getName() + " type " + table.getType() + " start " + table.getStorageAddress() + " " + binData.length + " filesize", ex); // table storage address extends beyond end of file JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(table), "Storage address for table \"" + table.getName() + "\" is out of bounds.\nPlease check ECU definition file.", "ECU Definition Error", JOptionPane.ERROR_MESSAGE); tableNodes.removeElementAt(i); i--; } catch (IndexOutOfBoundsException iex) { LOGGER.error(table.getName() + " type " + table.getType() + " start " + table.getStorageAddress() + " " + binData.length + " filesize", iex); // table storage address extends beyond end of file JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(table), "Storage address for table \"" + table.getName() + "\" is out of bounds.\nPlease check ECU definition file.", "ECU Definition Error", JOptionPane.ERROR_MESSAGE); tableNodes.removeElementAt(i); i--; } } else { tableNodes.removeElementAt(i); // decrement i because length of vector has changed i--; } } catch (NullPointerException ex) { LOGGER.error("Error Populating Table", ex); JOptionPane.showMessageDialog(SwingUtilities.windowForComponent(table), "There was an error loading table " + table.getName(), "ECU Definition Error", JOptionPane.ERROR_MESSAGE); tableNodes.removeElementAt(i); i--; } } } private void setEditStamp(byte[] binData, int address) { byte[] stampData = new byte[4]; System.arraycopy(binData, address+204, stampData, 0, stampData.length); String stamp = asHex(stampData); if (stamp.equalsIgnoreCase("FFFFFFFF")) { romID.setEditStamp(""); } else { StringBuilder niceStamp = new StringBuilder(stamp); niceStamp.replace(6, 9, String.valueOf(0xFF & stampData[3])); niceStamp.insert(6, " v"); niceStamp.insert(4, "-"); niceStamp.insert(2, "-"); niceStamp.insert(0, "20"); romID.setEditStamp(niceStamp.toString()); } } public void setRomID(RomID romID) { this.romID = romID; } public RomID getRomID() { return romID; } public String getRomIDString() { return romID.getXmlid(); } @Override public String toString() { String output = ""; output = output + "\n---- Rom ----" + romID.toString(); for (int i = 0; i < tableNodes.size(); i++) { output = output + tableNodes.get(i).getTable(); } output = output + "\n---- End Rom ----"; return output; } public String getFileName() { return fileName; } public Vector<Table> getTables() { Vector<Table> tables = new Vector<Table>(); for(TableTreeNode tableNode : tableNodes) { tables.add(tableNode.getTable()); } return tables; } public Vector<TableTreeNode> getTableNodes() { return this.tableNodes; } public void setFileName(String fileName) { this.fileName = fileName; } public byte[] saveFile() { final List<TableTreeNode> checksumTables = new ArrayList<TableTreeNode>(); for (TableTreeNode tableNode : tableNodes) { tableNode.getTable().saveFile(binData); if (tableNode.getTable().getName().contains("Checksum Fix")) { checksumTables.add(tableNode); } } if (checksumTables.size() == 1) { final TableTreeNode checksum = checksumTables.get(0); byte count = binData[checksum.getTable().getStorageAddress() + 207]; if (count == -1) { count = 1; } else { count++; } String currentDate = new SimpleDateFormat("yyMMdd").format(new Date()); String stamp = String.format("%s%02x", currentDate, count); byte[] romStamp = asBytes(stamp); System.arraycopy( romStamp, 0, binData, checksum.getTable().getStorageAddress() + 204, 4); setEditStamp(binData, checksum.getTable().getStorageAddress()); } for (TableTreeNode checksum : checksumTables) { if (!checksum.getTable().isLocked()) { calculateRomChecksum( binData, checksum.getTable().getStorageAddress(), checksum.getTable().getDataSize() ); } else if (checksum.getTable().isLocked() && !checksum.getTable().isButtonSelected()) { Object[] options = {"Yes", "No"}; final String message = String.format( "One or more ROM image Checksums is invalid. " + "Calculate new Checksums?%n" + "(NOTE: this will only fix the Checksums it will NOT repair a corrupt ROM image)"); int answer = showOptionDialog( SwingUtilities.windowForComponent(checksum.getTable()), message, "Checksum Fix", DEFAULT_OPTION, QUESTION_MESSAGE, null, options, options[0]); if (answer == 0) { calculateRomChecksum( binData, checksum.getTable().getStorageAddress(), checksum.getTable().getDataSize() ); } } } return binData; } public void clearData() { super.removeAllChildren(); // Hide and dispose all frames. for(TableTreeNode tableTreeNode : tableNodes) { TableFrame frame = tableTreeNode.getFrame(); frame.setVisible(false); try { frame.setClosed(true); } catch (PropertyVetoException e) { ; // Do nothing. } frame.dispose(); } tableNodes.clear(); binData = null; } public int getRealFileSize() { return binData.length; } public File getFullFileName() { return fullFileName; } public void setFullFileName(File fullFileName) { this.fullFileName = fullFileName; this.setFileName(fullFileName.getName()); for (TableTreeNode tableNode : tableNodes) { String frameTitle = this.getFileName() + " - " + tableNode.getTable().getName(); tableNode.getFrame().setTitle(frameTitle); } } public boolean isAbstract() { return isAbstract; } public void setAbstract(boolean isAbstract) { this.isAbstract = isAbstract; } public void refreshTableCompareMenus() { for(TableTreeNode tableNode : getTableNodes()) { tableNode.getFrame().refreshSimilarOpenTables(); } } @Override public DefaultMutableTreeNode getChildAt(int i) { return (DefaultMutableTreeNode) super.getChildAt(i); } @Override public DefaultMutableTreeNode getLastChild() { return (DefaultMutableTreeNode) super.getLastChild(); } }