├── .gitignore ├── COPYING ├── Makefile ├── README ├── aaptdump ├── bootimg.h ├── de9patch.c ├── new_strings.cpp ├── split_bootimg.c ├── xmlindent.c └── zzos /.gitignore: -------------------------------------------------------------------------------- 1 | split_bootimg 2 | new_strings 3 | .*.swp 4 | tags 5 | de9patch 6 | xmlindent 7 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: split_bootimg new_strings de9patch xmlindent 3 | 4 | split_bootimg: bootimg.h 5 | 6 | de9patch: de9patch.c 7 | gcc -lpng de9patch.c -o de9patch 8 | 9 | new_strings: new_strings.cpp 10 | g++ new_strings.cpp -o new_strings -lexpat 11 | 12 | xmlindent: xmlindent.c 13 | gcc xmlindent.c -o xmlindent -lexpat 14 | 15 | clean: 16 | $(RM) new_strings split_bootimg *.o core de9patch xmlindent 17 | 18 | .PHONY: clean all 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This project contains miscellanious tools for manipulating an Android platform, 2 | developed as part of the services rendered to our clients by Lingnu Open Source 3 | Consulting Ltd. 4 | 5 | All code is Copyrighted (C) 2010,2011 Lingnu Open Source Consulting Ltd. 6 | 7 | These tools were developed as part of our work for our clients, and are released 8 | to the general public under the GNU General Public License (GPL) either version 9 | 2 of the License, or (at your option) any later version. 10 | 11 | Lingnu's web page is at http://www.lingnu.com 12 | 13 | Available tools: 14 | split_bootimg - a generic tool for splitting a boot image created using 15 | mkbootimg. The tool auto-detects the actual (as opposed to reported) page 16 | size, and dumps all relevant info about the boot image. The tool makes no 17 | assumptions about the parameters used in order to create the boot image. 18 | 19 | new_strings - has 2 functions: 20 | + given 3 files : old_english_strings.xml newer_english_strings.xml localized_strings.xml 21 | will output new localized strings.xml based on old, new, and localized versions of 22 | of strings.xml (reflects changes between the old and new version : dropped, new 23 | and modified string entries) 24 | 25 | + given 2 files : old_localized_strings.xml newer_localized_strings.xml 26 | will output merged version of the 2 files, prefering newer string entries 27 | if exists. 28 | 29 | zzos - the reverse of aapt. Decompile the resources of an apk to a format 30 | suitable for recompilation using aapt. "zzos" is "aapt" shifted one 31 | letter down. 32 | 33 | de9patch - deserializes metadata of 9-patch compiled png file, writes out original 9-patch png 34 | 35 | xmlindent - buetify xml file 36 | 37 | -------------------------------------------------------------------------------- /aaptdump: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | use File::Path qw(make_path); 4 | use File::Basename; 5 | 6 | # Create under current directory 7 | while( $apkpath=shift ) { 8 | my ($apkname, $apkdir ) = fileparse( $apkpath, ( ".apk" ) ); 9 | print "Dumping $apkname\n"; 10 | 11 | if( !mkdir "$apkname" ) { 12 | print STDERR "Failed to create directory $apkname: $!\n"; 13 | return 1; 14 | } 15 | 16 | open( RESOURCES, "-|", "aapt", ("d", "--values", "resources", $apkpath) ) 17 | or die "Failed to read resources"; 18 | open( OUTPUT, ">", "$apkname/resources.txt" ) 19 | or die "Faile to create $apkname/resources.txt: $!"; 20 | 21 | while( ) { 22 | chomp; 23 | if( m/^\s*resource 0x.* \(PUBLIC\)$/ ) { 24 | s/ \(PUBLIC\)$//; 25 | } 26 | 27 | if( s/ flags=0x4([0-9a-f]{7})$/ flags=0x0\1/ ) { 28 | } elsif( m/^\s*resource 0x.*: t=0x03 d=0x[0-9a-f]{8} \(s=0x[0-9a-f]{4} r=0x[0-9a-f]{2}\)$/ ) 29 | { 30 | s/: t=0x03 d=0x[0-9a-f]{8} /: t=0x03 d=0x00000000 /; 31 | } 32 | 33 | print OUTPUT "$_\n"; 34 | } 35 | 36 | close OUTPUT; 37 | close RESOURCES; 38 | 39 | open( APKLIST, "-|", "aapt", ("l", $apkpath)) 40 | or die "Failed to open APK list"; 41 | 42 | while( ) { 43 | chomp; 44 | my $file=$_; 45 | 46 | if( m'^res/raw.*\.xml$' ) { 47 | } elsif( m'^res/.*\.xml$' ) { 48 | xml_dump( $apkpath, $_, $apkname ); 49 | } elsif( m'^res/(xml|anim|layout|menu)' ) { 50 | # ALL files under those directories are xml 51 | xml_dump( $apkpath, $_, $apkname ); 52 | } 53 | } 54 | } 55 | 56 | sub xml_dump 57 | { 58 | my( $apkpath, $filename, $outdir ) = @_; 59 | my $error; 60 | 61 | make_path( dirname("$outdir/$filename"), { error => \$error } ); 62 | 63 | open XMLFILE, "-|", "aapt", ("d", "xmltree", $apkpath, $filename) 64 | or die "Failed to dump XML tree $filename"; 65 | open OUTFILE, ">", "$outdir/$filename" 66 | or die "Faile to create output file $outdir/$filename: $!"; 67 | 68 | while( ) { 69 | chomp; 70 | 71 | s/^(\s*E: .*) \(line=([0-9]+)\)$/\1 (line=0)/; 72 | 73 | print OUTFILE "$_\n"; 74 | } 75 | 76 | close XMLFILE; 77 | close OUTFILE; 78 | } 79 | -------------------------------------------------------------------------------- /bootimg.h: -------------------------------------------------------------------------------- 1 | /* tools/mkbootimg/bootimg.h 2 | ** 3 | ** Copyright 2007, The Android Open Source Project 4 | ** 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); 6 | ** you may not use this file except in compliance with the License. 7 | ** You may obtain a copy of the License at 8 | ** 9 | ** http://www.apache.org/licenses/LICENSE-2.0 10 | ** 11 | ** Unless required by applicable law or agreed to in writing, software 12 | ** distributed under the License is distributed on an "AS IS" BASIS, 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ** See the License for the specific language governing permissions and 15 | ** limitations under the License. 16 | */ 17 | 18 | #ifndef _BOOT_IMAGE_H_ 19 | #define _BOOT_IMAGE_H_ 20 | 21 | typedef struct boot_img_hdr boot_img_hdr; 22 | 23 | #define BOOT_MAGIC "ANDROID!" 24 | #define BOOT_MAGIC_SIZE 8 25 | #define BOOT_NAME_SIZE 16 26 | #define BOOT_ARGS_SIZE 512 27 | 28 | struct boot_img_hdr 29 | { 30 | unsigned char magic[BOOT_MAGIC_SIZE]; 31 | 32 | unsigned kernel_size; /* size in bytes */ 33 | unsigned kernel_addr; /* physical load addr */ 34 | 35 | unsigned ramdisk_size; /* size in bytes */ 36 | unsigned ramdisk_addr; /* physical load addr */ 37 | 38 | unsigned second_size; /* size in bytes */ 39 | unsigned second_addr; /* physical load addr */ 40 | 41 | unsigned tags_addr; /* physical addr for kernel tags */ 42 | unsigned page_size; /* flash page size we assume */ 43 | unsigned unused[2]; /* future expansion: should be 0 */ 44 | 45 | unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ 46 | 47 | unsigned char cmdline[BOOT_ARGS_SIZE]; 48 | 49 | unsigned id[8]; /* timestamp / checksum / sha1 / etc */ 50 | }; 51 | 52 | /* 53 | ** +-----------------+ 54 | ** | boot header | 1 page 55 | ** +-----------------+ 56 | ** | kernel | n pages 57 | ** +-----------------+ 58 | ** | ramdisk | m pages 59 | ** +-----------------+ 60 | ** | second stage | o pages 61 | ** +-----------------+ 62 | ** 63 | ** n = (kernel_size + page_size - 1) / page_size 64 | ** m = (ramdisk_size + page_size - 1) / page_size 65 | ** o = (second_size + page_size - 1) / page_size 66 | ** 67 | ** 0. all entities are page_size aligned in flash 68 | ** 1. kernel and ramdisk are required (size != 0) 69 | ** 2. second is optional (second_size == 0 -> no second) 70 | ** 3. load each element (kernel, ramdisk, second) at 71 | ** the specified physical address (kernel_addr, etc) 72 | ** 4. prepare tags at tag_addr. kernel_args[] is 73 | ** appended to the kernel commandline in the tags. 74 | ** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr 75 | ** 6. if second_size != 0: jump to second_addr 76 | ** else: jump to kernel_addr 77 | */ 78 | 79 | #if 0 80 | typedef struct ptentry ptentry; 81 | 82 | struct ptentry { 83 | char name[16]; /* asciiz partition name */ 84 | unsigned start; /* starting block number */ 85 | unsigned length; /* length in blocks */ 86 | unsigned flags; /* set to zero */ 87 | }; 88 | 89 | /* MSM Partition Table ATAG 90 | ** 91 | ** length: 2 + 7 * n 92 | ** atag: 0x4d534d70 93 | ** x n 94 | */ 95 | #endif 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /de9patch.c: -------------------------------------------------------------------------------- 1 | // de9patch - decompile 9-patch png 2 | // Copyright (C) 2011 Lingnu Open Source Consulting Ltd. 3 | // Written by Asaf Ohaion 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License along 16 | // with this program; if not, write to the Free Software Foundation, Inc., 17 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | // 19 | 20 | #include 21 | #include 22 | 23 | #define PNG_DEBUG 3 24 | #include 25 | 26 | #define COLOR_TYPE_GRAYSCALE 0 27 | #define COLOR_TYPE_RGB 2 28 | #define COLOR_TYPE_PALLETE 3 29 | #define COLOR_TYPE_GRAYSCALE_ALPHA 4 30 | #define COLOR_TYPE_RGB_ALPHA 6 31 | 32 | 33 | typedef struct npTc_block_t { 34 | char wasDeserialized; 35 | char numXDivs; 36 | char numYDivs; 37 | char colors; 38 | int XDivs[255]; 39 | int YDivs[255]; 40 | int padding_left; 41 | int padding_right; 42 | int padding_top; 43 | int padding_bottom; 44 | } npTc_block; 45 | 46 | int width, height, found_nptc_chunk = 0; 47 | png_bytep * row_pointers; 48 | png_byte color_type; 49 | png_byte bit_depth; 50 | int bytes_per_pixel; 51 | 52 | 53 | void usage(char * progname) 54 | { 55 | printf("usage : %s in_filename out_filename\n", progname); 56 | exit(0); 57 | } 58 | 59 | void fail(char * message) 60 | { 61 | fprintf(stderr, "%s\n", message); 62 | exit(10); 63 | } 64 | 65 | int read_chunk_custom(png_structp ptr, png_unknown_chunkp chunk) 66 | { 67 | // printf("read_chunk_custom %s (size %d)\n", chunk->name, chunk->size); 68 | 69 | if (strcmp(chunk->name, "npTc")) 70 | return 0; 71 | 72 | found_nptc_chunk = 1; 73 | 74 | npTc_block * np_block = (npTc_block *)png_get_user_chunk_ptr(ptr); 75 | np_block->numXDivs = chunk->data[1]; 76 | np_block->numYDivs = chunk->data[2]; 77 | 78 | // paddings 79 | memcpy(&np_block->padding_left, &chunk->data[12], 4); 80 | memcpy(&np_block->padding_right, &chunk->data[16], 4); 81 | memcpy(&np_block->padding_bottom, &chunk->data[20], 4); 82 | memcpy(&np_block->padding_top, &chunk->data[24], 4); 83 | np_block->padding_left = ntohl(np_block->padding_left); 84 | np_block->padding_right = ntohl(np_block->padding_right); 85 | np_block->padding_top = ntohl(np_block->padding_top); 86 | np_block->padding_bottom = ntohl(np_block->padding_bottom); 87 | //printf("padding left(%d), right(%d), bottom(%d), top(%d)\n", 88 | // np_block->padding_left, np_block->padding_right, 89 | // np_block->padding_bottom, np_block->padding_top); 90 | int i; 91 | 92 | // XDivs 93 | for (i=0; i < np_block->numXDivs; i++) { 94 | memcpy(&np_block->XDivs[i], (chunk->data+32 + i*4), 4); 95 | np_block->XDivs[i] = ntohl(np_block->XDivs[i]); 96 | } 97 | 98 | // YDivs 99 | for (i=0; i < np_block->numYDivs; i++) { 100 | memcpy(&np_block->YDivs[i], (chunk->data+32 + (np_block->numXDivs + i)*4), 4); 101 | np_block->YDivs[i] = ntohl(np_block->YDivs[i]); 102 | } 103 | 104 | return 10; 105 | } 106 | 107 | void read_png_file(char * filename, npTc_block * np_block) 108 | { 109 | char header[8]; 110 | int x, y; 111 | 112 | png_structp png_ptr; 113 | png_infop info_ptr; 114 | int number_of_passes; 115 | 116 | /* open file and test for it being a png */ 117 | FILE *fp = fopen(filename, "rb"); 118 | if (!fp) 119 | fail("[read_png_file] File could not be opened for reading"); 120 | fread(header, 1, 8, fp); 121 | if (png_sig_cmp(header, 0, 8)) 122 | fail("[read_png_file] not recognized as a PNG file"); 123 | 124 | /* initialize stuff */ 125 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 126 | 127 | if (!png_ptr) 128 | fail("[read_png_file] png_create_read_struct failed"); 129 | 130 | info_ptr = png_create_info_struct(png_ptr); 131 | if (!info_ptr) 132 | fail("[read_png_file] png_create_info_struct failed"); 133 | 134 | if (setjmp(png_jmpbuf(png_ptr))) 135 | fail("[read_png_file] Error during init_io"); 136 | 137 | png_init_io(png_ptr, fp); 138 | png_set_sig_bytes(png_ptr, 8); 139 | 140 | png_set_read_user_chunk_fn(png_ptr, np_block, (png_user_chunk_ptr)read_chunk_custom); 141 | 142 | png_read_info(png_ptr, info_ptr); 143 | 144 | width = png_get_image_width(png_ptr, info_ptr); 145 | height = png_get_image_height(png_ptr, info_ptr); 146 | color_type = png_get_color_type(png_ptr, info_ptr); 147 | bit_depth = png_get_bit_depth(png_ptr, info_ptr); 148 | 149 | switch (color_type) 150 | { 151 | case COLOR_TYPE_GRAYSCALE: 152 | if (bit_depth < 8) 153 | bytes_per_pixel = 1; 154 | else 155 | bytes_per_pixel = bit_depth/8; 156 | break; 157 | case COLOR_TYPE_RGB: 158 | bytes_per_pixel = (bit_depth/8)*3; 159 | break; 160 | case COLOR_TYPE_PALLETE: 161 | fail("png pallete color type support not implemented yet"); 162 | break; 163 | case COLOR_TYPE_GRAYSCALE_ALPHA: 164 | bytes_per_pixel = (bit_depth/8)*2; 165 | break; 166 | case COLOR_TYPE_RGB_ALPHA: 167 | bytes_per_pixel = (bit_depth/8)*4; 168 | break; 169 | } 170 | 171 | // printf("bytes per pixel : %d \n", bytes_per_pixel); 172 | 173 | number_of_passes = png_set_interlace_handling(png_ptr); 174 | png_read_update_info(png_ptr, info_ptr); 175 | 176 | 177 | /* read file */ 178 | if (setjmp(png_jmpbuf(png_ptr))) 179 | fail("[read_png_file] Error during read_image"); 180 | 181 | row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height); 182 | for (y=0; y0; x -= bytes_per_pixel) 273 | swap_columns(x, x-bytes_per_pixel); 274 | 275 | // extend height 276 | height +=2; 277 | row_pointers = (png_bytep*) realloc(row_pointers, sizeof(png_bytep) * height); 278 | row_pointers[height-2] = malloc(sizeof(png_byte)*width*bytes_per_pixel); 279 | row_pointers[height-1] = malloc(sizeof(png_byte)*width*bytes_per_pixel); 280 | memset(&row_pointers[height-2][0], 0, width*bytes_per_pixel); 281 | memset(&row_pointers[height-1][0], 0, width*bytes_per_pixel); 282 | 283 | // center horizontally 284 | for (y=height-2; y>0; y--) { 285 | png_bytep tmp = row_pointers[y]; 286 | row_pointers[y] = row_pointers[y-1]; 287 | row_pointers[y-1] = tmp; 288 | } 289 | } 290 | 291 | void add_patches(npTc_block * np_block) 292 | { 293 | int i,j; 294 | 295 | // XDivs 296 | for (i=0; i < np_block->numXDivs; i+=2) 297 | for (j=np_block->XDivs[i]; j < np_block->XDivs[i+1]; j++) { 298 | png_bytep pix = &(row_pointers[0][(j+1)*bytes_per_pixel]); 299 | pix[bytes_per_pixel-1] = 255; 300 | } 301 | 302 | // YDivs 303 | for (i=0; i < np_block->numYDivs; i+=2) 304 | for (j=np_block->YDivs[i]; j < np_block->YDivs[i+1]; j++) { 305 | png_bytep pix = &(row_pointers[(j+1)][0]); 306 | pix[bytes_per_pixel-1] = 255; 307 | } 308 | } 309 | 310 | void add_paddings(npTc_block * np_block) 311 | { 312 | int i; 313 | 314 | // padding left / right 315 | for (i = (np_block->padding_left+1)*bytes_per_pixel; i < (width - np_block->padding_right-1)*bytes_per_pixel; i+=bytes_per_pixel) { 316 | png_bytep pix = &(row_pointers[height-1][i]); 317 | pix[bytes_per_pixel-1] = 255; 318 | } 319 | 320 | // padding bottom / top 321 | for (i = np_block->padding_bottom+1; i < (height - np_block->padding_top-1); i++) { 322 | png_bytep pix = &(row_pointers[i][(width-1)*bytes_per_pixel]); 323 | pix[bytes_per_pixel-1] = 255; 324 | } 325 | 326 | } 327 | 328 | int main(int argc, char * argv[]) 329 | { 330 | if (argc < 3) 331 | usage(argv[0]); 332 | 333 | npTc_block np_block; 334 | 335 | read_png_file(argv[1], &np_block); 336 | 337 | if (!found_nptc_chunk) 338 | fail("PNG does not contain npTc chunk"); 339 | 340 | add_borders(); 341 | add_patches(&np_block); 342 | add_paddings(&np_block); 343 | 344 | write_png_file(argv[2]); 345 | 346 | /* printf("padding left %d\n", np_block.padding_left); 347 | printf("padding right %d\n", np_block.padding_right); 348 | printf("padding top %d\n", np_block.padding_bottom); 349 | printf("padding bottom %d\n", np_block.padding_top);*/ 350 | 351 | return 0; 352 | } 353 | -------------------------------------------------------------------------------- /new_strings.cpp: -------------------------------------------------------------------------------- 1 | // newstrings.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "expat.h" 9 | 10 | #define BUFSIZE 4096 11 | 12 | enum diff_operation {NOP, ADDED, DELETED, MODIFIED}; 13 | 14 | using namespace std; 15 | 16 | typedef struct { 17 | XML_Parser *parser; 18 | map strings; 19 | string current_name; 20 | bool formatted; 21 | } parser_context; 22 | 23 | std::string quote( const std::string & raw ) 24 | { 25 | std::string ret; 26 | 27 | for( unsigned int i=0; i': 33 | ret+=">"; 34 | break; 35 | case '&': 36 | ret+="&"; 37 | break; 38 | default: 39 | ret+=raw[i]; 40 | break; 41 | } 42 | } 43 | 44 | return ret; 45 | } 46 | 47 | void tag_content(void *userData, const XML_Char *s, int len) 48 | { 49 | char str[len+1]; 50 | strncpy(str, s, len); 51 | str[len] = 0; 52 | parser_context* context = (parser_context*)userData; 53 | 54 | if (context->current_name.size() > 0) 55 | context->strings[context->current_name] += str; 56 | } 57 | 58 | void tag_start(void *userData, const XML_Char *name, const XML_Char **atts) 59 | { 60 | parser_context* context = (parser_context*)userData; 61 | 62 | for (int idx=0; atts[idx] != NULL; idx+=2) 63 | if (strcmp(atts[idx], "name") == 0) 64 | context->current_name = atts[idx+1]; 65 | else if ((strcmp(atts[idx], "translatable") == 0) && 66 | (strcmp(atts[idx+1], "false") == 0)) 67 | return; // ignore not translatable strings 68 | else if (strcmp(atts[idx], "formatted") == 0 ) 69 | context->formatted = strcmp(atts[idx+1], "false")!=0; 70 | else 71 | { 72 | cerr<<"unknown attribute '"<"<parser, tag_content); 77 | } 78 | 79 | void tag_end(void *userData, const XML_Char *name) 80 | { 81 | parser_context* context = (parser_context*)userData; 82 | 83 | XML_SetCharacterDataHandler(*context->parser, NULL); 84 | } 85 | 86 | 87 | void parse_strings_xml(char* filename, map& string_map) 88 | { 89 | ifstream file(filename, ios::in); 90 | 91 | if ( file.fail() ) { 92 | cerr<<"Cannot open "<& base, map& other, map& results) 129 | { 130 | map::iterator p; 131 | 132 | // search new strings: 133 | for(p = other.begin(); p != other.end(); p++) 134 | if ( base.find(p->first) == base.end() ) 135 | results[p->first] = ADDED; 136 | 137 | // search modified / deleted strings: 138 | for(p = base.begin(); p != base.end(); p++) { 139 | if ( other.find(p->first) == other.end() ) 140 | results[p->first] = DELETED; 141 | else if ( p->second != other[p->first] ) 142 | results[p->first] = MODIFIED; 143 | } 144 | 145 | } 146 | 147 | int merge2(char * old_strings_iw_file, char * new_strings_iw_file) 148 | { 149 | // parse 150 | map old_strings_iw; 151 | parse_strings_xml(old_strings_iw_file, old_strings_iw); 152 | 153 | map new_strings_iw; 154 | parse_strings_xml(new_strings_iw_file, new_strings_iw); 155 | 156 | // output 157 | cout<<""<"<::iterator p; 161 | for(p = old_strings_iw.begin(); p != old_strings_iw.end(); p++) { 162 | 163 | if ( new_strings_iw.find(p->first) != new_strings_iw.end() ) { 164 | cout<<"first<<"\" formatted=\"false\">"<< 165 | quote(new_strings_iw[p->first])<<""<first); 167 | } else 168 | cout<<"first<<"\" formatted=\"false\">"<< 169 | quote(p->second)<<""<first<<"\" formatted=\"false\">"<< 174 | quote(p->second)<<""<"< old_strings_en; 185 | parse_strings_xml(old_strings_en_file, old_strings_en); 186 | 187 | map new_strings_en; 188 | parse_strings_xml(new_strings_en_file, new_strings_en); 189 | 190 | map old_strings_iw; 191 | parse_strings_xml(old_strings_iw_file, old_strings_iw); 192 | 193 | // diff 194 | map diff_old_new; 195 | diff(old_strings_en, new_strings_en, diff_old_new); 196 | 197 | // output 198 | cout<<""<"<::iterator p; 202 | for(p = old_strings_iw.begin(); p != old_strings_iw.end(); p++) { 203 | diff_operation diff_op = NOP; 204 | if ( diff_old_new.find(p->first) != diff_old_new.end() ) 205 | diff_op = diff_old_new[p->first]; 206 | 207 | if (diff_op == DELETED) 208 | continue; 209 | 210 | cout << "first<<"\""; 211 | 212 | if (diff_op == MODIFIED) 213 | cout<<" MODIFIED"; 214 | 215 | cout<<">"; 216 | 217 | cout<second<<""<::iterator p_diff; 221 | for(p_diff = diff_old_new.begin(); p_diff != diff_old_new.end(); p_diff++) 222 | if (p_diff->second == ADDED) 223 | cout << "first<<"\" NEW"<<">"<<""<"< 4)) 240 | usage(argv[0]); 241 | 242 | if (argc == 3) 243 | return merge2(argv[1], argv[2]); 244 | 245 | return merge3(argv[1], argv[2], argv[3]); 246 | } 247 | -------------------------------------------------------------------------------- /split_bootimg.c: -------------------------------------------------------------------------------- 1 | /* 2 | split_bootimg.c - splits an image created with Android's mkbootimg 3 | Written by Shachar Shemesh 4 | Copyright (C) 2010 Lingnu Open Source Consulting Ltd. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, Inc., 18 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include "bootimg.h" 25 | 26 | #define MIN_PAGESIZE 2048 27 | 28 | #define min(a,b) ((a)>(b)?(b):(a)) 29 | 30 | int main( int argc, char *argv[] ) 31 | { 32 | if( argc<2 ) { 33 | fprintf( stderr, "Usage: split_bootimg boot.img\n" ); 34 | 35 | return 1; 36 | } 37 | 38 | FILE *boot_img=fopen( argv[1], "rb" ); 39 | if( boot_img==NULL ) { 40 | perror("Failed to open boot.img file"); 41 | 42 | return 2; 43 | } 44 | 45 | boot_img_hdr header; 46 | if( fread( &header, sizeof(header), 1, boot_img )!=1 ) { 47 | fprintf(stderr, "Short read on file while reading header %u\n", sizeof(header)); 48 | 49 | return 2; 50 | } 51 | 52 | // Is this the right type of file? 53 | if( strncmp( BOOT_MAGIC, header.magic, BOOT_MAGIC_SIZE )!=0 ) { 54 | fprintf( stderr, "Boot file header incorrect\n" ); 55 | 56 | return 2; 57 | } 58 | 59 | // Dump the header information 60 | printf("Kernel size: %u\nKernel load address: %p\nRam disk size: %u\nRam disk load address: %p\nSecond size: %u\nSecond address: %p\n", 61 | header.kernel_size, header.kernel_addr, header.ramdisk_size, header.ramdisk_addr, header.second_size, header.second_addr ); 62 | printf("Tags addr: %p\nPage size: %u\nUnused1: 0x%08x\nUnused2: 0x%08x\n\n", header.tags_addr, header.page_size, 63 | header.unused[0], header.unused[1] ); 64 | 65 | printf("Product name: \"%s\"\nCommand line: \"%s\"\n", header.name, header.cmdline ); 66 | printf("Id:"); 67 | unsigned int i; 68 | for( i=0; i<8; ++i ) 69 | printf(" 0x%08x", header.id[i]); 70 | printf("\n"); 71 | 72 | // Skip forward to the beginning of the first page 73 | fseek( boot_img, MIN_PAGESIZE, SEEK_SET ); 74 | 75 | // Sense out the actual page size 76 | unsigned int page_size=MIN_PAGESIZE; 77 | int ch; 78 | while( fgetc( boot_img )==0 ) 79 | page_size++; 80 | 81 | page_size-=page_size%1024; 82 | 83 | printf("Sensed page size: %u\n", page_size ); 84 | 85 | fseek( boot_img, page_size, SEEK_SET ); 86 | 87 | char filename[4096]; 88 | unsigned int filepart; 89 | for( filepart=strlen(argv[1])-1; filepart>0 && argv[1][filepart]!='/'; --filepart ) 90 | ; 91 | if( argv[1][filepart]=='/' ) 92 | ++filepart; 93 | 94 | // Write the kernel file 95 | snprintf( filename, sizeof(filename), "%s-kernel", argv[1]+filepart ); 96 | FILE *outfile=fopen(filename, "wb"); 97 | if( outfile==NULL ) { 98 | perror("Failed to open kernel output file"); 99 | 100 | return 2; 101 | } 102 | 103 | size_t written; 104 | for( i=0; i0 ) { 109 | written=fwrite( buffer, 1, written, outfile ); 110 | 111 | if( written==0 ) { 112 | printf("Written=%d\n", written ); 113 | perror("Writing to kernel file failed"); 114 | 115 | return 2; 116 | } 117 | } else if( written<0 ) { 118 | perror("Failed to read kernel from boot file"); 119 | 120 | return 2; 121 | } 122 | } 123 | fclose(outfile); 124 | 125 | // Write the ramdisk file 126 | size_t lastpos=ftell( boot_img ); 127 | if( (lastpos % page_size)!=0 ) { 128 | lastpos += page_size - (lastpos%page_size); 129 | 130 | fseek( boot_img, lastpos, SEEK_SET ); 131 | } 132 | 133 | snprintf( filename, sizeof(filename), "%s-ramdisk.gz", argv[1]+filepart ); 134 | outfile=fopen(filename, "wb"); 135 | if( outfile==NULL ) { 136 | perror("Failed to open ramdisk output file"); 137 | 138 | return 2; 139 | } 140 | 141 | for( i=0; i0 ) { 146 | written=fwrite( buffer, 1, written, outfile ); 147 | 148 | if( written==0 ) { 149 | perror("Writing to ramdisk file failed"); 150 | 151 | return 2; 152 | } 153 | } else if( written==0 ) { 154 | fprintf(stderr, "Short read of ramdisk image from boot file\n"); 155 | 156 | return 2; 157 | } else { 158 | perror("Error reading ram disk from boot file"); 159 | 160 | return 2; 161 | } 162 | } 163 | fclose(outfile); 164 | 165 | fclose( boot_img ); 166 | } 167 | -------------------------------------------------------------------------------- /xmlindent.c: -------------------------------------------------------------------------------- 1 | // xmlindent - organize xml indentation level and attributes 2 | // Copyright (C) 2011 Lingnu Open Source Consulting Ltd. 3 | // Written by Asaf Ohaion 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License along 16 | // with this program; if not, write to the Free Software Foundation, Inc., 17 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | // 19 | 20 | #include 21 | #include "expat.h" 22 | 23 | int depth = 0; 24 | 25 | void usage(char * progname) 26 | { 27 | printf("usage : %s xml_file\n", progname); 28 | exit(0); 29 | } 30 | 31 | void fail(char * message) 32 | { 33 | fprintf(stderr, "%s\n", message); 34 | exit(10); 35 | } 36 | 37 | void indent() 38 | { 39 | int i; 40 | for (i=0; i 0) { 53 | indent(); 54 | printf("\t"); 55 | } 56 | printf(" %s=\"%s\"", atts[i], atts[i + 1]); 57 | if (atts[i+2]) 58 | printf("\n"); 59 | } 60 | printf(">\n"); 61 | } 62 | 63 | void tag_end(void *userData, const XML_Char *name) 64 | { 65 | indent(); 66 | printf("\n", name); 67 | depth--; 68 | } 69 | 70 | int main(int argc, char * argv[]) 71 | { 72 | if (argc < 2) 73 | usage(argv[0]); 74 | 75 | char *buffer; 76 | int fsize; 77 | 78 | XML_Parser parser = XML_ParserCreate(NULL); 79 | if (! parser) 80 | fail("Couldn't allocate memory for parser"); 81 | 82 | FILE * fp = fopen(argv[1], "r"); 83 | 84 | fseek(fp, 0, SEEK_END); 85 | fsize = ftell(fp); 86 | rewind(fp); 87 | buffer = (char *)malloc(fsize+1); 88 | 89 | fread(buffer, 1, fsize, fp); 90 | 91 | buffer[fsize] = '\0'; 92 | 93 | XML_SetElementHandler(parser, tag_start, tag_end); 94 | 95 | XML_Parse(parser, buffer, fsize, 0); 96 | 97 | return 0; 98 | } 99 | -------------------------------------------------------------------------------- /zzos: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # zzos - decompile Android resources compiled with aapt 4 | # Copyright (C) 2011 Lingnu Open Source Consulting Ltd. 5 | # Written by Shachar Shemesh 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License along 18 | # with this program; if not, write to the Free Software Foundation, Inc., 19 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | # 21 | 22 | use Getopt::Long; 23 | use File::Basename; 24 | use File::Path qw(make_path); 25 | use POSIX; 26 | 27 | use feature "switch"; 28 | 29 | # Hashes for tracking the cross references 30 | 31 | # The main hash. Key is integer resource number. Value is "package:class/name" string 32 | my %references, %framework_references; 33 | # Flags of resources. Key is same as above. Value is integer 34 | my %references_flags, %framework_references_flags; 35 | # Bags of attribs. Key is string "package:name". Value is reference to hash: 36 | # Key is name of resource (as in %attrib_references). Value is integer, except for "flags", where 37 | # value is a hash reference: 38 | # Key is integer bit field or value. Value is the name of field. 39 | my %attrib_bags, %framework_attrib_bags; 40 | # Reverse hash of %references. Key is %references value, value is %references key. 41 | my %rev_references, %framework_rev_references; 42 | my $package_name, $apk_package_name, $package_ext; 43 | 44 | my %class_map=( 45 | raw=>undef, 46 | layout=>undef, 47 | xml=>undef, 48 | anim=>undef, 49 | menu=>undef, 50 | animator=>undef, 51 | accelerate_quad=>undef, 52 | interpolator=>undef, 53 | mipmap=>undef, 54 | id=>\&parse_id_value, 55 | string=>\&parse_string_value, 56 | dimen=>\&parse_dimen_value, 57 | color=>\&parse_color_value, 58 | drawable=>\&parse_color_value, 59 | bool=>\&parse_bool_value, 60 | integer=>\&parse_int_value, 61 | fraction=>\&parse_fraction_value, 62 | ); 63 | 64 | $REF_INVALID= 0x08000000; 65 | 66 | $REF_PUBLIC= 0x40000000; 67 | $REF_HARDCODE= 0x80000000; 68 | $REF_INTERNAL= 0x00000001; 69 | $REF_SPEC= 0x00000002; 70 | $REF_USED= 0x00000004; 71 | $REF_RENAMED= 0x00000010; 72 | $REF_DUPLICATE= 0x00000020; 73 | 74 | # Attribute type bits 75 | %attrib_types=( 76 | 0x00000001 => [ "reference", 0x01 ], 77 | 0x00000002 => [ "string", 0x03 ], 78 | 0x00000004 => [ "integer", 0x10 ], 79 | 0x00000008 => [ "boolean", 0x12 ], 80 | 0x00000010 => [ "color", 0x1c ], 81 | 0x00000020 => [ "float", 0x04 ], 82 | 0x00000040 => [ "dimension", 0x05 ], 83 | 0x00000080 => [ "fraction", 0x06 ], 84 | 0x00010000 => [ "enum", undef ], 85 | 0x00020000 => [ "flags", undef ], 86 | ); 87 | 88 | %attrib_references=( 89 | 0x01000000 => "format", 90 | 0x01000001 => "min", 91 | 0x01000002 => "max", 92 | 0x01000003 => "localization", # 0 - not required, 1 - suggested 93 | ); 94 | 95 | %plural_references=( 96 | 0x01000004 => "other", 97 | 0x01000005 => "zero", 98 | 0x01000006 => "one", 99 | 0x01000007 => "two", 100 | 0x01000008 => "few", 101 | 0x01000009 => "many", 102 | ); 103 | 104 | # Hard code null 105 | $framework_references{0}="null"; 106 | $framework_references_flags{0}=$REF_PUBLIC|$REF_HARDCODE; 107 | 108 | # Bit counting cache for num_set_bits in 4 bit values. 109 | my @bitcount=( 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 ); 110 | 111 | # Resource type open file descriptors 112 | my %type_files; 113 | 114 | sub help 115 | { 116 | print STDERR < \@framework_names, "output=s" => \$outdir, "nosubdir" => \$nosubdir ) 132 | or help(); 133 | 134 | for( @framework_names ) { 135 | read_resources( $_, $outdir, 1 ) or die "Failed to read framework resources for $_"; 136 | %framework_references = %references; 137 | %framework_references_flags = %references_flags; 138 | %framework_attrib_bags = %attrib_bags; 139 | %framework_rev_references = %rev_references; 140 | } 141 | 142 | if( $#ARGV==-1 ) { 143 | help(); 144 | 145 | exit 0; 146 | } 147 | 148 | my ($apkpath, $delayed_fail); 149 | my $delayed_fail=0; 150 | while( $apkpath = shift @ARGV ) { 151 | # For each APK to be processed 152 | my ($apkname, $apkdir ) = fileparse( $apkpath, ( ".apk" ) ); 153 | 154 | my $real_outdir="$outdir"; 155 | $real_outdir.="/$apkname" if ! $nosubdir; 156 | 157 | if( process_apk( $apkpath, "$real_outdir" ) ) { 158 | $delayed_fail = 1; 159 | print STDERR "process_apk failed for $apkname\n"; 160 | } 161 | } 162 | 163 | exit $delayed_fail; 164 | 165 | sub process_apk 166 | { 167 | my ( $apkpath, $outdir ) = @_; 168 | 169 | if( !mkdir "$outdir" ) { 170 | print STDERR "Failed to create directory $outdir: $!\n"; 171 | return 1; 172 | } 173 | 174 | set_package_name( $apkpath ); 175 | # List of files to be unzipped as they are 176 | my @ziplist, @patch9list, %rename_list; 177 | print STDOUT "Processing $apkpath\n"; 178 | 179 | if( ! read_resources( $apkpath, $outdir, 0 ) ) { 180 | return 1; 181 | } 182 | 183 | # Get a list of files in the package 184 | if( ! open( APKLIST, "-|", "aapt", ("l", $apkpath)) ) { 185 | print STDERR "Failed to open $apkpath: $!\n"; 186 | 187 | return 1; 188 | } 189 | 190 | # Scan all the files in the APK 191 | while( ) { 192 | chomp; 193 | my $file=$_; 194 | 195 | if( m/^resources.arsc$/ ) { 196 | # Do nothing. We handle this file through aapt dump resources. 197 | } elsif( m'^res/raw.*\.xml$' ) { 198 | # .xml files under raw are not compiled. 199 | push @ziplist, $_; 200 | } elsif( m'^AndroidManifest.xml$' ) { 201 | # AndroidManifest.xml is, obviously, an XML file. 202 | if( !xml_decompile( $apkpath, $_, $outdir ) ) { 203 | return 1; 204 | } 205 | } elsif( m'^res/.*\.xml$' ) { 206 | # All other .xml files under res are compiled 207 | if( !xml_decompile( $apkpath, $_, $outdir ) ) { 208 | return 1; 209 | } 210 | } elsif( m'^res/(xml|anim|layout|menu)' ) { 211 | # ALL files under those directories are xmls, regardless of extension 212 | if( !xml_decompile( $apkpath, $_, $outdir ) ) { 213 | return 1; 214 | } 215 | } elsif( m'^res/drawable.*\.9\.png$' ) { 216 | # 9Patch png's need to be decompiled after being extracted 217 | push @ziplist, $_; 218 | push @patch9list, $_; 219 | } else { 220 | push @ziplist, $_; 221 | } 222 | 223 | # lang-country combination get their "r" removed by aapt 224 | if( $file=~'^res/([^/]+)-([a-z]{2})-([A-Z]{2})(-[^/]*)?/' ) { 225 | my $name="res/$1-$2-$3$4"; 226 | if( ! exists $rename_list{$name} ) { 227 | $rename_list{$name}="res/$1-$2-r$3$4"; 228 | } 229 | } 230 | } 231 | 232 | close APKLIST; 233 | 234 | # Extract all of the files that need simple extraction 235 | system "unzip", ( "-q", $apkpath, @ziplist, "-d", $outdir ); 236 | 237 | # De9patch all relevant files 238 | de9patch( $outdir, \@patch9list ); 239 | 240 | # Rename incorrectly named directories 241 | for( keys %rename_list ) { 242 | rename "$outdir/$_", "$outdir/$rename_list{$_}"; 243 | } 244 | 245 | return dump_values( $apkpath, $outdir ); 246 | } 247 | 248 | sub de9patch 249 | { 250 | my( $outdir, $filelist )=@_; 251 | 252 | for( @$filelist ) { 253 | print " Decompiling $_\n"; 254 | (system "de9patch", ( "$outdir/$_", "$outdir/$_.tmp" ))==0 255 | or die "Failed to decompile $_"; 256 | rename "$outdir/$_.tmp", "$outdir/$_" 257 | or die "Failed to rename: $!"; 258 | } 259 | } 260 | 261 | sub read_resources 262 | { 263 | my ( $apkpath, $outdir, $framework ) = @_; 264 | 265 | # Inherit all of the framework references 266 | %references = %framework_references; 267 | %references_flags = %framework_references_flags; 268 | %attrib_bags = %framework_attrib_bags; 269 | %rev_references = %framework_rev_references; 270 | 271 | open RESFILE, "-|", "aapt", ( "d", "--values", "resources", $apkpath ) or 272 | die "Failed to extract resources"; 273 | 274 | my $state=0; 275 | my ($counter, $bag_index); 276 | my $bag, $bag_flags; 277 | my $lastclass; 278 | while( ) { 279 | chomp; 280 | 281 | my ($index, $package, $str); 282 | my $flags=$framework ? 0 : $REF_INTERNAL; 283 | 284 | $index=undef; 285 | 286 | if( $state == 0 ) { 287 | # Base state - usual processing 288 | if( m'Package Group \d+ id=\d+ packageCount=\d+ name=(.+)$' ) { 289 | if( !$framework ) { 290 | # If this is not a framework APK read, set the package name 291 | $package_name=$1; 292 | } else { 293 | $package_name=undef; 294 | } 295 | } elsif( m'^ *(spec )?resource 0x([0-9a-f]{8}) ([^:/]+):([^:/]+)/([^:]+): flags=0x([0-9a-f]+)' ) 296 | { 297 | ($index, $package, $str, $lastclass)=( hex($2), $3, "$4/$5", $4 ); 298 | 299 | if( $1 ) { 300 | $flags|=$REF_SPEC; 301 | } 302 | 303 | if( (hex($6)&0x40000000)!=0 ) { 304 | $flags|=$REF_PUBLIC; 305 | } 306 | 307 | if( exists( $references{$index} ) ) { 308 | # A package sometimes defines attributes belonging to the android: package through plus 309 | if( $framework || ($references_flags{$index} & $REF_INTERNAL) != 0 ) { 310 | print STDERR "$package_name $package\n"; 311 | die "Reference 0x$2 refers once to $references{$index} and once to $package:$str"; 312 | } 313 | } 314 | 315 | my $process=1; 316 | if( exists $rev_references{"$package:$str"} ) { 317 | if( $rev_references{"$package:$str"} ne $index ) { 318 | printf STDERR "Symbol 0x%08x has the same name as 0x%08x: $package:$str\n", 319 | $index, $rev_references{"$package:$str"}; 320 | 321 | # Search for a free name to use 322 | my( $ext, $i )=("___COPY", 1); 323 | while( exists $rev_references{"$package:$str$ext$i"} ) { 324 | ++$i; 325 | } 326 | 327 | $str="$str$ext$i"; 328 | $flags|=$REF_RENAMED|$REF_DUPLICATE; 329 | } else { 330 | # Symbol defined twice with same everything - ignore second time 331 | $process=0; 332 | } 333 | } 334 | 335 | if( $process ) { 336 | $references{$index}="$package:$str"; 337 | $rev_references{"$package:$str"}=$index; 338 | 339 | $references_flags{$index}=$flags; 340 | } 341 | } elsif( m"(spec )?resource 0x([0-9a-f]{8}) ([-a-zA-Z\.]+):([^:/]+)/([^:]+): ( \(PUBLIC\))?" ) 342 | { 343 | ($index, $package, $type, $str)=( hex($2), $3, $4, $5 ); 344 | 345 | if( $1 ) { 346 | $flags|=$REF_SPEC; 347 | } 348 | 349 | if( $6 ) { 350 | $flags|=$REF_PUBLIC; 351 | } 352 | 353 | if( $type eq "attr" ) { 354 | # We don't care about style bags at this point 355 | $state=1; # Start of bag 356 | $bag_index = "$package:$str"; 357 | } 358 | } elsif( m'^ INVALID TYPE CONFIG FOR RESOURCE 0x([0-9a-f]+)$' ) { 359 | print STDERR "ERROR: Invalid type config for resource 0x$1: creating place holder\n"; 360 | # Just put something, and we'll most likely pad this later on 361 | $index=hex($1); 362 | $references{$index}=sprintf("%s:%s/INVALID%08x", $package_name, $lastclass, $index); 363 | $references_flags{$index}=$REF_INVALID; 364 | # Do not define the reverse mapping - we made up the name anyways 365 | } else { 366 | } 367 | } elsif( $state == 1 ) { 368 | # Start of bag 369 | if( m'Parent=0x([0-9a-f]{8}), Count=(\d+)' ) { 370 | die "Attrib style with parent 0x$1" if hex($1)!=0; 371 | $state = 2; # Cleanly inside the bag 372 | $counter = $2+0; 373 | $bag = { }; 374 | $bag_flags = {}; 375 | 376 | die "Zero counter on bag 0x$1" if $counter==0; 377 | } else { 378 | die "Line \"$_\" inside bag"; 379 | $state = 0; 380 | } 381 | } elsif( $state == 2 ) { 382 | # Inside bag 383 | if( m'#(\d+) \(Key=0x([0-9a-f]{8})\): \(color\) #([0-9a-f]{8})' ) { 384 | if( exists $attrib_references{hex($2)} ) { 385 | # Hardcoded reference 386 | $bag->{$attrib_references{hex($2)}}=hex($3); 387 | } else { 388 | $$bag_flags{hex($2)}=hex($3); 389 | } 390 | 391 | $counter--; 392 | } else { 393 | die "Inside bag, got unparsable \"$_\"" if( $counter>0 ); 394 | } 395 | 396 | if( $counter == 0 ) { 397 | $state = 0; 398 | if( scalar keys %$bag_flags ) { 399 | $bag->{flags}=$bag_flags; 400 | } 401 | $attrib_bags{$bag_index} = $bag; 402 | } 403 | } else { 404 | die "Unable to parse resource line \"$_\""; 405 | } 406 | } 407 | 408 | close RESFILE; 409 | 410 | return 1; 411 | } 412 | 413 | sub set_package_name 414 | { 415 | my ($apkpath)=@_; 416 | if( !open XMLFILE, "-|", "aapt", ("d", "xmltree", $apkpath, "AndroidManifest.xml") ) { 417 | print STDERR "Failed to dump XML tree $filename\n"; 418 | return 0; 419 | } 420 | 421 | while() { 422 | chomp; 423 | 424 | if( m'^ A: package="(.*)" \(Raw: ".*"\)$' ) { 425 | $flag = 0; 426 | 427 | $apk_package_name = $1; 428 | last; 429 | } 430 | } 431 | 432 | close XMLFILE; 433 | } 434 | 435 | sub xml_decompile 436 | { 437 | my( $apkpath, $filename, $outdir ) = @_; 438 | my $error; 439 | 440 | print STDOUT " Generating $filename\n"; 441 | 442 | make_path( dirname("$outdir/$filename"), { error => \$error } ); 443 | if( @$error ) { 444 | print STDERR "Error creating ".dirname("$outdir/$filename").": $!\n"; 445 | 446 | return 0; 447 | } 448 | 449 | if( !open XMLFILE, "-|", "aapt", ("d", "xmltree", $apkpath, $filename) ) { 450 | print STDERR "Failed to dump XML tree $filename\n"; 451 | return 0; 452 | } 453 | if( ! open OUTFILE, ">", "$outdir/$filename" ) { 454 | print STDERR "Failed to create XML file $outdir/$filename: $!\n"; 455 | 456 | close XMLFILE; 457 | return 0; 458 | } 459 | 460 | print OUTFILE ''."\n"; 461 | my @elements=(); 462 | my @schemas; 463 | my %namespaces, %rev_namespaces; 464 | my $current_level=-1; 465 | my $element_open=0; 466 | my $line_number=0; 467 | 468 | while() { 469 | chomp; 470 | $line_number++; 471 | 472 | given ($_) { 473 | when(m'^( *)N: ([^=]+)=(http://schemas.android.com/apk/res/)(.*)$') { 474 | $namespaces{$2}=$4; 475 | $rev_namespaces{$4}=$2; 476 | local $new_level=length $1; 477 | $new_level/=2; 478 | local $firstline=1; 479 | while( $current_level>=$new_level ) { 480 | # Close all pending open Elements 481 | if( $elements[$current_level] ) { 482 | print OUTFILE " "x($current_level*2) if !$firstline; 483 | 484 | if( $element_open ) { 485 | print OUTFILE " />\n"; 486 | $element_open=0; 487 | } else { 488 | print OUTFILE "\n"; 489 | } 490 | $firstline=0; 491 | } 492 | --$current_level; 493 | } 494 | 495 | if( $element_open ) { 496 | # Close pending elements 497 | print OUTFILE ">\n"; 498 | $element_open=0; 499 | } 500 | 501 | $new_level<=$current_level+1 or 502 | die("Jumped for level $current_level to $new_level in one go at $filename:$line_number"); 503 | $current_level=$new_level; 504 | $elements[$new_level]=""; 505 | push( @schemas, "xmlns:$2=\"$3$4\""); 506 | } 507 | when(m'^( *)N: ([^=]+)=(.*)$') { 508 | $namespaces{$2}=$3; 509 | $rev_namespaces{$3}=$2; 510 | local $new_level=length $1; 511 | $new_level/=2; 512 | local $firstline=1; 513 | while( $current_level>=$new_level ) { 514 | # Close all pending open Elements 515 | if( $elements[$current_level] ) { 516 | print OUTFILE " "x($current_level*2) if !$firstline; 517 | 518 | if( $element_open ) { 519 | print OUTFILE " />\n"; 520 | $element_open=0; 521 | } else { 522 | print OUTFILE "\n"; 523 | } 524 | $firstline=0; 525 | } 526 | --$current_level; 527 | } 528 | 529 | if( $element_open ) { 530 | # Close pending elements 531 | print OUTFILE ">\n"; 532 | $element_open=0; 533 | } 534 | 535 | $new_level<=$current_level+1 or 536 | die("Jumped for level $current_level to $new_level in one go at $filename:$line_number"); 537 | $current_level=$new_level; 538 | $elements[$new_level]=""; 539 | push( @schemas, "xmlns:$2=\"$3\""); 540 | } 541 | when(/^( *)E: ([^ ]+)/) { 542 | # Element 543 | local $new_level=length $1; 544 | $new_level/=2; 545 | local $firstline=1; 546 | while( $current_level>=$new_level ) { 547 | # Close all pending open Elements 548 | if( $elements[$current_level] ) { 549 | print OUTFILE " "x($current_level*2) if !$firstline; 550 | 551 | if( $element_open ) { 552 | print OUTFILE " />\n"; 553 | $element_open=0; 554 | } else { 555 | print OUTFILE "\n"; 556 | } 557 | $firstline=0; 558 | } 559 | --$current_level; 560 | } 561 | $nospace=0; 562 | 563 | if( $element_open ) { 564 | # Close pending elements 565 | print OUTFILE ">\n"; 566 | $element_open=0; 567 | } 568 | 569 | $new_level<=$current_level+1 or 570 | die("Jumped for level $current_level to $new_level in one go at $filename:$line_number"); 571 | 572 | $current_level=$new_level; 573 | $elements[$current_level]=$2; 574 | print OUTFILE "$1<$2"; 575 | for( @schemas ) { 576 | print OUTFILE " $_"; 577 | } 578 | @schemas=(); # Empty the array 579 | $element_open=1; 580 | } 581 | when(m'^( *)A: (([^:/]+):)?([^)]+)(\(0x[0-9a-f]+\))?=(.*)$') { 582 | # An attribute - we need to parse to find out which 583 | # Attribute with value 584 | $element_open or die "Attribute with no open element at $filename:$line_number"; 585 | 586 | my ($indent, $attr, $val)=($1, $4, $6); 587 | if( $2 ) { 588 | die "Attribute from undeclared namespace $3: \"$_\"" 589 | if ! exists $namespaces{$3}; 590 | $attr="$3:$4"; 591 | $val=parse_attribute($6, "$namespaces{$3}:$4", 1); 592 | } elsif( $val=~'^@0x([0-9a-f]+)( .*)$' ) { 593 | $val="@".format_reference(hex($1)); 594 | } elsif( $val=~'^\?0x([0-9a-f]+)( .*)$' ) { 595 | $val="?".format_xml_styleref(hex($1), \%rev_namespaces); 596 | } elsif( $val=~'\(Raw: "(.*)"\)$' ) { 597 | $val=escape_string($1, 1); 598 | } else { 599 | die "Unparsable attribute"; 600 | } 601 | 602 | print OUTFILE "\n$indent $attr=\"$val\""; 603 | } 604 | when(/^( *)C: "(.*)"$/) { 605 | # Tag content 606 | $element_open or die "Content with no open element at $filename:$line_number"; 607 | 608 | # Unescape the string, and escape it differently 609 | my $content=$2; 610 | $content=~s/\\n/\n/g; 611 | $content=~s/\\t/\t/g; 612 | $content=~s/\\\\/\\/g; 613 | $content=~s/&/&/g; 614 | 615 | print OUTFILE ">$content"; 616 | $element_open=0; 617 | $nospace=1; 618 | } 619 | when(/^Adding resources to ResTable:/) { 620 | # Administrative messages 621 | } 622 | default { 623 | die "Unknown line at $filename:$line_number: \"$_\""; 624 | } 625 | } 626 | } 627 | 628 | if( $element_open ) { 629 | # Close pending elements 630 | print OUTFILE ">\n"; 631 | $element_open=0; 632 | } 633 | while( $current_level>=0 ) { 634 | # Close all pending open Elements 635 | if( $elements[$current_level] ) { 636 | print OUTFILE " "x($current_level*2) if !$nospace; 637 | $nospace=0; 638 | print OUTFILE "\n"; 639 | } 640 | --$current_level; 641 | } 642 | 643 | close XMLFILE; 644 | close OUTFILE; 645 | 646 | return 1; 647 | } 648 | 649 | sub num_set_bits 650 | { 651 | my ($val)=@_; 652 | my $ret=0, $i=0; 653 | 654 | # Use a translation table for each nibble. There are 8 nibbles in 32 bit 655 | while( $i<8 && $val!=0 ) { 656 | $ret+=$bitcount[$val&0x0f]; 657 | $val>>=4; 658 | $i++; 659 | } 660 | 661 | return $ret; 662 | } 663 | 664 | sub reparse_attrib 665 | { 666 | my ( $attrib, $val ) = @_; 667 | $val = $val * 1; 668 | 669 | # Is there anything to translate? 670 | if( !exists( $attrib_bags{$attrib} ) ) { 671 | return undef; 672 | } 673 | 674 | my $bag = $attrib_bags{$attrib}; 675 | die "Attrib $attrib with no format" if ! exists $bag->{format}; 676 | 677 | my $ret=undef; 678 | my $bag_flags=$bag->{format}; 679 | if( ($bag_flags&0x00030000) != 0 ) { 680 | die "Attrib $attrib should have flags, but doesn't" if ! exists $bag->{flags}; 681 | 682 | # Attrib has either enum or flags, either way this code will handle it 683 | $ret=reparse_attrib_enum_flags($bag->{flags}, $attrib, $val); 684 | if( ($bag_flags&0x00010000) == 0 and $val==0 and not defined $ret ) { 685 | # These are flags, not enums, and the data is zero 686 | $ret=""; 687 | } 688 | 689 | $bag_flags&= ~0x00030000; 690 | } 691 | 692 | return $ret if defined $ret; 693 | 694 | if( $bag_flags & 0x0004 ) { 695 | # Integer is one of the accepted types 696 | return sprintf "%d", $val; 697 | } 698 | 699 | my $strval=sprintf "%08x", $val; 700 | for( keys %attrib_types ) { 701 | if( ($_ & 0xfff8) ) { 702 | if( ($bag_flags&$_)!=0 ) { 703 | $ret = format_value( $strval, ${$attrib_types{$_}}[1] ); 704 | 705 | $bag_flags &= ~$_; 706 | } 707 | } 708 | } 709 | 710 | die sprintf "Unhandled flags in bag $attrib 0x%08x", $bag_flags 711 | if $bag_flags!=0 && ! defined $ret; 712 | die sprintf "Failed to format attrib $attrib with value #%08x", $val 713 | if ! defined $ret; 714 | 715 | return $ret; 716 | } 717 | 718 | sub reparse_attrib_enum_flags 719 | { 720 | my ($bag, $attrib, $val)=@_; 721 | 722 | my ($ret, $data, $zero )=(undef, 0, undef); 723 | my $rev = ~$val; 724 | my @selected; 725 | my @bag_keys=keys( %$bag ); 726 | 727 | @bag_keys = sort { num_set_bits($bag->{$b}) <=> num_set_bits($bag->{$a}) } @bag_keys; 728 | 729 | for( @bag_keys ) { 730 | if( ($rev & $bag->{$_})==0 ) { 731 | die sprintf "Attrib $attrib contains key 0x%08x which is undefined" 732 | if ! exists( $references{$_} ); 733 | if( ($data | $bag->{$_}) != $data ) { 734 | $data |= $bag->{$_}; 735 | push @selected, $_; 736 | } elsif( $bag->{$_}==0 ) { 737 | $references{$_} =~ '/(.*)$'; 738 | $zero = $1; 739 | } 740 | } 741 | 742 | last if( $data!=0 && $data==$val ); # We are done anyways 743 | } 744 | 745 | return $zero if $val==0 && $zero; 746 | return undef if $val!=$data || $#selected==-1; 747 | 748 | # We now need to eliminate overlapping values 749 | $val=0; 750 | while( $#selected > -1 ) { 751 | my $att = shift @selected; 752 | my $att_val=$bag->{$att}; 753 | my $flag=0; 754 | 755 | # This value is not contained by any values that came before, but it might be contained 756 | # by values later on. 757 | FUTURE: for( @selected ) { 758 | # Does the current attribute change the $i attribute? 759 | if( ($bag->{$_}|$att_val|$val)==($bag->{$_}|$val) ) { 760 | $flag=1; 761 | last FUTURE; 762 | } 763 | } 764 | 765 | if( !$flag ) { 766 | $references{$att} =~ '/(.*)$'; 767 | $ret .= "|" if length($ret)>0; 768 | $ret .= $1; 769 | $val |= $att_val; 770 | } 771 | } 772 | 773 | die sprintf("Internal logic error val=#%08x data=#%08x", $val, $data) if $val != $data; 774 | 775 | return $ret; 776 | } 777 | 778 | # Parse a floating point type 779 | sub parse_type_4 780 | { 781 | my $val=hex($_[0]); 782 | my $value=0.0; 783 | 784 | # Zero is a special case in FP 785 | if( $val!=0 ) { 786 | # 1 bit of sign, 8 bits of exponent, 23 bits of mantissa 787 | my $man=$val % 0x800000; 788 | $man/=2**23; 789 | $man+=1; 790 | 791 | $val>>=23; 792 | 793 | my $exp=$val % 0x100; 794 | $exp-=0x7f; 795 | $val>>=8; 796 | 797 | $value=$man*2**$exp; 798 | 799 | if( $val ) { 800 | $value= -$value; 801 | } 802 | } 803 | 804 | return $value; 805 | } 806 | 807 | # Parse a dimension type 808 | sub parse_type_5 809 | { 810 | my $val=signedhex($_[0]); 811 | 812 | # Extract the value type 813 | my $value=$val/256; 814 | $value+=0.5; 815 | $value=floor($value); 816 | my $unit; 817 | 818 | # Check which unit to use 819 | given( $val & 0x0f ) { 820 | when(0) { $unit='px'; } 821 | when(1) { $unit='dip'; } 822 | when(2) { $unit='sp'; } 823 | when(3) { $unit='pt'; } 824 | when(4) { $unit='in'; } 825 | when(5) { $unit='mm'; } 826 | default { die "$filename:$line_number: Unknown unit for type: \"$_[0]\"\n"; } 827 | } 828 | 829 | # Check what split between fraction and whole 830 | given( ($val & 0x30)>>4 ) { 831 | when(0) { }; # Integer 832 | when(1) { $value/=128 }; # Fix point 7 bit shift 833 | when(2) { $value/=32768 }; # Fix point 15 bit shift 834 | when(3) { $value/=8388608 }; # Fix point 23 bit shift 835 | } 836 | 837 | return "$value$unit"; 838 | } 839 | 840 | # Parse fraction type 841 | sub parse_type_6 842 | { 843 | my $val=signedhex($_[0]); 844 | 845 | # 4 bit relation 846 | my $relation=$val&0x7; 847 | $val >>= 4; 848 | # 4 bit fraction type 849 | my $type=$val&0x7; 850 | $val >>= 4; 851 | 852 | # 23 bit mantissa 853 | my $mantissa=$val&((1<<23)-1); 854 | $val >>= 23; 855 | 856 | # $val is zero if the number is positive 857 | if( $val ) { 858 | # Number is negative - perform two's complement on it to turn it positive 859 | $mantissa ^= (1<<23)-1; 860 | $mantissa ++; 861 | $val="-"; 862 | } else { 863 | $val=""; 864 | } 865 | 866 | given( $type ) { 867 | when( 0x0 ) { 868 | # Number is whole - do nothing 869 | } 870 | when( 0x1 ) { 871 | # 16 bit whole, 7 bit fraction 872 | $mantissa /= 2.0 ** 7; 873 | } 874 | when( 0x2 ) { 875 | # 8 bit whole, 15 bit fraction 876 | $mantissa /= 2.0 ** 15; 877 | } 878 | when( 0x3 ) { 879 | # 23 bits of fraction 880 | $mantissa /= 2.0 ** 23; 881 | } 882 | default { 883 | die "While parsing fraction type, unknown type $type"; 884 | } 885 | } 886 | 887 | $mantissa *= 100; # Turn it into percents 888 | 889 | given( $relation ) { 890 | when( 0 ) { 891 | $relation=""; 892 | } 893 | when( 1 ) { 894 | $relation="p"; 895 | } 896 | default { 897 | die "While parsing fraction type, unknown relation $relation at $filename:$line_number"; 898 | } 899 | } 900 | 901 | return "$val$mantissa%$relation"; 902 | } 903 | 904 | # Parse type boolean 905 | sub parse_type_12 906 | { 907 | my $val=signedhex($_[0]); 908 | 909 | if( $val==0 ) { 910 | return "false"; 911 | } elsif( $val==-1 ) { 912 | return "true"; 913 | } else { 914 | die sprintf "Boolean is 0x%08x, neither 0 nor -1", $val; 915 | } 916 | } 917 | 918 | # Parse type ARGB8 919 | sub parse_type_1c 920 | { 921 | my $val=hex($_[0]); 922 | 923 | return sprintf "#%08x", $val; 924 | } 925 | 926 | # Parse type RGB8 927 | sub parse_type_1d 928 | { 929 | my $val=hex($_[0]); 930 | 931 | # Mask out the Alpha channel 932 | $val&=0x00ffffff; 933 | 934 | return sprintf "#%06x", $val; 935 | } 936 | 937 | # Parse type ARGB4 938 | sub parse_type_1e 939 | { 940 | my $val=hex($_[0]); 941 | 942 | # Mask out the Alpha channel 943 | my $value=0; 944 | 945 | $val>>=4; 946 | $value=$val&0xf; 947 | 948 | $val>>=4; 949 | $value|=$val&0xf0; 950 | 951 | $val>>=4; 952 | $value|=$val&0xf00; 953 | 954 | $val>>=4; 955 | $value|=$val&0xf000; 956 | 957 | return sprintf "#%04x", $value; 958 | } 959 | 960 | # Parse type RGB4 961 | sub parse_type_1f 962 | { 963 | my $val=hex($_[0]); 964 | 965 | # Mask out the Alpha channel 966 | my $value=0; 967 | 968 | $val>>=4; 969 | $value=$val&0xf; 970 | 971 | $val>>=4; 972 | $value|=$val&0xf0; 973 | 974 | $val>>=4; 975 | $value|=$val&0xf00; 976 | 977 | return sprintf "#%03x", $value; 978 | } 979 | 980 | sub parse_attribute 981 | { 982 | my ($value, $attrib, $known_string)=@_; 983 | 984 | given( $value ) { 985 | when(/^.*\(Raw: "(.*)"\)$/) { 986 | return escape_string($1, $known_string); 987 | } 988 | when(/^\(type 0x4\)0x(.*)$/) { 989 | return parse_type_4($1); 990 | } 991 | when(/^\(type 0x5\)0x(.*)$/) { 992 | return parse_type_5($1); 993 | } 994 | when(/^\(type 0x6\)0x(.*)$/) { 995 | return parse_type_6($1); 996 | } 997 | when(/^\(type 0x10\)0x(.*)$/) { 998 | my $val=reparse_attrib( $attrib, hex($1) ); 999 | 1000 | if( !defined($val) ) { 1001 | $val=signedhex($1); 1002 | } 1003 | 1004 | return $val; 1005 | } 1006 | when(/^\(type 0x11\)0x(.*)$/) { 1007 | my $val=reparse_attrib( $attrib, hex($1) ); 1008 | 1009 | if( !defined($val) ) { 1010 | $val=sprintf "0x%x", hex($1); 1011 | } 1012 | 1013 | return $val; 1014 | } 1015 | when(/^\(type 0x12\)0x(.*)$/) { 1016 | return parse_type_12($1); 1017 | } 1018 | when(/^\(type 0x1c\)0x(.*)$/) { 1019 | return parse_type_1c($1); 1020 | } 1021 | when(/^\(type 0x1d\)0x(.*)$/) { 1022 | return parse_type_1d($1); 1023 | } 1024 | when(/^\(type 0x1e\)0x(.*)$/) { 1025 | return parse_type_1e($1); 1026 | } 1027 | when(/^\(type 0x1f\)0x(.*)$/) { 1028 | return parse_type_1f($1); 1029 | } 1030 | when(/^\@0x([0-9a-f]+)$/) { 1031 | # Resource reference 1032 | my $res=hex($1); 1033 | 1034 | return "@".format_reference($res); 1035 | } 1036 | when(/^\?0x([0-9a-f]+)$/) { 1037 | # Theme reference 1038 | exists( $references{hex($1)} ) or 1039 | die "Attribute points to theme resource 0x$1 which doesn't exist"; 1040 | 1041 | return "?$references{hex($1)}"; 1042 | } 1043 | default { 1044 | die "Unparsable line \"$_\""; 1045 | } 1046 | } 1047 | } 1048 | 1049 | # Convert all 32 bit values to signed 32 bit, regardless of whether our perl implementation is 32 1050 | # or 64 bit. 1051 | sub make_signed { 1052 | local $ret=shift(@_)+0; 1053 | 1054 | if ($ret>=0x80000000) { 1055 | $ret-=0x100000000; 1056 | } 1057 | 1058 | return $ret; 1059 | } 1060 | sub signedhex { 1061 | local $ret=hex(shift(@_)); 1062 | 1063 | return make_signed($ret); 1064 | } 1065 | 1066 | sub printhash { 1067 | my ($hash) = @_; 1068 | 1069 | my $ret="{ "; 1070 | for ( keys( %$hash ) ) { 1071 | $ret .= $_."=>".$$hash{$_}." "; 1072 | } 1073 | 1074 | $ret.="}"; 1075 | 1076 | return $ret; 1077 | } 1078 | 1079 | sub dump_values 1080 | { 1081 | my ( $apkpath, $outdir )=@_; 1082 | 1083 | if( ! open RESOURCES, "-|", "aapt", ( "d", "--values", "resources", $apkpath) ) { 1084 | print STDERR "Failed to dump resources from $apkpath: $!\n"; 1085 | 1086 | return 1; 1087 | } 1088 | 1089 | my $state=0; 1090 | my $configname=undef; 1091 | my ($class, $name, $type, $index); 1092 | my $error; 1093 | 1094 | while( ) { 1095 | chomp; 1096 | 1097 | if( m'Package Group \d+ id=\d+ packageCount=\d+ name=(.+)$' ) { 1098 | $package_name=$1; 1099 | 1100 | $package_ext = $package_name eq $apk_package_name ? "" : ".$1"; 1101 | } elsif( m'^ *config (\d+)( .*)?$' ) { # Old aapt output format 1102 | # Start of new configuration block 1103 | print "found config $2\n"; 1104 | close_pending_files(); # Close all open pending files 1105 | $configname=parse_config_name($2); 1106 | } elsif( m'^ *config (.*):$' ) { # New (jellybeans) aapt output format 1107 | # Start of new configuration block 1108 | print "Found config $1\n"; 1109 | close_pending_files(); # Close all open pending files 1110 | if( "$1" eq "(default)" ) { 1111 | $configname=""; 1112 | } else { 1113 | $configname="-".parse_config_name_new($1); 1114 | } 1115 | } elsif( m'^ *resource 0x([0-9a-f]+) ([^:]+):([^/]+)/([^:]+): t=0x([0-9a-f]+)' ) { 1116 | die "Unexpected resource value" if ! defined $configname; 1117 | 1118 | die "Resource define non-package value" if $2 ne $package_name; 1119 | die "First time defined with value" if !exists $references{hex($1)}; 1120 | die "Inconsistent name of reference: $1 $2:$3/$4 ne $references{hex($1)}" 1121 | if $references{hex($1)} ne "$2:$3/$4"; 1122 | ($class, $name, $type, $index)=( $3, $4, $5, hex($1) ); 1123 | $state = 1; 1124 | } elsif( defined $name && $state==1 ) { 1125 | if( !parse_values( $index, $class, $name, $type, $_, $outdir, $configname ) ) { 1126 | return 1; 1127 | } 1128 | 1129 | ($class, $name, $type)=(undef, undef, undef); 1130 | $state=0; 1131 | } elsif( m'^ *resource 0x([0-9a-f]+) ([^:]+):array/([^:]+): ( \(PUBLIC\))?$' ) { 1132 | die "Unexpected resource value" if ! defined $configname; 1133 | 1134 | die "Resource define non-package value" if $2 ne $package_name; 1135 | die "First time defined with value" if !exists $references{hex($1)}; 1136 | die "Inconsistent name of reference" if $references{hex($1)} ne "$2:array/$3"; 1137 | 1138 | $class="array"; 1139 | $name=$3; 1140 | $state=2; 1141 | $references_flags{hex($1)} |= $REF_USED; 1142 | } elsif( $state==2 ) { 1143 | m'^ *Parent=0x([0-9a-f]+), Count=(\d+)$' or 1144 | die "Unexpected text \"$_\" after array start"; 1145 | die "Non-zero parent for array \"$_\"" if hex($1)!=0; 1146 | 1147 | $count=$2+0; # Make sure perl knows it is, indeed, an integer 1148 | $state=3; 1149 | 1150 | write_array_open($name, $outdir, $configname); 1151 | 1152 | if( $count==0 ) { 1153 | write_array_close($name); 1154 | $state=0; 1155 | } 1156 | } elsif( $state==3 ) { 1157 | m'^ *#(\d+) \(Key=0x[0-9a-f]+\): (.*)$' or 1158 | die "Unexpected text \"$_\" for array item"; 1159 | 1160 | write_array_item($2); 1161 | 1162 | if( --$count == 0 ) { 1163 | write_array_close($name); 1164 | $state=0; 1165 | } 1166 | } elsif( m'^ *resource 0x([0-9a-f]+) ([^:]+):attr/([^:]+): ' && 0 ) { 1167 | die "Unexpected resource value" if ! defined $configname; 1168 | 1169 | die "Resource define non-package value" if $2 ne $package_name; 1170 | die "First time defined with value" if !exists $references{hex($1)}; 1171 | die "Inconsistent name of reference. Was $references{hex($1)} now $2:attr/$3" 1172 | if $references{hex($1)} ne "$2:attr/$3"; 1173 | 1174 | $class="attr"; 1175 | $name=$3; 1176 | $state=4; 1177 | $references_flags{hex($1)} |= $REF_USED; 1178 | } elsif( $state==4 ) { 1179 | m'^ *Parent=0x([0-9a-f]+), Count=(\d+)$' or 1180 | die "Unexpected text \"$_\" after attr start"; 1181 | die "Non-zero parent for attribute \"$_\"" if hex($1)!=0; 1182 | 1183 | $count=$2+0; # Make sure perl knows it is, indeed, an integer 1184 | die "Attribute with count different than 1" if $count!=1; 1185 | 1186 | $state++; 1187 | 1188 | if( $count==0 ) { 1189 | $state=0; 1190 | } 1191 | } elsif( $state==4 ) { 1192 | m'^ *#(\d+) \(Key=0x[0-9a-f]+\): \(color\) #(.*)$' or 1193 | die "Unexpected text \"$_\" for attr item"; 1194 | 1195 | write_attr($name, hex($2)); 1196 | 1197 | if( --$count == 0 ) { 1198 | $state=0; 1199 | } 1200 | } elsif( m'^ *resource 0x([0-9a-f]+) ([^:]+):style/([^:]+): ( \(PUBLIC\))?$' ) { 1201 | die "Unexpected resource value" if ! defined $configname; 1202 | 1203 | die "Resource define non-package value" if $2 ne $package_name; 1204 | die "First time defined with value" if !exists $references{hex($1)}; 1205 | die "Inconsistent name of reference" if $references{hex($1)} ne "$2:style/$3"; 1206 | 1207 | $class="style"; 1208 | $name=$3; 1209 | $state=30; 1210 | $references_flags{hex($1)} |= $REF_USED; 1211 | } elsif( $state==30 ) { 1212 | m'^ *Parent=0x([0-9a-f]+), Count=(\d+)$' or 1213 | die "Unexpected text \"$_\" after array start"; 1214 | $count=$2+0; # Make sure perl knows it is, indeed, an integer 1215 | $state=31; 1216 | 1217 | write_style_open($name, $outdir, $configname, hex($1)); 1218 | 1219 | if( $count==0 ) { 1220 | write_style_close($name); 1221 | $state=0; 1222 | } 1223 | } elsif( $state==31 ) { 1224 | m'^ *#(\d+) \(Key=0x([0-9a-f]+)\): (.*)$' or 1225 | die "Unexpected text \"$_\" for style item"; 1226 | 1227 | write_style_item($3, hex($2)); 1228 | 1229 | if( --$count == 0 ) { 1230 | write_style_close($name); 1231 | $state=0; 1232 | } 1233 | } elsif( m'^ *resource 0x([0-9a-f]+) ([^:]+):plurals/([^:]+): $' ) { 1234 | die "Unexpected resource value" if ! defined $configname; 1235 | 1236 | die "Resource define non-package value" if $2 ne $package_name; 1237 | die "First time defined with value" if !exists $references{hex($1)}; 1238 | die "Inconsistent name of reference" if $references{hex($1)} ne "$2:plurals/$3"; 1239 | 1240 | $class="plurals"; 1241 | $name=$3; 1242 | $state=40; 1243 | $references_flags{hex($1)} |= $REF_USED; 1244 | } elsif( $state==40 ) { 1245 | m'^ *Parent=0x([0-9a-f]+), Count=(\d+)$' or 1246 | die "Unexpected text \"$_\" after array start"; 1247 | die "Plurals $name with non-empty parent" if hex($1)!=0; 1248 | 1249 | $count=$2+0; # Make sure perl knows it is, indeed, an integer 1250 | $state=41; 1251 | 1252 | write_plurals_open($name, $outdir, $configname); 1253 | 1254 | if( $count==0 ) { 1255 | write_plurals_close($name); 1256 | $state=0; 1257 | } 1258 | } elsif( $state==41 ) { 1259 | m'^ *#(\d+) \(Key=0x([0-9a-f]+)\): (.*)$' or 1260 | die "Unexpected text \"$_\" for plurals item"; 1261 | 1262 | write_plurals_item($3, hex($2)); 1263 | 1264 | if( --$count == 0 ) { 1265 | write_plurals_close($name); 1266 | $state=0; 1267 | } 1268 | } else { 1269 | # XXX print "Unmatched: $_\n"; 1270 | } 1271 | } 1272 | 1273 | die "Ended while still inside bag state=$state" if $state!=0; 1274 | 1275 | close( RESOURCES ); 1276 | 1277 | dump_attributes($outdir); 1278 | 1279 | close_pending_files(); 1280 | 1281 | return write_public_file($outdir); 1282 | } 1283 | 1284 | sub parse_config_name 1285 | { 1286 | my ($string)=@_; 1287 | 1288 | my $ret=""; 1289 | 1290 | # The order here is important! See http://developer.android.com/guide/topics/resources/providing-resources.html#AlternativeResources 1291 | 1292 | if( $string=~/mcc=(\d+)/ ) { 1293 | # MCC code 1294 | $ret.="-mcc$1"; 1295 | } 1296 | if( $string=~/mnc=(\d+)/ ) { 1297 | # MNC code, must come after MCC 1298 | $ret.="-mnc$1"; 1299 | } 1300 | if( $string=~/lang=([a-z]+)/ ) { 1301 | # Language 1302 | $ret.="-$1"; 1303 | } 1304 | if( $string=~/swdp=([0-9]+)/ ) { 1305 | # Smallest Width 1306 | $ret.="-sw$1dp"; 1307 | } 1308 | if( $string=~/wdp=([0-9]+)/ ) { 1309 | # Available width 1310 | $ret.="-w$1dp"; 1311 | } 1312 | if( $string=~/hdp=([0-9]+)/ ) { 1313 | # Available height 1314 | $ret.="-h$1dp"; 1315 | } 1316 | if( $string=~/cnt=([A-Z]+)/ ) { 1317 | # Country, must come after language 1318 | $ret.="-r$1"; 1319 | } 1320 | if( $string=~/sz=(\d+) \((\S+)\)/ ) { 1321 | # Screen size 1322 | $ret.="-$2"; 1323 | } 1324 | if( $string=~/lng=(\d+) \((\S+)\)/ ) { 1325 | # Long vs. Normal screen 1326 | $ret.="-$2"; 1327 | } 1328 | if( $string=~/orient=(\d+) \((\S+)\)/ ) { 1329 | # Screen orientation 1330 | $ret.="-$2"; 1331 | } 1332 | if( $string=~/type=(\d+) \((\S+)\)/ ) { 1333 | # Type - car or desk 1334 | $ret.="-$2"; 1335 | } 1336 | if( $string=~/night=(\d+) \((\S+)\)/ ) { 1337 | # Daytime - night vs. day (notnight) 1338 | $ret.="-$2"; 1339 | } 1340 | if( $string=~/density=(\S+)/ ) { 1341 | # Screen pixel density 1342 | if( $1==120 ) { 1343 | $ret.="-ldpi"; 1344 | } elsif( $1==160 ) { 1345 | $ret.="-mdpi"; 1346 | } elsif( $1==240 ) { 1347 | $ret.="-hdpi"; 1348 | } elsif( $1==320 ) { 1349 | $ret.="-xhdpi"; 1350 | } elsif( $1 eq "no" ) { 1351 | $ret.="-nodpi"; 1352 | } else { 1353 | die "Unsupported density mode $1: $string"; 1354 | } 1355 | } 1356 | if( $string=~/touch=(\d+) \((\S+)\)/ ) { 1357 | # Touch vs. Stylus 1358 | $ret.="-$2"; 1359 | } 1360 | if( $string=~/keyhid=(\d+) \((\S+)\)/ ) { 1361 | # Keyboard availability 1362 | if( $1==1 ) { 1363 | $ret.="-keysexposed"; 1364 | } elsif( $1==2 ) { 1365 | $ret.="-keyshidden"; 1366 | } elsif( $1==3 ) { 1367 | $ret.="-keyssoft"; 1368 | } else { 1369 | die "Key status $1($2) unknown"; 1370 | } 1371 | } 1372 | if( $string=~/kbd=(\d+) \((\S+)\)/ ) { 1373 | # Primary input method 1374 | $ret.="-$2"; 1375 | } 1376 | if( $string=~/navhid=(\d+) \((\S+)\)/ ) { 1377 | # Navigation keys status 1378 | if( $1==4 ) { 1379 | $ret.="-navexposed"; 1380 | } elsif( $1==8 ) { 1381 | $ret.="-navhidden"; 1382 | } else { 1383 | die "Unsupported navigation availability value $1: $string"; 1384 | } 1385 | } 1386 | if( $string=~/nav=(\d+) \((\S+)\)/ ) { 1387 | # Primary non-touch navigation method 1388 | $ret.="-$2"; 1389 | } 1390 | if( $string=~/w=(\d+) h=(\d+)/ ) { 1391 | # Dropped from the documentation as if it never existed: 1392 | # Screen width and height 1393 | $ret.="-$1x$2"; 1394 | } 1395 | if( $string=~/sdk=(\d+)/ ) { 1396 | # XXX Might add code to not print if minimal version dictated by other attributes 1397 | # Minimal SDK version 1398 | $ret.="-v$1"; 1399 | } 1400 | 1401 | return $ret; 1402 | } 1403 | 1404 | sub parse_config_name_new 1405 | { 1406 | my ($string)=@_; 1407 | 1408 | # Fix JB's aapt country resource to add the required "r" 1409 | $string=~s/^(.+-)?([a-z]{2})-([A-Z]{2})(-.+)?$/\1\2-r\3\4/; 1410 | 1411 | # Fix the mcc/mnc order 1412 | $string=~s/^(.+-)?([0-9]+)mcc(-.+)?$/\1mcc\2\3/; 1413 | $string=~s/^(.+-)?([0-9]+)mnc(-.+)?$/\1mnc\2\3/; 1414 | 1415 | return $string; 1416 | } 1417 | 1418 | sub open_config_file 1419 | { 1420 | my( $class, $outdir, $configname, $filename )=@_; 1421 | return $type_files{$class} if exists $type_files{$class}; 1422 | 1423 | make_path( "$outdir/res/values$configname", { error => \$error } ); 1424 | my $fileref; 1425 | if( !defined $filename ) { 1426 | $filename="${class}s"; 1427 | } 1428 | 1429 | $filename = "$filename$package_ext"; 1430 | 1431 | if( -f "$outdir/res/values$configname/${filename}.xml" ) { 1432 | # More than one config with the same modifiers defined the same class 1433 | print STDERR "Redundant configuration of values$configname/${filename}.xml. Creating copy.\n"; 1434 | $filename=".$filename"; 1435 | 1436 | my $count=1; 1437 | while( -f "$outdir/res/values$configname/${filename}.$count.xml" ) { 1438 | $count++; 1439 | } 1440 | 1441 | $filename="$filename.$count"; 1442 | } 1443 | 1444 | open $fileref, ">$outdir/res/values$configname/${filename}.xml" or 1445 | die "Failed to open res/values$configname/${filename}.xml: $!"; 1446 | 1447 | print $fileref ''."\n"; 1448 | print $fileref "\n"; 1449 | 1450 | $type_files{$class}=$fileref; 1451 | 1452 | return $type_files{$class}; 1453 | } 1454 | 1455 | sub close_pending_files 1456 | { 1457 | for( keys %type_files ) { 1458 | my $fileref = $type_files{$_}; 1459 | print $fileref "\n"; 1460 | close $fileref; 1461 | 1462 | delete $type_files{$_}; 1463 | } 1464 | } 1465 | 1466 | sub parse_values 1467 | { 1468 | my( $index, $class, $name, $type, $string, $outdir, $configname )=@_; 1469 | 1470 | if( ! exists $class_map{$class} ) { 1471 | print STDERR "Project defines non-standard class \"$class\"\n"; 1472 | $class_map{$class}=\&parse_unknown_value; 1473 | } 1474 | 1475 | $references_flags{$index} |= $REF_USED; 1476 | if( defined $class_map{$class} ) { 1477 | my $fileref = open_config_file( $class, $outdir, $configname ); 1478 | 1479 | ${class_map{$class}}( $name, $type, $string, $class ); 1480 | } 1481 | 1482 | return 1; 1483 | } 1484 | 1485 | sub parse_unknown_value 1486 | { 1487 | my( $name, $type, $string, $class )=@_; 1488 | 1489 | $type=hex($type); 1490 | my $fileref = $type_files{$class}; 1491 | print $fileref " ".format_value($string, $type). 1492 | "\n"; 1493 | } 1494 | 1495 | sub parse_id_value 1496 | { 1497 | my( $name, $type, $string, $class )=@_; 1498 | 1499 | $type=hex($type); 1500 | my $fileref = $type_files{id}; 1501 | print $fileref " ".format_value($string, $type)."\n"; 1502 | } 1503 | 1504 | sub parse_bool_value 1505 | { 1506 | my( $name, $type, $string, $class )=@_; 1507 | 1508 | if( $string!~'^\s*\(color\) #(\S+)$' ) { 1509 | die "Invalid boolean format: \"$string\""; 1510 | } 1511 | 1512 | $type=hex($type); 1513 | my $fileref = $type_files{bool}; 1514 | print $fileref " ".format_value($1, $type)."\n"; 1515 | } 1516 | 1517 | sub parse_int_value 1518 | { 1519 | my( $name, $type, $string, $class )=@_; 1520 | 1521 | if( $string!~'^\s*\(color\) #(\S+)$' ) { 1522 | die "Invalid integer format: \"$string\""; 1523 | } 1524 | 1525 | my $fileref = $type_files{integer}; 1526 | printf $fileref " %d\n", signedhex($1); 1527 | } 1528 | 1529 | sub parse_fraction_value 1530 | { 1531 | my( $name, $type, $string, $class )=@_; 1532 | 1533 | if( $string!~'^\s*\(fraction\) (\S+)%$' ) { 1534 | die "Invalid fraction format: $name $type $class \"$string\""; 1535 | } 1536 | 1537 | my $fileref = $type_files{fraction}; 1538 | printf $fileref " <$class name=\"$name\">%f%%\n", $1; 1539 | } 1540 | 1541 | # Accept a string as reported by aapt, and issue it in a form suitable for XML output 1542 | sub escape_string 1543 | { 1544 | my($string,$known_string)=@_; 1545 | 1546 | $string=~s/&/&/g; 1547 | $string=~s//>/g; 1549 | $string=~s/"/u0022/g; 1550 | $string=~s/\t/\\t/g; 1551 | # $string=~s/ /\\u0020/g; 1552 | $string=~s/\N{U+200e}/\\u200e/g; # LRM 1553 | $string=~s/\N{U+200f}/\\u200f/g; # RLM 1554 | $string=~s/^@/\\@/g; 1555 | $string=~s/^\?/\\?/g; 1556 | 1557 | if( ! $known_string ) { 1558 | return '"'.$string.'"'; 1559 | } else { 1560 | return $string; 1561 | } 1562 | } 1563 | 1564 | sub parse_string_value 1565 | { 1566 | my( $name, $type, $string, $class )=@_; 1567 | my $fileref = $type_files{string}; 1568 | 1569 | $type=hex($type); 1570 | if( $type==3 ) { 1571 | # Direct UTF-8 string 1572 | if( $string!~'^\s*\(string8\) "(.*)"$' && 1573 | $string!~'^\s*\(string16\) "(.*)"$' ) 1574 | { 1575 | die "Invalid string format: \"$string\""; 1576 | } 1577 | print $fileref " ".escape_string($1). 1578 | "\n"; 1579 | } elsif( $type==1 ) { 1580 | if( $string!~'^\s*\(reference\) 0x([0-9a-f]+)$' ) { 1581 | die "Invalid string format: \"$string\""; 1582 | } 1583 | 1584 | die "Invalid reference 0x$1" if !exists $references{hex($1)}; 1585 | 1586 | print $fileref " @".$references{hex($1)}."\n"; 1587 | } else { 1588 | die "Unhandled string type $type: \"$string\""; 1589 | } 1590 | } 1591 | 1592 | sub parse_dimen_value 1593 | { 1594 | my( $name, $type, $string, $class )=@_; 1595 | 1596 | my $fileref = $type_files{dimen}; 1597 | 1598 | if( $type==1 ) { 1599 | # Reference 1600 | if( $string!~'^\s*\(reference\) 0x([0-9a-f]+)$' ) { 1601 | die "Invalid reference format: \"$string\""; 1602 | } 1603 | 1604 | die "Invalid reference 0x$1" if !exists $references{hex($1)}; 1605 | 1606 | print $fileref " @".format_reference(hex($1))."\n"; 1607 | } elsif( $type==5 ) { 1608 | # Processed dimension 1609 | if( $string!~'^\s*\(dimension\) (\S+)$' ) { 1610 | die "Invalid dimension format: \"$string\""; 1611 | } 1612 | print $fileref " $1\n"; 1613 | } elsif( $type==6 ) { 1614 | if( $string!~'^\s*\(fraction\) (\S+)%$' ) { 1615 | die "Invalid fraction format: \"$string\""; 1616 | } 1617 | printf $fileref " %f%%\n", $1*100; 1618 | } else { 1619 | die "Unhandled dimen type $type: \"$string\""; 1620 | } 1621 | } 1622 | 1623 | sub parse_color_value 1624 | { 1625 | my( $name, $type, $string, $class )=@_; 1626 | 1627 | my $fileref = $type_files{$class}; 1628 | 1629 | $type = hex($type); 1630 | if( $type==0x1f || $type==0x1c || $type==0x1d || $type==0x1e ) { 1631 | # Processed color 1632 | if( $string!~'^\s*\(color\) #(\S+)$' ) { 1633 | die "Invalid string format: \"$string\""; 1634 | } 1635 | print $fileref " <$class name=\"$name\">".format_value($1, $type)."\n"; 1636 | } elsif( $type==3 ) { 1637 | # Do nothing. Reference to external XML 1638 | } elsif( $type==1 ) { 1639 | if( $string!~'^\s*\(reference\) 0x([0-9a-f]+)$' ) { 1640 | die "Invalid reference format: \"$string\""; 1641 | } 1642 | 1643 | die "Invalid reference 0x$1" if !exists $references{hex($1)}; 1644 | 1645 | print $fileref " <$class name=\"$name\">@".$references{hex($1)}."\n"; 1646 | } else { 1647 | die "Unhandled color type $type: \"$string\""; 1648 | } 1649 | } 1650 | 1651 | sub write_array_open 1652 | { 1653 | my($name, $outdir, $configname)=@_; 1654 | 1655 | my $fileref = open_config_file( "array", $outdir, $configname ); 1656 | 1657 | print $fileref " \n" 1658 | } 1659 | 1660 | sub write_array_item 1661 | { 1662 | my($string)=@_; 1663 | 1664 | my $fileref = $type_files{array}; 1665 | 1666 | my($value,,$comment)=parse_content($string); 1667 | 1668 | if( $comment eq "" ) { 1669 | print $fileref " $value\n"; 1670 | } else { 1671 | print $fileref " $value \n"; 1672 | } 1673 | } 1674 | 1675 | sub write_array_close 1676 | { 1677 | my($name)=@_; 1678 | 1679 | my $fileref = $type_files{array}; 1680 | 1681 | print $fileref " \n\n" 1682 | } 1683 | 1684 | sub write_attr 1685 | { 1686 | my ($name, $type)=@_; 1687 | } 1688 | 1689 | sub write_style_open 1690 | { 1691 | my($name, $outdir, $configname, $parent)=@_; 1692 | 1693 | die sprintf("Parent 0x%08x does not exist", $parent) 1694 | if !exists $references{$parent}; 1695 | 1696 | my $fileref = open_config_file( "style", $outdir, $configname ); 1697 | 1698 | if( $parent==0 ) { 1699 | print $fileref " \n\n"; 1754 | } 1755 | 1756 | sub write_plurals_open 1757 | { 1758 | my($name, $outdir, $configname)=@_; 1759 | 1760 | my $fileref = open_config_file( "plural", $outdir, $configname ); 1761 | 1762 | print $fileref " \n"; 1763 | } 1764 | 1765 | sub write_plurals_item 1766 | { 1767 | my($string, $nameref)=@_; 1768 | 1769 | die sprintf( "Plural references non-existing resource 0x%08x", $nameref ) 1770 | if ! exists $plural_references{$nameref}; 1771 | 1772 | my $fileref = $type_files{plural}; 1773 | 1774 | my($value,$type,$comment)=parse_content($string); 1775 | $value = add_format_index( $value ); 1776 | print $fileref " $value\n"; 1777 | } 1778 | 1779 | sub add_format_index 1780 | { 1781 | my $ret=$_[0]; 1782 | 1783 | # should we handle this at all? 1784 | if( $ret!~/%[sd].*%[sd]/ || $ret=~/%\d+\$[sd]/ ) 1785 | { 1786 | return $ret; 1787 | } 1788 | 1789 | my $i=1; 1790 | while( $ret=~/^(([^%]*%[^sd])*[^%]*)%([sd])(.*)$/ ) { 1791 | $ret="$1%$i\$$3$4"; 1792 | ++$i; 1793 | } 1794 | 1795 | return $ret; 1796 | } 1797 | 1798 | sub write_plurals_close 1799 | { 1800 | my($name)=@_; 1801 | 1802 | my $fileref = $type_files{plural}; 1803 | 1804 | print $fileref " \n"; 1805 | } 1806 | 1807 | sub dump_attributes 1808 | { 1809 | my ($outdir)=@_; 1810 | 1811 | my $fileref = open_config_file( "attr", $outdir, "", "attrs" ); 1812 | 1813 | for( keys %attrib_bags ) { 1814 | if( $_=~'^([^:/]+):([^:/]+)$' ) { 1815 | if( $1 eq $apk_package_name ) { 1816 | # Only handle the attribs that belong to this package 1817 | my($key, $name)=($_, $2); 1818 | 1819 | # Mark the attribute as used 1820 | $references_flags{$rev_references{"$1:attr/$2"}} |= $REF_USED; 1821 | 1822 | print $fileref " {format}; 1828 | 1829 | my $format=$bag->{format}; 1830 | 1831 | # Do we have a scalar format? 1832 | if( ($format&0xffff)!=0 ) { 1833 | my $format_string; 1834 | for( keys %attrib_types ) { 1835 | if( ($_&0xffff)!=0 && ($format&$_)!=0 ) { 1836 | $format_string.="|" if $format_string; 1837 | $format_string.=${$attrib_types{$_}}[0]; 1838 | } 1839 | } 1840 | print $fileref " format=\"$format_string\""; 1841 | } 1842 | 1843 | # Hard coding the types is not nice, but somewhat necessary 1844 | if( exists $bag->{min} ) { 1845 | print $fileref " min=\"$bag->{min}\""; 1846 | } 1847 | if( exists $bag->{max} ) { 1848 | print $fileref " max=\"$bag->{max}\""; 1849 | } 1850 | if( exists $bag->{localization} ) { 1851 | die "Localization type present but is not 1 (is $bag->{localization}) in $key" 1852 | if $bag->{localization}!=1; 1853 | print $fileref " localization=\"suggested\""; 1854 | } 1855 | 1856 | # Do we need content to this XML element? 1857 | if( ($format&0xffff0000)==0 ) { 1858 | # Element is done 1859 | print $fileref " />\n"; 1860 | } else { 1861 | die "Attrib $key with content has no bag information" 1862 | if ! exists $bag->{flags}; 1863 | my $bag_flags=$bag->{flags}; 1864 | print $fileref ">\n"; 1865 | if( $format&0x00010000 ) { 1866 | # Enum list 1867 | for( keys %$bag_flags ) { 1868 | die sprintf "Attrib $key has enum element 0x%08x with no name", $_ 1869 | if ! exists $references{$_}; 1870 | $references{$_}=~'^([^:/]+):([^:/]+)/([^:/]+)$' or 1871 | die "Attrib $key reference $references{$_} with bad format"; 1872 | my ($package, $class, $name)=($1, $2, $3); 1873 | die "Attrib $key references $references{$_} which is not an id" 1874 | if $class ne "id"; 1875 | die "Attrib $key references $references{$_} which is not in package" 1876 | if $package ne $apk_package_name; 1877 | 1878 | $references_flags{$_} |= $REF_USED; 1879 | printf $fileref " \n", 1880 | make_signed($bag_flags->{$_}); 1881 | } 1882 | } elsif( $format&0x00020000 ) { 1883 | # Flags list 1884 | for( keys %$bag_flags ) { 1885 | die sprintf "Attrib $key has enum element 0x%08x with no name", $_ 1886 | if ! exists $references{$_}; 1887 | $references{$_}=~'^([^:/]+):([^:/]+)/([^:/]+)$' or 1888 | die "Attrib $key reference $references{$_} with bad format"; 1889 | my ($package, $class, $name)=($1, $2, $3); 1890 | die "Attrib $key references $references{$_} which is not an id" 1891 | if $class ne "id"; 1892 | die "Attrib $key references $references{$_} which is not in package" 1893 | if $package ne $apk_package_name; 1894 | 1895 | $references_flags{$_} |= $REF_USED; 1896 | printf $fileref " \n", 1897 | $bag_flags->{$_}; 1898 | } 1899 | } else { 1900 | die "Unknown bag format for $key"; 1901 | } 1902 | 1903 | print $fileref " \n"; 1904 | } 1905 | } 1906 | } else { 1907 | die "Internal error in attrib bag $_"; 1908 | } 1909 | } 1910 | } 1911 | 1912 | # Return a list of parsed name, type and comment (if any) 1913 | sub parse_content 1914 | { 1915 | my($string)=@_; 1916 | my($ret, $type, $comment); 1917 | 1918 | given( $string ) { 1919 | when( m'^\((reference)\) 0x([0-9a-f]+)$' ) { 1920 | $type=$1; 1921 | my $ref=hex($2); 1922 | 1923 | $ret="@".format_reference($ref); 1924 | } 1925 | when( m'^\((color)\) #([0-9a-f]+)$' ) { 1926 | # We cannot tell a color from an integer 1927 | $type=$1; 1928 | $ret="#$2"; 1929 | $comment="or int ".signedhex($2); 1930 | } 1931 | when( m'^\((string8)\) "(.*)"$' ) { 1932 | $type=$1; 1933 | $ret=escape_string($2); 1934 | } 1935 | when( m'^\((string16)\) "(.*)"$' ) { 1936 | $type=$1; 1937 | $ret=escape_string($2); 1938 | } 1939 | when( m'^\((dimension)\) (.*)$' ) { 1940 | $type=$1; 1941 | $ret=$2; 1942 | } 1943 | when( m'^\((float)\) (.*)$' ) { 1944 | $type=$1; 1945 | $ret=$2; 1946 | } 1947 | when( m'^\((attribute)\) 0x(.*)$' ) { 1948 | $type=$1; 1949 | $ret="?".format_reference(hex($2)); 1950 | } 1951 | default { 1952 | die "Don't know how to parse \"$_\""; 1953 | } 1954 | } 1955 | 1956 | return($ret, $type, $comment); 1957 | } 1958 | 1959 | sub write_public_file 1960 | { 1961 | my($outdir)=@_; 1962 | 1963 | my $fileref = open_config_file( "public", $outdir, "", "public" ); 1964 | 1965 | my @keys = sort keys %references; 1966 | # Padding related values 1967 | my ( $pad_start, $pad_end, $pad_base_name, $pad_class ); 1968 | 1969 | for( @keys ) { 1970 | my( $key, $value, $flags )=($_, $references{$_}, $references_flags{$_}); 1971 | 1972 | if( $key!=0 ) { 1973 | $value=~'([^:/]+):([^:/]+)/([^:/]+)' or die "Invalid reference value $value"; 1974 | my( $package, $class, $name )=($1, $2, $3); 1975 | 1976 | if( $package eq $package_name ) { 1977 | # This is our reference 1978 | 1979 | if( ($flags&$REF_USED)!=0 ) { 1980 | # Emit cached padding, if any 1981 | if( defined $pad_start ) { 1982 | emit_public_padding( $fileref, $pad_start, $pad_end, $pad_base_name, 1983 | $pad_class ); 1984 | $pad_start=undef; 1985 | } 1986 | 1987 | printf $fileref " \n", 1988 | $class, $key, $name; 1989 | } else { 1990 | # This reference was never defined, needs to be padded. 1991 | if( defined $pad_start ) { 1992 | # We have previous padding cached. We need to either merge or emit and cache 1993 | 1994 | $name =~ '^(.*[^0-9])([0-9]*)$'; 1995 | if( $key==$pad_end+1 && $class eq $pad_class && $pad_base_name eq $1 ) { 1996 | # The entries match - combine them 1997 | $pad_end++; 1998 | } else { 1999 | emit_public_padding( $fileref, $pad_start, $pad_end, $pad_base_name, 2000 | $pad_class ); 2001 | $pad_start=undef; 2002 | } 2003 | } 2004 | 2005 | if( ! defined $pad_start ) { 2006 | # Cache the entry for future use 2007 | $pad_start=$key; 2008 | $pad_end=$key; 2009 | $pad_class=$class; 2010 | $name =~ '^(.*[^0-9])([0-9]*)$'; 2011 | $pad_base_name=$1; 2012 | } 2013 | } 2014 | } 2015 | } 2016 | } 2017 | 2018 | if( defined $pad_start ) { 2019 | # We have previous padding cached. We need to emit 2020 | 2021 | emit_public_padding( $fileref, $pad_start, $pad_end, $pad_base_name, $pad_class ); 2022 | } 2023 | 2024 | close_pending_files(); 2025 | } 2026 | 2027 | sub emit_public_padding 2028 | { 2029 | my ( $fileref, $start, $end, $name, $class )=@_; 2030 | 2031 | printf $fileref 2032 | " \n", 2033 | $class, $start, $name, $end; 2034 | } 2035 | 2036 | sub format_value 2037 | { 2038 | my( $value, $type )=@_; 2039 | 2040 | if( $type==0x00 ) { 2041 | # NULL 2042 | } elsif( $type==0x01 ) { 2043 | # Reference 2044 | die "Type reference not yet supported \"$value\""; 2045 | } elsif( $type==0x02 ) { 2046 | # Attribute 2047 | die "Type attribute not yet supported \"$value\""; 2048 | } elsif( $type==0x03 ) { 2049 | # String 2050 | $value =~ '^\s*\(string(8|16)\) "(.*)"$' or die "Cannot parse \"$value\""; 2051 | return escape_string($2); 2052 | } elsif( $type==0x04 ) { 2053 | # Float 2054 | return parse_type_4($value); 2055 | } elsif( $type==0x05 ) { 2056 | # Dimension 2057 | return parse_type_5($value); 2058 | } elsif( $type==0x06 ) { 2059 | # Fraction 2060 | return parse_type_6($value); 2061 | } elsif( $type==0x10 ) { 2062 | # Decimal integer 2063 | return signedhex($value); 2064 | } elsif( $type==0x11 ) { 2065 | # Hexadecimal integer 2066 | return sprintf "0x%x", hex($1); 2067 | } elsif( $type==0x12 ) { 2068 | # Boolean 2069 | return parse_type_12($value); 2070 | } elsif( $type==0x1c ) { 2071 | # ARGB8 2072 | return parse_type_1c($value); 2073 | } elsif( $type==0x1d ) { 2074 | # RGB8 2075 | return parse_type_1d($value); 2076 | } elsif( $type==0x1e ) { 2077 | # ARGB4 2078 | return parse_type_1e($value); 2079 | } elsif( $type==0x1f ) { 2080 | # RGB4 2081 | return parse_type_1f($value); 2082 | } else { 2083 | die sprintf("Unknown type %02x with value %s", $type, $value); 2084 | } 2085 | } 2086 | 2087 | sub format_reference 2088 | { 2089 | my ($index)=@_; 2090 | 2091 | die sprintf "Reference 0x%08x is not defined", $index if ! exists $references{$index}; 2092 | 2093 | # Handle NULL first off 2094 | return $references{$index} if $index==0; 2095 | 2096 | $references{$index}=~'^([^:/]+):([^:/]+)/([^:/]+)$' or die "Invalid reference format \"$references{$index}\""; 2097 | my ( $package, $class, $name ) = ( $1, $2, $3 ); 2098 | 2099 | my $ret="$class/$name"; 2100 | 2101 | # Add package name for external references 2102 | if( $package ne $apk_package_name ) { 2103 | $ret="$package:$ret"; 2104 | 2105 | if( ($references_flags{$index} & $REF_INTERNAL)!=0 ) { 2106 | # Add plus for references that need to be generated 2107 | $ret="+".$ret; 2108 | } elsif( ($references_flags{$index} & $REF_PUBLIC)==0 ) { 2109 | # Add star for references to private resources (as if that makes it okay to reference private resources...) 2110 | $ret="*".$ret; 2111 | } 2112 | } 2113 | 2114 | return $ret; 2115 | } 2116 | 2117 | sub format_xml_styleref 2118 | { 2119 | my( $index, $translation )=@_; 2120 | 2121 | exists( $references{$index} ) or 2122 | die sprintf "Attribute points to theme resource 0x%08x which doesn't exist", $index; 2123 | 2124 | $references{$index} =~ '^([^:/]+):([^:/]+)/([^:/]+)$' or 2125 | die "Invalid reference $references{$index}"; 2126 | 2127 | 2128 | my( $package, $class, $name )=($1, $2, $3); 2129 | 2130 | exists $translation->{$package} or 2131 | die "Attribute $references{$index} belongs to undefined namespace"; 2132 | if( $class ne "attr" ) { 2133 | print "Style reference not to attribute $references{$index}\n"; 2134 | return "$references{$index}"; 2135 | } 2136 | 2137 | return "$translation->{$package}:attr/$name"; 2138 | } 2139 | --------------------------------------------------------------------------------