public Rom unmarshallRom(Node rootNode, Rom rom)
      throws XMLParseException, RomNotFoundException, StackOverflowError, Exception {
    Node n;
    NodeList nodes = rootNode.getChildNodes();

    progress.update("Creating tables...", 15);

    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) ((double) i / (double) nodes.getLength() * 40);
      progress.update("Creating tables...", 10 + 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.getTable(unmarshallAttribute(n, "name", "unknown"));
          } catch (TableNotFoundException e) {
            /* table does not already exist (do nothing) */
          }

          try {
            table = unmarshallTable(n, table, rom);
            table.setRom(rom);
            rom.addTable(table);
          } catch (TableIsOmittedException ex) {
            // table is not supported in inherited def (skip)
            if (table != null) {
              rom.removeTable(table.getName());
            }
          } catch (XMLParseException ex) {
            LOGGER.error("Error unmarshalling rom", ex);
          }

        } else {
          /* unexpected element in Rom (skip)*/
        }
      } else {
        /* unexpected node-type in Rom (skip)*/
      }
    }
    return rom;
  }
  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(
                    (Object) rom.getTable(unmarshallAttribute(tableNode, "base", "none")));

      } catch (TableNotFoundException ex) {
        /* table not found, do nothing */

      } catch (NullPointerException ex) {
        JOptionPane.showMessageDialog(
            parent,
            new DebugPanel(ex, parent.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)
      if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("3D")) {
        table = new Table3D(settings);

      } else if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("2D")) {
        table = new Table2D(settings);

      } else if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("1D")) {
        table = new Table1D(settings);

      } else if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("X Axis")
          || unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("Y Axis")) {
        table = new Table1D(settings);

      } else if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("Static Y Axis")
          || unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("Static X Axis")) {
        table = new Table1D(settings);

      } else if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("Switch")) {
        table = new TableSwitch(settings);

      } else {
        throw new XMLParseException("Error loading table.");
      }
    }

    // unmarshall table attributes
    table.setName(unmarshallAttribute(tableNode, "name", table.getName()));
    table.setType(
        RomAttributeParser.parseTableType(
            unmarshallAttribute(tableNode, "type", String.valueOf(table.getType()))));
    if (unmarshallAttribute(tableNode, "beforeram", "false").equalsIgnoreCase("true")) {
      table.setBeforeRam(true);
    }

    if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("Static X Axis")
        || unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("Static Y Axis")) {
      table.setIsStatic(true);
      ((Table1D) table).setIsAxis(true);
    } else if (unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("X Axis")
        || unmarshallAttribute(tableNode, "type", "unknown").equalsIgnoreCase("Y Axis")) {
      ((Table1D) table).setIsAxis(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()))));
    table.setEndian(
        RomAttributeParser.parseEndian(
            unmarshallAttribute(tableNode, "endian", String.valueOf(table.getEndian()))));
    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() == Table.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() == Table.TABLE_2D) { // if table is 2D, parse axis

            if (RomAttributeParser.parseTableType(unmarshallAttribute(n, "type", "unknown"))
                    == Table.TABLE_Y_AXIS
                || RomAttributeParser.parseTableType(unmarshallAttribute(n, "type", "unknown"))
                    == Table.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());
              tempTable.setAxisParent(table);
              ((Table2D) table).setAxis(tempTable);
            }
          } else if (table.getType() == Table.TABLE_3D) { // if table is 3D, populate axiis
            if (RomAttributeParser.parseTableType(unmarshallAttribute(n, "type", "unknown"))
                == Table.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());
              tempTable.setAxisParent(table);
              ((Table3D) table).setXAxis(tempTable);

            } else if (RomAttributeParser.parseTableType(unmarshallAttribute(n, "type", "unknown"))
                == Table.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());
              tempTable.setAxisParent(table);
              ((Table3D) table).setYAxis(tempTable);
            }
          }

        } else if (n.getNodeName().equalsIgnoreCase("scaling")) {
          // check whether scale already exists. if so, modify, else use new instance
          Scale baseScale = new Scale();
          try {
            baseScale = table.getScaleByName(unmarshallAttribute(n, "name", "x"));
          } catch (Exception ex) {
          }

          table.setScale(unmarshallScale(n, baseScale));

        } else if (n.getNodeName().equalsIgnoreCase("data")) {
          // parse and add data to table
          DataCell dataCell = new DataCell();
          dataCell.setDisplayValue(unmarshallText(n));
          dataCell.setTable(table);
          table.addStaticDataCell(dataCell);

        } else if (n.getNodeName().equalsIgnoreCase("description")) {
          table.setDescription(unmarshallText(n));

        } else if (n.getNodeName().equalsIgnoreCase("state")) {
          // set on/off values for switch type
          if (unmarshallAttribute(n, "name", "").equalsIgnoreCase("on")) {
            ((TableSwitch) table).setOnValues(unmarshallAttribute(n, "data", "0"));

          } else if (unmarshallAttribute(n, "name", "").equalsIgnoreCase("off")) {
            ((TableSwitch) table).setOffValues(unmarshallAttribute(n, "data", "0"));
          }

        } else {
          /*unexpected element in Table (skip) */
        }
      } else {
        /* unexpected node-type in Table (skip) */
      }
    }

    return table;
  }