├── README.md ├── src └── main │ ├── java │ └── castlevania3 │ │ ├── BadPasswordException.java │ │ ├── GameState.java │ │ ├── TestPasswordDecoding.java │ │ ├── PasswordsImageGenerator.java │ │ └── Passwords.java │ └── resources │ └── castlevania3 │ └── images │ ├── a-r.png │ ├── a-w.png │ ├── b-r.png │ ├── b-w.png │ ├── c-r.png │ ├── c-w.png │ ├── d-r.png │ ├── d-w.png │ ├── e-r.png │ ├── e-w.png │ ├── f-r.png │ ├── f-w.png │ ├── g-r.png │ ├── g-w.png │ ├── h-r.png │ ├── h-w.png │ ├── heart.png │ ├── rosary.png │ ├── whip.png │ ├── akama-r.png │ ├── blank-r.png │ ├── blank-w.png │ ├── grant-0.png │ ├── grant-1.png │ ├── matrix-0.png │ ├── matrix-1.png │ ├── okuda-r.png │ ├── okuda-w.png │ ├── sypha-0.png │ ├── sypha-1.png │ ├── trevor-0.png │ ├── trevor-1.png │ ├── urata-r.png │ ├── urata-w.png │ ├── alucard-0.png │ ├── alucard-1.png │ ├── fujimoto-r.png │ ├── fujimoto-w.png │ ├── help_me-r.png │ └── help_me-w.png ├── .gitignore ├── pom.xml ├── LICENSE └── asm └── castlevania3.asm /README.md: -------------------------------------------------------------------------------- 1 | # Castlevania III Password Algorithm 2 | 3 | The project description is located [here](https://meatfighter.com/castlevania3-password). -------------------------------------------------------------------------------- /src/main/java/castlevania3/BadPasswordException.java: -------------------------------------------------------------------------------- 1 | package castlevania3; 2 | 3 | public class BadPasswordException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/a-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/a-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/a-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/a-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/b-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/b-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/b-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/b-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/c-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/c-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/c-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/c-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/d-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/d-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/d-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/d-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/e-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/e-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/e-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/e-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/f-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/f-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/f-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/f-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/g-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/g-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/g-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/g-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/h-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/h-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/h-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/h-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/heart.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/rosary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/rosary.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/whip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/whip.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/akama-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/akama-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/blank-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/blank-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/blank-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/blank-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/grant-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/grant-0.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/grant-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/grant-1.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/matrix-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/matrix-0.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/matrix-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/matrix-1.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/okuda-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/okuda-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/okuda-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/okuda-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/sypha-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/sypha-0.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/sypha-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/sypha-1.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/trevor-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/trevor-0.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/trevor-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/trevor-1.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/urata-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/urata-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/urata-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/urata-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/alucard-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/alucard-0.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/alucard-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/alucard-1.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/fujimoto-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/fujimoto-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/fujimoto-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/fujimoto-w.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/help_me-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/help_me-r.png -------------------------------------------------------------------------------- /src/main/resources/castlevania3/images/help_me-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meatfighter/castlevania3-passwords/HEAD/src/main/resources/castlevania3/images/help_me-w.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 11 | .mvn/wrapper/maven-wrapper.jar 12 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.meatfighter 6 | castlevania3-passwords 7 | 1.0-SNAPSHOT 8 | jar 9 | 10 | UTF-8 11 | 1.8 12 | 1.8 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/castlevania3/GameState.java: -------------------------------------------------------------------------------- 1 | package castlevania3; 2 | 3 | public class GameState { 4 | 5 | private String name; 6 | private int savePoint; 7 | private int partner; 8 | private int mode; 9 | private int toggleMaskIndex; 10 | 11 | public String getName() { 12 | return name; 13 | } 14 | 15 | public void setName(final String name) { 16 | this.name = name; 17 | } 18 | 19 | public int getSavePoint() { 20 | return savePoint; 21 | } 22 | 23 | public void setSavePoint(final int savePoint) { 24 | this.savePoint = savePoint; 25 | } 26 | 27 | public int getPartner() { 28 | return partner; 29 | } 30 | 31 | public void setPartner(final int partner) { 32 | this.partner = partner; 33 | } 34 | 35 | public int getMode() { 36 | return mode; 37 | } 38 | 39 | public void setMode(final int mode) { 40 | this.mode = mode; 41 | } 42 | 43 | public int getToggleMaskIndex() { 44 | return toggleMaskIndex; 45 | } 46 | 47 | public void setToggleMaskIndex(final int toggleMaskIndex) { 48 | this.toggleMaskIndex = toggleMaskIndex; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | final StringBuilder sb = new StringBuilder(); 54 | sb.append(String.format(" name: %s%n", name)); 55 | sb.append(String.format(" savePoint: %s%n", Passwords.SAVE_POINTS[savePoint])); 56 | sb.append(String.format(" partner: %s%n", Passwords.PARTNERS[partner])); 57 | sb.append(String.format(" mode: %s%n", Passwords.MODE_NAMES[mode])); 58 | sb.append(String.format("toggleMaskIndex: %d%n", toggleMaskIndex)); 59 | return sb.toString(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/castlevania3/TestPasswordDecoding.java: -------------------------------------------------------------------------------- 1 | package castlevania3; 2 | 3 | public final class TestPasswordDecoding { 4 | 5 | private static final String[] NAMES 6 | = { "", "B", "C", "D", "E", "F", "G", "H", "HELP ME", "AKAMA", "FUJIMOTO", "URATA", "OKUDA" }; 7 | 8 | public static void main(final String... args) throws BadPasswordException { 9 | 10 | final int[][] password = new int[4][4]; 11 | final GameState gameState = new GameState(); 12 | for (int mode = 0; mode < Passwords.MODES.length; ++mode) { 13 | for (int name = 0; name < NAMES.length; ++name) { 14 | for (int toggleMaskIndex = 0; toggleMaskIndex < Passwords.TOGGLE_MASKS.length; ++toggleMaskIndex) { 15 | for (int partner = 0; partner < Passwords.PARTNERS.length; ++partner) { 16 | for (int savePoint = 0; savePoint < Passwords.SAVE_POINTS.length; ++savePoint) { 17 | if (Passwords.isValidSavePoint(NAMES[name], savePoint, partner, mode)) { 18 | Passwords.encode(NAMES[name], savePoint, partner, mode, toggleMaskIndex, password); 19 | Passwords.decode(NAMES[name], password, gameState); 20 | if (gameState.getMode() != mode 21 | || gameState.getPartner() != partner 22 | || gameState.getSavePoint() != savePoint 23 | || gameState.getToggleMaskIndex() != toggleMaskIndex) { 24 | throw new RuntimeException(); 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | 34 | private TestPasswordDecoding() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/castlevania3/PasswordsImageGenerator.java: -------------------------------------------------------------------------------- 1 | package castlevania3; 2 | 3 | import java.awt.*; 4 | import java.awt.image.*; 5 | import java.io.*; 6 | import javax.imageio.*; 7 | 8 | public final class PasswordsImageGenerator { 9 | 10 | private static final int IMAGE_LEFT_MARGIN = 64; 11 | 12 | private static final int MATRIX_ELEMENT_SIZE = 24; 13 | 14 | private static final int MATRIX_LEFT_MARGIN = 4; 15 | 16 | private static final int MATRIX_TOP_MARGIN = 5; 17 | 18 | private static final int GLYPH_MARGIN = 2; 19 | 20 | private static final String[][] NAMES = { 21 | { "", "B", "C", "D", "E", "F", "G", "H", "HELP ME", "OKUDA", "URATA", "FUJIMOTO", }, 22 | { "", "B", "C", "D", "E", "F", "G", "H", "HELP ME", "AKAMA", "AKAMA", "OKUDA", "URATA", "FUJIMOTO", }, 23 | }; 24 | 25 | private static final int[][] MODES = { 26 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, 27 | { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, }, 28 | }; 29 | 30 | private static BufferedImage[] loadImages(final String... names) throws IOException { 31 | final BufferedImage[] images = new BufferedImage[names.length]; 32 | for (int i = names.length - 1; i >= 0; --i) { 33 | images[i] = ImageIO.read(PasswordsImageGenerator.class.getResourceAsStream( 34 | String.format("/castlevania3/images/%s.png", names[i]))); 35 | } 36 | return images; 37 | } 38 | 39 | private static int computeMaxAgentHeight(final BufferedImage[] agentImages) { 40 | int maxHeight = 0; 41 | for (final BufferedImage image : agentImages) { 42 | maxHeight = Math.max(maxHeight, image.getHeight()); 43 | } 44 | return maxHeight; 45 | } 46 | 47 | private static void renderAgents(final BufferedImage[][] agentImages, final BufferedImage[][] glyphImages, 48 | final BufferedImage matrixImage, final int y, final int maxAgentHeight, final Graphics2D g, 49 | final int name, final int mode, final int partner, final int toggleMaskIndex) { 50 | 51 | final BufferedImage agentImage = agentImages[toggleMaskIndex][partner]; 52 | final BufferedImage glyphImage = glyphImages[mode][name]; 53 | 54 | final int glyphX = (IMAGE_LEFT_MARGIN - glyphImage.getWidth()) / 2; 55 | final int glyphY = (matrixImage.getHeight() - (maxAgentHeight + glyphImage.getHeight())) / 2 - GLYPH_MARGIN; 56 | g.drawImage(glyphImage, glyphX, y + glyphY, null); 57 | 58 | final int agentX = (IMAGE_LEFT_MARGIN - agentImage.getWidth()) / 2; 59 | final int agentY = matrixImage.getHeight() - 1 - glyphY - agentImage.getHeight(); 60 | g.drawImage(agentImage, agentX, y + agentY, null); 61 | } 62 | 63 | private static void renderPassword(final int[][] password, final int x, final int y, final Graphics2D g, 64 | final BufferedImage matrixImage, final BufferedImage[] markImages) { 65 | g.drawImage(matrixImage, x, y, null); 66 | for (int i = password.length - 1; i >= 0; --i) { 67 | final int[] row = password[i]; 68 | final int Y = y + MATRIX_TOP_MARGIN + i * MATRIX_ELEMENT_SIZE; 69 | for (int j = row.length - 1; j >= 0; --j) { 70 | final int mark = row[j]; 71 | if (mark != Passwords.MARK_BLANK) { 72 | g.drawImage(markImages[mark - 1], x + MATRIX_LEFT_MARGIN + j * MATRIX_ELEMENT_SIZE, Y, null); 73 | } 74 | } 75 | } 76 | } 77 | 78 | public static void main(final String... args) throws IOException { 79 | 80 | final BufferedImage[] matrixImages = loadImages("matrix-0", "matrix-1"); 81 | final BufferedImage[] markImages = loadImages("whip", "rosary", "heart"); 82 | final BufferedImage[][] glyphImages = { 83 | loadImages("blank-w", "b-w", "c-w", "d-w", "e-w", "f-w", "g-w", "h-w", "help_me-w", "okuda-w", "urata-w", 84 | "fujimoto-w"), 85 | loadImages("blank-r", "b-r", "c-r", "d-r", "e-r", "f-r", "g-r", "h-r", "help_me-r", "akama-r", "akama-r", 86 | "okuda-r", "urata-r", "fujimoto-r"), 87 | }; 88 | final BufferedImage[][] agentImages = { 89 | loadImages("trevor-0", "sypha-0", "grant-0", "alucard-0"), 90 | loadImages("trevor-1", "sypha-1", "grant-1", "alucard-1"), 91 | }; 92 | 93 | final int maxAgentHeight = computeMaxAgentHeight(agentImages[0]); 94 | 95 | final BufferedImage image = new BufferedImage( 96 | IMAGE_LEFT_MARGIN + Passwords.SAVE_POINTS.length * matrixImages[0].getWidth(), 97 | (MODES[0].length + MODES[1].length) * Passwords.TOGGLE_MASKS.length * Passwords.PARTNERS.length 98 | * matrixImages[0].getHeight(), 99 | BufferedImage.TYPE_INT_RGB); 100 | final Graphics2D g = image.createGraphics(); 101 | 102 | final int[][] password = new int[4][4]; 103 | int row = 0; 104 | int passwordCount = 0; 105 | for (int i = 0; i < MODES.length; ++i) { 106 | for (int j = 0; j < MODES[i].length; ++j) { 107 | final int mode = MODES[i][j]; 108 | final String name = NAMES[i][j]; 109 | for (int toggleMaskIndex = 0; toggleMaskIndex < Passwords.TOGGLE_MASKS.length; ++toggleMaskIndex) { 110 | for (int partner = 0; partner < Passwords.PARTNERS.length; ++partner) { 111 | int x = IMAGE_LEFT_MARGIN; 112 | final int y = row * matrixImages[0].getHeight(); 113 | renderAgents(agentImages, glyphImages, matrixImages[0], y, maxAgentHeight, g, j, i, 114 | partner, toggleMaskIndex); 115 | for (int savePoint = 0; savePoint < Passwords.SAVE_POINTS.length; ++savePoint) { 116 | if (Passwords.isValidSavePoint(name, savePoint, partner, mode)) { 117 | Passwords.encode(name, savePoint, partner, mode, toggleMaskIndex, 118 | password); 119 | renderPassword(password, x, y, g, matrixImages[(row & 1) ^ (savePoint & 1)], 120 | markImages); 121 | ++passwordCount; 122 | } 123 | x += matrixImages[0].getWidth(); 124 | } 125 | ++row; 126 | } 127 | } 128 | } 129 | } 130 | 131 | g.dispose(); 132 | ImageIO.write(image, "png", new File("passwords.png")); 133 | 134 | System.out.format("Rendered %d passwords.%n", passwordCount); 135 | } 136 | 137 | private PasswordsImageGenerator() { 138 | } 139 | } -------------------------------------------------------------------------------- /src/main/java/castlevania3/Passwords.java: -------------------------------------------------------------------------------- 1 | package castlevania3; 2 | 3 | public final class Passwords { 4 | 5 | public static final String[] SAVE_POINTS = { 6 | "1-1 Warakiya Village - Skull Knight", // 0x00 7 | "2-1 Clock Tower of Untimely Death (climb up) - Nasty Grant", // 0x01 8 | "2-4 Clock Tower of Untimely Death (climb down to the Forest of Darkness)", // 0x02 9 | "3-0 Forest of Darkness (from the Warakiya Village) - Cyclops plus Sypha or Murky Marsh", // 0x03 10 | "3-1 Forest of Darkness (from the Clock Tower) - Cyclops plus Sypha or Murky Marsh", // 0x04 11 | "4-A Haunted Ship of Fools - Snake Man Sentinel and Death Fire (Mummies and Cyclops)", // 0x05 12 | "5-A Tower of Terror - Frankenstein's Monster", // 0x06 13 | "6-A Causeway of Chaos - Water Dragons", // 0x07 14 | "4-1 Murky Marsh of Morbid Morons - Giant Bat", // 0x08 15 | "5-1 Caves (entering) - Alucard", // 0x09 16 | "5-6 Caves (escaping) - Skull Knight King or Sunken City", // 0x0A 17 | "6-1 Sunken City of Poltergeists - Bone Dragon King", // 0x0B 18 | "6-1 Castle Basement - Frankenstein's Monster", // 0x0C 19 | "7-1 Morbid Mountains - Giant Bat and Death Fire King (Mummies, Cyclops, and Leviathan)", // 0x0D 20 | "7-A Rampart and Lookout Tower - Death Fire King (Mummies, Cyclops, and Leviathan)", // 0x0E 21 | "8-1 Castle Entrance - Grim Reaper", // 0x0F 22 | "9-1 Villa and Waterfalls - Doppelgänger", // 0x10 23 | "A-1 Clock Tower and Castle Keep - Dracula", // 0x11 24 | }; 25 | 26 | public static final String[] PARTNERS = { 27 | "none", // 0 28 | "Sypha Belnades", // 1 29 | "Grant Danasty", // 2 30 | "Alucard", // 3 31 | }; 32 | 33 | public static final int[] TOGGLE_MASKS = { 34 | 0x55, // 0 - even bits 35 | 0xAA, // 1 - odd bits 36 | }; 37 | 38 | // 03:B8B6 - valid save points for each partner 39 | private static final int[] VALID_SAVE_POINT_BITS = { 0xFF_FF_FF, 0x07_03_FF, 0x2F_FF_FF, 0x00_3D_FF, }; 40 | 41 | private static final boolean[][] VALID_SAVE_POINTS = new boolean[PARTNERS.length][SAVE_POINTS.length]; 42 | 43 | static { 44 | for (int partner = PARTNERS.length - 1; partner >= 0; --partner) { 45 | for (int savePoint = SAVE_POINTS.length - 1; savePoint >= 0; --savePoint) { 46 | VALID_SAVE_POINTS[partner][savePoint] = (VALID_SAVE_POINT_BITS[partner] 47 | & (0x80_00_00 >> savePoint)) != 0; 48 | } 49 | } 50 | } 51 | 52 | public static final int MARK_BLANK = 0; 53 | public static final int MARK_WHIP = 1; 54 | public static final int MARK_ROSERY = 2; 55 | public static final int MARK_HEART = 3; 56 | 57 | private static final char[] MARK_NAMES = { '.', 'W', 'R', 'H' }; 58 | 59 | // 03:B6B2 - scramble sequences 60 | private static final int[][] SCRAMBLES = { 61 | { 0x00, 0x33, 0x20, 0x13, 0x22, 0x01, 0x11, 0x03, 0x32, }, 62 | { 0x12, 0x10, 0x02, 0x32, 0x23, 0x13, 0x30, 0x21, 0x01, }, 63 | { 0x31, 0x13, 0x01, 0x22, 0x10, 0x30, 0x33, 0x03, 0x21, }, 64 | }; 65 | 66 | // 03:B937 - scramble sequence selectors 67 | private static final int[] SELECTORS = { 0x01, 0x1B, 0x02, 0x35, 0x19, 0x03, 0x37, 0x1A, 0x36, }; 68 | 69 | public static final int MODE_NORMAL = 0; 70 | public static final int MODE_HARD = 1; 71 | 72 | public static final int[] MODES = { 73 | MODE_NORMAL, // 0 74 | MODE_HARD, // 1 75 | }; 76 | 77 | public static final String[] MODE_NAMES = { 78 | "Normal", // 0 79 | "Hard", // 1 80 | }; 81 | 82 | // 03:B6E6 - sum of the elements 83 | private static final int NAME_HASH_SEED = 28; 84 | 85 | // glyph tile indices from the Pattern Table 86 | private static final int TILE_SPACE = 0x00; 87 | private static final int TILE_PERIOD = 0x4B; 88 | private static final int TILE_A = 0x50; 89 | private static final int TILE_EXCLAMATION_MARK = 0x6A; 90 | private static final int TILE_QUESTION_MARK = 0x6B; 91 | 92 | private static boolean isSpecialName(final String name) { 93 | switch(name) { 94 | case "AKAMA": 95 | case "FUJIMOTO": 96 | case "URATA": 97 | case "OKUDA": 98 | return true; 99 | default: 100 | return false; 101 | } 102 | } 103 | 104 | public static boolean isValidSavePoint(final String name, final int savePoint, final int partner, final int mode) { 105 | return (mode == MODE_HARD) || VALID_SAVE_POINTS[partner][savePoint] || isSpecialName(name); 106 | } 107 | 108 | private static int toMark(final char markName) { 109 | switch(Character.toUpperCase(markName)) { 110 | case 'W': 111 | return MARK_WHIP; 112 | case 'R': 113 | return MARK_ROSERY; 114 | case 'H': 115 | return MARK_HEART; 116 | default: 117 | return MARK_BLANK; 118 | } 119 | } 120 | 121 | private static char toMarkName(final int mark) { 122 | return MARK_NAMES[mark]; 123 | } 124 | 125 | private static int toTile(final char c) { 126 | switch(c) { 127 | case ' ': 128 | return TILE_SPACE; 129 | case '.': 130 | return TILE_PERIOD; 131 | case '!': 132 | return TILE_EXCLAMATION_MARK; 133 | case '?': 134 | return TILE_QUESTION_MARK; 135 | default: 136 | if (c >= 'A' && c <= 'Z') { 137 | return c - 'A' + TILE_A; 138 | } 139 | throw new RuntimeException("Invalid character."); 140 | } 141 | } 142 | 143 | private static int hashName(final String name) { 144 | int nameHash = NAME_HASH_SEED; 145 | for (int i = name.length() - 1; i >= 0; --i) { 146 | nameHash += toTile(name.charAt(i)); 147 | } 148 | return nameHash & 7; 149 | } 150 | 151 | private static int encodePayload(final String name, final int savePoint, final int partner, 152 | final int mode, final int toggleMaskIndex) { 153 | return (hashName(name) << 5) | ((savePoint & 1) << 4) | (toggleMaskIndex << 3) | (partner << 1) | mode; 154 | } 155 | 156 | private static int hashPayload(final int payload, final int savePoint, final int toggleMaskIndex) { 157 | 158 | final int nibbleSum = 0x0F & ((payload >> 4) + payload); 159 | 160 | final int toggledPayload = TOGGLE_MASKS[toggleMaskIndex] ^ payload; 161 | final int toggledNibbleSum = 0x0F & ((toggledPayload >> 4) + toggledPayload); 162 | 163 | final int sums = (nibbleSum << 4) | toggledNibbleSum; 164 | 165 | return 0xFF & (savePoint + sums); 166 | } 167 | 168 | private static void clearPassword(final int[][] password) { 169 | for (int i = password.length - 1; i >= 0; --i) { 170 | final int[] row = password[i]; 171 | for (int j = row.length - 1; j >= 0; --j) { 172 | row[j] = MARK_BLANK; 173 | } 174 | } 175 | } 176 | 177 | public static int[][] encode(final String name, final int savePoint, final int partner, final int mode, 178 | final int toggleMaskIndex) { 179 | final int[][] password = new int[4][4]; 180 | encode(name, savePoint, partner, mode, toggleMaskIndex, password); 181 | return password; 182 | } 183 | 184 | public static int[][] encode(final GameState gameState) { 185 | final int[][] password = new int[4][4]; 186 | encode(gameState, password); 187 | return password; 188 | } 189 | 190 | public static void encode(final GameState gameState, final int[][] password) { 191 | encode(gameState.getName(), gameState.getSavePoint(), gameState.getPartner(), gameState.getMode(), 192 | gameState.getToggleMaskIndex(), password); 193 | } 194 | 195 | public static void encode(final String name, final int savePoint, final int partner, final int mode, 196 | final int toggleMaskIndex, final int[][] password) { 197 | 198 | clearPassword(password); 199 | 200 | final int selector = SELECTORS[savePoint >> 1]; 201 | final int selectorRowCol = (0x30 & selector) | (0x03 & (selector >> 2)); 202 | int[] scrambles = null; 203 | for (int i = SCRAMBLES.length - 1; i >= 0; --i) { 204 | scrambles = SCRAMBLES[i]; 205 | if (scrambles[0] == selectorRowCol) { 206 | break; 207 | } 208 | } 209 | 210 | final int payload = encodePayload(name, savePoint, partner, mode, toggleMaskIndex); 211 | final int payloadHash = hashPayload(payload, savePoint, toggleMaskIndex); 212 | 213 | for (int i = scrambles.length - 1; i >= 0; --i) { 214 | final int row = scrambles[i] >> 4; 215 | final int col = scrambles[i] & 0x03; 216 | password[row][col] = (i == 0) 217 | ? (selector & 3) 218 | : (((payload >> (i - 1)) & 1) << 1) | ((payloadHash >> (i - 1)) & 1); 219 | } 220 | } 221 | 222 | private static int[] findScrambles(final int[][] password) throws BadPasswordException { 223 | 224 | int[] scrambles = null; 225 | for (int i = SCRAMBLES.length - 1; i >= 0; --i) { 226 | final int rowCol = SCRAMBLES[i][0]; 227 | if (password[rowCol >> 4][rowCol & 0x03] != MARK_BLANK) { 228 | if (scrambles == null) { 229 | scrambles = SCRAMBLES[i]; 230 | } else { 231 | throw new BadPasswordException(); 232 | } 233 | } 234 | } 235 | 236 | if (scrambles == null) { 237 | throw new BadPasswordException(); 238 | } 239 | 240 | return scrambles; 241 | } 242 | 243 | private static int findSelectorIndex(final int[][] password, final int[] scrambles) { 244 | final int rowCol = scrambles[0]; 245 | final int selector = (rowCol & 0x30) | ((rowCol & 0x03) << 2) | password[rowCol >> 4][rowCol & 0x03]; 246 | int selectorIndex = SELECTORS.length - 1; 247 | while (SELECTORS[selectorIndex] != selector && selectorIndex > 0) { 248 | --selectorIndex; 249 | } 250 | return selectorIndex; 251 | } 252 | 253 | private static void verifyAllNonblanksInScrambles(final int[][] password, final int[] scrambles) 254 | throws BadPasswordException { 255 | 256 | for (int row = password.length - 1; row >= 0; --row) { 257 | final int[] passwordRow = password[row]; 258 | middle: for (int col = passwordRow.length - 1; col >= 0; --col) { 259 | final int mark = passwordRow[col]; 260 | if (mark != MARK_BLANK) { 261 | for (int k = scrambles.length - 1; k >= 0; --k) { 262 | if (row == (scrambles[k] >> 4) && col == (scrambles[k] & 0x03)) { 263 | continue middle; 264 | } 265 | } 266 | throw new BadPasswordException(); 267 | } 268 | } 269 | } 270 | } 271 | 272 | private static int decodePayload(final int[][] password, final int[] scrambles) { 273 | return decodeData(password, scrambles, 1); 274 | } 275 | 276 | private static int decodePayloadHash(final int[][] password, final int[] scrambles) { 277 | return decodeData(password, scrambles, 0); 278 | } 279 | 280 | private static int decodeData(final int[][] password, final int[] scrambles, final int shift) { 281 | int data = 0; 282 | for (int i = scrambles.length - 1; i > 0; --i) { 283 | final int rowCol = scrambles[i]; 284 | data = (data << 1) | ((password[rowCol >> 4][rowCol & 0x03] >> shift) & 1); 285 | } 286 | return data; 287 | } 288 | 289 | public static GameState decode(final String name, final int[][] password) throws BadPasswordException { 290 | final GameState gameState = new GameState(); 291 | decode(name, password, gameState); 292 | return gameState; 293 | } 294 | 295 | public static void decode(final String name, final int[][] password, GameState gameState) 296 | throws BadPasswordException { 297 | 298 | final int[] scrambles = findScrambles(password); 299 | final int selectorIndex = findSelectorIndex(password, scrambles); 300 | verifyAllNonblanksInScrambles(password, scrambles); 301 | 302 | final int state = decodePayload(password, scrambles); 303 | final int nameHash = state >> 5; 304 | final int savePoint = (selectorIndex << 1) | ((state >> 4) & 1); 305 | final int toggleMaskIndex = (state >> 3) & 1; 306 | final int partner = (state >> 1) & 3; 307 | final int mode = state & 1; 308 | 309 | if (!isValidSavePoint(name, savePoint, partner, mode) 310 | || hashName(name) != nameHash 311 | || hashPayload(state, savePoint, toggleMaskIndex) != decodePayloadHash(password, scrambles)) { 312 | throw new BadPasswordException(); 313 | } 314 | 315 | gameState.setMode(mode); 316 | gameState.setName(name); 317 | gameState.setPartner(partner); 318 | gameState.setSavePoint(savePoint); 319 | gameState.setToggleMaskIndex(toggleMaskIndex); 320 | } 321 | 322 | public static void print(final int[][] password) { 323 | for (int row = 0; row < password.length; ++row) { 324 | final int[] passwordRow = password[row]; 325 | final StringBuilder sb = new StringBuilder(); 326 | for (int col = 0; col < passwordRow.length; ++col) { 327 | if (sb.length() > 0) { 328 | sb.append(' '); 329 | } 330 | sb.append(toMarkName(passwordRow[col])); 331 | } 332 | System.out.println(sb); 333 | } 334 | } 335 | 336 | public static int[][] parse(final char[][] password) { 337 | final int[][] parsed = new int[4][4]; 338 | parse(password, parsed); 339 | return parsed; 340 | } 341 | 342 | public static void parse(final char[][] password, final int[][] parsed) { 343 | for (int i = password.length - 1; i >= 0; --i) { 344 | final char[] passwordRow = password[i]; 345 | final int[] parsedRow = parsed[i]; 346 | for (int j = passwordRow.length - 1; j >= 0; --j) { 347 | parsedRow[j] = toMark(passwordRow[j]); 348 | } 349 | } 350 | } 351 | 352 | public static int[][] parse(final String[] password) { 353 | final int[][] parsed = new int[4][4]; 354 | parse(password, parsed); 355 | return parsed; 356 | } 357 | 358 | public static void parse(final String[] password, final int[][] parsed) { 359 | for (int i = password.length - 1; i >= 0; --i) { 360 | final String passwordRow = password[i]; 361 | final int[] parsedRow = parsed[i]; 362 | for (int j = passwordRow.length() - 1; j >= 0; --j) { 363 | parsedRow[j] = toMark(passwordRow.charAt(j)); 364 | } 365 | } 366 | } 367 | 368 | public static int[][] parse(final String password) { 369 | final int[][] parsed = new int[4][4]; 370 | parse(password, parsed); 371 | return parsed; 372 | } 373 | 374 | public static void parse(final String password, final int[][] parsed) { 375 | for (int i = parsed.length - 1; i >= 0; --i) { 376 | final int[] parsedRow = parsed[i]; 377 | for (int j = parsed.length - 1; j >= 0; --j) { 378 | parsedRow[j] = toMark(password.charAt((i << 2) | j)); 379 | } 380 | } 381 | } 382 | 383 | private Passwords() { 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 489 | USA 490 | 491 | Also add information on how to contact you by electronic and paper mail. 492 | 493 | You should also get your employer (if you work as a programmer) or your 494 | school, if any, to sign a "copyright disclaimer" for the library, if 495 | necessary. Here is a sample; alter the names: 496 | 497 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 498 | library `Frob' (a library for tweaking knobs) written by James Random 499 | Hacker. 500 | 501 | , 1 April 1990 502 | Ty Coon, President of Vice 503 | 504 | That's all there is to it! 505 | -------------------------------------------------------------------------------- /asm/castlevania3.asm: -------------------------------------------------------------------------------- 1 | ; Segments of "Castlevania III: Dracula's Curse" related to password encoding and decoding. 2 | 3 | ; RAM map 4 | ; 5 | ; Addr Size Description 6 | ; ---- ---- ----------------------------------------------------------------------------------------------------------- 7 | ; 0004 toggleMask (50 or A0) 8 | ; 0008 9 _unscrambledPassword 9 | ; 0010 _nameHash (00--07) 10 | ; 001A frameCounter (00--FF) 11 | ; 002E savePoint (00--11) 12 | ; 002F escapedClockTower (00 = no, 01 = yes) 13 | ; 0032 block (00--0E) 14 | ; 0032 subBlock (zero-indexed) 15 | ; 0035 lives 16 | ; 003A partner (FF = none, 01 = Sypha, 02 = Grant, 03 = Alucard) 17 | ; 003C playerEnergy (00--40) 18 | ; 003D bossEnergy (00--40) 19 | ; 00FF hud (B0 = visible, B1 = hidden) 20 | ; 0084 hearts 21 | ; 0400 16 SPRITE_0 22 | ; 041C 16 MARK_YS 23 | ; 0438 16 MARK_XS 24 | ; 0454 16 MARK_ATTRIBS 25 | ; 048C 16 SPRITE_1 26 | ; 0788 payload (b765 = name hash, b4 = savePoint.0, b3 = frameCounter.0, b21 = partner, b0 = mode) 27 | ; 0789 payloadHash 28 | ; 078A nameHash (00--07) 29 | ; 078B badPasswordReason 30 | ; 078F scramblesRowIndex (00--02) 31 | ; 0790 16 password (b54 = row, b32 = column, b10 = mark [0 = none, 1 = whip, 2 = rosary, 3 = heart]) 32 | ; 07A0 9 unscrambledPassword 33 | ; 07F6 mode (00 = normal, 01 = hard) 34 | ; 07F8 8 name ([ ] = 00, [.] = 4B, [A-Z!?] = 50--6B) 35 | 36 | ; ROM map 37 | ; 38 | ; bk:addr Description 39 | ; ------- ------------------------------------------------------------------------------------------------------------- 40 | ; 00:8FB0 convertBlockSubBlockToSavePoint() 41 | ; 00:8FD1 BLOCK_SUB_BLOCK_TO_SAVE_POINT (45 bytes) 42 | ; 00:8FFE checkForSpecialNames() 43 | ; 00:9005 checkForSomeSpecialNames() 44 | ; 00:9031 isSpecialName() 45 | ; 00:904D SPECIAL_NAME_ADDRESSES (5 words) 46 | ; 00:9057 SPECIAL_NAMES (40 bytes) 47 | ; 00:90C0 resetHeartsAndEnergy() 48 | ; 00:90CD reset4Aand4D() 49 | ; 50 | ; 03:B2F8 submitPassword() 51 | ; 03:B339 handleValidPassword: 52 | ; 03:B5AF showPassword() 53 | ; 03:B647 _drawPassword() 54 | ; 03:B64A encode() 55 | ; 03:B656 decode() 56 | ; 03:B675 resetPasswordVars() 57 | ; 03:B682 clearPassword() 58 | ; 03:B68F unscramblePassword() 59 | ; 03:B6B2 SCRAMBLES (27 bytes) 60 | ; 03:B6CD hashName() 61 | ; 03:B6E6 NAME_HASH_SEEDS (8 bytes) 62 | ; 03:B6EE encodePayload() 63 | ; 03:B756 extractPayloadVarsAndVerifyNameHash() 64 | ; 03:B79A findScrambles() 65 | ; 03:B72A hashPayload() 66 | ; 03:B7D6 throwDefaultBadPassword: 67 | ; 03:B7D8 throwBadPassword: 68 | ; 03:B7DF LEADERS (3 bytes) 69 | ; 03:B7E2 squeezeRowCol() 70 | ; 03:B7F0 verifyAllNonblanksInScrambles() 71 | ; 03:B82C decodePayloadAndPayloadHash() 72 | ; 03:B865 verifyPayloadHash() 73 | ; 03:B87F isValidSavePoint() 74 | ; 03:B8B6 VALID_SAVE_POINTS (6 bytes) 75 | ; 03:B8BC BIT_MASKS (8 bytes) 76 | ; 03:B8C4 createUnscrambledPassword: 77 | ; 03:B940 drawPassword() 78 | ; 03:B937 SELECTORS (9 bytes) 79 | ; 03:B97E SPRITE_B (4 bytes) 80 | ; 03:B982 SPRITE_A (4 bytes) 81 | ; 03:B986 MATRIX_COORDINATES (32 bytes) 82 | ; 83 | ; 7F:E2E6 switchBanks() 84 | ; 7F:E593 _checkForSomeSpecialNames() 85 | 86 | 87 | ; convertBlockSubBlockToSavePoint() 88 | 00:8FB0 LDA $0032 89 | 00:8FB2 ASL A 90 | 00:8FB3 CLC 91 | 00:8FB4 ADC $0032 92 | 00:8FB6 TAY ; Y = 3 * block; 93 | 00:8FB7 LDA $8FD1,Y ; if (subBlock > BLOCK_SUB_BLOCK_TO_SAVE_POINT[Y]) { 94 | 00:8FBA CMP $0033 ; ++Y; 95 | 00:8FBC BCS $8FBF ; } 96 | 00:8FBE INY 97 | 00:8FBF LDA $8FD2,Y 98 | 00:8FC2 CMP #$03 99 | 00:8FC4 BEQ $8FC9 100 | 00:8FC6 STA $002E ; savePoint = (BLOCK_SUB_BLOCK_TO_SAVE_POINT[Y + 1] != 3 || escapedClockTower == 0) 101 | ; ? BLOCK_SUB_BLOCK_TO_SAVE_POINT[Y + 1] : 4; 102 | 00:8FC8 RTS 103 | 00:8FC9 LDY $002F 104 | 00:8FCB BEQ $8FC6 105 | 00:8FCD LDA #$04 106 | 00:8FCF BNE $8FC6 ; return; 107 | 108 | ; BLOCK_SUB_BLOCK_TO_SAVE_POINT 109 | ; Each row corresponds to a block. Each block contains 1 or 2 save points. If a block contains 1 save point, the first 110 | ; column contains $10 and the second and third columns both contain its save point. Otherwise, if the sub-block exceeds 111 | ; the value in the first column, then the save point in the third column is used; else, the save point in the second 112 | ; column is used. 113 | 00:8FD1 .byte $10, $00, $00 ; 0: 1-1 114 | 00:8FD4 .byte $02, $01, $02 ; 1: 2-1, 2-4 115 | 00:8FD7 .byte $10, $03, $04 ; 2: 3-0, 3-1 116 | 00:8FDA .byte $10, $05, $05 ; 3: 4-A 117 | 00:8FDD .byte $10, $06, $06 ; 4: 5-A 118 | 00:8FE0 .byte $10, $07, $07 ; 5: 6-A 119 | 00:8FE3 .byte $10, $08, $08 ; 6: 4-1 120 | 00:8FE6 .byte $04, $09, $0A ; 7: 5-1, 5-6 121 | 00:8FE9 .byte $10, $0B, $0B ; 8: 6-1 122 | 00:8FEC .byte $10, $0C, $0C ; 9: 6-1' 123 | 00:8FEF .byte $10, $0D, $0D ; A: 7-1 124 | 00:8FF2 .byte $10, $0E, $0E ; B: 7-A 125 | 00:8FF5 .byte $10, $0F, $0F ; C: 8-1 126 | 00:8FF8 .byte $10, $10, $10 ; D: 9-1 127 | 00:8FFB .byte $10, $11, $11 ; E: A-1 128 | 129 | ; checkForSpecialNames() 130 | ; out: carry (false = no, true = yes) 131 | ; Y (1 = "HELP ME ", 2 = "AKAMA ", 3 = "OKUDA ", 4, = "URATA ", 5 = "FUJIMOTO") 132 | 00:8FFE LDY #$00 ; Y = 0; 133 | 00:9000 JSR $9031 ; if (isSpecialName()) { 134 | 00:9003 BCS $902E ; Y = 1; 135 | ; return; 136 | ; } 137 | 138 | ; checkForSomeSpecialNames() 139 | ; out: carry (false = no, true = yes) 140 | ; Y (2 = "AKAMA ", 3 = "OKUDA ", 4, = "URATA ", 5 = "FUJIMOTO") 141 | 00:9005 LDY #$02 ; Y = 2; 142 | 00:9007 JSR $9031 ; if (isSpecialName()) { 143 | 00:900A BCS $902B ; Y = 2; 144 | ; return; 145 | ; } 146 | 147 | 00:900C LDY #$04 ; Y = 4; 148 | 00:900E JSR $9031 ; if (isSpecialName()) { 149 | 00:9011 BCS $9022 ; Y = 3; 150 | ; return; 151 | ; } 152 | 153 | 00:9013 LDY #$06 ; Y = 6; 154 | 00:9015 JSR $9031 ; if (isSpecialName()) { 155 | 00:9018 BCS $9025 ; Y = 4; 156 | ; return; 157 | ; } 158 | 159 | 00:901A LDY #$08 ; Y = 8; 160 | 00:901C JSR $9031 ; if (isSpecialName()) { 161 | 00:901F BCS $9028 ; Y = 5; 162 | ; return; 163 | ; } 164 | 165 | 00:9021 RTS 166 | 167 | 00:9022 LDY #$03 168 | 00:9024 RTS 169 | 170 | 00:9025 LDY #$04 171 | 00:9027 RTS 172 | 173 | 00:9028 LDY #$05 174 | 00:902A RTS 175 | 176 | 00:902B LDY #$02 177 | 00:902D RTS 178 | 179 | 00:902E LDY #$01 180 | 00:9030 RTS 181 | 182 | ; isSpecialName() 183 | ; in: Y (2 * special name index) 184 | ; out: carry (false = no, true = yes) 185 | 00:9031 LDA $904D,Y 186 | 00:9034 STA $0008 187 | 00:9036 LDA $904E,Y 188 | 00:9039 STA $0009 ; specialName = *SPECIAL_NAME_ADDRESSES[Y / 2]; 189 | 00:903B LDY #$00 ; 190 | 00:903D LDA $07F8,Y ; for (Y = 0; Y < 8; ++Y) { 191 | 00:9040 CMP ($08),Y ; if (name[Y] != specialName[Y]) { 192 | 00:9042 BNE $904B ; carry = false; 193 | 00:9044 INY ; return; 194 | 00:9045 CPY #$08 ; } 195 | 00:9047 BNE $903D ; } 196 | 197 | 00:9049 SEC ; carry = true; 198 | 00:904A RTS ; return; 199 | 200 | 00:904B CLC 201 | 00:904C RTS 202 | 203 | ; SPECIAL_NAME_ADDRESSES 204 | 00:904D .word $9057, $905F, $9067, $906F, $9077 205 | 206 | ; SPECIAL_NAMES 207 | 00:9057 .byte $57, $54, $5B, $5F, $00, $5C, $54, $00 ; "HELP ME " // Start and continue with 10 lives. 208 | 00:905F .byte $50, $5A, $50, $5C, $50, $00, $00, $00 ; "AKAMA " // Start in Hard Mode alone. 209 | 00:9067 .byte $5E, $5A, $64, $53, $50, $00, $00, $00 ; "OKUDA " // Start in Normal Mode with Alucard. 210 | 00:096F .byte $64, $61, $50, $63, $50, $00, $00, $00 ; "URATA " // Start in Normal Mode with Sypha. 211 | 00:0977 .byte $55, $64, $59, $58, $5C, $5E, $63, $5E ; "FUJIMOTO" // Start in Normal Mode with Grant. 212 | 213 | 214 | 00:907F LDA #$B0 215 | 00:9081 STA $00FF ; hud = 0xB0; // visible 216 | 00:9083 JSR $90CD ; reset4Aand4D(); 217 | 00:9086 JSR $90C0 ; resetHeartsAndEnergy(); 218 | 00:9089 LDA #$02 219 | 00:908B STA $003E ; mem[$003E] = 0x02; 220 | 00:908D JSR $8FFE ; checkForSpecialNames(); // results in carry and Y 221 | 00:9090 BCC $90B7 ; if (carry == 1) { // if special name 222 | 00:9092 DEY ; if (--Y == 0) { 223 | 00:9093 BEQ $90BC ; lives = 10; 224 | ; return; // "HELP ME ": start/continue with 10 lives 225 | ; } 226 | 00:9095 DEY ; if (--Y == 0) { 227 | 00:9096 BEQ $90B2 ; lives = 2; 228 | ; mode = 1; 229 | ; return; // "AKAMA ": start/continue in Hard Mode alone 230 | ; } 231 | 00:9098 LDA $003A ; if (partner != 0xFF) { // if (partner != none) 232 | 00:909A CMP #$FF ; lives = 2; 233 | 00:909C BNE $90B7 ; return; // continue in Normal Mode with a partner 234 | ; } 235 | 00:909E DEY ; if (--Y == 0) { 236 | 00:909F BEQ $90AC ; partner = 0x03; 237 | ; lives = 2; 238 | ; return; // "OKUDA ": start/continue in Normal Mode with Alucard 239 | ; } 240 | 00:90A1 DEY ; if (--Y == 0) { 241 | 00:90A2 BEQ $90A8 ; partner = 0x01; 242 | ; lives = 2; 243 | ; return; ; "URATA ": start/continue in Normal Mode with Sypha. 244 | ; } 245 | 00:90A4 LDA #$02 246 | 00:90A6 BNE $90AE 247 | 00:90A8 LDA #$01 248 | 00:90AA BNE $90AE 249 | 00:90AC LDA #$03 250 | 00:90AE STA $003A ; partner = 0x02; 251 | 00:90B0 BNE $90B7 ; } 252 | 00:90B2 LDA #$01 253 | 00:90B4 STA $07F6 254 | 00:90B7 LDA #$02 255 | 00:90B9 STA $0035 ; lives = 2; 256 | 00:90BB RTS ; return; 257 | 00:90BC LDA #$10 258 | 00:90BE BNE $90B9 259 | 260 | ; resetHeartsAndEnergy() 261 | 00:90C0 LDA #$05 262 | 00:90C2 STA $0084 ; hearts = 5; 263 | 00:90C4 LDA #$40 264 | 00:90C6 STA $003C ; playerEnergy = 0x40; // full energy 265 | 00:90C8 LDA #$40 266 | 00:90CA STA $003D ; bossEnergy = 0x40; // full energy 267 | 00:90CC RTS ; return; 268 | 269 | ; reset4Aand4D() 270 | 00:90CD LDA #$40 271 | 00:90CF STA $004A ; mem[0x004A] = 0x40; 272 | 00:90D1 LDA #$43 273 | 00:90D3 STA $004D ; mem[0x004D] = 0x43; 274 | 00:90D5 RTS ; return; 275 | 276 | ; submitPassword() 277 | 03:B2F8 INC $0019 278 | 03:B2FA JSR $B48D 279 | 03:B2FD JMP $B471 280 | 03:B300 JSR $B3DB 281 | 03:B303 JSR $B50C 282 | 03:B306 JSR $B3B9 283 | 03:B309 LDA $0026 284 | 03:B30B AND #$30 285 | 03:B30D BNE $B313 286 | 03:B30F LDA $002D 287 | 03:B311 BEQ $B338 288 | 03:B313 LDA $0026 289 | 03:B315 AND #$20 290 | 03:B317 BNE $B349 291 | 03:B319 JSR $B656 ; decode(); 292 | 03:B31C LDX #$05 293 | 03:B31E JSR $B627 294 | 03:B321 LDA $078B ; if (badPasswordReason == 0) { 295 | 03:B324 BEQ $B339 ; goto handleValidPassword; 296 | 03:B326 LDA #$40 ; } 297 | 03:B328 JSR $E25F 298 | 03:B32B LDA #$09 299 | 03:B32D STA $0019 300 | 03:B32F LDA #$23 301 | 03:B331 JSR $ECE9 302 | 03:B334 LDA #$78 303 | 03:B336 STA $0030 304 | 03:B338 RTS ; return; 305 | 306 | ; handleValidPassword: 307 | 03:B339 LDA #$78 308 | 03:B33B STA $0030 309 | 03:B33D LDA #$07 310 | 03:B33F STA $0160 311 | 03:B342 LDA #$0A 312 | 03:B344 STA $0019 313 | 03:B346 JMP $B066 314 | 03:B349 JSR $B066 315 | 03:B34C LDA #$0B 316 | 03:B34E STA $0019 317 | 03:B350 RTS ; return; 318 | 319 | ; showPassword() 320 | 03:B5AF STA $0025 321 | 03:B5B1 STA $5105 322 | 03:B5B4 JSR $EBFD 323 | 03:B5B7 LDA #$98 324 | 03:B5B9 LDX #$1A 325 | 03:B5BB JSR $EBD5 326 | 03:B5BE JSR $E2D6 327 | 03:B5C1 LDA #$62 328 | 03:B5C3 JSR $E25F 329 | 03:B5C6 INC $0019 330 | 03:B5C8 JSR $B1C7 331 | 03:B5CB JSR $B625 332 | 03:B5CE JSR $B675 ; resetPasswordVars(); 333 | 03:B5D1 JSR $B64A ; encode(); 334 | 03:B5D4 JSR $B28B 335 | 03:B5D7 JSR $B647 ; _drawPassword(); 336 | 03:B5DA JSR $B066 337 | 03:B5DD LDA #$03 338 | 03:B5DF STA $001C 339 | 03:B5E1 JMP $B3FB 340 | 03:B5E4 LDA $00B4 341 | 03:B5E6 CMP #$FF 342 | 03:B5E8 BEQ $B604 343 | 03:B5EA LDA $001D 344 | 03:B5EC STA $0015 345 | 03:B5EE JSR $B598 346 | 03:B5F1 JSR $FBA4 347 | 03:B5F4 LDA $00B4 348 | 03:B5F6 CMP #$FF 349 | 03:B5F8 BNE $B60F 350 | 03:B5FA LDA #$00 351 | 03:B5FC LDX $0015 352 | 03:B5FE STX $001D 353 | 03:B600 STA $0300,X 354 | 03:B603 RTS ; return; 355 | 356 | ; _drawPassword() 357 | 03:B647 JMP $B940 ; drawPassword(); 358 | 359 | ; encode() 360 | 03:B64A JSR $B6CD ; hashName(); // result in A 361 | 03:B64D STA $078A ; nameHash = A; 362 | 03:B650 JSR $B6EE ; encodePayload(); 363 | 03:B653 JMP $B8C4 ; goto createUnscrambledPassword; 364 | 365 | ; decode() 366 | 03:B656 JSR $B6CD ; hashName(); // result in A 367 | 03:B659 STA $0010 ; _nameHash = A; 368 | 03:B65B JSR $B79A ; findScrambles(); 369 | 03:B65E JSR $B7F0 ; verifyAllNonblanksInScrambles(); 370 | 03:B661 JSR $B68F ; unscramblePassword(); 371 | 03:B664 JSR $B82C ; decodePayloadAndPayloadHash(); 372 | 03:B667 JSR $E593 ; _checkForSomeSpecialNames(); // results in carry and Y 373 | 03:B66A BCS $B66F ; if (carry == 1) { 374 | 03:B66C JSR $B87F ; isValidSavePoint(); // name is special 375 | ; } 376 | 03:B66F JSR $B756 ; extractPayloadVarsAndVerifyNameHash(); 377 | 03:B672 JMP $B865 ; verifyPayloadHash(); 378 | 379 | ; resetPasswordVars() 380 | 03:B675 LDA #$00 381 | 03:B677 LDX #$00 ; for (X = 0; X < 0x10; ++X) { 382 | 03:B679 STA $0780,X ; mem[0x0780 + X] = 0; 383 | 03:B67C INX 384 | 03:B67D CPX #$10 385 | 03:B67F BCC $B679 ; } 386 | 03:B681 RTS ; return; 387 | 388 | ; clearPassword() 389 | 03:B682 LDY #$00 390 | 03:B684 LDA #$00 ; for (Y = 0; Y < 0x10; ++Y) { 391 | 03:B686 STA $0790,Y ; password[Y] = 0; // no mark 392 | 03:B689 INY 393 | 03:B68A CPY #$10 394 | 03:B68C BCC $B686 ; } 395 | 03:B68E RTS ; return; 396 | 397 | ; unscramblePassword() 398 | ; Copies 9 marks from password to unscrambledPassword based on the SCRAMBLES[9 * scramblesRowIndex] sequence. 399 | 03:B68F LDX #$00 400 | 03:B691 LDA $078F 401 | 03:B694 ASL A 402 | 03:B695 ASL A 403 | 03:B696 ASL A 404 | 03:B697 ADC $078F 405 | 03:B69A STA $0000 406 | 03:B69C LDY $0000 ; for (X = 0; X < 9; ++X, ++v0000) { 407 | 03:B69E LDA $B6B2,Y ; A = SCRAMBLES[9 * scramblesRowIndex + X]; 408 | 03:B6A1 JSR $B7E2 ; squeezeRowCol(); // result in Y 409 | 03:B6A4 LDA $0790,Y 410 | 03:B6A7 STA $07A0,X ; unscrambledPassword[X] = password[Y]; 411 | 03:B6AA INC $0000 412 | 03:B6AC INX 413 | 03:B6AD CPX #$09 414 | 03:B6AF BCC $B69C ; } 415 | 03:B6B1 RTS ; return; 416 | 417 | ; SCRAMBLES 418 | ; This table contains 3 sequences of matrix elements used to encode the game state. The row and column are stored in 419 | ; the high and low nibbles, respectively. 420 | 03:B6B2 .byte $00, $33, $20, $13, $22, $01, $11, $03, $32 ; 0 421 | 03:B6BB .byte $12, $10, $02, $32, $23, $13, $30, $21, $01 ; 1 422 | 03:B6C4 .byte $31, $13, $01, $22, $10, $30, $33, $03, $21 ; 2 423 | 424 | ; hashName() 425 | ; out: A = name hash (0--7) 426 | 03:B6CD LDA #$00 427 | 03:B6CF STA $0000 ; sum = 0; 428 | 03:B6D1 TAX 429 | 03:B6D2 LDA $07F8,X ; for (X = 0; X < 8; ++X) { 430 | 03:B6D5 CLC 431 | 03:B6D6 ADC $B6E6,X 432 | 03:B6D9 CLC 433 | 03:B6DA ADC $0000 434 | 03:B6DC STA $0000 ; sum += name[X] + NAME_HASH_SEEDS[X]; 435 | 03:B6DE INX 436 | 03:B6DF CPX #$08 437 | 03:B6E1 BNE $B6D2 ; } 438 | 03:B6E3 AND #$07 ; A = sum % 8; 439 | 03:B6E5 RTS ; return; 440 | 441 | ; NAME_HASH_SEEDS 442 | ; Due to the modulo operation, this table is pointless; the values can be tallied ahead of time. However, the 443 | ; intention may have been to apply this table only to the nonblank characters. But that check is not there. 444 | 03:B6E6 .byte $07, $03, $01, $06, $02, $04, $05, $00 445 | 446 | ; encodePayload() 447 | 03:B6EE LDA $078A 448 | 03:B6F1 STA $0000 ; payload = nameHash; 449 | 03:B6F3 LDA $002E ; if (savePoint >= 0x11) { 450 | 03:B6F5 CMP #$11 ; savePoint = 0x11; 451 | 03:B6F7 BCC $B6FB ; } 452 | 03:B6F9 LDA #$11 453 | 03:B6FB STA $002E 454 | 03:B6FD LSR A 455 | 03:B6FE ROL $0000 ; payload = (payload << 1) | (savePoint & 1); 456 | 03:B700 LDA $001A 457 | 03:B702 LSR A 458 | 03:B703 ROL $0000 ; payload = (payload << 1) | (frameCounter & 1); 459 | 03:B705 ROL $0000 460 | 03:B707 ROL $0000 461 | 03:B709 LDA $003A 462 | 03:B70B BPL $B70F 463 | 03:B70D LDA #$00 464 | 03:B70F ORA $0000 ; payload = (payload << 2) | (partner == 0xFF ? 0 : partner); 465 | 03:B711 ASL A 466 | 03:B712 ORA $07F6 467 | 03:B715 STA $0788 ; payload = (payload << 1) | mode; 468 | 03:B718 LDA $001A 469 | 03:B71A LSR A 470 | 03:B71B LDA #$50 471 | 03:B71D BCC $B721 472 | 03:B71F LDA #$A0 473 | 03:B721 STA $0004 ; toggleMask = (frameCounter & 1) == 0 ? 0xA0 : 0x50; 474 | 03:B723 JSR $B72A ; hashPayload(payload); // result in A 475 | 03:B726 STA $0789 ; payloadHash = A; 476 | 03:B729 RTS ; return; 477 | 478 | ; hashPayload() 479 | 03:B72A LDA $0788 480 | 03:B72D AND #$F0 481 | 03:B72F STA $0002 ; highNibble = payload & 0xF0; 482 | 03:B731 LDA $0788 483 | 03:B734 ASL A 484 | 03:B735 ASL A 485 | 03:B736 ASL A 486 | 03:B737 ASL A 487 | 03:B738 STA $0003 ; lowNibble = payload << 4; 488 | 03:B73A CLC 489 | 03:B73B ADC $0002 490 | 03:B73D STA $0001 ; nibbleSum = highNibble + lowNibble; 491 | 03:B73F LDA $0004 492 | 03:B741 EOR $0002 493 | 03:B743 STA $0000 ; toggledHighNibble = highNibble ^ toggleMask; 494 | 03:B745 LDA $0004 495 | 03:B747 EOR $0003 496 | 03:B749 CLC 497 | 03:B74A ADC $0000 498 | 03:B74C LSR A 499 | 03:B74D LSR A 500 | 03:B74E LSR A 501 | 03:B74F LSR A 502 | 03:B750 ORA $0001 503 | 03:B752 CLC 504 | 03:B753 ADC $002E ; A = savePoint + (nibbleSum | ((toggledHighNibble + (lowNibble ^ toggleMask)) >> 4)); 505 | 03:B755 RTS ; return; 506 | 507 | ; extractPayloadVarsAndVerifyNameHash() 508 | 03:B756 LDA $0788 509 | 03:B759 AND #$01 510 | 03:B75B STA $07F6 ; mode = payload & 1; 511 | 03:B75E LDA $0788 512 | 03:B761 LSR A 513 | 03:B762 AND #$03 ; A = (payload >> 1) & 3; 514 | 03:B764 BNE $B768 ; if (A == 0) { 515 | 03:B766 LDA #$FF ; A = 0xFF; // no partner 516 | ; } 517 | 03:B768 STA $003A ; partner = A; 518 | 03:B76A LDA $0788 519 | 03:B76D AND #$10 520 | 03:B76F BEQ $B777 521 | 03:B771 LDA $002E 522 | 03:B773 ORA #$01 ; if ((payload & 0x10) != 0) { 523 | 03:B775 STA $002E ; savePoint |= 1; 524 | 03:B777 LDA $0788 ; } 525 | 03:B77A LSR A 526 | 03:B77B LSR A 527 | 03:B77C LSR A 528 | 03:B77D LSR A 529 | 03:B77E LSR A 530 | 03:B77F STA $078A ; nameHash = payload >> 5; 531 | 03:B782 CMP $0010 ; if (nameHash != _nameHash) { 532 | 03:B784 BEQ $B78B ; A = 0x10; 533 | 03:B786 LDA #$10 ; goto throwBadPassword; 534 | 03:B788 JMP $B7D8 ; } 535 | 03:B78B LDA $002E ; if (savePoint == 2 || savePoint == 4) { 536 | 03:B78D CMP #$02 ; escapedClockTower = 1; 537 | 03:B78F BEQ $B795 ; } 538 | 03:B791 CMP #$04 539 | 03:B793 BNE $B799 540 | 03:B795 LDA #$01 541 | 03:B797 STA $002F 542 | 03:B799 RTS ; return; 543 | 544 | ; findScrambles() 545 | 03:B79A LDA #$02 546 | 03:B79C STA $0000 547 | 03:B79E LDA #$00 548 | 03:B7A0 STA $0001 ; markCount = 0; 549 | 03:B7A2 LDY $0000 ; for (i = 2; i >= 0; --i) { 550 | 03:B7A4 LDA $B7DF,Y ; Y = LEADERS[i]; 551 | 03:B7A7 JSR $B7E2 ; squeezeRowCol(); // result in Y 552 | 03:B7AA LDA $0790,Y ; if ((password[Y] & 3) == 0) { 553 | 03:B7AD AND #$03 ; continue; // if blank, continue 554 | 03:B7AF BEQ $B7CB ; } 555 | 03:B7B1 LDA $0000 556 | 03:B7B3 STA $078F ; scramblesRowIndex = i; 557 | 03:B7B6 INC $0001 ; ++markCount; 558 | 03:B7B8 LDX #$00 ; for (X = 0; X < 9; ++X) { 559 | 03:B7BA LDA $0790,Y ; 560 | 03:B7BD CMP $B937,X ; if (password[Y] == SELECTORS[X]) { 561 | 03:B7C0 BEQ $B7C7 ; break; 562 | 03:B7C2 INX ; } 563 | 03:B7C3 CPX #$09 564 | 03:B7C5 BNE $B7BD ; } 565 | 03:B7C7 TXA 566 | 03:B7C8 ASL A 567 | 03:B7C9 STA $002E ; savePoint = X << 1; 568 | 03:B7CB DEC $0000 569 | 03:B7CD BPL $B7A2 ; } 570 | 03:B7CF LDA $0001 ; if (markCount != 1) { 571 | 03:B7D1 CMP #$01 ; goto throwDefaultBadPassword; 572 | 03:B7D3 BNE $B7D6 ; } 573 | 03:B7D5 RTS ; return; 574 | 575 | throwDefaultBadPassword: 576 | 03:B7D6 LDA #$01 ; A = 1; 577 | 578 | throwBadPassword: 579 | ; in: A = bad password reason 580 | 03:B7D8 ORA $078B 581 | 03:B7DB STA $078B ; badPasswordReason |= A; 582 | 03:B7DE RTS ; return; 583 | 584 | ; LEADERS 585 | ; Exactly one of the elements at (0, 0), (1, 2), and (3, 1) is marked nonblank. The index of the element of this table 586 | ; corresponding to that nonblank mark determines which of the 3 scramble sequences is used (scramblesRowIndex). 587 | 03:B7DF .byte $00, $12, $31 588 | 589 | ; squeezeRowCol() 590 | ; in: A = ..rr..cc 591 | ; out: Y = ....rrcc 592 | 03:B7E2 PHA 593 | 03:B7E3 AND #$30 594 | 03:B7E5 LSR A 595 | 03:B7E6 LSR A 596 | 03:B7E7 STA $0007 597 | 03:B7E9 PLA 598 | 03:B7EA AND #$03 599 | 03:B7EC ORA $0007 600 | 03:B7EE TAY ; Y = ((A & 0x30) >> 2) | (A & 0x03); 601 | 03:B7EF RTS ; return; 602 | 603 | ; verifyAllNonblanksInScrambles() 604 | 03:B7F0 LDA $078F 605 | 03:B7F3 ASL A 606 | 03:B7F4 ASL A 607 | 03:B7F5 ASL A 608 | 03:B7F6 ADC $078F 609 | 03:B7F9 STA $0000 ; scramblesRowOffset = 9 * scramblesRowIndex; 610 | 03:B7FB LDA #$0F 611 | 03:B7FD STA $0001 612 | 03:B7FF LDY $0001 ; outer: for (rowCol = 0x0F; rowCol >= 0; --rowCol) { // ....rrcc 613 | 03:B801 LDA $0790,Y 614 | 03:B804 AND #$03 615 | 03:B806 BEQ $B827 ; if ((password[rowCol] & 0x03) == 0) { // if blank, continue 616 | ; continue; 617 | ; } 618 | 03:B808 LDA $0000 619 | 03:B80A STA $0002 ; offset = scramblesRowOffset; 620 | 03:B80C LDA #$09 621 | 03:B80E STA $0003 ; for (i = 9; i > 0; --i, ++offset) { 622 | 03:B810 LDY $0002 623 | 03:B812 LDA $B6B2,Y ; A = SCRAMBLES[offset]; 624 | 03:B815 JSR $B7E2 ; squeezeRowCol(); // result in Y 625 | 03:B818 CPY $0001 ; if (Y == rowCol) { 626 | 03:B81A BEQ $B827 ; continue outer; 627 | ; } 628 | 03:B81C INC $0002 629 | 03:B81E DEC $0003 630 | 03:B820 BNE $B810 ; } 631 | 03:B822 LDA #$02 ; A = 2; 632 | 03:B824 JMP $B7D8 ; goto throwBadPassword; // nonblank element not in scramble row 633 | 03:B827 DEC $0001 634 | 03:B829 BPL $B7FF ; } 635 | 03:B82B RTS ; return; 636 | 637 | ; decodePayloadAndPayloadHash() 638 | 03:B82C LDX #$00 639 | 03:B82E LDA $07A1,X ; for (X = 0; X < 8; ++X) { 640 | 03:B831 STA $08,X ; _unscrambledPassword[X] = unscrambledPassword[X + 1]; 641 | 03:B833 INX 642 | 03:B834 CPX #$08 643 | 03:B836 BCC $B82E ; } 644 | 03:B838 LDA #$00 645 | 03:B83A STA $0000 646 | 03:B83C STA $0001 647 | 03:B83E LDY #$00 ; payloadHash = payload = 0; 648 | 03:B840 LDX #$00 649 | 03:B842 LSR $08,X ; for (X = Y = 0; Y < 8; ++Y, ++X) { 650 | 03:B844 ROR $0001 ; payloadHash = ((_unscrambledPassword[X] & 1)) << 7) | (payloadHash >> 1); 651 | 03:B846 LSR $08,X ; _unscrambledPassword[X] >>= 1; 652 | 03:B848 ROR $0000 ; payload = ((_unscrambledPassword[X] & 1) << 7) | (payload >> 1); 653 | 03:B84A INX ; _unscrambledPassword[X] >>= 1; 654 | 03:B84B INY 655 | 03:B84C CPY #$08 656 | 03:B84E BCC $B842 ; } 657 | 03:B850 LDA $0001 658 | 03:B852 STA $0789 659 | 03:B855 LDA $0000 660 | 03:B857 STA $0788 661 | 03:B85A AND #$10 662 | 03:B85C LSR A 663 | 03:B85D LSR A 664 | 03:B85E LSR A 665 | 03:B85F LSR A 666 | 03:B860 ORA $002E 667 | 03:B862 STA $002E ; savePoint |= (payload & 0x10) >> 4; 668 | 03:B864 RTS ; return; 669 | 670 | ; verifyPayloadHash() 671 | 03:B865 LDY #$50 672 | 03:B867 LDA $0788 673 | 03:B86A AND #$08 674 | 03:B86C BEQ $B870 675 | 03:B86E LDY #$A0 676 | 03:B870 STY $0004 ; toggleMask = ((payload & 0x08) != 0) ? 0xA0 : 0x50; 677 | 03:B872 JSR $B72A ; hashPayload(); // result in A 678 | 03:B875 CMP $0789 ; if (A == payloadHash) { 679 | 03:B878 BEQ $B864 ; return; 680 | ; } 681 | 03:B87A LDA #$04 ; A = 4; 682 | 03:B87C JMP $B7D8 ; goto throwBadPassword; 683 | 684 | ; isValidSavePoint() 685 | 03:B87F LDA $0788 ; if ((payload & 1) != 0) { 686 | 03:B882 AND #$01 ; return; // Hard Mode 687 | 03:B884 BNE $B8B5 ; } 688 | 03:B886 LDA $002E ; if (savePoint >= 0x12) { 689 | 03:B888 CMP #$12 ; A = 8; 690 | 03:B88A BCS $B8B0 ; goto throwBadPassword; // Invalid savePoint 691 | ; } 692 | 03:B88C CMP #$10 ; if (savePoint >= 0x10) { 693 | 03:B88E BCS $B8B5 ; return; // final 2 blocks 694 | ; } 695 | 03:B890 LDA $0788 696 | 03:B893 AND #$06 697 | 03:B895 STA $0000 ; _partner = payload & 0x06; 698 | 03:B897 BEQ $B8B5 ; if (_partner == 0) { 699 | ; return; // no partner 700 | ; } 701 | 03:B899 LDA $002E 702 | 03:B89B AND #$08 703 | 03:B89D LSR A 704 | 03:B89E LSR A 705 | 03:B89F LSR A 706 | 03:B8A0 ORA $0000 707 | 03:B8A2 TAY 708 | 03:B8A3 LDA $002E 709 | 03:B8A5 AND #$07 710 | 03:B8A7 TAX 711 | 03:B8A8 LDA $B8B4,Y 712 | 03:B8AB AND $B8BC,X 713 | 03:B8AE BNE $B8B5 ; if ((VALID_SAVE_POINTS[(_partner | ((savePoint & 0x08) >> 3)) - 2] 714 | ; & BIT_MASKS[savePoint & 0x07]) != 0) { 715 | ; return; 716 | ; } 717 | 03:B8B0 LDA #$08 718 | 03:B8B2 JMP $B7D8 719 | 03:B8B5 RTS 720 | 721 | ; VALID_SAVE_POINTS 722 | ; In normal mode, a partner may only be used along pathway of save points which starts when the partner was first 723 | ; encountered in the game. Each bit of the elements of this table correspond to a save point. A partner may only be 724 | ; used in a save point where the associated bit is 1. Each pair of elements maps to a different partner. Within the 725 | ; pair, the bits correspond to the following save points: 726 | ; 0: (1-1, 2-1, 2-4, 3-0, 3-1, 4-A, 5-A, 6-A) 727 | ; 1: (4-1, 5-1, 5-6, 6-1, 6-1', 7-1, 7-A, 8-1) 728 | ; All partners may be used in save points 9-1 and A-1. 729 | 03:B8B6 .byte $07 ; Sypha (4-A, 5-A, 6-A) 730 | 03:B8B7 .byte $03 ; Sypha (7-A, 8-1) 731 | 03:B8B8 .byte $2F ; Grant (2-4, 3-1, 4-A, 5-A, 6-A) 732 | 03:B8B9 .byte $FF ; Grant (4-1, 5-1, 5-6, 6-1, 6-1', 7-1, 7-A, 8-1) 733 | 03:B8BA .byte $00 ; Alucard () 734 | 03:B8BB .byte $3D ; Alucard (5-6, 6-1, 6-1', 7-1, 8-1) 735 | 736 | ; BIT_MASKS 737 | ; This is used to extract bits from the elements of VALID_SAVE_POINTS. 738 | 03:B8BC .byte $80, $40, $20, $10, $08, $04, $02, $01 739 | 740 | createUnscrambledPassword: 741 | 03:B8C4 LDA $0788 742 | 03:B8C7 STA $0000 ; _payload = payload; 743 | 03:B8C9 LDA $0789 744 | 03:B8CC STA $0001 ; _payloadHash = payloadHash; 745 | 03:B8CE LDX #$08 746 | 03:B8D0 LDA #$00 747 | 03:B8D2 STA $08,X ; for (X = 8; X >= 0; --X) { 748 | 03:B8D4 DEX ; _unscrambledPassword[X] = 0; 749 | 03:B8D5 BPL $B8D2 ; } 750 | 03:B8D7 LDX #$00 751 | 03:B8D9 LSR $0000 ; for (X = 0; X < 8; ++X) { 752 | 03:B8DB ROL $08,X ; _unscrambledPassword[X] = (_unscrambledPassword[X] << 1) | (_payload & 1); 753 | 03:B8DD LSR $0001 ; _payload >>= 1; 754 | 03:B8DF ROL $08,X ; _unscrambledPassword[X] = (_unscrambledPassword[X] << 1) | (_payloadHash & 1); 755 | 03:B8E1 INX ; _payloadHash >>= 1; 756 | 03:B8E2 CPX #$08 757 | 03:B8E4 BCC $B8D9 ; } 758 | 03:B8E6 LDA $002E 759 | 03:B8E8 LSR A 760 | 03:B8E9 TAY ; Y = savePoint >> 1; 761 | 03:B8EA LDX #$02 762 | 03:B8EC LDA $B937,Y ; for (X = 2; X >= 0; --X) { 763 | 03:B8EF AND #$0C 764 | 03:B8F1 LSR A 765 | 03:B8F2 LSR A 766 | 03:B8F3 STA $0000 767 | 03:B8F5 LDA $B937,Y 768 | 03:B8F8 AND #$30 769 | 03:B8FA ORA $0000 ; 770 | 03:B8FC CMP $B7DF,X ; if (LEADERS[X] == ((SELECTORS[Y] & 0x30) | ((SELECTORS[Y] & 0x0C) >> 2))) { 771 | 03:B8FF BEQ $B904 ; break; 772 | 03:B901 DEX ; } 773 | 03:B902 BPL $B8EC ; } 774 | 03:B904 STX $078F ; scramblesRowIndex = X; 775 | 03:B907 TXA 776 | 03:B908 ASL A 777 | 03:B909 ASL A 778 | 03:B90A ASL A 779 | 03:B90B ADC $078F 780 | 03:B90E TAY ; Y = 9 * scramblesRowIndex; 781 | 03:B90F LDX #$00 782 | 03:B911 LDA $B6B3,Y ; for (X = 0; X < 9; ++X, ++Y) { // one too many iterations? 783 | 03:B914 AND #$30 784 | 03:B916 STA $0001 785 | 03:B918 LDA $B6B3,Y 786 | 03:B91B AND #$03 787 | 03:B91D ASL A 788 | 03:B91E ASL A 789 | 03:B91F ORA $0001 790 | 03:B921 ORA $08,X 791 | 03:B923 STA $07A1,X ; unscrambledPassword[X + 1] = ((SCRAMBLES[Y + 1] & 0x03)) << 2) | (SCRAMBLES[Y + 1] & 0x30) 792 | 03:B926 INY ; | _unscrambledPassword[X]; // ..rrccmm 793 | 03:B927 INX 794 | 03:B928 CPX #$09 ; 795 | 03:B92A BCC $B911 ; } 796 | 03:B92C LDA $002E 797 | 03:B92E LSR A 798 | 03:B92F TAY 799 | 03:B930 LDA $B937,Y 800 | 03:B933 STA $07A0 ; unscrambledPassword[0] = SELECTORS[savePoint >> 1]; 801 | 03:B936 RTS ; return; 802 | 803 | ; SELECTORS 804 | ; Exactly one of the elements at (0, 0), (1, 2), and (3, 1) is marked nonblank. This table contains the 9 possible ways 805 | ; to achieve that. The element to mark is determined by bits 1--4 of savePoint, which is used as the index into this 806 | ; table. Since savePoint cannot exceed $11, the index covers 0--8. 807 | ; W00, H12, R00, W31, W12, H00, H31, R12, R31 808 | 03:B937 .byte $01, $1B, $02, $35, $19, $03, $37, $1A, $36 809 | 810 | ; drawPassword() 811 | 03:B940 LDA #$00 812 | 03:B942 STA $0000 813 | 03:B944 LDX #$05 814 | 03:B946 LDY $0000 ; for(X = 5, markIndex = 0; markIndex < 9; ++markIndex, ++X) { 815 | 03:B948 LDA $07A0,Y 816 | 03:B94B AND #$03 817 | 03:B94D TAY ; Y = unscrambledPassword[markIndex] & 0x03; // ......mm 818 | 03:B94E LDA $B982,Y 819 | 03:B951 STA $0400,X ; SPRITE_0[X] = SPRITE_A[Y]; 820 | 03:B954 LDA $B97E,Y 821 | 03:B957 STA $048C,X ; SPRITE_1[X] = SPRITE_B[Y]; 822 | 03:B95A LDY $0000 823 | 03:B95C LDA $07A0,Y 824 | 03:B95F AND #$3C 825 | 03:B961 LSR A 826 | 03:B962 TAY ; Y = (0x3C & unscrambledPassword[markIndex]) >> 1; // ...rrcc. 827 | 03:B963 LDA $B986,Y 828 | 03:B966 STA $041C,X ; MARK_YS[X] = MATRIX_COORDINATES[Y]; 829 | 03:B969 LDA $B987,Y 830 | 03:B96C STA $0438,X ; MARK_XS[X] = MATRIX_COORDINATES[Y + 1]; 831 | 03:B96F LDA #$00 832 | 03:B971 STA $0454,X ; MARK_ATTRIBS[X] = 0; 833 | 03:B974 INX 834 | 03:B975 INC $0000 835 | 03:B977 LDA $0000 836 | 03:B979 CMP #$09 837 | 03:B97B BCC $B946 ; } 838 | 03:B97D RTS ; return; 839 | 840 | ; SPRITE_B 841 | 03:B97E .byte $00, $14, $0C, $0C ; blank, whip, rosary, heart 842 | 843 | ; SPRITE_A 844 | 03:B982 .byte $00, $42, $FC, $F4 ; blank, whip, rosary, heart 845 | 846 | ; MATRIX_COORDINATES 847 | ; These are (y, x)-coordinates corresponding to positions within the password matrix. 848 | 03:B986 .byte $7A, $5D, $7A, $75, $7A, $8D, $7A, $A5, $92, $5D, $92, $75, $92, $8D, $92, $A5 849 | 03:B996 .byte $AA, $5D, $AA, $75, $AA, $8D, $AA, $A5, $C2, $5D, $C2, $75, $C2, $8D, $C2, $A5 850 | 851 | ; switchBanks() 852 | 7F:E2E6 STA $0021 853 | 7F:E2E8 STA $5115 854 | 7F:E2EB RTS ; return; 855 | 856 | ; _checkForSomeSpecialNames() 857 | ; out: carry (false = no, true = yes) 858 | ; Y (2 = "AKAMA ", 3 = "OKUDA ", 4, = "URATA ", 5 = "FUJIMOTO") 859 | 7F:E593 LDA #$80 ; A = 0x80; 860 | 7F:E595 JSR $E2E6 ; switchBanks(); 861 | 7F:E598 JSR $9005 ; checkForSomeSpecialNames(); 862 | 7F:E59B LDA #$82 ; A = 0x82; 863 | 7F:E59D JMP $E2E6 ; switchBanks(); --------------------------------------------------------------------------------