package com.bytezone.diskbrowser.wizardry; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import com.bytezone.diskbrowser.applefile.AbstractFile; import com.bytezone.diskbrowser.utilities.HexFormatter; class MazeLevel extends AbstractFile { public final int level; private List messages; private List monsters; private List items; public MazeLevel (byte[] buffer, int level) { super ("Level " + level, buffer); this.level = level; } @Override public String getHexDump () { // StringBuilder text = new StringBuilder (super.getHexDump ()); StringBuilder text = new StringBuilder (); text.append ("West walls/doors\n\n"); text.append (HexFormatter.format (buffer, 0, 120)); addWalls (text, 0); text.append ("\nSouth walls/doors\n\n"); text.append (HexFormatter.format (buffer, 120, 120)); addWalls (text, 120); text.append ("\nEast walls/doors\n\n"); text.append (HexFormatter.format (buffer, 240, 120)); addWalls (text, 240); text.append ("\nNorth walls/doors\n\n"); text.append (HexFormatter.format (buffer, 360, 120)); addWalls (text, 360); text.append ("\nEncounters\n\n"); text.append (HexFormatter.format (buffer, 480, 80)); addEncounters (text, 480); text.append ("\nExtras\n\n"); text.append (HexFormatter.format (buffer, 560, 200)); addExtras (text, 560); text.append ("\nIndex\n\n"); text.append ( String.format ("%04X: %s%n%n", 760, HexFormatter.getHexString (buffer, 760, 8))); text.append (" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); text.append (String.format ("%04X: ", 760)); for (int i = 0; i < 8; i++) { int val = buffer[760 + i] & 0xFF; text.append (String.format ("%X:%X ", val % 16, val / 16)); } String[] extras = { "", "Stairs", "Pit", "Chute", "Spinner", "Darkness", "Teleport", "Ouch", "Elevator", "Rock/Water", "Fizzle", "Message/Item", "Monster" }; List messageList = new ArrayList (); text.append ("\n\nAddresses/Messages/Items\n\n"); for (int i = 768, j = 0; i < 800; i += 2, j++) { int val = buffer[760 + j / 2] & 0xFF; String extra = (j % 2) == 0 ? extras[val % 16] : extras[val / 16]; MazeAddress address = getAddress (j); int k = (j % 2) == 0 ? val % 16 : val / 16; if (k == 11) messageList.add (address); text.append (String.format ("%04X-%04X-%04X: %04X %04X %04X : %X %X %s%n", i, i + 32, i + 64, address.level, address.row, address.column, j, k, extra)); } text.append ("\nRest\n\n"); text.append (HexFormatter.format (buffer, 864, buffer.length - 864)); text.append ("\n\n"); for (MazeAddress address : messageList) { Message message = getMessage (address.row); text.append (address.row + "\n"); text.append (message.getText ()); text.append ("\n"); } return text.toString (); } private void addWalls (StringBuilder text, int ptr) { text.append ("\n\n"); for (int i = 0; i < 20; i++) text.append (String.format (" Col %2d: %s%n", i, HexFormatter.getHexString (buffer, ptr + i * 6, 6))); } private void addEncounters (StringBuilder text, int ptr) { text.append ("\n\n"); for (int i = 0; i < 20; i++) text.append (String.format (" Col %2d: %s%n", i, HexFormatter.getHexString (buffer, ptr + i * 4, 4))); } private void addExtras (StringBuilder text, int ptr) { // StringBuilder text2 = new StringBuilder (); text.append ("\n\n"); for (int i = 0; i < 20; i++) { text.append (String.format (" Col %2d: ", i)); for (int j = 0; j < 10; j++) { int val = buffer[ptr + i * 10 + j] & 0xFF; int left = val / 16; // 0:F int right = val % 16; // 0:F text.append (String.format ("%X:%X ", right, left)); int c2 = buffer[760 + right / 2] & 0xFF; // 760 + 0:7 int d2 = (right % 2 == 0) ? c2 % 16 : c2 / 16; // text2.append (String.format ("%02X %02X ", c2, d2)); int c1 = buffer[760 + left / 2] & 0xFF; int d1 = (left % 2 == 0) ? c1 % 16 : c1 / 16; // text2.append (String.format ("%02X %02X%n", c1, d1)); } text.append ("\n"); // text2.append ("\n"); } // text.append (text2.toString ()); } @Override public BufferedImage getImage () { Dimension cellSize = new Dimension (22, 22); image = new BufferedImage (20 * cellSize.width + 1, 20 * cellSize.height + 1, BufferedImage.TYPE_USHORT_555_RGB); Graphics2D g = image.createGraphics (); g.setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); for (int row = 0; row < 20; row++) for (int column = 0; column < 20; column++) { MazeCell cell = getLocation (row, column); int x = column * cellSize.width; int y = image.getHeight () - (row + 1) * cellSize.height - 1; cell.draw (g, x, y); } return image; } public void setMessages (List messages) { this.messages = messages; } public void setMonsters (List monsters) { this.monsters = monsters; } public void setItems (List items) { this.items = items; } public MazeCell getLocation (int row, int column) { MazeAddress address = new MazeAddress (level, row, column); MazeCell cell = new MazeCell (address); // doors and walls int BYTES_PER_COL = 6; // 20 / 4 = 5 bytes + 1 wasted int CELLS_PER_BYTE = 4; // 2 bits each int BITS_PER_ROW = 2; int offset = column * BYTES_PER_COL + row / CELLS_PER_BYTE; int value = buffer[offset] & 0xFF; value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW; // push 0, 2, 4, 6 bits cell.westWall = ((value & 1) == 1); // use rightmost bit value >>>= 1; // push 1 bit cell.westDoor = ((value & 1) == 1); // use rightmost bit value = buffer[offset + 120] & 0xFF; value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW; cell.southWall = ((value & 1) == 1); value >>>= 1; cell.southDoor = ((value & 1) == 1); value = buffer[offset + 240] & 0xFF; value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW; cell.eastWall = ((value & 1) == 1); value >>>= 1; cell.eastDoor = ((value & 1) == 1); value = buffer[offset + 360] & 0xFF; value >>>= (row % CELLS_PER_BYTE) * BITS_PER_ROW; cell.northWall = ((value & 1) == 1); value >>>= 1; cell.northDoor = ((value & 1) == 1); // monster table BYTES_PER_COL = 4; // 3 bytes + 1 wasted CELLS_PER_BYTE = 8; // 1 bit each BITS_PER_ROW = 1; offset = column * BYTES_PER_COL + row / CELLS_PER_BYTE; // 4 bytes/column, 1 bit/row value = buffer[offset + 480] & 0xFF; value >>>= row % 8; cell.monsterLair = ((value & 1) == 1); // square extra offset = column * 10 + row / 2; // 10 bytes/column, 4 bits/row value = buffer[offset + 560] & 0xFF; int b = (row % 2 == 0) ? value % 16 : value / 16; // 0:F int c = buffer[760 + b / 2] & 0xFF; // 760:767 int d = (b % 2 == 0) ? c % 16 : c / 16; switch (d) { case 0: // normal break; case 1: cell.stairs = true; cell.addressTo = getAddress (b); break; case 2: cell.pit = true; break; case 3: cell.chute = true; cell.addressTo = getAddress (b); break; case 4: cell.spinner = true; break; case 5: cell.darkness = true; break; case 6: cell.teleport = true; cell.addressTo = getAddress (b); break; case 7: // ouchy cell.unknown = d; break; case 8: cell.elevator = true; MazeAddress elevatorAddress = getAddress (b); cell.elevatorTo = elevatorAddress.row; // HexFormatter.intValue (buffer[800 + b * 2], buffer[801 + b * 2]); cell.elevatorFrom = elevatorAddress.column; // HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]); break; case 9: // rock/water cell.rock = true; break; case 10: cell.spellsBlocked = true; break; case 11: MazeAddress messageAddress = getAddress (b); // int messageNum = HexFormatter.intValue (buffer[800 + b * 2], buffer[801 + b * 2]); Message m = getMessage (messageAddress.row); if (m != null) cell.message = m; cell.messageType = messageAddress.column; // HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]); int itemID = -1; if (cell.messageType == 2 && items != null) // obtain Item { // itemID = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]); itemID = messageAddress.level; cell.itemObtained = items.get (itemID); } if (cell.messageType == 5 && items != null) // requires Item { // itemID = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]); itemID = messageAddress.level; cell.itemRequired = items.get (itemID); } if (cell.messageType == 4) { // value = HexFormatter.intValue (buffer[768 + b * 2], buffer[769 + b * 2]); itemID = messageAddress.level; if (value <= 100) { cell.monsterID = value; cell.monsters = monsters; } else { int val = (value - 64536) * -1; System.out.println ("Value : " + val); // this gives Index error: 20410, Size 104 in Wizardry_III/legacy2.dsk if (items != null && val < items.size ()) cell.itemObtained = items.get (val); // check this if (cell.itemObtained == null) System.out.printf ("Item %d not found%n", val); } } break; case 12: MazeAddress monsterAddress = getAddress (b); // cell.monsterID = HexFormatter.intValue (buffer[832 + b * 2], buffer[833 + b * 2]); cell.monsterID = monsterAddress.column; cell.monsters = monsters; break; default: System.out.println ("Unknown extra: " + d); cell.unknown = d; break; } return cell; } private MazeAddress getAddress (int b) // 0:F { int x = b * 2; return new MazeAddress (HexFormatter.intValue (buffer[768 + x], buffer[769 + x]), HexFormatter.intValue (buffer[800 + x], buffer[801 + x]), HexFormatter.intValue (buffer[832 + x], buffer[833 + x])); } private Message getMessage (int messageNo) { if (messages == null) return null; for (Message m : messages) if (m.match (messageNo)) return m; // if (cell.message == null) // System.out.println ("message not found : " + messageNum); return null; } public int getRows () { return 20; } public int getColumns () { return 20; } /* * Pascal code decompiled by Tom Ewers * * TWALL = (OPEN, WALL, DOOR, HIDEDOOR); TSQUARE = (NORMAL, STAIRS, PIT, CHUTE, SPINNER, DARK, TRANSFER, OUCHY, BUTTONZ, ROCKWATE, FIZZLE, SCNMSG, ENCOUNTE); TMAZE = RECORD W : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL; S : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL; E : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL; N : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF TWALL; FIGHTS : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF 0..1; SQREXTRA : PACKED ARRAY[ 0..19] OF PACKED ARRAY[ 0..19] OF 0..15; SQRETYPE : PACKED ARRAY[ 0..15] OF TSQUARE; AUX0 : PACKED ARRAY[ 0..15] OF INTEGER; AUX1 : PACKED ARRAY[ 0..15] OF INTEGER; AUX2 : PACKED ARRAY[ 0..15] OF INTEGER; ENMYCALC : PACKED ARRAY[ 1..3] OF RECORD MINENEMY : INTEGER; MULTWORS : INTEGER; WORSE01 : INTEGER; RANGE0N : INTEGER; PERCWORS : INTEGER; END; END; */ }