/* * RomRaider Open-Source Tuning, Logging and Reflashing * Copyright (C) 2006-2014 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.validateRomChecksum; import static com.romraider.util.ByteUtil.indexOfBytes; import static com.romraider.util.HexUtil.asBytes; import static javax.swing.JOptionPane.ERROR_MESSAGE; import static javax.swing.JOptionPane.INFORMATION_MESSAGE; import static javax.swing.JOptionPane.WARNING_MESSAGE; import static javax.swing.JOptionPane.showMessageDialog; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Insets; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import javax.swing.AbstractButton; import javax.swing.ButtonGroup; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTextArea; import com.romraider.Settings; public class TableSwitch extends Table { private static final long serialVersionUID = -4887718305447362308L; private final ButtonGroup buttonGroup = new ButtonGroup(); private final Map<String, byte[]> switchStates = new HashMap<String, byte[]>(); private int dataSize = 0; public TableSwitch() { super(); storageType = 1; type = Settings.TABLE_SWITCH; locked = true; removeAll(); setLayout(new BorderLayout()); } @Override public void setDataSize(int size) { if (dataSize == 0) dataSize = size; } @Override public int getDataSize() { return dataSize; } @Override public void populateTable(byte[] input, int romRamOffset) throws ArrayIndexOutOfBoundsException, IndexOutOfBoundsException { JPanel radioPanel = new JPanel(new GridLayout(0, 1)); radioPanel.add(new JLabel(" " + getName())); for (String stateName : switchStates.keySet()) { JRadioButton button = new JRadioButton(stateName); buttonGroup.add(button); radioPanel.add(button); } add(radioPanel, BorderLayout.CENTER); // Validate the ROM image checksums. // if the result is >0: position of failed checksum // if the result is 0: all the checksums matched // if the result is -1: all the checksums have been previously disabled if (super.getName().contains("Checksum Fix")) { int result = validateRomChecksum(input, getStorageAddress(), dataSize); String message = String.format( "Checksum No. %d is invalid in table: %s%n" + "The ROM image may be corrupt or it has been %n" + "hex edited manually.%n" + "The checksum can be corrected when the ROM is saved.", result, super.getName()); if (result > 0) { showMessageDialog(this, message, "ERROR - Checksums Failed", WARNING_MESSAGE); setButtonsUnselected(buttonGroup); } else if (result == -1){ message = "All Checksums are disabled."; showMessageDialog(this, message, "Warning - Checksum Status", INFORMATION_MESSAGE); getButtonByText(buttonGroup, "on").setSelected(true); } else { getButtonByText(buttonGroup, "off").setSelected(true); locked = false; } return; } // Validate XML switch definition data against the ROM data to select // the appropriate switch setting or throw an error if there is a // mismatch and disable this table's editing ability. if (!beforeRam) { this.ramOffset = romRamOffset; } Map<String, Integer> sourceStatus = new HashMap<String, Integer>(); for (String stateName : switchStates.keySet()) { byte[] sourceData = new byte[dataSize]; System.arraycopy( input, storageAddress - ramOffset, sourceData, 0, dataSize); int compareResult = indexOfBytes(sourceData, getValues(stateName)); if (compareResult == -1) { getButtonByText(buttonGroup, stateName).setSelected(false); } else { getButtonByText(buttonGroup, stateName).setSelected(true); } sourceStatus.put(stateName, compareResult); } for (String source : sourceStatus.keySet()) { if (sourceStatus.get(source) != -1) { locked = false; break; } } if (locked) { String mismatch = String.format("Table: %s%nTable editing has been disabled.%nDefinition file or ROM image may be corrupt.", super.getName()); showMessageDialog(this, mismatch, "ERROR - Data Mismatch", ERROR_MESSAGE); setButtonsUnselected(buttonGroup); } } @Override public void setName(String name) { super.setName(name); } @Override public int getType() { return Settings.TABLE_SWITCH; } @Override public void setDescription(String description) { super.setDescription(description); JTextArea descriptionArea = new JTextArea(description); descriptionArea.setOpaque(false); descriptionArea.setEditable(false); descriptionArea.setWrapStyleWord(true); descriptionArea.setLineWrap(true); descriptionArea.setMargin(new Insets(0,5,5,5)); add(descriptionArea, BorderLayout.SOUTH); } @Override public byte[] saveFile(byte[] input) { if (!super.getName().contains("Checksum Fix")) { if (!locked) { JRadioButton selectedButton = getSelectedButton(buttonGroup); System.arraycopy( switchStates.get(selectedButton.getText()), 0, input, getStorageAddress() - ramOffset, dataSize); } } return input; } public void setValues(String name, String input) { switchStates.put(name, asBytes(input)); } public byte[] getValues(String key) { return switchStates.get(key); } @Override public Dimension getFrameSize() { int height = verticalOverhead + 75; int width = horizontalOverhead; if (height < minHeight) { height = minHeight; } int minWidth = isLiveDataSupported() ? minWidthOverlay : minWidthNoOverlay; if (width < minWidth) { width = minWidth; } return new Dimension(width, height); } public ButtonGroup getButtonGroup() { return this.buttonGroup; } public Map<String, byte[]> getSwitchStates() { return this.switchStates; } @Override public void cursorUp() { } @Override public void cursorDown() { } @Override public void cursorLeft() { } @Override public void cursorRight() { } @Override public void shiftCursorUp() { } @Override public void shiftCursorDown() { } @Override public void shiftCursorLeft() { } @Override public void shiftCursorRight() { } @Override public boolean isLiveDataSupported() { return false; } @Override public boolean isButtonSelected() { if (buttonGroup.getSelection() == null) { return false; } else { return true; } } @Override public boolean equals(Object other) { // TODO: Validate DTC equals. try { if(null == other) { return false; } if(other == this) { return true; } if(!(other instanceof TableSwitch)) { return false; } TableSwitch otherTable = (TableSwitch)other; if( (null == this.getName() && null == otherTable.getName()) || (this.getName().isEmpty() && otherTable.getName().isEmpty()) ) { ;// Skip name compare if name is null or empty. } else if(!this.getName().equalsIgnoreCase(otherTable.getName())) { return false; } if(this.getDataSize() != otherTable.getDataSize()) { return false; } if(this.getSwitchStates() == otherTable.getSwitchStates()) { return true; } // Compare Map Keys Set<String> keys = new HashSet<String>(this.getSwitchStates().keySet()); Set<String> otherKeys = new HashSet<String>(otherTable.getSwitchStates().keySet()); if(keys.size() != otherKeys.size()) { return false; } if(!keys.containsAll(otherKeys)) { return false; } // Compare Map Values. Set<byte[]> values = new HashSet<byte[]>(this.getSwitchStates().values()); Set<byte[]> otherValues = new HashSet<byte[]>(otherTable.getSwitchStates().values()); if(values.equals(otherValues)) { return true; } // Compare DTC. Is there a better way to compare the DTC? for(String key : keys) { JRadioButton button = getButtonByText(this.getButtonGroup(), key); JRadioButton otherButton = getButtonByText(otherTable.getButtonGroup(), key); if(button.isSelected() != otherButton.isSelected()) { return false; } } return true; } catch(Exception ex) { // TODO: Log Exception. return false; } } @Override public void populateCompareValues(Table compareTable) { return; // Do nothing. } @Override public void calcCellRanges() { return; // Do nothing. } @Override public void drawTable() { return; // Do nothing. } @Override public void updateTableLabel() { return; // Do nothing. } @Override public void setCurrentScale(Scale curScale) { return; // Do nothing. } // returns the selected radio button in the specified group private static JRadioButton getSelectedButton(ButtonGroup group) { for (Enumeration<AbstractButton> e = group.getElements(); e.hasMoreElements(); ) { JRadioButton b = (JRadioButton)e.nextElement(); if (b.getModel() == group.getSelection()) { return b; } } return null; } // Unselects & disables all radio buttons in the specified group private static void setButtonsUnselected(ButtonGroup group) { for (Enumeration<AbstractButton> e = group.getElements(); e.hasMoreElements(); ) { JRadioButton b = (JRadioButton)e.nextElement(); b.setSelected(false); b.setEnabled(false); } } // returns the radio button based on its display text private static JRadioButton getButtonByText(ButtonGroup group, String text) { for (Enumeration<AbstractButton> e = group.getElements(); e.hasMoreElements(); ) { JRadioButton b = (JRadioButton)e.nextElement(); if (b.getText().equalsIgnoreCase(text)) { return b; } } return null; } }