1081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson/*
2081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * Copyright (C) 2011 The Android Open Source Project
3081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson *
4081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * Licensed under the Apache License, Version 2.0 (the "License");
5081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * you may not use this file except in compliance with the License.
6081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * You may obtain a copy of the License at
7081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson *
8081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson *      http://www.apache.org/licenses/LICENSE-2.0
9081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson *
10081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * Unless required by applicable law or agreed to in writing, software
11081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * distributed under the License is distributed on an "AS IS" BASIS,
12081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * See the License for the specific language governing permissions and
14081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * limitations under the License.
15081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson */
16081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
17081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilsonpackage com.android.dx.dex;
18081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
19dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilsonimport com.android.dx.io.DexBuffer;
207baa3b69f87348aa2f4f16375a66be59965e8dd4Dan Bornsteinimport com.android.dx.util.DexException;
21081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilsonimport java.io.IOException;
22dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilsonimport java.io.UnsupportedEncodingException;
23081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilsonimport java.util.Arrays;
24081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
25081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson/**
26081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson * The file header and map.
27081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson */
28081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilsonpublic final class TableOfContents {
29081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
30081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    /*
31081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson     * TODO: factor out ID constants.
32081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson     */
33081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
34081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section header = new Section(0x0000);
35081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section stringIds = new Section(0x0001);
36081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section typeIds = new Section(0x0002);
37081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section protoIds = new Section(0x0003);
38081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section fieldIds = new Section(0x0004);
39081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section methodIds = new Section(0x0005);
40081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section classDefs = new Section(0x0006);
41081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section mapList = new Section(0x1000);
42081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section typeLists = new Section(0x1001);
43081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section annotationSetRefLists = new Section(0x1002);
44081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section annotationSets = new Section(0x1003);
45081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section classDatas = new Section(0x2000);
46081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section codes = new Section(0x2001);
47081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section stringDatas = new Section(0x2002);
48081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section debugInfos = new Section(0x2003);
49081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section annotations = new Section(0x2004);
50081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section encodedArrays = new Section(0x2005);
51081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section annotationsDirectories = new Section(0x2006);
52081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public final Section[] sections = {
53081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            header, stringIds, typeIds, protoIds, fieldIds, methodIds, classDefs, mapList,
54081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            typeLists, annotationSetRefLists, annotationSets, classDatas, codes, stringDatas,
55081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            debugInfos, annotations, encodedArrays, annotationsDirectories
56081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    };
57081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
58081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public int checksum;
59081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public byte[] signature;
60081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public int fileSize;
61081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public int linkSize;
62081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public int linkOff;
63081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public int dataSize;
64081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public int dataOff;
65081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
66081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    public TableOfContents() {
67081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        signature = new byte[20];
68081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    }
69081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
70dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson    public void readFrom(DexBuffer buffer) throws IOException {
71dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        readHeader(buffer.open(0));
72dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        readMap(buffer.open(mapList.off));
734ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        computeSizesFromOffsets();
74dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson    }
75dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson
76dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson    private void readHeader(DexBuffer.Section headerIn) throws UnsupportedEncodingException {
77dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        byte[] magic = headerIn.readByteArray(8);
789fdbd91288a237eb58e18e4de9c729c3c268c318Dan Bornstein        int apiTarget = DexFormat.magicToApi(magic);
799fdbd91288a237eb58e18e4de9c729c3c268c318Dan Bornstein
800083c42a76583417303d5795ab60e3653d326c74Jesse Wilson        if (apiTarget != DexFormat.API_NO_EXTENDED_OPCODES) {
81081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            throw new DexException("Unexpected magic: " + Arrays.toString(magic));
82081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
83081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
84dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        checksum = headerIn.readInt();
85dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        signature = headerIn.readByteArray(20);
86dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        fileSize = headerIn.readInt();
87dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        int headerSize = headerIn.readInt();
88081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        if (headerSize != SizeOf.HEADER_ITEM) {
89081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            throw new DexException("Unexpected header: 0x" + Integer.toHexString(headerSize));
90081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
91dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        int endianTag = headerIn.readInt();
92081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        if (endianTag != DexFormat.ENDIAN_TAG) {
93081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            throw new DexException("Unexpected endian tag: 0x" + Integer.toHexString(endianTag));
94081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
95dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        linkSize = headerIn.readInt();
96dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        linkOff = headerIn.readInt();
97dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        mapList.off = headerIn.readInt();
98081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        if (mapList.off == 0) {
99081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            throw new DexException("Cannot merge dex files that do not contain a map");
100081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
101dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        stringIds.size = headerIn.readInt();
102dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        stringIds.off = headerIn.readInt();
103dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        typeIds.size = headerIn.readInt();
104dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        typeIds.off = headerIn.readInt();
105dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        protoIds.size = headerIn.readInt();
106dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        protoIds.off = headerIn.readInt();
107dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        fieldIds.size = headerIn.readInt();
108dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        fieldIds.off = headerIn.readInt();
109dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        methodIds.size = headerIn.readInt();
110dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        methodIds.off = headerIn.readInt();
111dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        classDefs.size = headerIn.readInt();
112dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        classDefs.off = headerIn.readInt();
113dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        dataSize = headerIn.readInt();
114dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson        dataOff = headerIn.readInt();
115081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    }
116081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
117dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson    private void readMap(DexBuffer.Section in) throws IOException {
118081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        int mapSize = in.readInt();
119081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        Section previous = null;
120081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        for (int i = 0; i < mapSize; i++) {
121081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            short type = in.readShort();
122081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            in.readShort(); // unused
123081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            Section section = getSection(type);
124081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            int size = in.readInt();
125081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            int offset = in.readInt();
126081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
127d1e1668224cde06bbe5ad8953c7fb86d1d0d40e4Jesse Wilson            if ((section.size != 0 && section.size != size)
128081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson                    || (section.off != -1 && section.off != offset)) {
129081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson                throw new DexException("Unexpected map value for 0x" + Integer.toHexString(type));
130081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            }
131081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
132081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            section.size = size;
133081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            section.off = offset;
134081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
1354ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            if (previous != null && previous.off > section.off) {
1364ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson                throw new DexException("Map is unsorted at " + previous + ", " + section);
137081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            }
138081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
139081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            previous = section;
140081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
1414ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        Arrays.sort(sections);
1424ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson    }
143081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
1444ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson    public void computeSizesFromOffsets() {
1454ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        int end = dataOff + dataSize;
1464ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        for (int i = sections.length - 1; i >= 0; i--) {
1474ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            Section section = sections[i];
1484ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            if (section.off == -1) {
1494ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson                continue;
1504ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            }
1514ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            if (section.off > end) {
1524ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson                throw new DexException("Map is unsorted at " + section);
153081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            }
1544ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            section.byteCount = end - section.off;
1554ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            end = section.off;
156081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
157081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    }
158081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
159081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    private Section getSection(short type) {
160081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        for (Section section : sections) {
161081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            if (section.type == type) {
162081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson                return section;
163081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            }
164081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
165081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        throw new IllegalArgumentException("No such map item: " + type);
166081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    }
167081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
168dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson    public void writeHeader(DexBuffer.Section out) throws IOException {
1690083c42a76583417303d5795ab60e3653d326c74Jesse Wilson        out.write(DexFormat.apiToMagic(DexFormat.API_NO_EXTENDED_OPCODES).getBytes("UTF-8"));
170081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(checksum);
171081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.write(signature);
172081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(fileSize);
173081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(SizeOf.HEADER_ITEM);
174081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(DexFormat.ENDIAN_TAG);
175081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(linkSize);
176081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(linkOff);
177081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(mapList.off);
178081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(stringIds.size);
179081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(stringIds.off);
180081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(typeIds.size);
181081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(typeIds.off);
182081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(protoIds.size);
183081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(protoIds.off);
184081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(fieldIds.size);
185081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(fieldIds.off);
186081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(methodIds.size);
187081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(methodIds.off);
188081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(classDefs.size);
189081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(classDefs.off);
190081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(dataSize);
191081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(dataOff);
192081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    }
193081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
194dc8ad6cbf19b62d8c50527a0a38fe82b937370f1Jesse Wilson    public void writeMap(DexBuffer.Section out) throws IOException {
195081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        int count = 0;
1964ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        for (Section section : sections) {
197d1e1668224cde06bbe5ad8953c7fb86d1d0d40e4Jesse Wilson            if (section.exists()) {
198081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson                count++;
199081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            }
200081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
201081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
202081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        out.writeInt(count);
2034ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        for (Section section : sections) {
204d1e1668224cde06bbe5ad8953c7fb86d1d0d40e4Jesse Wilson            if (section.exists()) {
2054ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson                out.writeShort(section.type);
206081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson                out.writeShort((short) 0);
2074ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson                out.writeInt(section.size);
2084ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson                out.writeInt(section.off);
209081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            }
210081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
211081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    }
212081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
2134ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson    public static class Section implements Comparable<Section> {
214081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        public final short type;
215d1e1668224cde06bbe5ad8953c7fb86d1d0d40e4Jesse Wilson        public int size = 0;
216081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        public int off = -1;
217081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        public int byteCount = 0;
218081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson
219081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        public Section(int type) {
220081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson            this.type = (short) type;
221081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson        }
2224ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson
22320d269ea2a9e8d41b298134f3937c6d959288b53Jesse Wilson        public boolean exists() {
224d1e1668224cde06bbe5ad8953c7fb86d1d0d40e4Jesse Wilson            return size > 0;
22520d269ea2a9e8d41b298134f3937c6d959288b53Jesse Wilson        }
22620d269ea2a9e8d41b298134f3937c6d959288b53Jesse Wilson
2274ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        public int compareTo(Section section) {
2284ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            if (off != section.off) {
2294ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson                return off < section.off ? -1 : 1;
2304ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            }
2314ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            return 0;
2324ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        }
2334ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson
2344ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        @Override public String toString() {
2354ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson            return String.format("Section[type=%#x,off=%#x,size=%#x]", type, off, size);
2364ceb6bc262c780c456c4d40222a2d0a56eaec02aJesse Wilson        }
237081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson    }
238081c7142b29ccd6e1744b26e097b6a4d7c12f2bdJesse Wilson}
239