Search code examples
javagzipbase64endiannesstmx

String base64 decoded un-gziped from little-endian 4-byte int to java int


I'm trying to implement TMX files in Android and I was hoping someone could help. Based on the TMX guide, in order to get the GID's I have to

first base64 decode the string, then gunzip the resulting data if the compression attribute is set to "gzip" as in the above example. Finally, you can read 4 bytes at a time for each GID from the beginning of the data stream until the end.

I think I've figured out the base64 decoding and 'gunzip' but the result from the code below is 27,0,0,0 repeating. I think the output is supposed to be

(0,0) (1,0) (2,0) (3,0) (0,1) (1,1) (2,1) (3,1) (0,2) (1,2) (2,2) (3,2)

Thanks!

 public static void main( String[] args )
 {
 String myString = "H4sIAAAAAAAAAO3NoREAMAgEsLedAfafE4+s6l0jolNJiif18tt/Fj8AAMC9ARtYg28AEAAA";

 byte[] decode = Base64.decodeBase64(myString);

 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decode); 
 GZIPInputStream gzipInputStream;

 int read;
 try
 {
      gzipInputStream = new GZIPInputStream(byteArrayInputStream);

      InputStreamReader inputStreamReader = new InputStreamReader(gzipInputStream);
      BufferedReader bufferedReader = new BufferedReader(inputStreamReader, 4);

      while ( ( read = bufferedReader.read() ) != -1 )
      {
           System.out.println("read :" + read);
      }
 }
 catch (IOException e)
 {
      e.printStackTrace();
 }
 }

Solution

  • Don't use Readers for anything but character data!

    Use a DataInput to read the integers. Decorate your GZIPInputStream with a DataInputStream and use readInt.

    If the ints are little-endian, you'll need to reverse the order of the bytes for the type. Java uses network byte order (big-endian). For integers, this can be done with Integer.reverseBytes.

    You can print the hex values using:

    System.out.format("%08x", (int) n);
    

    How to read all int values from a stream of arbitrary length:

    One mechanism would be to use the available() method which estimates the number of remaining bytes:

    byte[] ints = {0x00, 0x00, 0x00, (byte) 0xFF,
                   (byte) 0xAA, (byte) 0xBB, (byte) 0xEE, (byte) 0xFF};
    ByteArrayInputStream array = new ByteArrayInputStream(ints);
    DataInputStream data = new DataInputStream(array);
    while(data.available() > 0) {
      int reversed = Integer.reverseBytes(data.readInt());
      System.out.format("%08x%n", reversed);
    }
    

    In the general case, available() is not a reliable mechanism. But you can augment your stream with a buffer to check data availability:

      public static void main(String[] args) throws IOException {
        byte[] ints = {0x00, 0x00, 0x00, (byte) 0xFF,
                        (byte) 0xAA, (byte) 0xBB, (byte) 0xEE, (byte) 0xFF};
        ByteArrayInputStream array = new ByteArrayInputStream(ints);
        BufferedInputStream buffer = new BufferedInputStream(array);
        DataInputStream data = new DataInputStream(buffer);
        while(hasData(data)) {
          int reversed = Integer.reverseBytes(data.readInt());
          System.out.format("%08x%n", reversed);
        }
      }
    
      public static boolean hasData(InputStream in) throws IOException {
        in.mark(1);
        try {
          return in.read() != -1;
        } finally {
          in.reset();
        }
      }