com.crashlytics.tools.android_2.1.0

transferred);
  }
  
  public static abstract interface ProgressListener
  {
    public abstract void bytesWritten(long paramLong);
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.ListenableOutputStream
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;

public class NamedThreadBuilder
  implements ThreadFactory
{
  private String _name;
  private ThreadFactory _backedFactory;
  
  public NamedThreadBuilder(String name)
  {
    _name = name;
    _backedFactory = Executors.defaultThreadFactory();
  }
  
  final AtomicLong count = new AtomicLong(0L);
  
  public Thread newThread(Runnable runnable)
  {
    Thread thread = _backedFactory.newThread(runnable);
    thread.setName(_name + "-" + count.getAndIncrement());
    return thread;
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.NamedThreadBuilder
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

public class NumberUtils
{
  public static int toInt(String str, int defaultValue)
  {
    if (str == null) {
      return defaultValue;
    }
    try
    {
      return Integer.parseInt(str);
    }
    catch (NumberFormatException nfe) {}
    return defaultValue;
  }
  
  public static String toString(Number num)
  {
    return num == null ? null : num.toString();
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.NumberUtils
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

public class Pair<A, B>
{
  public final A fst;
  public final B snd;
  
  public static <A, B> Pair<A, B> pairOf(A fst, B snd)
  {
    return new Pair(fst, snd);
  }
  
  public Pair(A fst, B snd)
  {
    this.fst = fst;
    this.snd = snd;
  }
  
  public boolean equals(Object obj)
  {
    if (!(obj instanceof Pair)) {
      return false;
    }
    Pair<?, ?> other = (Pair)obj;
    return ((fst == null) && (fst == null)) || ((fst.equals(fst)) && (((snd == null) && (snd == null)) || (snd.equals(snd))));
  }
  
  public int hashCode()
  {
    return (fst != null ? fst.hashCode() : 0) ^ (snd != null ? snd.hashCode() : 0) + -17960947;
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.Pair
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

import java.io.UnsupportedEncodingException;

public abstract interface ParseStringStrategy
{
  public abstract String parseStringFrom(byte[] paramArrayOfByte)
    throws UnsupportedEncodingException;
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.ParseStringStrategy
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Map.Entry;
import java.util.Properties;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.io.IOUtils;

public class PropertiesUtils
{
  public static final String OPT_HELP = "help";
  public static final String OPT_PROPERTIES_PATH = "properties";
  
  public static Properties read(File propertiesFile)
    throws IOException
  {
    Properties propertiesFromDisk = new Properties();
    InputStreamReader in = null;
    try
    {
      in = new InputStreamReader(new FileInputStream(propertiesFile), "UTF-8");
      propertiesFromDisk.load(in);
    }
    finally
    {
      IOUtils.closeQuietly(in);
    }
    return propertiesFromDisk;
  }
  
  public static void write(File propertiesFile, Properties props, String comment)
    throws IOException
  {
    OutputStream out = null;
    try
    {
      out = new BufferedOutputStream(new FileOutputStream(propertiesFile));
      props.store(out, comment);
    }
    finally
    {
      IOUtils.closeQuietly(out);
    }
  }
  
  public static void injectPropertyInFile(File propertiesFile, Properties propertiesToUpdate, String header)
    throws IOException
  {
    Properties props = null;
    try
    {
      props = read(propertiesFile);
    }
    catch (IOException io) {}
    if (props == null)
    {
      propertiesFile.delete();
      propertiesFile.createNewFile();
      props = new Properties();
    }
    boolean changedAny = false;
    for (Map.Entry<Object, Object> e : propertiesToUpdate.entrySet()) {
      if ((!props.containsKey(e.getKey())) || (!e.getValue().equals(props.get(e.getKey()))))
      {
        props.put(e.getKey(), e.getValue());
        changedAny = true;
      }
    }
    if (changedAny) {
      write(propertiesFile, props, header);
    }
  }
  
  public static Properties processArgs(String mainClassName, String[] args, Options options)
    throws Exception
  {
    Properties properties = null;
    try
    {
      CommandLineParser parser = new GnuParser();
      CommandLine line = parser.parse(options, args);
      if (line.hasOption("help"))
      {
        HelpFormatter formatter = new HelpFormatter();
        formatter.printHelp(mainClassName, options);
      }
      return getProperties(line);
    }
    catch (Exception e)
    {
      HelpFormatter formatter = new HelpFormatter();
      formatter.printHelp(mainClassName, options);
      throw e;
    }
  }
  
  private static Properties getProperties(CommandLine line)
    throws Exception
  {
    Properties propertiesFromDisk = new Properties();
    File propertiesFile = null;
    if (line.hasOption("properties"))
    {
      propertiesFile = new File(line.getOptionValue("properties"));
      try
      {
        propertiesFromDisk = read(propertiesFile);
      }
      catch (IOException io)
      {
        propertiesFromDisk = new Properties();
      }
    }
    Properties optionsProperties = new Properties();
    for (Option opt : line.getOptions()) {
      if (opt.getValue() != null) {
        optionsProperties.put(opt.getOpt(), opt.getValue());
      } else {
        optionsProperties.put(opt.getOpt(), "true");
      }
    }
    propertiesFromDisk.putAll(optionsProperties);
    return propertiesFromDisk;
  }
  
  public static String toString(Properties properties)
  {
    StringBuilder argsStr = new StringBuilder();
    for (Map.Entry<Object, Object> argEntry : properties.entrySet()) {
      argsStr.append("\n-" + argEntry.getKey() + " " + argEntry.getValue());
    }
    return argsStr.toString();
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.PropertiesUtils
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

public class StringUtils
{
  public static String capitalize(String s)
  {
    int strLen;
    if ((s == null) || ((strLen = s.length()) == 0)) {
      return s;
    }
    int strLen;
    char firstChar = s.charAt(0);
    if (Character.isTitleCase(firstChar)) {
      return s;
    }
    return strLen + Character.toTitleCase(firstChar) + s.substring(1);
  }
  
  public static boolean equals(String s1, String s2)
  {
    if (s1 == s2) {
      return true;
    }
    if ((s1 == null) || (s2 == null)) {
      return false;
    }
    return s1.equals(s2);
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.StringUtils
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

import java.io.UnsupportedEncodingException;

public class Utf16ParseStringStrategy
  implements ParseStringStrategy
{
  public String parseStringFrom(byte[] buf)
    throws UnsupportedEncodingException
  {
    if (buf.length % 2 != 0) {
      return null;
    }
    int nullTerminatorLength = 0;
    
    int littleByteIndex = buf.length - 2;
    
    int bigByteIndex = buf.length - 1;
    while ((nullTerminatorLength < buf.length) && (buf[littleByteIndex] == 0) && (buf[bigByteIndex] == 0))
    {
      nullTerminatorLength += 2;
      littleByteIndex -= 2;
      bigByteIndex -= 2;
    }
    if (nullTerminatorLength > 0)
    {
      byte[] subString = new byte[buf.length - nullTerminatorLength];
      System.arraycopy(buf, 0, subString, 0, buf.length - nullTerminatorLength);
      buf = subString;
    }
    return new String(buf, "UTF-16LE");
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.Utf16ParseStringStrategy
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils;

import java.io.UnsupportedEncodingException;

public class Utf8ParseStringStrategy
  implements ParseStringStrategy
{
  public String parseStringFrom(byte[] buf)
    throws UnsupportedEncodingException
  {
    int nullTerminatorLength = 0;
    
    int index = buf.length - 1;
    while ((nullTerminatorLength < buf.length) && (buf[index] == 0))
    {
      nullTerminatorLength++;
      index--;
    }
    if (nullTerminatorLength > 0)
    {
      byte[] subString = new byte[buf.length - nullTerminatorLength];
      System.arraycopy(buf, 0, subString, 0, buf.length - nullTerminatorLength);
      buf = subString;
    }
    return new String(buf, "UTF-8");
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.Utf8ParseStringStrategy
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils.abx;

public class AbxAttribute
{
  String name;
  String value;
  int index;
  
  public String getName()
  {
    return name;
  }
  
  public void setName(String name)
  {
    this.name = name;
  }
  
  public String getValue()
  {
    return value;
  }
  
  public void setValue(String value)
  {
    this.value = value;
  }
  
  public int getIndex()
  {
    return index;
  }
  
  public void setIndex(int index)
  {
    this.index = index;
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.abx.AbxAttribute
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils.abx;

public enum AbxConstants$AbxFileType
{
  MANIFEST,  STRINGS;
  
  private AbxConstants$AbxFileType() {}
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.abx.AbxConstants.AbxFileType
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils.abx;

public class AbxConstants
{
  public static final int RES_NULL_TYPE = 0;
  public static final int RES_STRING_POOL_TYPE = 1;
  public static final int RES_TABLE_TYPE = 2;
  public static final int RES_XML_TYPE = 3;
  public static final int RES_XML_FIRST_CHUNK_TYPE = 256;
  public static final int RES_XML_START_NAMESPACE_TYPE = 256;
  public static final int RES_XML_END_NAMESPACE_TYPE = 257;
  public static final int RES_XML_START_ELEMENT_TYPE = 258;
  public static final int RES_XML_END_ELEMENT_TYPE = 259;
  public static final int RES_XML_CDATA_TYPE = 260;
  public static final int RES_XML_LAST_CHUNK_TYPE = 383;
  public static final int RES_XML_RESOURCE_MAP_TYPE = 384;
  public static final int RES_TABLE_PACKAGE_TYPE = 512;
  public static final int RES_TABLE_TYPE_TYPE = 513;
  public static final int RES_TABLE_TYPE_SPEC_TYPE = 514;
  public static final int RES_DATA_TYPE_STRING = 3;
  public static final int RES_FLAG_COMPLEX = 1;
  
  public static enum AbxFileType
  {
    MANIFEST,  STRINGS;
    
    private AbxFileType() {}
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.abx.AbxConstants
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils.abx;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

public class AbxNode
{
  public static int ROOT = 1;
  private int lineNumber;
  private String name;
  private String namespacePrefix;
  private String namespaceURI;
  private int namespaceLineNumber;
  private HashMap<String, AbxAttribute> attrs = new HashMap();
  private ArrayList<AbxNode> children = new ArrayList();
  
  public AbxNode()
  {
    this(null);
  }
  
  public AbxNode(AbxNode parent)
  {
    if (parent != null) {
      parent.addChild(this);
    }
  }
  
  public int getLinenumber()
  {
    return lineNumber;
  }
  
  public void setLineNumber(int lineNumber)
  {
    this.lineNumber = lineNumber;
  }
  
  public String getName()
  {
    return name;
  }
  
  public void setName(String name)
  {
    this.name = name;
  }
  
  public String getNamespacePrefix()
  {
    return namespacePrefix;
  }
  
  public void setNamespacePrefix(String namespacePrefix)
  {
    this.namespacePrefix = namespacePrefix;
  }
  
  public String getNamespaceURI()
  {
    return namespaceURI;
  }
  
  public void setNamespaceURI(String namespaceURI)
  {
    this.namespaceURI = namespaceURI;
  }
  
  public int getNamespaceLineNumber()
  {
    return namespaceLineNumber;
  }
  
  public void setNamespaceLineNumber(int namespaceLineNumber)
  {
    this.namespaceLineNumber = namespaceLineNumber;
  }
  
  public Collection<AbxAttribute> getAttrs()
  {
    return attrs.values();
  }
  
  public void addAttribute(AbxAttribute attr)
  {
    attrs.put(name, attr);
  }
  
  public String getAttribute(String string)
  {
    AbxAttribute attribute = (AbxAttribute)attrs.get(string);
    return attribute == null ? null : value;
  }
  
  public void setAttrs(Collection<AbxAttribute> attrs)
  {
    this.attrs.clear();
    for (AbxAttribute attr : attrs) {
      this.attrs.put(name, attr);
    }
  }
  
  public ArrayList<AbxNode> getChildren()
  {
    return children;
  }
  
  public void addChild(AbxNode child)
  {
    children.add(child);
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.abx.AbxNode
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils.abx;

import com.android.sdklib.util.SparseArray;
import com.crashlytics.tools.utils.ApkUtils;
import com.crashlytics.tools.utils.ParseStringStrategy;
import com.crashlytics.tools.utils.Utf16ParseStringStrategy;
import com.crashlytics.tools.utils.Utf8ParseStringStrategy;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Stack;

public class AbxParser
{
  private static final int STRING_LENGTH_BYTES = 2;
  private final byte[] mData;
  private byte[] _chunkTypeBuf = new byte[2];
  private byte[] _headerSizeBuf = new byte[2];
  private byte[] _chunkSizeBuf = new byte[4];
  private int _headerSize;
  private int _chunkSize;
  private byte[] _buf1 = new byte[1];
  private byte[] _buf2 = new byte[2];
  private byte[] _buf4 = new byte[4];
  private int _stringTypeId = -1;
  private SparseArray<AbxStringPoolEntry> _stringPoolManifest = new SparseArray();
  private SparseArray<AbxStringPoolEntry> _stringPoolValues = new SparseArray();
  private SparseArray<AbxStringPoolEntry> _stringPoolKeys = new SparseArray();
  private SparseArray<AbxStringPoolEntry> _stringPoolResTypes = new SparseArray();
  private ArrayList<Integer> _resMap = new ArrayList();
  private int nsPrefixIndex = -1;
  private int nsUriIndex = -1;
  private int nsLineNumber = 0;
  
  public AbxParser(byte[] binaryXml)
  {
    mData = binaryXml;
  }
  
  public AbxNode parseManifest()
    throws IOException
  {
    PositionInputStream in = null;
    
    AbxNode root = null;
    try
    {
      in = new PositionInputStream(new ByteArrayInputStream(mData));
      
      in.read(_chunkTypeBuf);
      if (AbxUtils.toInt(_chunkTypeBuf, false) != 3)
      {
        ApkUtils.logW("Invalid Android Binary XML file.", null);
        return null;
      }
      in.read(_headerSizeBuf);
      int headerSize = AbxUtils.toInt(_headerSizeBuf, false);
      
      in.read(_chunkSizeBuf);
      int chunkSize = AbxUtils.toInt(_chunkSizeBuf, false);
      
      ApkUtils.log("Header Size: " + headerSize + " Chunk size: " + chunkSize);
      
      in.read(_chunkTypeBuf);
      if (AbxUtils.toInt(_chunkTypeBuf, false) == 1)
      {
        ApkUtils.log("String Pool for manifest...");
        in.read(_headerSizeBuf);
        headerSize = AbxUtils.toInt(_headerSizeBuf, false);
        
        in.read(_chunkSizeBuf);
        chunkSize = AbxUtils.toInt(_chunkSizeBuf, false);
        
        ApkUtils.log("String Pool for manifest...Header Size: " + headerSize + " Chunk Size: " + chunkSize);
        
        byte[] spBuf = new byte[chunkSize - 8];
        in.read(spBuf);
        
        parseStringPool(spBuf, headerSize, chunkSize, _stringPoolManifest, new Utf16ParseStringStrategy());
        
        in.read(_chunkTypeBuf);
      }
      if (AbxUtils.toInt(_chunkTypeBuf, false) == 384)
      {
        in.read(_headerSizeBuf);
        headerSize = AbxUtils.toInt(_headerSizeBuf, false);
        
        in.read(_chunkSizeBuf);
        chunkSize = AbxUtils.toInt(_chunkSizeBuf, false);
        
        byte[] rmBuf = new byte[chunkSize - 8];
        in.read(rmBuf);
        
        parseResMapping(rmBuf, headerSize, chunkSize);
        
        in.read(_chunkTypeBuf);
      }
      if (AbxUtils.toInt(_chunkTypeBuf, false) == 256)
      {
        in.read(_headerSizeBuf);
        headerSize = AbxUtils.toInt(_headerSizeBuf, false);
        
        in.read(_chunkSizeBuf);
        chunkSize = AbxUtils.toInt(_chunkSizeBuf, false);
        
        byte[] nsStartBuf = new byte[chunkSize - 8];
        in.read(nsStartBuf);
        
        parseStartNameSpace(nsStartBuf, headerSize, chunkSize);
      }
      in.read(_chunkTypeBuf);
      int chunkType = AbxUtils.toInt(_chunkTypeBuf, false);
      
      Stack<AbxNode> currentParent = new Stack();
      while (chunkType != 257)
      {
        ApkUtils.log("Parsing XML node...Chunk_Type " + chunkType);
        
        in.read(_headerSizeBuf);
        headerSize = AbxUtils.toInt(_headerSizeBuf, false);
        
        in.read(_chunkSizeBuf);
        chunkSize = AbxUtils.toInt(_chunkSizeBuf, false);
        
        byte[] elementBuf = new byte[chunkSize - 8];
        in.read(elementBuf);
        if (chunkType == 258)
        {
          AbxNode parent = currentParent.size() > 0 ? (AbxNode)currentParent.peek() : null;
          currentParent.add(parseXMLStart(parent, elementBuf, headerSize, chunkSize));
        }
        else if (chunkType == 259)
        {
          parseXMLEnd(elementBuf, headerSize, chunkSize);
          
          root = (AbxNode)currentParent.pop();
        }
        in.read(_chunkTypeBuf);
        chunkType = AbxUtils.toInt(_chunkTypeBuf, false);
      }
      if (chunkType == 257)
      {
        in.read(_headerSizeBuf);
        headerSize = AbxUtils.toInt(_headerSizeBuf, false);
        
        in.read(_chunkSizeBuf);
        chunkSize = AbxUtils.toInt(_chunkSizeBuf, false);
        
        byte[] nsEndBuf = new byte[chunkSize - 8];
        in.read(nsEndBuf);
        
        parseEndNameSpace(nsEndBuf, headerSize, chunkSize);
      }
    }
    finally
    {
      if (in != null) {
        in.close();
      }
    }
    return root;
  }
  
  private void parseStringPool(byte[] spBuf, int headerSize, int chunkSize, SparseArray<AbxStringPoolEntry> list, ParseStringStrategy parseStringStrategy)
    throws IOException
  {
    ByteArrayInputStream in = new ByteArrayInputStream(spBuf);
    
    byte[] buffer = new byte[4];
    in.read(buffer);
    
    int stringCount = AbxUtils.toInt(buffer, false);
    in.read(buffer);
    int styleCount = AbxUtils.toInt(buffer, false);
    in.read(buffer);
    int flag = AbxUtils.toInt(buffer, false);
    in.read(buffer);
    int stringStart = AbxUtils.toInt(buffer, false);
    in.read(buffer);
    int styleStart = AbxUtils.toInt(buffer, false);
    
    ApkUtils.log("String Count: " + stringCount + " Style Count: " + styleCount + " Flag: " + flag + " String Start: " + stringStart + " Style Start: " + styleStart);
    
    int[] stringIndices = new int[stringCount];
    if (stringCount > 0) {
      for (int i = 0; i < stringCount; i++)
      {
        in.read(buffer);
        stringIndices[i] = AbxUtils.toInt(buffer, false);
      }
    }
    if (styleCount > 0) {
      in.skip(styleCount * 4);
    }
    for (int i = 0; i < stringCount; i++)
    {
      int stringLen = 0;
      if (i == stringCount - 1)
      {
        if (styleStart == 0)
        {
          stringLen = chunkSize - stringIndices[i] - headerSize - 4 * stringCount;
          ApkUtils.log("Last String size: " + stringLen + " Chunk_Size: " + chunkSize + " Index: " + stringIndices[i]);
        }
        else
        {
          stringLen = styleStart - stringIndices[i];
        }
      }
      else {
        stringLen = stringIndices[(i + 1)] - stringIndices[i];
      }
      byte[] shortBuffer = new byte[2];
      in.read(shortBuffer);
      
      byte[] buf = new byte[stringLen - 2];
      in.read(buf);
      
      String str = parseStringStrategy.parseStringFrom(buf);
      ApkUtils.log("Found in String Pool: " + str + " offset: " + stringIndices[i] + ", index: " + i);
      if ((list.size() < i) && (list.valueAt(i) != null)) {
        throw new IOException("Overwriting value at index " + i + ": old value: " + list.valueAt(i) + ", new value: " + str);
      }
      list.append(i, new AbxStringPoolEntry(str, i));
    }
    ApkUtils.log("[String Pool] Size: " + _stringPoolValues.size());
    ApkUtils.log("[String Pool] " + _stringPoolValues);
  }
  
  private void parseResMapping(byte[] rmBuf, int headerSize, int chunkSize)
    throws IOException
  {
    ByteArrayInputStream in = new ByteArrayInputStream(rmBuf);
    
    int resIdCount = rmBuf.length / 4;
    
    byte[] intBuffer = new byte[4];
    for (int i = 0; i < resIdCount; i++)
    {
      in.read(intBuffer);
      _resMap.add(Integer.valueOf(AbxUtils.toInt(intBuffer, false)));
    }
    ApkUtils.log("[Res Mapping] Resource Mapping " + _resMap);
  }
  
  private void parseStartNameSpace(byte[] nsStartBuf, int headerSize, int chunkSize)
    throws IOException
  {
    ByteArrayInputStream in = new ByteArrayInputStream(nsStartBuf);
    
    byte[] intBuffer = new byte[4];
    in.read(intBuffer);
    nsLineNumber = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    
    in.read(intBuffer);
    nsPrefixIndex = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    nsUriIndex = AbxUtils.toInt(intBuffer, false);
    
    ApkUtils.log("[Namespace Start]Line Number: " + nsLineNumber + " Prefix: " + _stringPoolManifest.valueAt(nsPrefixIndex) + " URI: " + _stringPoolManifest.valueAt(nsUriIndex));
  }
  
  private AbxNode parseXMLStart(AbxNode parent, byte[] xmlStartBuf, int headerSize, int chunkSize)
    throws IOException
  {
    AbxNode node = new AbxNode(parent);
    
    ByteArrayInputStream in = new ByteArrayInputStream(xmlStartBuf);
    
    byte[] intBuf = new byte[4];
    
    in.read(intBuf);
    int lineNumber = AbxUtils.toInt(intBuf, false);
    node.setLineNumber(lineNumber);
    
    in.read(intBuf);
    
    in.read(intBuf);
    
    in.read(intBuf);
    int nameIndex = AbxUtils.toInt(intBuf, false);
    
    byte[] shortBuffer = new byte[2];
    
    in.read(shortBuffer);
    
    in.read(shortBuffer);
    
    in.read(shortBuffer);
    int attributeCount = AbxUtils.toInt(shortBuffer, false);
    
    in.skip(6L);
    
    ApkUtils.log("[XML Node] Name: " + (nameIndex == -1 ? "-1" : _stringPoolManifest.valueAt(nameIndex)) + " Attr count: " + attributeCount);
    if (nameIndex != -1)
    {
      node.setName(((AbxStringPoolEntry)_stringPoolManifest.valueAt(nameIndex)).getValue());
      if ((nsPrefixIndex != -1) && (nsUriIndex != -1))
      {
        node.setNamespacePrefix(((AbxStringPoolEntry)_stringPoolManifest.valueAt(nsPrefixIndex)).getValue());
        node.setNamespaceURI(((AbxStringPoolEntry)_stringPoolManifest.valueAt(nsUriIndex)).getValue());
      }
    }
    if (attributeCount == 0) {
      return node;
    }
    for (int i = 0; i < attributeCount; i++)
    {
      AbxAttribute attr = new AbxAttribute();
      
      in.read(intBuf);
      
      in.read(intBuf);
      int attrNameIndex = AbxUtils.toInt(intBuf, false);
      
      in.read(intBuf);
      int attrRawValue = AbxUtils.toInt(intBuf, false);
      
      String attrValue = "";
      if (attrRawValue == -1)
      {
        in.read(shortBuffer);
        
        int dataSize = AbxUtils.toInt(shortBuffer, false);
        
        in.skip(1L);
        
        int dataType = in.read();
        
        in.read(intBuf);
        int data = AbxUtils.toInt(intBuf, false);
        attrValue = "" + data;
      }
      else
      {
        attrValue = ((AbxStringPoolEntry)_stringPoolManifest.valueAt(attrRawValue)).getValue();
        
        in.skip(8L);
      }
      if (attrNameIndex != -1)
      {
        attr.setName(((AbxStringPoolEntry)_stringPoolManifest.valueAt(attrNameIndex)).getValue());
        attr.setValue(attrValue);
        attr.setIndex(i);
        node.addAttribute(attr);
      }
    }
    return node;
  }
  
  private void parseXMLEnd(byte[] xmlEndBuf, int headerSize, int chunkSize)
    throws IOException
  {
    ByteArrayInputStream in = new ByteArrayInputStream(xmlEndBuf);
    
    byte[] intBuffer = new byte[4];
    in.read(intBuffer);
    int lineNumber = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    
    int comment = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    int nsIndex = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    int nameIndex = AbxUtils.toInt(intBuffer, false);
    
    ApkUtils.log("[XML_END] Line Number: " + lineNumber + " Namespace: " + nsIndex + " Name: " + (nameIndex == -1 ? "-1" : _stringPoolManifest.valueAt(nameIndex)));
    if (nameIndex != -1)
    {
      AbxNode node = new AbxNode();
      node.setName(((AbxStringPoolEntry)_stringPoolManifest.valueAt(nameIndex)).getValue());
      node.setLineNumber(lineNumber);
      node.setNamespacePrefix(((AbxStringPoolEntry)_stringPoolManifest.valueAt(nsPrefixIndex)).getValue());
      node.setNamespaceURI(((AbxStringPoolEntry)_stringPoolManifest.valueAt(nsUriIndex)).getValue());
    }
  }
  
  private void parseEndNameSpace(byte[] nsStartBuf, int headerSize, int chunkSize)
    throws IOException
  {
    ByteArrayInputStream in = new ByteArrayInputStream(nsStartBuf);
    
    byte[] intBuffer = new byte[4];
    in.read(intBuffer);
    int lineNumber = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    
    int comment = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    int prefixIndex = AbxUtils.toInt(intBuffer, false);
    
    in.read(intBuffer);
    int uriIndex = AbxUtils.toInt(intBuffer, false);
    
    ApkUtils.log("[Namespace END]Line Number: " + lineNumber + " Prefix: " + prefixIndex + " URI: " + uriIndex);
  }
  
  public StringResourcesMap parseResourceTable(ParseStringStrategy strategy)
    throws IOException
  {
    StringResourcesMap map = new StringResourcesMap();
    
    ApkUtils.log("[Res_Table] Parsing resources.arsc...");
    
    PositionInputStream in = null;
    try
    {
      in = new PositionInputStream(new ByteArrayInputStream(mData));
      
      in.read(_buf2);
      if (AbxUtils.toInt(_buf2, false) != 2)
      {
        ApkUtils.logW("Invalid resources.arsc file.", null);
        return null;
      }
      in.read(_buf2);
      
      in.read(_buf4);
      
      in.read(_buf4);
      
      in.read(_buf2);
      byte[] spBuf;
      if (AbxUtils.toInt(_buf2, false) == 1)
      {
        ApkUtils.log("String Pool...");
        in.read(_buf2);
        _headerSize = AbxUtils.toInt(_buf2, false);
        
        in.read(_buf4);
        _chunkSize = AbxUtils.toInt(_buf4, false);
        
        spBuf = new byte[_chunkSize - 8];
        in.read(spBuf);
        
        ApkUtils.log("Parsing string pool at absolute loc: " + in.getPosition());
        parseStringPool(spBuf, _headerSize, _chunkSize, _stringPoolValues, strategy);
        
        in.read(_buf2);
      }
      else
      {
        ApkUtils.logW("Expected string pool containing values.", null);
        return null;
      }
      ApkUtils.log("[Res_Table] Chunk type: " + AbxUtils.toInt(_buf2, false));
      ApkUtils.log("=== parseResPackage() ===");
      if (AbxUtils.toInt(_buf2, false) == 512) {
        parseResPackage(in, map, strategy);
      }
    }
    finally
    {
      if (in != null) {
        in.close();
      }
    }
    return map;
  }
  
  private void parseResPackage(PositionInputStream in, StringResourcesMap map, ParseStringStrategy strategy)
    throws IOException
  {
    in.read(_buf2);
    _headerSize = AbxUtils.toInt(_buf2, false);
    
    in.read(_buf4);
    _chunkSize = AbxUtils.toInt(_buf4, false);
    
    in.read(_buf4);
    int packgageId = AbxUtils.toInt(_buf4, false);
    
    ApkUtils.log("String Pool...Header Size: " + _headerSize + " Chunk Size: " + _chunkSize + " Packg_ID: " + packgageId);
    
    byte[] packageNameBuffer = new byte['?'];
    in.read(packageNameBuffer);
    
    String packageName = AbxUtils.toString(packageNameBuffer, false);
    ApkUtils.log("Package Name: " + packageName);
    
    in.read(_buf4);
    int typeStrings = AbxUtils.toInt(_buf4, false);
    
    in.read(_buf4);
    int lastPublicType = AbxUtils.toInt(_buf4, false);
    
    in.read(_buf4);
    int keyString = AbxUtils.toInt(_buf4, false);
    
    in.read(_buf4);
    int lastPublicKey = AbxUtils.toInt(_buf4, false);
    
    ApkUtils.log("[Res_Table] typeStrings=" + typeStrings + " lastPublicType=" + lastPublicType + " keyString=" + keyString + " lastPublicKey=" + lastPublicKey);
    
    in.read(_buf2);
    if (AbxUtils.toInt(_buf2, false) == 1)
    {
      ApkUtils.log("typeString String Pool...");
      in.read(_buf2);
      _headerSize = AbxUtils.toInt(_buf2, false);
      
      in.read(_buf4);
      _chunkSize = AbxUtils.toInt(_buf4, false);
      
      ApkUtils.log("String Pool...Header Size: " + _headerSize + " Chunk Size: " + _chunkSize);
      
      byte[] spBuf = new byte[_chunkSize - 8];
      in.read(spBuf);
      
      parseStringPool(spBuf, _headerSize, _chunkSize, _stringPoolResTypes, new Utf8ParseStringStrategy());
      tryToFindStringResId();
      if (_stringTypeId < 0)
      {
        _stringPoolResTypes = new SparseArray();
        parseStringPool(spBuf, _headerSize, _chunkSize, _stringPoolResTypes, new Utf16ParseStringStrategy());
        tryToFindStringResId();
        if (_stringTypeId < 0) {
          throw new IOException("Could not find string type in resource table.");
        }
      }
      in.read(_buf2);
    }
    if (AbxUtils.toInt(_buf2, false) == 1)
    {
      ApkUtils.log("String Pool...");
      in.read(_buf2);
      _headerSize = AbxUtils.toInt(_buf2, false);
      
      in.read(_buf4);
      _chunkSize = AbxUtils.toInt(_buf4, false);
      
      ApkUtils.log("String Pool...Header Size: " + _headerSize + " Chunk Size: " + _chunkSize);
      
      byte[] spBuf = new byte[_chunkSize - 8];
      in.read(spBuf);
      
      parseStringPool(spBuf, _headerSize, _chunkSize, _stringPoolKeys, strategy);
      
      in.read(_buf2);
    }
    for (;;)
    {
      long absoluteOffset = in.getPosition();
      ApkUtils.log("Absolute byte offset is " + absoluteOffset);
      ApkUtils.log("Next table type is 0x" + Integer.toHexString(AbxUtils.toInt(_buf2, false)));
      if (AbxUtils.toInt(_buf2, false) == 514)
      {
        in.read(_buf2);
        _headerSize = AbxUtils.toInt(_buf2, false);
        
        in.read(_buf4);
        _chunkSize = AbxUtils.toInt(_buf4, false);
        
        ApkUtils.log("Typespec table...Header Size: " + _headerSize + " Chunk Size: " + _chunkSize);
        
        in.read(_buf1);
        int id = AbxUtils.toInt(_buf1, false);
        
        in.read(_buf1);
        int res0 = AbxUtils.toInt(_buf1, false);
        
        in.read(_buf2);
        int res1 = AbxUtils.toInt(_buf2, false);
        
        in.read(_buf4);
        int entryCount = AbxUtils.toInt(_buf4, false);
        
        ApkUtils.log("[Typespec] (N/A) id: " + id + " (" + ((AbxStringPoolEntry)_stringPoolResTypes.get(id - 1)).getValue() + "); res0: " + res0 + "; res1: " + res1 + "; entryCount: " + entryCount);
        
        in.skip(_chunkSize - 8 - 8);
        
        in.read(_buf2);
      }
      else if (AbxUtils.toInt(_buf2, false) == 513)
      {
        in.read(_buf2);
        _headerSize = AbxUtils.toInt(_buf2, false);
        
        in.read(_buf4);
        _chunkSize = AbxUtils.toInt(_buf4, false);
        
        ApkUtils.log("Type table...Header Size: " + _headerSize + " Chunk Size: " + _chunkSize);
        
        in.read(_buf1);
        int id = AbxUtils.toInt(_buf1, false);
        
        in.read(_buf1);
        int res0 = AbxUtils.toInt(_buf1, false);
        
        in.read(_buf2);
        int res1 = AbxUtils.toInt(_buf2, false);
        
        in.read(_buf4);
        int entryCount = AbxUtils.toInt(_buf4, false);
        
        in.read(_buf4);
        int entriesStart = AbxUtils.toInt(_buf4, false);
        
        in.read(_buf4);
        int resTableConfigSize = AbxUtils.toInt(_buf4, false);
        in.skip(resTableConfigSize - 4);
        
        ApkUtils.log("[ResTable_type] id: " + id + " (" + ((AbxStringPoolEntry)_stringPoolResTypes.get(id - 1)).getValue() + "); res0: " + res0 + "; res1: " + res1 + "; entryCount: " + entryCount + "; entriesStart: " + entriesStart + "; resTableConfigSize: " + resTableConfigSize);
        if (id != _stringTypeId)
        {
          int bytesToSkip = _chunkSize - 8 - resTableConfigSize - 12;
          
          ApkUtils.log("Skipping table type chunk with ID " + id + " (" + bytesToSkip + " bytes)");
          long actuallySkippedBytes = in.skip(bytesToSkip);
          ApkUtils.log("Actually skipped " + actuallySkippedBytes + " bytes");
          in.read(_buf2);
        }
        else
        {
          for (int i = 0; i < entryCount; i++)
          {
            in.read(_buf4);
            int entryOffset = AbxUtils.toInt(_buf4, false);
            ApkUtils.log("[ResTable_type] Offset for entry " + i + ": " + entryOffset);
          }
          for (int i = 0; i < entryCount; i++)
          {
            in.read(_buf2);
            int entryKeyStructSize = AbxUtils.toInt(_buf2, false);
            
            in.read(_buf2);
            int flags = AbxUtils.toInt(_buf2, false);
            
            in.read(_buf4);
            int stringpoolIndex = AbxUtils.toInt(_buf4, false);
            String key = ((AbxStringPoolEntry)_stringPoolKeys.get(stringpoolIndex)).getValue();
            
            ApkUtils.log("[ResTable_entry] key: " + stringpoolIndex + " = " + key + "[size: " + entryKeyStructSize + "]");
            if ((flags & 0x1) > 0) {
              throw new IOException("Complex types are currently not supported.");
            }
            in.read(_buf2);
            int entryValueStructSize = AbxUtils.toInt(_buf2, false);
            
            in.read(_buf1);
            
            int res0v = AbxUtils.toInt(_buf1, false);
            
            in.read(_buf1);
            int dataType = AbxUtils.toInt(_buf1, false);
            if (dataType != 3)
            {
              ApkUtils.log("[ResTable_entry] Value is not of type string but instead: " + dataType + ". Skipping. [size: " + entryValueStructSize + "]");
              
              in.skip(entryValueStructSize - 4);
            }
            else
            {
              in.read(_buf4);
              int stringpoolValueIndex = AbxUtils.toInt(_buf4, false);
              AbxStringPoolEntry value = (AbxStringPoolEntry)_stringPoolValues.get(stringpoolValueIndex);
              ApkUtils.log("[ResTable_entry] value: " + stringpoolValueIndex + " = " + value);
              
              map.put(key, value);
            }
          }
          in.read(_buf2);
          
          break;
        }
      }
      else
      {
        throw new IOException("Could not find the list of strings in the app's resource table.");
      }
    }
  }
  
  private void tryToFindStringResId()
  {
    for (int i = 0; i < _stringPoolResTypes.size(); i++)
    {
      AbxStringPoolEntry entry = (AbxStringPoolEntry)_stringPoolResTypes.valueAt(i);
      ApkUtils.log("Entry: " + entry.getValue() + ": " + entry.getIndex());
      if ("string".equals(entry.getValue()))
      {
        _stringTypeId = (entry.getIndex() + 1);
        
        break;
      }
    }
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.abx.AbxParser
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils.abx;

public class AbxStringPoolEntry
{
  private final String value;
  private final int index;
  
  public AbxStringPoolEntry(String value, int index)
  {
    this.value = value;
    this.index = index;
  }
  
  public String getValue()
  {
    return value;
  }
  
  public int getIndex()
  {
    return index;
  }
  
  public String toString()
  {
    return "(" + value + ": 0x" + Integer.toHexString(index) + ")";
  }
}

/* Location:
 * Qualified Name:     com.crashlytics.tools.utils.abx.AbxStringPoolEntry
 * Java Class Version: 6 (50.0)
 * JD-Core Version:    0.7.1
 */
package com.crashlytics.tools.utils.abx;

import java.io.ByteArrayInputStream;
import java.io.IOException;

public class AbxUtils
{
  private static final char[] hexStr = { 'A', 'B', 'C', 'D', 'E', 'F' };
  public static final int DEFAULT_PACKAGE_ID = 127;
  
  public static String toHex(byte[] bytes)
  {
    if ((bytes == null) || (bytes.length == 0)) {
      return "";
    }
    String hex = "";
    for (int i = 0; i < bytes.length; i++)
    {
      int num = 0xFF & bytes[i];
      int div = num / 16;
      int rem = num % 16;
      if (div > 9)
      {
        div -= 10;
        hex = hex + " 0x" + hexStr[div];
      }
      else
      {
        hex = hex + " 0x" + div;
      }
      if (rem > 9)
      {
        rem -= 10;
        hex = hex + "" + hexStr[rem];
      }
      else
      {
        hex = hex + "" + rem;
      }
    }
    return hex;
  }
  
  public static int toInt(byte[] bytes, boolean isBigEndian)
  {
    int x = 0;
    int numOfBytes = bytes.length;
    if (numOfBytes > 4) {
      numOfBytes = 4;
    }
    for (int i = 0; i < numOfBytes; i++) {
      if (i == 0)
      {
        if (isBigEndian) {
          x = 0xFF & bytes[i];
        } else {
          x = 0xFF & bytes[(numOfBytes - 1)];
        }
      }
      else if (isBigEndian) {
        x = x << 8 | 0xFF & bytes[i];
      } else {
        x = x << 8 | 0xFF & bytes[(numOfBytes - 1 - i)];
      }
    }
    return x;
  }
  
  public static long toLong(byte[] bytes, boolean isBigEndian)
  {
    long x = 0L;
    int numOfBytes = bytes.length;
    if (numOfBytes > 8) {
      numOfBytes = 8;
    }
    for (int i = 0; i < numOfBytes; i++) {
      if (i == 0)
      {
        if (isBigEndian) {
          x = 0xFF & bytes[i];
        } else {
          x = 0xFF & bytes[(numOfBytes - 1)];
        }
      }
      else if (isBigEndian) {
        x = x << 8 | 0xFF & bytes[i];
      } else {
        x = x << 8 | 0xFF & bytes[(numOfBytes - 1 - i)];
      }
    }
    return x;
  }
  
  public static String toString(byte[] charBuf, boolean isBigEndian)
    throws IOException
  {
    StringBuffer strBuf = new StringBuffer();
    byte[] buf_2 = new byte[2];
    ByteArrayInputStream in = new ByteArrayInputStream(charBuf);
    while (in.read(buf_2) != -1)
    {
      int code = toInt(buf_2, isBigEndian);
      if (code == 0) {
        break;
      }
      strBuf.append((char)code);
    }
    return strBuf.toString();
  }
  
  public static int resIdFromEntryIndex(int packageId, int typeIndex, i
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109

Further reading...

For more information on Java 1.5 Tiger, you may find Java 1.5 Tiger, A developer's Notebook by D. Flanagan and B. McLaughlin from O'Reilly of interest.

New!JAR listings


Copyright 2006-2019. Infinite Loop Ltd