package com.bytezone.diskbrowser.infocom; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.swing.tree.DefaultMutableTreeNode; import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.disk.DefaultAppleFileSource; import com.bytezone.diskbrowser.disk.Disk; import com.bytezone.diskbrowser.disk.DiskAddress; import com.bytezone.diskbrowser.utilities.HexFormatter; class CodeManager extends AbstractFile { private final Header header; private int codeSize; private final Map routines = new TreeMap (); public CodeManager (Header header) { super ("Code", header.buffer); this.header = header; } void addNodes (DefaultMutableTreeNode root, InfocomDisk disk) { root.setAllowsChildren (true); // should be set by now - do this better! codeSize = header.stringPointer - header.highMemory; int count = 0; for (Routine routine : routines.values ()) { String name = String.format ("%3d %s (%04X)", ++count, routine.getName (), routine.startPtr / 2); DefaultAppleFileSource dafs = new DefaultAppleFileSource (name, routine, disk); dafs.setSectors (getSectors (routine, disk.getDisk ())); DefaultMutableTreeNode node = new DefaultMutableTreeNode (dafs); node.setAllowsChildren (false); root.add (node); } } private List getSectors (Routine routine, Disk disk) { int blockNo = routine.startPtr / 256 + 48; int size = routine.length; List blocks = new ArrayList (); while (size > 0) { blocks.add (disk.getDiskAddress (blockNo++)); size -= 256; } return blocks; } void addMissingRoutines () { System.out.printf ("%nWalking the code block%n%n"); int total = 0; int ptr = header.highMemory; while (ptr < header.stringPointer) { if (ptr >= 0 && ptr % 2 == 1) // routine must start on a word boundary ptr++; if (containsRoutineAt (ptr)) { ptr += getRoutine (ptr).length; continue; } Routine routine = addRoutine (ptr, 0); if (routine == null) { System.out.printf ("Invalid routine found : %05X%n", ptr); ptr = findNextRoutine (ptr + 1); System.out.printf ("skipping to %05X%n", ptr); if (ptr == 0) break; } else { total++; ptr += routine.length; } } System.out.printf ("%n%d new routines found by walking the code block%n%n", total); } private int findNextRoutine (int address) { for (Routine routine : routines.values ()) if (routine.startPtr > address) return routine.startPtr; return 0; } @Override public String getText () { StringBuilder text = new StringBuilder (); int count = 0; int nextAddress = header.highMemory; text.append (" # Address Size Lines Strings Called Calls Gap Pack\n"); text.append ("--- ------- ---- ----- ------- ------ ----- --- ----\n"); for (Routine r : routines.values ()) { int gap = r.startPtr - nextAddress; text.append (String.format ( "%3d %05X %5d %3d %2d %3d %3d %4d %04X%n", ++count, r.startPtr, r.length, r.instructions.size (), r.strings, r.calledBy.size (), r.calls.size (), gap, r.startPtr / 2)); nextAddress = r.startPtr + r.length; } text.deleteCharAt (text.length () - 1); return text.toString (); } boolean containsRoutineAt (int address) { return (routines.containsKey (address)); } void addCodeRoutines () { List routines = header.objectManager.getCodeRoutines (); System.out.println ("Adding " + routines.size () + " code routines"); for (Integer address : routines) addRoutine (address, 0); } void addActionRoutines () { List routines = header.grammar.getActionRoutines (); System.out.println ("Adding " + routines.size () + " action routines"); for (Integer address : routines) addRoutine (address, 0); } Routine addRoutine (int address, int caller) { if (address == 0) // stack-based call return null; if (address > header.fileLength) return null; // check whether we already have this routine if (routines.containsKey (address)) { Routine routine = routines.get (address); routine.addCaller (caller); return routine; } // try to create a new Routine Routine r = new Routine (address, header, caller); if (r.length == 0) // invalid routine return null; // recursively add all routines called by this one routines.put (address, r); for (int ptr : r.calls) addRoutine (ptr, address); return r; } Routine getRoutine (int address) { return routines.get (address); } @Override public String getHexDump () { // this depends on codeSize being set after the strings have been processed return HexFormatter.format (buffer, header.highMemory, codeSize); } }