private IEnumerable <XmlParserEventCode> ParserIterator() { while (true) { ClearState(); if (_reader.BaseStream.Position >= _reader.BaseStream.Length) { // If we're at the end of the file, stop reading chunks. // Don't try to catch an EndOfStreamException - this way, // we avoid an exception being created. break; } ResChunk_header header; try { header = _reader.ReadResChunk_header(); } catch (EndOfStreamException) { // Keep this just in case. break; } var subStream = new BoundedStream(_reader.BaseStream, header.Size - 8); var subReader = new ResReader(subStream); switch (header.Type) { case ResourceType.RES_XML_TYPE: yield return(XmlParserEventCode.StartDocument); _reader = subReader; // Bound whole file continue; // Don't skip content case ResourceType.RES_STRING_POOL_TYPE: var stringPoolHeader = subReader.ReadResStringPool_header(header); Strings = subReader.ReadResStringPool(stringPoolHeader); break; case ResourceType.RES_XML_RESOURCE_MAP_TYPE: var resourceMap = subReader.ReadResResourceMap(header); ResourceMap = resourceMap; break; case ResourceType.RES_XML_START_NAMESPACE_TYPE: _currentNode = subReader.ReadResXMLTree_node(header); _currentExtension = subReader.ReadResXMLTree_namespaceExt(); yield return(XmlParserEventCode.StartNamespace); break; case ResourceType.RES_XML_END_NAMESPACE_TYPE: _currentNode = subReader.ReadResXMLTree_node(header); _currentExtension = subReader.ReadResXMLTree_namespaceExt(); yield return(XmlParserEventCode.EndNamespace); break; case ResourceType.RES_XML_START_ELEMENT_TYPE: _currentNode = subReader.ReadResXMLTree_node(header); var attrExt = subReader.ReadResXMLTree_attrExt(); _currentExtension = attrExt; _attributes = new List <ResXMLTree_attribute>(); for (var i = 0; i < attrExt.AttributeCount; i++) { _attributes.Add(subReader.ReadResXMLTree_attribute()); } yield return(XmlParserEventCode.StartTag); break; case ResourceType.RES_XML_END_ELEMENT_TYPE: _currentNode = subReader.ReadResXMLTree_node(header); _currentExtension = subReader.ReadResXMLTree_endElementExt(); yield return(XmlParserEventCode.EndTag); break; case ResourceType.RES_XML_CDATA_TYPE: _currentNode = subReader.ReadResXMLTree_node(header); _currentExtension = subReader.ReadResXMLTree_cdataExt(); yield return(XmlParserEventCode.Text); break; default: Debug.WriteLine(String.Format("Warning: Skipping chunk of type {0} (0x{1:x4})", header.Type, (int)header.Type)); break; } var junk = subStream.ReadFully(); if (junk.Length > 0) { Debug.WriteLine(String.Format("Warning: Skipping {0} bytes at the end of a {1} (0x{2:x4}) chunk.", junk.Length, header.Type, (int)header.Type)); } } }
public static ArscFile Read(Stream stream) { var file = new ArscFile(); using (var reader = new ResReader(stream)) { var chunkHeader = reader.ReadResChunk_header(); if (chunkHeader.Type != ResourceType.RES_TABLE_TYPE) { throw new ApkReaderException("No RES_TABLE_TYPE found!"); } if (chunkHeader.Size != stream.Length) { throw new ApkReaderException("The buffer size not matches to the resource table size."); } file.Length = chunkHeader.Size; var resTable = reader.ReadResTable_header(chunkHeader); var globalStringPoolHeader = reader.ReadResStringPool_header(reader.ReadResChunk_header()); var globalStringPool = reader.ReadResStringPool(globalStringPoolHeader); file.GlobalStringPool = globalStringPool; for (var packageIndex = 0; packageIndex < resTable.PackageCount; packageIndex++) { var package = new ArscPackage(); file.Packages.Add(package); chunkHeader = reader.ReadResChunk_header(); var endPosition = stream.Position - 8 + chunkHeader.Size; if (chunkHeader.Type != ResourceType.RES_TABLE_PACKAGE_TYPE) { throw new ApkReaderException(); } var packageHeader = reader.ReadResTable_package(chunkHeader); package.Id = packageHeader.Id; package.Name = packageHeader.Name.TrimEnd('\0'); chunkHeader = reader.ReadResChunk_header(); if (chunkHeader.Type != ResourceType.RES_STRING_POOL_TYPE) { throw new ApkReaderException(); } package.TypeStringPool = reader.ReadResStringPool(reader.ReadResStringPool_header(chunkHeader)); chunkHeader = reader.ReadResChunk_header(); if (chunkHeader.Type != ResourceType.RES_STRING_POOL_TYPE) { throw new ApkReaderException(); } package.KeyStringPool = reader.ReadResStringPool(reader.ReadResStringPool_header(chunkHeader)); do { chunkHeader = reader.ReadResChunk_header(); switch (chunkHeader.Type) { case ResourceType.RES_TABLE_TYPE_SPEC_TYPE: var th = reader.ReadResTable_typeSpec(chunkHeader); package.TypeSpecsData = new uint[th.EntryCount]; for (var i = 0; i < th.EntryCount; i++) { package.TypeSpecsData[i] = reader.ReadUInt32(); } break; case ResourceType.RES_TABLE_TYPE_TYPE: var table = reader.ReadResTable_type(chunkHeader); var arscTable = new ArscTable { Config = table.Config }; package.Tables.Add(arscTable); var entryIndices = new int[table.EntryCount]; for (var i = 0; i < table.EntryCount; i++) { entryIndices[i] = reader.ReadInt32(); } var entries = new ResTable_entry[table.EntryCount]; for (var i = 0; i < table.EntryCount; i++) { if (entryIndices[i] == -1) { continue; } var resourceId = (packageHeader.Id << 24) | (table.RawID << 16) | i; var entry = reader.ReadResTable_entry(); entries[i] = entry; if ((entry.Flags & EntryFlags.FLAG_COMPLEX) == 0) { var value = reader.ReadRes_value(); arscTable.Values[Convert.ToUInt32(resourceId)] = value; } else { var parent = reader.ReadInt32(); var count = reader.ReadInt32(); for (var x = 0; x < count; x++) { var refName = reader.ReadUInt32(); var value = reader.ReadRes_value(); } } } break; default: throw new ApkReaderException($"Unknow Data Type : {chunkHeader.Type}"); } } while (stream.Position < endPosition); } } return(file); }