pastebin - collaborative debugging

pastebin is a collaborative debugging tool allowing you to share and modify code snippets while chatting on IRC, IM or a message board.

This site is developed to XHTML and CSS2 W3C standards. If you see this paragraph, your browser does not support those standards and you need to upgrade. Visit WaSP for a variety of options.

treasuremaster private pastebin - collaborative debugging tool What's a private pastebin?


Posted by ketti on Tue 15 Sep 22:09
report abuse | download | new post

  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. }

Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.

Syntax highlighting:

To highlight particular lines, prefix each line with @@


Remember me so that I can delete my post