Guest User

ketti

a guest
Sep 15th, 2009
281
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 6.80 KB | None | 0 0
  1. /**
  2.  * Java implementation of the secret password check and the You Win code generation algorithm
  3.  * in the NES game "Treasure Master"
  4.  *
  5.  * Note: The password check is possibly incomplete.
  6.  *
  7.  * @author ketti
  8.  * @see http://www.reddit.com/r/TreasureMaster/
  9.  */
  10. public class TreasureMaster
  11. {
  12.     private static final int YOUWINCODE_LENGTH = 0x18;
  13.     private static final int PASSWORD_LENGTH = 0x18;
  14.     private static final int BUFFER_LENGTH = 0x0F;
  15.     private static final int CHECK_LENGTH_1 = 0x0D;
  16.     private static final int CHECK_LENGTH_2 = 0x08;
  17.     private static final int SERIAL_LENGTH = 0x08;
  18.  
  19.     private static final byte[] PPU = new byte[] {(byte)0xFD, (byte)0x22, (byte)0x3C, (byte)0x40};
  20.    
  21.     private static char[] CONVERSION_TABLE = new char[] {
  22.             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  23.             'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N',
  24.             'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', '!'};
  25.    
  26.     public static void main(String[] args)
  27.     {
  28.         String secret = "3HDJL9DNQV2WYTV4S91RXR86";
  29.         System.out.println("Secret password \"" + secret + "\" is " +
  30.                 (checkSecretPassword(secret) ? "" : "in") + "valid.\n");
  31.        
  32.        
  33.        
  34.         String serial = "12345678";
  35.         int score = 79900;
  36.         int lives = 4;
  37.        
  38.         System.out.println("Serial: " + serial);
  39.         System.out.println("Score: " + score);
  40.         System.out.println("Lives: " + lives);
  41.        
  42.         System.out.println("\nYou Win codes:");
  43.         for (int random = 0; random < 0x10; random++)
  44.         {
  45.             System.out.println(computeYouWinCode(serial, score, lives, random));
  46.         }
  47.     }
  48.  
  49.    
  50.     public static boolean checkSecretPassword(String string)
  51.     {
  52.         if (string.length() != PASSWORD_LENGTH)
  53.         {
  54.             throw new IllegalArgumentException("Secret password must be " +
  55.                     PASSWORD_LENGTH + " characters long.");
  56.         }
  57.        
  58.         // Convert the secret password string to the "Treasure Master format".
  59.         byte[] password = new byte[PASSWORD_LENGTH];
  60.         char[] chars = string.toCharArray();
  61.         for (int i = 0; i < PASSWORD_LENGTH; i++)
  62.         {
  63.             boolean found = false;
  64.             for (int j = 0; j < CONVERSION_TABLE.length; j++)
  65.             {
  66.                 if (chars[i] == CONVERSION_TABLE[j])
  67.                 {
  68.                     password[i] = (byte)j;
  69.                     found = true;
  70.                     break;
  71.                 }
  72.             }
  73.             if (!found)
  74.             {
  75.                 throw new IllegalArgumentException("Illegal character in secret password found: " + chars[i]);
  76.             }
  77.         }
  78.        
  79.  
  80.         // Compute buffer contents for the first check (somewhere around $B0BE)
  81.         byte[] buffer = new byte[BUFFER_LENGTH];
  82.         for (int i = 0; i < PASSWORD_LENGTH; i++)
  83.         {
  84.             byte b = password[i];
  85.            
  86.             CarryContainer c = new CarryContainer();
  87.             b = c.shiftLeftWithCarry(b);
  88.             b = c.shiftLeftWithCarry(b);
  89.             b = c.shiftLeftWithCarry(b);
  90.  
  91.             for (int k = 0; k < 5; k++)
  92.             {
  93.                 b = c.rotateLeftThroughCarry(b);
  94.                
  95.                 for (int j = BUFFER_LENGTH - 1; j >= 0; j--)
  96.                 {
  97.                     buffer[j] = c.rotateLeftThroughCarry(buffer[j]);
  98.                 }
  99.             }
  100.         }
  101.        
  102.         int sum = computeChecksum(buffer, CHECK_LENGTH_1);
  103.  
  104.         byte lowByte = (byte)(sum & 0xFF);
  105.         byte highByte = (byte)((sum >> 8) & 0xFF);
  106.        
  107.         // Compare computed checksum with checksum bytes in the buffer
  108.         if ((lowByte != buffer[CHECK_LENGTH_1]) || (highByte != buffer[CHECK_LENGTH_1 + 1]))
  109.         {
  110.             return false;
  111.         }
  112.  
  113.         // For the second check to execute the first byte of the buffer has to be smaller than 0x20
  114.         if (buffer[0] >= 0x20)
  115.         {
  116.             return false;
  117.         }
  118.        
  119.         // Compute buffer contents for the second check (somewhere around $B0EB)
  120.         CarryContainer c = new CarryContainer();
  121.         for (int i = buffer[0]; i > 0; i--)
  122.         {
  123.             for (int j = buffer.length - 1; j >= 0; j--)
  124.             {
  125.                 buffer[j] = c.rotateLeftThroughCarry(buffer[j]);
  126.             }
  127.         }
  128.  
  129.         sum = computeChecksum(buffer, CHECK_LENGTH_2);
  130.        
  131.         return ((byte)(sum & 0xFF)) == buffer[8];
  132.     }
  133.    
  134.     public static String computeYouWinCode(final String serial, final int score,
  135.             final int lives, final int random)
  136.     {
  137.         byte[] serialBytes = new byte[SERIAL_LENGTH];
  138.         byte[] scoreBytes = new byte[SERIAL_LENGTH];
  139.         byte[] buffer = new byte[BUFFER_LENGTH];
  140.  
  141.         if (serial.length() != SERIAL_LENGTH)
  142.         {
  143.             throw new IllegalArgumentException("Serial must be " + SERIAL_LENGTH + " characters long.");
  144.         }
  145.  
  146.         // Convert serial to byte array
  147.         char[] serialChars = serial.toCharArray();
  148.         for (int i = 0; i < SERIAL_LENGTH; i++)
  149.         {
  150.             char c = serialChars[i];
  151.             if ((c < '0') || (c > '9'))
  152.             {
  153.                 throw new IllegalArgumentException("Invalid character in serial: " + c);
  154.             }
  155.            
  156.             serialBytes[i] = (byte)(c - '0');
  157.         }
  158.  
  159.         // Copy PPU bytes to the buffer
  160.         for (int i = 0; i < PPU.length; i++)
  161.         {
  162.             buffer[i] = PPU[i];
  163.         }
  164.        
  165.         // Convert score to "Treasure Master format"
  166.         int s = score / 10;
  167.         for (int i = SERIAL_LENGTH - 1; i >= 0 ; i--)
  168.         {
  169.             scoreBytes[i] = (byte)(s % 10);
  170.             s /= 10;
  171.         }
  172.        
  173.         // Put score and serial into the buffer
  174.         byte b;
  175.         CarryContainer c = new CarryContainer();
  176.         for (int i = 0; i < SERIAL_LENGTH; i++)
  177.         {
  178.             b = scoreBytes[i];
  179.             b = c.shiftLeftWithCarry(b);
  180.             b = c.shiftLeftWithCarry(b);
  181.             b = c.shiftLeftWithCarry(b);
  182.             b = c.shiftLeftWithCarry(b);
  183.             buffer[i + 4] = (byte)(serialBytes[i] | b);
  184.         }
  185.  
  186.         // Put lives and random number into the buffer
  187.         b = (byte)lives;
  188.         b = c.shiftLeftWithCarry(b);
  189.         b = c.shiftLeftWithCarry(b);
  190.         b = c.shiftLeftWithCarry(b);
  191.         b = c.shiftLeftWithCarry(b);
  192.         buffer[4 + SERIAL_LENGTH] = (byte)((random & 0x0F) | b);
  193.        
  194.         // Compute checksum for that buffer and append it to the buffer
  195.         int sum = computeChecksum(buffer, CHECK_LENGTH_1);
  196.         buffer[CHECK_LENGTH_1] = (byte)(sum & 0xFF);
  197.         buffer[CHECK_LENGTH_1 + 1] = (byte)((sum >> 8) & 0xFF);
  198.  
  199.         // Create You Win code string
  200.         StringBuffer sb = new StringBuffer(YOUWINCODE_LENGTH);
  201.         c.setCarry(false);
  202.         for (int i = 0; i < YOUWINCODE_LENGTH; i++)
  203.         {
  204.             b = 0;
  205.            
  206.             for (int j = 0; j < 5; j++)
  207.             {
  208.                 for (int k = BUFFER_LENGTH - 1; k >= 0; k--)
  209.                 {
  210.                     buffer[k] = c.rotateLeftThroughCarry(buffer[k]);
  211.                 }
  212.                 b = c.rotateLeftThroughCarry(b);
  213.             }
  214.            
  215.             sb.insert(i, CONVERSION_TABLE[b]);
  216.         }
  217.        
  218.         return sb.toString();
  219.     }
  220.    
  221.     private static int computeChecksum(final byte[] buffer, final int length)
  222.     {
  223.         int sum = 0;
  224.         for (int i = 0; i < length; i++)
  225.         {
  226.             sum += buffer[i] & 0xFF;
  227.         }
  228.        
  229.         return sum;
  230.     }
  231.    
  232.     static class CarryContainer
  233.     {
  234.         private boolean carry;
  235.  
  236.         public CarryContainer()
  237.         {
  238.             this(false);
  239.         }
  240.        
  241.         public CarryContainer(final boolean carry)
  242.         {
  243.             this.carry = carry;
  244.         }
  245.  
  246.         public byte shiftLeftWithCarry(final byte b)
  247.         {
  248.             // ASL
  249.             carry = ((b & 0x80) != 0);
  250.             return (byte)(b << 1);
  251.         }
  252.        
  253.         public byte rotateLeftThroughCarry(byte b)
  254.         {
  255.             // ROL
  256.             boolean newCarry = ((b & 0x80) != 0);
  257.             b = (byte)((b << 1) | ((carry) ? 1 : 0));
  258.             carry = newCarry;
  259.            
  260.             return b;
  261.         }
  262.        
  263.         public void setCarry(boolean carry)
  264.         {
  265.             this.carry = carry;
  266.         }
  267.     }
  268. }
  269.  
Add Comment
Please, Sign In to add comment