From b8ddff8878642743feab1867238c290af8761079 Mon Sep 17 00:00:00 2001 From: Daniel Skinner Date: Thu, 14 Jan 2016 18:06:09 -0600 Subject: [PATCH] internal/binres: table marshal methods with pack func The OpenTable func now references a prepacked version of the resources.arsc file with a number of entries removed. The current size of this file is 62KB. This could be dropped further by implementing utf8 in string pool during marshal, utf16 encoding exploded the original size by approximately 20%. Another potential improvement is to allow type entries to be packed sparsely which may provide significant savings if not zipping. Change-Id: Ie139c2bdb0e3c5a9212516d18cf627d75774e187 Reviewed-on: https://go-review.googlesource.com/18649 Reviewed-by: David Crawshaw --- internal/binres/binres.go | 8 +- internal/binres/binres_test.go | 134 ++++++++++++- internal/binres/data/packed.arsc.gz | Bin 0 -> 64106 bytes internal/binres/sdk.go | 142 +++++++++++++ internal/binres/table.go | 297 +++++++++++++++++++++++++--- 5 files changed, 546 insertions(+), 35 deletions(-) create mode 100644 internal/binres/data/packed.arsc.gz create mode 100644 internal/binres/sdk.go diff --git a/internal/binres/binres.go b/internal/binres/binres.go index e79f63a..4a9ced9 100644 --- a/internal/binres/binres.go +++ b/internal/binres/binres.go @@ -50,8 +50,6 @@ import ( "encoding/xml" "fmt" "io" - "os" - "path" "unicode" ) @@ -187,11 +185,7 @@ type xnode struct { } func UnmarshalXML(r io.Reader) (*XML, error) { - sdkdir := os.Getenv("ANDROID_HOME") - if sdkdir == "" { - return nil, fmt.Errorf("ANDROID_HOME env var not set") - } - tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar")) + tbl, err := OpenTable() if err != nil { return nil, err } diff --git a/internal/binres/binres_test.go b/internal/binres/binres_test.go index a97c013..ea5d4ab 100644 --- a/internal/binres/binres_test.go +++ b/internal/binres/binres_test.go @@ -12,7 +12,7 @@ import ( "log" "math" "os" - "path" + "path/filepath" "strings" "testing" ) @@ -219,7 +219,7 @@ func TestOpenTable(t *testing.T) { if sdkdir == "" { t.Skip("ANDROID_HOME env var not set") } - tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar")) + tbl, err := OpenTable() if err != nil { t.Fatal(err) } @@ -258,7 +258,7 @@ func TestTableRefByName(t *testing.T) { if sdkdir == "" { t.Skip("ANDROID_HOME env var not set") } - tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar")) + tbl, err := OpenTable() if err != nil { t.Fatal(err) } @@ -276,6 +276,132 @@ func TestTableRefByName(t *testing.T) { } } +func testPackResources(t *testing.T) { + packResources() + f, err := os.Open(filepath.Join("data", "packed.arsc.gz")) + if err != nil { + t.Fatal(err) + } + fi, err := f.Stat() + if err != nil { + t.Fatal(err) + } + t.Logf("packed.arsc.gz %vKB", fi.Size()/1024) +} + +func TestTableMarshal(t *testing.T) { + tbl, err := OpenSDKTable() + if err != nil { + t.Fatal(err) + } + + bin, err := tbl.MarshalBinary() + if err != nil { + t.Fatal(err) + } + + xtbl := new(Table) + if err := xtbl.UnmarshalBinary(bin); err != nil { + t.Fatal(err) + } + + if len(tbl.pool.strings) != len(xtbl.pool.strings) { + t.Fatal("tbl.pool lengths don't match") + } + if len(tbl.pkgs) != len(xtbl.pkgs) { + t.Fatal("tbl.pkgs lengths don't match") + } + + pkg, xpkg := tbl.pkgs[0], xtbl.pkgs[0] + if err := compareStrings(t, pkg.typePool.strings, xpkg.typePool.strings); err != nil { + t.Fatal(err) + } + if err := compareStrings(t, pkg.keyPool.strings, xpkg.keyPool.strings); err != nil { + t.Fatal(err) + } + + if len(pkg.specs) != len(xpkg.specs) { + t.Fatal("pkg.specs lengths don't match") + } + + for i, spec := range pkg.specs { + xspec := xpkg.specs[i] + if spec.id != xspec.id { + t.Fatal("spec.id doesn't match") + } + if spec.entryCount != xspec.entryCount { + t.Fatal("spec.entryCount doesn't match") + } + if len(spec.entries) != len(xspec.entries) { + t.Fatal("spec.entries lengths don't match") + } + for j, mask := range spec.entries { + xmask := xspec.entries[j] + if mask != xmask { + t.Fatal("entry mask doesn't match") + } + } + if len(spec.types) != len(xspec.types) { + t.Fatal("spec.types length don't match") + } + for j, typ := range spec.types { + xtyp := xspec.types[j] + if typ.id != xtyp.id { + t.Fatal("typ.id doesn't match") + } + if typ.entryCount != xtyp.entryCount { + t.Fatal("typ.entryCount doesn't match") + } + if typ.entriesStart != xtyp.entriesStart { + t.Fatal("typ.entriesStart doesn't match") + } + if len(typ.indices) != len(xtyp.indices) { + t.Fatal("typ.indices length don't match") + } + for k, index := range typ.indices { + xindex := xtyp.indices[k] + if index != xindex { + t.Errorf("type index doesn't match at %v, have %v, want %v", k, xindex, index) + } + } + if len(typ.entries) != len(xtyp.entries) { + t.Fatal("typ.entries lengths don't match") + } + for k, nt := range typ.entries { + xnt := xtyp.entries[k] + if nt == nil { + if xnt != nil { + t.Fatal("nt is nil but xnt is not") + } + continue + } + if nt.size != xnt.size { + t.Fatal("entry.size doesn't match") + } + if nt.flags != xnt.flags { + t.Fatal("entry.flags don't match") + } + if nt.key != xnt.key { + t.Fatal("entry.key doesn't match") + } + + if nt.parent != xnt.parent { + t.Fatal("entry.parent doesn't match") + } + if nt.count != xnt.count { + t.Fatal("entry.count doesn't match") + } + for l, val := range nt.values { + xval := xnt.values[l] + if val.name != xval.name { + t.Fatal("value.name doesn't match") + } + } + } + } + } +} + func BenchmarkTableRefByName(b *testing.B) { sdkdir := os.Getenv("ANDROID_HOME") if sdkdir == "" { @@ -285,7 +411,7 @@ func BenchmarkTableRefByName(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - tbl, err := OpenTable(path.Join(sdkdir, "platforms/android-15/android.jar")) + tbl, err := OpenTable() if err != nil { b.Fatal(err) } diff --git a/internal/binres/data/packed.arsc.gz b/internal/binres/data/packed.arsc.gz new file mode 100644 index 0000000000000000000000000000000000000000..01865e0048d46312c2507b8fa20eab32976ac78f GIT binary patch literal 64106 zcmb@ucT`hr^euYMu~S4rDbhS5y?2mykS0ZtqJRhyDFG<~LP;RNQKU(eV(5v|QHu0V zq_;q%hTZ}Ry#)v%BwWsY_s{pn_}w=gBY*7dZ;Z9~`qtcY&h_oW8*}}SKW=zN{c*lR z4TbSM&$&f237TRy5c>Kd$q7953LR-fJ`B5ydnx@!Xv*`a7^k5`W>@c1Lpotj15eJz zue!19(r-GWZu4DixV_@jEn%0Qh^o+ym7n-;-^IU+!9-9iHDm#UCa$Ct^hS1q=g!re z$`zkF523Tb_$*oAP*%D-6J1z-Q|oPa`o9dQ8m9{bp(3Y>M?pVYd}kbGW)l~+e{p3o zAeiDQr1Ri&(a$%!XxoP%;#NRlNahA1Ww&tJA`;hhGo z)7jqzejt&fBpV!>yyS*EW}15#02M?+UidvkK}=nmW1{4wgzT}GB5D7R1g{k1K#oOQ z{K>~k(Dk0=Lz!;E*8J_u<5~8xmCb!X4oT1ZK{wyO=uUG0 zCK5Yw)nGW2)k>qy2f}^K!M)uBW&WnH#anvNbW>+*ds68^*xL+fdR^L^xE4BqxOj7$ zZ;sTw0IH}kCcw&(#6v&?zJx@F2%MzJ9}7iZb6s|dNOOEA@1GjQ6zNisSy%t_FJz_m zzT#)59m0*5>rPoF!p$Af#mgPAb_8fjayx64kM1r_WLkOPVd_b&{KpVGo@ZUStQGl? z{woqP7($6TtPoV`Dwna(9!2I)S*S4%tRqSrTE zR{bK#pPc+>OO1X04U_xz_*(hC1e?|C=s8qTnQ52(G+8+L)tm6Y;>JRF3d1x7PE>~> z)IiFj`b|?k*>ah7oeAM4y}A5k^`V15t6)QS{izH6il0^6IIcpF&{oGn-9V-QZwK-(VUR^x(bw!h=YfguDOrXH=YKg`ofkk!dJHl{kz(eH zZ+xzid_FyzUI(V=<4;l~nb{BvVxam25uU>EoAZlm>H;V6≫I7SFW!3fG-JnW`^3 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ᬙ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

xE zKrYgaiNw-m1!)9KcL4kUhna_GS|oHz`@z764pfM_7PMl$yj z2%k#qg&=g%fG7YnuZMWM91V9QFM2*aW-)<*|>nHlN_Uo#;VZQAcYqx^2sqO=pf*}4m4>) zaZQfVKu<#oZ7BwjLTgG0h@?V~G0&k6*&e&{RGV+Bp7ee}P5Qz^UPR14w}0{C^j8`9 zFT4joXjJ60Ej*6J2Gz4_JkuG=Va5#K>nai*sKn!FE-`q`F|&8V-;^I>kCi}bfw#D9K^+9 zXba|%V%=VW9y7`{thp(uOff= z0)UKM{!70>0YF+RkVSpnfPP>oE&xcJ<;#pyaxfO(iWh#oyL?JkUnrH@q2(*+wUfJZ z`}S;V-?6e-{J-F0r;DNv7YYs~Buwqo=Tn^!KmzRUwP{gA#O>1;u#5k7iE?=R{%lw8 z)DHdKnX%hR-);|E_?`l+?sSgbHzIC#Y20h}9RIbQVsYM6NAyL7bKAsE3ia%^y{R`M zm>H|&kROZL&Q6z$JSPr_z@c(qus}5+y2iZ^?(bfW6>>VJTqLBhJE>jHp<2N+4i0Q0 zh%GLn*u55g#}A~nhsDM>ONhPO@vr=Q#i^{J@>sF$9Qps+IY6@*1MagE4&0XwOL)c- zzWm?23X;no?(JGK=e7gv09aTmJ-W%_@A!=7r%3F-^m0hBLGaQUR$_d1AuIH&3RZ-z$yPoIN0>n12~XP4RjZ8C-a z2ylR{5j!l~h5Gq~&$gN5?YR>h;=q5;OvQIsGmt(jVAyu~{;t*7_>BAFs_}9~3RmN9 z02$A1fj}mGoY?(+`8xobq{k9cZVDU|bSgS0%#P7}+M|uqr!$ z7|7jkGrKQrGZE*ryP#-2xTW-e?%J)$?8_v`OFHtqFDM{9T8O7X~jpIk_)8^`)D}+fr9q z$l>%$bFWlGtnZDP%MPDEv*)sG{lk-cF0As(s!bRE(ELN^SECSxR34`nu{IYoS*;aK zs~z_6TWrNe2l>j?2#g9iVIBs=Jvg^n#tYCbeg+He<7>^v|1@^gQP@BSL4(cSF8Hr%;@mr4#`U)eJUp6{_u zdg34~Y-c8WhX1*UW@3(cY0+B0s;~UjO^ZJ&Lp$dMMH9Rn6fT|9cv;(j=iRJomfDcQ zJ(0E84YQ6S-G1tJ+nqmZVRi~+{?wh5pct==H*3w4$8hK z&+K}>!#3{s`Lvfa{p!9ZH9~+q@cZXpIy?Bh6F(z+aHnnT+jF7+!)QRl2hzC*@AePL z&GU&STAY(EI^OT*d#c9m^NSSvqXXuC0QTjx3VO%*`0u>LnQNbaQ+=86GOZ}Xfo!1P z2Y|}eNZl~9bQg>&FHW5aj~kwM>hy4J6$^cKyEN$cNb~ihOz9eHPw>TNfj=d_!w*~Y zBlJe>2baV+a-)YwNMDzPjgI~TtNj9N|H2>oWzX<6+8b@SD1CGCQnSn-xf)cW$x~)< zWl%BP_|NqkPFtZZ@G4Mff5kngYQ27~#Lc4t;(sz~S}XCd+;?;QMxAb!h8RDec{f(8 z{L^%((@^P+yI@`Fu#tUBA!y=qdZbNZlnJCoMGlK%?i0JWj% zj{sikE7WOWh9T;!w`d3KpFpv-Ptbp;4LB3%w*qo|g_ttTbCT43Pr(%!!4+0y0M;ar zX;hii?;*muI=XOKtYlzIBH|&rIFmg$>Kqg?ju1e)gx`91-9>1F?`^9%IYxEyLM2Ix z98p(E(!laOqV*Tyo~R;sai-O%*$8zoP=K>*wAa(}ZheADDzm-Paxidb^xj4IP1GCs zp+CVjN25D%>C6LJjG3B+X_oBb z_e$*RaVe%#jeHyKd4%hT^j*D2aM4Bcn^0@cj*+KbBpIy9kJF3A!?;Z5R%Ou1+Y2kB zjy5Jq@Z$H>%AnH(erE66zK+Zn0SWN5A2K73w%6MW?{w{EhK;J6z?+oDAWKK9yHVjz z(RMJlOKomey{1R=2SS#oQ*&NK5O=A#b^pxuj_D!Z_&P^>y)}!8t@Inzx?mdaI~?ym z?A;fs+;yIe7+JfEeFTrK>&9&=QfOo}AJB^1 zJh^zUQn`53sB2zmu>L12*OyfGs~Y;&M1(0kTHo$*v03*QUW>UPx`o)!oN*s@=|I?z z_}#UC*jf=oj$u9+Rdm==PBq4_zCnHi6MPW?qv1D)Ujp;oR6}bss5M&TuN3dEvt08o|I{S*+GGcL(BhL2o#i}I z$E=}QxWbPulBz1iKAQF{)Q}Zx-I={p)9W0ZRdmY_yDak&ztP1HOre@@QB#m=)UkS8 zjZ=H!EqXhRua}k#CN2!cwZAp4(l+-+@&T$VvaPx}>4=9BI|Nt4mYV0g`y@E0pD;Z} z5>IU_oi#^`5^AtmcPz>88ET%PCKP=-z=;)fi@T?er;2@F>a_Txw<=LlhN=0nFtAIM zeA3aMs_+{-Kj1d{VQEb3SI;!n7$Laqd=`f!<1VD)Xg&1Zf~}2M0DvD8{w61vqc=(zvY^_xSG8 z1Ua`y3mlZ4^Nc;QC$!D+o5G87M26F7@lCu1XJl6Fxd#WfIhHD!{xqiaKO> zLt#OW&AC+58Z-cC3AqMOh^TnEY1;MEeKd5Lqg=z8j=4N6KsZo391Q#R26#iv6-BlE z5pGovhMb{LV(^=}AlsJdXGy6(6~{jmYpqGwrOIeAyu0xg(y8A{JR)!v(v4bc_cnRu z!>Vt`ZZvdqH!nQblNs08>EK;lWz=XVd!#}%G1-q#ck6`{qV;Dw+%=^N zJFn6kRsa5=dHR%hT>QToW=QuK@_NV98^4-j$i++h7)8e?+l`jRz`Ys6? z1o-~YZ4owlb1l5jkcSGprnzPohssdwXN!Bad;p1mOB`42n|@$;j0>l zHgpBwuiCivWFqkC+mYgKq*Y<1dXAV;w&w>o7_X~%>v@zi*|fQ_e39IGfoWPpZFiot zpWeC}oGrv66ptRM2G3X98I20A1=O@&OT4qG>}8a;W@s`$dgZf`iG7Q;znX_hr;&<% zi%Wi#@11bDy)&j$dZj77XRpDd%|{did|ZuA!NK`DNq)^?!J^%U@pbA{El_~f$US&R z>Njde*t2Xb7IjV8zdBR8GE?gp>v5%%htKcs(yJ7wd5Z3}-%jdu^>46(qjYt9=;%H@cHyQo-2sTBY;?Q}LF#)q9bVuk}OcE}&gNJg_gmZlKwk&T_M!=Z=o}QGtjQ`f~aG0gEmixbo=iAkP6|UPy|07J6o^i8k}iS&;RY381<#-`*y~qaqseru0ZiwKVYOUed=si z#2q|H@$3`N3)%jI2Upxae4M_IsM>g)HNiXed*tVH&lA({Lu2w6S3ghREnP^`i4vc+ zr!$rJGX>X96S&>ifet_SL(|wi*vua(f!Hv`3B-NlefPD znNYqkF{7Xc``lO@RsFdI^-mclW868nd%soavF0fJM2sbjBbZuDw!Y-6~kyK~~7zV}f&soxYzHE5$ob~=uf z0_>Q$cMDn8KBxPB;N*!4J?MykPS6F8#@92a;FI}Y;jQmbRgFtVa$~6h0|Z|Y(#!ES z^(vAkzH>N~6ACkV-#3kTmGNZwDSh;QN~L_-*}C@&2UnQJIR3SVL1yp@q!Rh-{Hu)o z48Qro@aSUP&5fax0p2nRFU`}n1uCGU5As9n3JAxs%+bJibu;}TlcMNLS8 zUQ1KqE(Ty?Bj0}JON~E=-;#X|SP9W7oOTn9pQzR51}ffmi~)8T1DjfpfiqJ%1ZoP0 zQ2c3gTJqP{if=G4>U9Is*kTO4q`k!E9bRGw$*{QJK2_rrXwWgao@5&Lumd%-})iZHT_VL z`jo~;*SGszdvtmv8GGwp0bt{908tPykjM#w;G7sxYZ_E%nbD6SMW++O>bKE&1HF3N zuyEky8X_?8(l)MpbwF(ds67`^e%<;={K~oYL%Ih{t!#lqk)H`QsAaQJm-R!n`%JA= z9*KY5aq5oA<(`hA1ED^SCYj<_lz%2%{T2bR1EX^e_0|FdKAh?KnJyEmFOzDJZ`2Cl zyReO+Ldw)iW{>h~9g{vT>AI577S$i6!wZezH?`XCe>ic|$JB}`?EhlL?BL;;#}>lM zuNeS3{*Qvk)q$#z^6NMyfHM!j;h4vEz*ryfr%bI-*MQ*-w%grM0FI$98o*d}sPb#Q z7NCO!@VQzFu)eu1;fsJw9&GbjC}V0RzI~$9?S8FVMdjBw0Q|4#Q7dMK0H5wR0a&tv z@)6XBNH!q*cdx~z;2D4HzpbSb2iFh6Jq>Uc>J=-x8(kqW8J*Dz(BjKVgkj%w!isLw z?z$D-D;eRY(Be2RTveUUhy+bi^^LwC0h970#RV?#AAak@)D^~n7dPhW(N2=0N#AagDE}V+hGBj6J6i zN}8P{wbYeRACwNlbZj~ugId;~rfMnqa24plL4{J#BZR^oE;mmW4A$x-b$?oCr||w+ zVNB18MBL`al*)pU^q_;_NsUet%Nf921;7EwyETAsl}-`~An1)3H(zR+O8vABdj+&= zcanwy+#Lf8RcFZQQnwgHU*z}EmEz=hxEb@nFj z*9zm@DZ~;qWrb1Y#T^?2h$gt4M0gzTBn1KRj{yEW(lIgs=FeW-7K99eguT|c&UWgS z1LN-)OjE1Xf!x7uE|&@mP#Bjj@pO!~CEnW=hBg545`ehv#Z{1=rfL9GtDi#P+|n^vV0cCL z)#odW+HJtorx06a>CdKNojs`Fp0a~~QkU3F4H_e&*M8${8IW8*c+*YN)d3eWCYzmKCV5!?|q_g~vKYz6LcPCvzax&8+Uk16KJo@DJ-I(d9uSdBE_N2hK5{t9)=P_!vHym?Pn-={EDW>&7Ga5$0iP^ct$w^CB`od z?gq--4a{8#dOKV#Ial8>m^O`;n3INO5?rjH&@9IA)c1J&&QcMFju&k%)yg@fyu1hN zV&5+hz-k7WU%S@2`yPanYlEtm1j<;yVNdp#u_pJIGOfUzoAXUgO!%eM?(RpMHM6wg zWM(M1weQA!!=iRYNjj8f%)B@$Q%aN9pLwF`yqMJi?$0GJ)I5y{4~KpXp}T4LXaBZ* zefQMQDd3++kQm>=zH@QU29;B|(7VTOpp(~y=UPX5|G z`o49tf+iVJ|Cl~o8#=J?-rHzeW`^ZJ8hWv#@95>E3ZMLv$5hc8nfpd{RpX~QOv^%3 zSL5g_5hQ}Y_jJMP4~^q+Sp||aEi>I8&BFtwbNX_9`DB)()lJfypt5V1{F`i zQ4R-Mx=%tmjg6oQw@&hS{WK!5&`L+!9$cRkMA#hLg+`baA_DE9f#CXF?RrcpwR@2| zpFsO(wc7r-K2BrfY$WhTEu3w6aU4MSk4xXP6Jw)4F?H!POuYn^aa%Pvx8j!!lbtB5K~) zvtt*6WABonQj2VzWS7hGS~S!Prdw|Z@R;XD-+J2A29q(juZa>NJCzz{`qvH zW?yn?-C>C9=ee%+50eXT7Zb@o*OS8Z&gLj(!oQ<4;a2v9ipZ=+aGK$88Z+`;nq_ps zmSe!e;n<)NrOoEyNLiY9{fuD{Z4+RebTcxN6=S_W@@p)M}!IbXTFHvcLpC zOen#q=#az8gejr&OVq{8X=c+xmaq}74D@F-G|cnuj~}67YCE_~Es=DE@e~j(h7(lR ztUdjsBEZ3Ovtc5}Q*ASx+&v+41a5n^6sAmIB;3)tLV!6=+tIr&(wRHr;c0E`nQ2S_@U3WNuh81s%n7%(;o8H zhI0k&*~+Hx9avkG1{77%uR9)q8)qKqvC? zQkmjwBiEV?qlMwCKmOtULq25n223cy3aU5Z8MC~$&4Sy%3_X>w(tZ3fzedGi?(`9@ zKtfvfNT%B7h$9V()SX$#t4F(v%le)z`W~w*9+*6aVvlA1jGcrabv~A0@9stI4yp|y zTkAv^PO^qMh>Xun-JjvV_)SfMjIN}klqwaK5uh^P&M8r5^ z_T%2016{f*F^*sf+{D#Zo95Xb48qo-rGNM`c8oEaygFUGE-OVc?>5;RU`sz~68%$c=>cxQ^5B1TnLi=mp^jvdgV3$!@bym@-T)$l^i8$Dp4I|DhL7hE5t z`C69%V9vmBpwX|-05IE{H`QBd6ZdDcN_Av^WTdNz)nYalYxNeh1 zX3&DH9bah{TsCukP~vOt0t|b6Ag50zVD9`t&V=UZ*Jg<8bBTaCMF2)+^lRfQO*jc) z#}63!L8Y&CJ%IQwz%1_Q=vRQi0)QVMKnRA|__|fyfgCO1Oa{Q82E=vam9I4vs8$0b zIMuxAzTkQX7B$)lEd4I{QiltthV) z|2A5`eo318ebY#`B+syUq;IkGTZ9dLrf<`pIcIRRV>qs44^AnjcuKxaPpEL0^VURS z(C*0-AA@!g_$T+`F5Fizc3RfNjTtcBPtC=butTVAC7e}-hM%tRM#h9r%>47dpU-e( zFd8}L!325v1a!%b<-c7hY!_~r!sfFvMp3va*wn)*_7s1-^pw$V?H!ZH*Tt64w?}TQ zx>fyrG}X*MIn!Ox-6|i0or-Q_3E_rwjdG)v*AeY$F^!>(EKbN~*a1!!w5mgVJ%s9= z8~ZdqH(q&P{^}g;T8DVN@&WE+`$@-B+)DdN>(ksl>pJbIcs(J+UT*6Y0U(pVI|eB1 zfm1~achfZIS&mbd7uG|d6{xPkDFQ!u+Ho>5bp86WbiCt~0EkCzvrk>7w%ZFKcDA@J zr%jdc<98!0mb<5X_^~@mc4K#g)z!QoT?WOG+ z{3Y$!JqT&;hpGBDtPn!J<pL*(S)_Lzn2nH4MN>Yk4A)>iv>h6Y z-`~<+vMI9{Ip;{T2#+eZZ&e=piEWc>hjLb1Asv(JA+&-3`dqY6B7SXV!jIEeLfd(8 zOKHsJ{(&*KW$`KaZr+~2!|Q6xxOhFzstK|KoKP>c^|^#Py_YAv<+ywRQ;0d)vfn?2 zi)3Vr61&)u7#z|+>V>`SS0k=qgVi!-%4BztAno`FeZfGnqw7cQhnpxUdqMxtS}AMI z(2X+8xR6*HYrWfLO#`Zvl-B-@pVOwho71k#pS!-6|M=&+sH($?q9$cIFlKV{cDy7;bkJC`;^3_yxfsHD>qrm$b{p<^pI zC@J+xM6Aj5;tRD250Hmor{6O*4;4x5sp-XBH4j%wtm5<{CK8+*HPl`2^X>%L2(xHWmNwF=^;-M)`CAc^i*i=c7h$6w<63VvH$EJ0dl;9~eC(q_LSoAYI2C{qD1NWyO$FE8aBRMbq=JUC*em3g))k zd8mu)M~_kYSV3KxF(#Nz_FkzK!3$8hb@Pb7)IeLvJF&w-wLt=>XyJnfa_aF^y;bXMh zihpaWGi-rJTckhfLZ~Gyeyma7&|hQn)?O&i%M!z7mFEff{d;xJdMK56bGxoP(YzbM z_pMqu;Mdb@Uu3Xvl83Jlj1H`%&Wg|zhTrR{|7kGxR7zCDsP*`JU(3L15E&1RJyl84 zjEB#RIU5BuF(>Zg5%PReRBM?6K0fh%R)w82R{|FYTwS(=&Ir` z6KO_9^`ZwsU-P1rK9LXoY8=e(I&-gea(N_!T34t_*YT7&?B6i_9<0*RxTf~;j4E&N z%RMAg3(QLU1MT977cA}lR7y*AvHZl)nQ-WI)L7575-9NraE>A&Frt%NppEu$~AtWWZ;P0LRs183@KTby@z zArboz;hz3r9@p}5WJH+-Xu>pG-D!P~LF+(tov z%0F)PhT}304441rig$;G=>=yly&X)Kyh~vftKU<*`n<|3|0eEo=*=I-NdXz%p(YM4 zZhC+E7UEVA1dJ&ZZ%}+`srsUN@1s|69n#NZNPD)D@U%umGGS%EpU4Z`U8Nbv_w**B z_GW9qf){>uxUSsV?Po5qM|Kk}=8sWZuZ7kwFKYQc)nLGFv$sy;r$b!_Sb-t%!hi*O zKF6gl{1J3QySh9NS`ckOCLdD}%gvt7M!;x&brtl|AZKnVZmur87CNy|U7mt~1(E71 zf&<94Q+-Hp_|wN*%Xl>6pZNry#l`|R6Tg|6Ik1*I+&kSn%?NPSJLyj_<<#kZ)`l|! z7A%(0uUeZ^PQVA{8z)pMevSXhWl0BUENzzy2kP^$wIh<;rT?`?owJBH6}Fp_ENoe zbSQMlC>0?|=4c=%MTae1-B|huJ}`#*6xB|6K_haZBx)1(Vi)$?rBLj*p0$gb9<0gM z>Q(v;?9fG?2G)Wi>c#Uyx7QWnB(DU!g=*0+SJB?g`N0(U-SO z25%9Mz4`Yuk9@R9Yp{-#sw_Eh8ubD3u8!0<7*U_ zR8;a*Ey!qOb7MXfQlQyyVZ;^$i0{?u(DCXHMe@r|nIzQ)#cfg{LHnBj(bhEc@^x1!V( zFIeI6#qfa}e|T7%*3dw$g4&BUJhsy+9=u$-;FmNUH$1xGo^l6qvDN2mk9I9xpJC03 z&S$?#RPsSw{EW2y1U(Amf&IcC{__o#Ij8gdB`FHUQAle2zRqq(OpYzYZ|+=lz4@Z? zz4hm@A=5w6-%Ga!is3aqINkl6?>GLCcX~}8Oo98UGs|m7>iIt5bfq}ot$^kh(3Z9>08Wd=I?zvA4n?=g$B$&`n_uYxf*%Y z>KA|xj`P@MPYy0e@aqig;PKFjQQZU^&{=bc>4n_ievVbPEW|ZcTZQn+Le}XwRwv*q_!lC9 zLV?Gl!1s09Ik~gAj^&+GuKe}8?DvlyG`3xiJKB_?J0@UkeP6=ZaoKE2E?(TZ;DF5M zq=p8~hJ#o?b%q_;9gbQ&G_2rZRDu2A2WL;9%QlLg6$an2AHh5bh1&kvK^&>Yr1H)v zdcg+0U`Bo7<_x3sH{9dh4BnP#se13Mg1K=6(43V$)P$ z<;I}%qzd?Pgb`(bO@Uuhf^R9Su+}?Y#lUpw)7pE}L1U4y$203AE|r1y$*d?t>P4{Q zYtq%nIE_JNk5?G-lR3=fqgBhSSsz)7k;T2J{K%l@g@wL}*7Eu6W{-$=!y!hLQ^b9x zJdUaNXO2@Rbo^?%3?B>k5E8}y%EBdko;deXXea5ZT|n{oSd7y_q*SF-{pCq_by*#$HOaj zwAIFd?upNDKaYD$4PzLWe)Z?LL*0oxOkEzslQ+&+FDWWjQPzxnhY6BbZF^B%ll*N* zM)Zxw4$YA>Soug)Vg`E)on5KWdp$i3=$cBnUIR;hT6=GE*Ru?2-SVNl?Iv=ZuYKR&FV%S!zYCwU z{4K@phezWw^~DnlKK$zHZ(rt=4|m@%FFa^^Tx|YazT&Mp@8*Knf_ zZ42I+Lk}g^r-k(ntGhkN_x44448L@k>GtVR*`J5I6ZRjdo;thXamfgC*YKX>!#UjH z9J}G^`j5LKD%L1wZF_$~@Lsp~!mq8g&Re=|X=Lz0w^;`|=U11{)Vr~x#`nw_h&}h# zejw$R@ob@L{qxZIZu2Xy-Yva9?nbn)IqVqUc!o4%&b9Zga~-!?p9}uXZKlgL>w;Xb zZ7*sTvU1LRIAhNFxA}kfv_AgVZHvzRbFg>8@jEwe*X%ezEQ#X>2fNKaeQlogt-tE@ z-(Qk-`2}HXW7iD$=>I>(r+<4&Kf-NJ#kEBRaZd_bwi4!a7(V@N-a|3= z|D2fQGgzw_s8z^sDQs^luH8_msukT|DGpRAiYpXnzf^p2UH!OL`=m2qXJ>Xq=R^O_ zskNN}t2-OyS@E(g_LZ#UvMgb7R90{+hk^k3}_!H!9WU*>kQsSfBWA+w5~88{C$-Ty-uu@qF9dM>mA#>h#{A znQrq=U;UjuMHS`6dGGz9FEiW80!#C)zPJk-@{gy>%k9W$-8N%E2+3_x_*FOS|9Ao8 zX6e^K0r3kg4D`L@@jTRmn)d=MiYn9;3uhkkqTG^}Aukdwiarkf(t=4yH8e|V42LEx z7TN!C+?_6PvgoQ&KT}Jv0q%|y%~Y`(`Ppm2Hw><=dGegq&Mys_zdD~MpNz(;qXk9ljE8Mx{Xb>w;gz4yld|@ioDeDMgc2kw zauY*w%oyMwx#Z{mPx2iNu0mI#i;$#(-}ESZhUo;gJmF8_`qQ{ z_dF=)IpcFVr>LiSYw<1EpD-qPi1c0U6PwGU+m~i>6$NFhn}fQ9C>av&KSViAbhc z_L2-}Wr0AG$ArT)BGK@hVY z>CuYPA>wDJ>BBczLOCY5%%itcHcrpN=hyQw>3*RpB``c~OJdAeFu#=Njj!^WiW}MK zHPv_rpVRWX_2yu-If$<=@WbbL^)4O>Z|B29)+g?wNOz{+TwWRIROc+%%5Qm1$Kx;- z*!x;{U&`M1?fp`19N3PH>@PFUm9ORc$QS!F$Ab9MzI@{ft|y*~&Bj(^(O7`i=+qRY z4odPrn)0WvhN8X_BaQ2Z&%&02FDe_I>c|x5`h-TIn9eCQw^mLkAI)vZHO|MauqqEz zTS4YxrR?G4Le6e%u8rW}Fp+!xJbVKy%2e8L%$T2&?I)J)pS%wY&;XsJQA%~vCd=|%v=fG8kyBUg;ugOi}9;ECj zpLnPHF7af7OP7NtvNj?ySe@Tc*mk7(p}<{v52+HRNYfpzS>vT1g=R`vq$Z|4xRHKx5B2+-bzf^^(U)kIxK-i=c7k@aPNZ@$huBA^ zJqy!&LX;^15uhFsidDXYv+y;XE621MOkAXL5|Rjh1EKbNALUV>cXT!S$aFnT3ln;6 zzev@&b(w^|qwad!eZ#ig^;{3`8r%mviuq!v!3(Ajd;dFItOYzqi$%o}ic2!GELzYT ztv^s{M2gGag)6=*Ao_3NOL;Ec?Fsrga1xC#GZj&vy(NGYL?x|rF$a~0r9BX8QJ?@L z5Mt`cFj0>i{ki9=>F(Z|tz*Fo6lPUYy%}ucF2+}I3Uho|K2xmnqzM`I3iS%F=AZ`I zh|s0nEB|91x9at1C|L|(L=O^U%asS`xE`L>frJ}{8L72 z?kuj{-<0Z2XO2bibE)OV^>`xZoRNn6b0W6s^&ke=L{BmJVqO65f*ryhR|e9y>K9MrTe8gKZ^kh&7nnTAJ~Syn0hE7W7gxm+)<4?dq$YFvynIoCtIo5E>DMqk_u zV#`u2pi&{#_>4E+(i)<3$aOyBSB#2k9Uvg-;;xc+>KJ`itOg~PzKb!Pp-PQk4{4B9 z*vA+dO!uakIyy43WH_EqriYtOG@NalvRD6*cgQ4xWl&5#Zd{J@u`WPBHKLRwPaDqm zoi@7rN5VxmWX3>$<`cik}naE?*IH19uLEDMJyfN z&75{NZ!w)S2FQcS%3kEx#GtsqTO-kGsMO)1DL3`Y%_-lYq&s=MncY$4EDy&z*Lc#p zrkO2qsFnCU8j8}e&+o{LOXa7G8|ACzEgzKDNLj9WMQP?zxhwt|mIm@63McOI?s6nD zYnh5m33(7yJh*|}OisQCLQS63;4EXQ&r^j7ssgf{#to3q+=)B7rER?^vK9A(AAq%d zzJJsbQK8_(PmSnaPAum_-vy(C+#ip{Xkhb8pZg952VImsD=k8i*FfaDa|L&r^0c#% zE66Rx3Ax4ON(kNq5ow{{2s_rm4cZyrX8aRaD@nixNkz-h(dg;d)$+z_83={u$n+eS z@>b&OI1JA8b?;0^VyHG}pKdnx3RuB{NO#R7bq!behmA#!McOmRwj>6P#l%RnMP_GW zL|V>WZ8-=A*O4x1wpa#FSRFRTOnbUpzahzt=CqllLYO_YCzoZQUx{-sXm9(|QL>03 zVL7W@4K7WQV+?g9?QDa9s3c2SBm-F!WFM5c#s1Tablf__DD0%3>C2=3_?pf=15>J6 zJ1J#tY&CCEevn~AtIGOWw39q?iGHa%6KO``gk3yW`7C@rCI|J<1S(R~7FuybKCir* zwO84UR!JD5bXTD_tnWOG0LwO$^>mGk!9@s{PqJt73#hpb$EigPQ)f$C#o3ZpDFv>^ zlo+Ns13B2&)v_EPb_neRZ>@i1c~Fdq;?xwbxcqvwszkq4{S8>jU5Q6xi?BGDr8O{1 zm!a;g=e)&S2YgqCLP{3PoLR1dP{qZ*pZ6=fq(Uc6km3ZjTsCo%+Ip7= zA*TwxFKr^Loc#R5;q!l&E<3a$3!}Lbor54mAx%gYB|Qnq$`bnKWWY)!WHpM1q~Mxro5Fb(L=G!dvpW77UI`{mPe+iJ~5opCMOtgR@Z@oYBm^b z!z20X%XPr!*9~#so6nV$5`CfZjz1e+heV<4nC3A4>4x4_rEH&3B1C)~!~uWM#mo-p zHy@^O?+vl?4#A+4YMj~8e3&^#U+(e}{Zq9D(Or7CAzJ^iQb1}-?qOiNVENOPVI;vP zFBpl*J$_ggb{~R9zN*70_O}N@bRV)Rp34tW6upu`3>9cU6squ&)0m5^@2yb-SO{q2}4bp6{-i%&HsorJ8{<;%w$Xll$DJn^Wi7=>%Gk( zwIQa$glKh$hYKV#GjYFmfmo24YWD4#Fu&?C(f2gGj3cfewRx3_8dEvxck-yEuU*KH z#l%AvQ-;Pt{az$Qk3~#~8RBVB4VGf{;1EbhI?;QoX9?H0jG12C6lWVVYs9(i# zy2>|jUF7q*>->Y%f^W5rC|O!2sdMpjqDQBV{gi{yMY}}?`)gb_HXjrLDKb?uE;f+q zTWH4`j`y8w7~HD7#hi8|ZiUbjswi9I+{B=rN#H58C(%3!fkbd>uR{Z_^j$PM${YQO zNS?$X56ZBDHE%DhzjjKKG(hYp36PF3e26i$T;6PaJI4l_2JdqNBp$zTFEs3}NLhxeX++pJMqy4F>N2TZQLK!YYr%nq`F1@yAltLpGIcZzu)@-sYp`2y~@ z-E?doEX9n{{$QHyHJKgcL1Lht$aUzq>!=U14<=!&B-2UgR1EPXMNILYUXDAqvqco& z@zcEd+)S(j);sd02_bJ4mx2v|EU+#*JNTm5R#fNg=gbaIgG*-hU80KnPVpA|2Pt0M zHA6yG2~dy(*^3hGDw~k-X36Jp7eI&QLx+_iQ{r^rX*>V49w(T+p4<=drI>d+UsNTv z5yQZo&z*}WW6>E6ln$P)JdnFWPT~4;-{UUf+H-3U5`Tpe7uCkrK}V$?ZG}D2I`nP* zxGUr?8$kdwr)h^iq@6B;++`NzE_RT+n1d8ucdd#)RS0>W+z;?Y(3b~63oJ4OQ9W?c z^1l6(emjT4k?=l%gp!Aaf&(BAWs8U#rkiIgg{9bade1&^WRc)9^+H42YXYPd=c1dE zbeW*44bLpm4ph=fbf!IvpW7fZuIDcIKk(>0&vklqB(WCv&&_o3d!`Z}WjWGSZyqBv zb5)K_e|wXMP$V)^Qy5=p^ujZ+1?}p@Yxw#!&2dn;NXOu}faG$rgK+O;igr>GUxK+d z#i+yh&3DZklr_w0austbhTs0S4%~11#WnMlUeJ!DKO9#1KTscK|9NtCZn|sJz8=*& zpQeIh{7i{|B?jT@- zQIL+N);=@Z$?14@1{K+fT?a(O7NM6;4DV9XOD4`Wl<+p-$?ay5fFuagW)Kq1&dS{o z+IOSPXz`m#m5bg#K=&aGFFF!0GL+rK{j`1}V*n&_wvet60RgPlT+l8j5S6bMqOU>M}2KTSB< zCrpr1#SOOjN1V0z297&6my?Rw$9i{>bP=^FFIZ|w9~-RjU08Wi4g@H+WTeEH-jN{V zS<7eQA)HXG5o`hX_S5qE$JJlTBb0r5zfZpFroCpSwN5f2?NTD`Fy>hj14$5`7F?TI zug!t&P81e|@%NYey)c2iRs0#cqs_5DL*p<`jRqQ3LI%RA}#QAiVRSEqwzh)l|I?kc$^r@#Zyu^m_hyp%zfY9LtI zs5+>}`i@hdu1Uw%f)dEE3c=sOEkFX(>FvA^x$ooNHVnj1!Xnc{AqUzhW7$Uf_Urd* zd9n&TI+h0N6GmIR!TDAECES^K0%i;J(K(lH;k}eG^ay^^tVAG-~ zYPj4s=+USVlOKLp=j2ybhi7rv*y7rEzNe|;SAM^CHv2CU@T7K`vv(`H2fcr(Ke#-y zoKc=MOp)8x4Vs)Y$@dek%B|#n+y_Gh$d7bRD$i*Ww{nOu_LOJa-8z!DGihoSt&CdQ zHn^iPQ!*&gxN2fN9>nUY@WKTT+r~LTErb^=*5FI9-4NOYTeTT?)h`zMXu|D76lJ_+ zxIIil-q?$&#W<~<_OgO29wI`TL~*3JYC`#c>kwc|?-D`htvv$T88o$CwkI5E39A3bEIq>-H6X`7f58@(*#{6{lG-t^eE?!+{Ewsf)*I6 z9!XTHO7&MRI|^E+0#stvzDYgkhA_GgqM4tR?KL=+Q_6 zSDIFLC$MjN^{OW8q$wWHjdSt8z*cS^rw6vYeket%@q!&*S%YD^v`f?sGLe2nS2~ev zw8xiYyRa456ev3HBlwkbGs>eAS&3U9X1SE8sPCZf~rem|DiU47rkwB-mmq zGWyG3EmgANwS;sBcC_g^q;&bf2h>GRC*HtQu$hpvREhlH>bYG>Q$|m@2fpu)EWxQ7 zwm#XAm~2GHVfU=zYh$e*1ot*@j)-l60@6IPl(nPgR(9nxLva<&PU1>x-K zt`5I9huC)?tv3(pF0Cl~Zu^b}QG3^}|Lv|a{9!V#gYX{;amC-a<{`W>yEe{gEO*tuQC+*7Ly#QtJQt88zqL&4_}l z)%>Cbu${ohLbFOA@j?!xewvD^5z+CZj`*~Pa#oxSW|`u_K>IC+Jk@$9|-YY!_M zxQAh(!S%n#{>us&!DB3Y8PR%-Y5jW>{AR2HVnUz6^~Li4cMq0%O5@4&O3iWmdf`EH eb&lQlx_e9B?0R8dIAey{j6~eY{@HQPjQ;^`&i`ou literal 0 HcmV?d00001 diff --git a/internal/binres/sdk.go b/internal/binres/sdk.go new file mode 100644 index 0000000..7570539 --- /dev/null +++ b/internal/binres/sdk.go @@ -0,0 +1,142 @@ +package binres + +import ( + "archive/zip" + "bytes" + "compress/gzip" + "fmt" + "io" + "os" + "path" + "path/filepath" +) + +func apiJarPath() (string, error) { + sdkdir := os.Getenv("ANDROID_HOME") + if sdkdir == "" { + return "", fmt.Errorf("ANDROID_HOME env var not set") + } + return path.Join(sdkdir, "platforms/android-15/android.jar"), nil +} + +func apiResources() ([]byte, error) { + p, err := apiJarPath() + if err != nil { + return nil, err + } + zr, err := zip.OpenReader(p) + if err != nil { + return nil, err + } + defer zr.Close() + + buf := new(bytes.Buffer) + for _, f := range zr.File { + if f.Name == "resources.arsc" { + rc, err := f.Open() + if err != nil { + return nil, err + } + _, err = io.Copy(buf, rc) + if err != nil { + return nil, err + } + rc.Close() + break + } + } + if buf.Len() == 0 { + return nil, fmt.Errorf("failed to read resources.arsc") + } + return buf.Bytes(), nil +} + +// packResources produces a stripped down version of the resources.arsc from api jar. +func packResources() error { + tbl, err := OpenSDKTable() + if err != nil { + return err + } + + tbl.pool.strings = []string{} // should not be needed + pkg := tbl.pkgs[0] + + // drop language string entries + for _, typ := range pkg.specs[3].types { + if typ.config.locale.language != 0 { + for j, nt := range typ.entries { + if nt == nil { // NoEntry + continue + } + pkg.keyPool.strings[nt.key] = "" + typ.indices[j] = NoEntry + typ.entries[j] = nil + } + } + } + + // drop strings from pool for specs to be dropped + for _, spec := range pkg.specs[4:] { + for _, typ := range spec.types { + for _, nt := range typ.entries { + if nt == nil { // NoEntry + continue + } + // don't drop if there's a collision + var collision bool + for _, xspec := range pkg.specs[:4] { + for _, xtyp := range xspec.types { + for _, xnt := range xtyp.entries { + if xnt == nil { + continue + } + if collision = nt.key == xnt.key; collision { + break + } + } + } + } + if !collision { + pkg.keyPool.strings[nt.key] = "" + } + } + } + } + + // entries are densely packed but probably safe to drop nil entries off the end + for _, spec := range pkg.specs[:4] { + for _, typ := range spec.types { + var last int + for i, nt := range typ.entries { + if nt != nil { + last = i + } + } + typ.entries = typ.entries[:last+1] + typ.indices = typ.indices[:last+1] + } + } + + // keeping 0:attr, 1:id, 2:style, 3:string + pkg.typePool.strings = pkg.typePool.strings[:4] + pkg.specs = pkg.specs[:4] + + bin, err := tbl.MarshalBinary() + if err != nil { + return err + } + + f, err := os.Create(filepath.Join("data", "packed.arsc.gz")) + if err != nil { + return err + } + defer f.Close() + + zw := gzip.NewWriter(f) + defer zw.Close() + + if _, err := zw.Write(bin); err != nil { + return err + } + return nil +} diff --git a/internal/binres/table.go b/internal/binres/table.go index 005c2a9..280757f 100644 --- a/internal/binres/table.go +++ b/internal/binres/table.go @@ -5,11 +5,13 @@ package binres import ( - "archive/zip" "bytes" + "compress/gzip" "errors" "fmt" "io" + "os" + "path/filepath" "strings" "unicode/utf16" ) @@ -56,33 +58,36 @@ type Table struct { pkgs []*Package } -// OpenTable decodes resources.arsc from an sdk platform jar. -func OpenTable(path string) (*Table, error) { - zr, err := zip.OpenReader(path) +// OpenSDKTable decodes resources.arsc from sdk platform jar. +func OpenSDKTable() (*Table, error) { + bin, err := apiResources() + if err != nil { + return nil, err + } + tbl := new(Table) + if err := tbl.UnmarshalBinary(bin); err != nil { + return nil, err + } + return tbl, nil +} + +// OpenTable decodes the prepacked resources.arsc for the supported sdk platform. +func OpenTable() (*Table, error) { + f, err := os.Open(filepath.Join("data", "packed.arsc.gz")) + if err != nil { + return nil, err + } + defer f.Close() + zr, err := gzip.NewReader(f) if err != nil { return nil, err } defer zr.Close() - buf := new(bytes.Buffer) - for _, f := range zr.File { - if f.Name == "resources.arsc" { - rc, err := f.Open() - if err != nil { - return nil, err - } - _, err = io.Copy(buf, rc) - if err != nil { - return nil, err - } - rc.Close() - break - } + var buf bytes.Buffer + if _, err := io.Copy(&buf, zr); err != nil { + return nil, err } - if buf.Len() == 0 { - return nil, fmt.Errorf("failed to read resources.arsc") - } - tbl := new(Table) if err := tbl.UnmarshalBinary(buf.Bytes()); err != nil { return nil, err @@ -167,6 +172,38 @@ func (tbl *Table) UnmarshalBinary(bin []byte) error { return nil } +func (tbl *Table) MarshalBinary() ([]byte, error) { + bin := make([]byte, 12) + putu16(bin, uint16(ResTable)) + putu16(bin[2:], 12) + putu32(bin[8:], uint32(len(tbl.pkgs))) + + if tbl.pool.IsUTF8() { + tbl.pool.flags ^= UTF8Flag + defer func() { + tbl.pool.flags |= UTF8Flag + }() + } + + b, err := tbl.pool.MarshalBinary() + if err != nil { + return nil, err + } + bin = append(bin, b...) + + for _, pkg := range tbl.pkgs { + b, err = pkg.MarshalBinary() + if err != nil { + return nil, err + } + bin = append(bin, b...) + } + + putu32(bin[4:], uint32(len(bin))) + + return bin, nil +} + // Package contains a collection of resource data types. type Package struct { chunkHeader @@ -257,6 +294,62 @@ func (pkg *Package) UnmarshalBinary(bin []byte) error { return nil } +func (pkg *Package) MarshalBinary() ([]byte, error) { + bin := make([]byte, 284) + putu16(bin, uint16(ResTablePackage)) + putu16(bin[2:], 284) + + putu32(bin[8:], pkg.id) + p := utf16.Encode([]rune(pkg.name)) + for i, x := range p { + putu16(bin[12+i*2:], x) + } + + if pkg.typePool != nil { + if pkg.typePool.IsUTF8() { + pkg.typePool.flags ^= UTF8Flag + defer func() { + pkg.typePool.flags |= UTF8Flag + }() + } + + b, err := pkg.typePool.MarshalBinary() + if err != nil { + return nil, err + } + putu32(bin[268:], uint32(len(bin))) + putu32(bin[272:], pkg.lastPublicType) + bin = append(bin, b...) + } + + if pkg.keyPool != nil { + if pkg.keyPool.IsUTF8() { + pkg.keyPool.flags ^= UTF8Flag + defer func() { + pkg.keyPool.flags |= UTF8Flag + }() + } + b, err := pkg.keyPool.MarshalBinary() + if err != nil { + return nil, err + } + putu32(bin[276:], uint32(len(bin))) + putu32(bin[280:], pkg.lastPublicKey) + bin = append(bin, b...) + } + + for _, spec := range pkg.specs { + b, err := spec.MarshalBinary() + if err != nil { + return nil, err + } + bin = append(bin, b...) + } + + putu32(bin[4:], uint32(len(bin))) + return bin, nil +} + // TypeSpec provides a specification for the resources defined by a particular type. type TypeSpec struct { chunkHeader @@ -289,6 +382,31 @@ func (spec *TypeSpec) UnmarshalBinary(bin []byte) error { return nil } +func (spec *TypeSpec) MarshalBinary() ([]byte, error) { + bin := make([]byte, 16+len(spec.entries)*4) + putu16(bin, uint16(ResTableTypeSpec)) + putu16(bin[2:], 16) + putu32(bin[4:], uint32(len(bin))) + + bin[8] = byte(spec.id) + // [9] = 0 + // [10:12] = 0 + putu32(bin[12:], uint32(len(spec.entries))) + for i, x := range spec.entries { + putu32(bin[16+i*4:], x) + } + + for _, typ := range spec.types { + b, err := typ.MarshalBinary() + if err != nil { + return nil, err + } + bin = append(bin, b...) + } + + return bin, nil +} + // Type provides a collection of entries for a specific device configuration. type Type struct { chunkHeader @@ -298,8 +416,46 @@ type Type struct { entryCount uint32 // number of uint32 entry configuration masks that follow entriesStart uint32 // offset from header where Entry data starts - // TODO implement and decode Config if strictly necessary - // config Config // configuration this collection of entries is designed for (portrait, sw600dp, etc) + // configuration this collection of entries is designed for + config struct { + size uint32 + imsi struct { + mcc uint16 // mobile country code + mnc uint16 // mobile network code + } + locale struct { + language uint16 + country uint16 + } + screenType struct { + orientation uint8 + touchscreen uint8 + density uint16 + } + input struct { + keyboard uint8 + navigation uint8 + inputFlags uint8 + inputPad0 uint8 + } + screenSize struct { + width uint16 + height uint16 + } + version struct { + sdk uint16 + minor uint16 // always 0 + } + screenConfig struct { + layout uint8 + uiMode uint8 + smallestWidthDP uint16 + } + screenSizeDP struct { + width uint16 + height uint16 + } + } indices []uint32 // values that map to typePool entries []*Entry @@ -323,6 +479,30 @@ func (typ *Type) UnmarshalBinary(bin []byte) error { return fmt.Errorf("res0 res1 not zero") } + typ.config.size = btou32(bin[20:]) + typ.config.imsi.mcc = btou16(bin[24:]) + typ.config.imsi.mnc = btou16(bin[26:]) + typ.config.locale.language = btou16(bin[28:]) + typ.config.locale.country = btou16(bin[30:]) + typ.config.screenType.orientation = uint8(bin[32]) + typ.config.screenType.touchscreen = uint8(bin[33]) + typ.config.screenType.density = btou16(bin[34:]) + typ.config.input.keyboard = uint8(bin[36]) + typ.config.input.navigation = uint8(bin[37]) + typ.config.input.inputFlags = uint8(bin[38]) + typ.config.input.inputPad0 = uint8(bin[39]) + typ.config.screenSize.width = btou16(bin[40:]) + typ.config.screenSize.height = btou16(bin[42:]) + typ.config.version.sdk = btou16(bin[44:]) + typ.config.version.minor = btou16(bin[46:]) + typ.config.screenConfig.layout = uint8(bin[48]) + typ.config.screenConfig.uiMode = uint8(bin[49]) + typ.config.screenConfig.smallestWidthDP = btou16(bin[50:]) + typ.config.screenSizeDP.width = btou16(bin[52:]) + typ.config.screenSizeDP.height = btou16(bin[54:]) + + // fmt.Println("language/country:", u16tos(typ.config.locale.language), u16tos(typ.config.locale.country)) + buf := bin[typ.headerByteSize:typ.entriesStart] for len(buf) > 0 { typ.indices = append(typ.indices, btou32(buf)) @@ -348,6 +528,36 @@ func (typ *Type) UnmarshalBinary(bin []byte) error { return nil } +func (typ *Type) MarshalBinary() ([]byte, error) { + bin := make([]byte, 56+len(typ.entries)*4) + putu16(bin, uint16(ResTableType)) + putu16(bin[2:], 56) + + bin[8] = byte(typ.id) + // [9] = 0 + // [10:12] = 0 + putu32(bin[12:], uint32(len(typ.entries))) + putu32(bin[16:], uint32(56+len(typ.entries)*4)) + + var ntbin []byte + for i, nt := range typ.entries { + if nt == nil { // NoEntry + putu32(bin[56+i*4:], NoEntry) + continue + } + putu32(bin[56+i*4:], uint32(len(ntbin))) + b, err := nt.MarshalBinary() + if err != nil { + return nil, err + } + ntbin = append(ntbin, b...) + } + bin = append(bin, ntbin...) + + putu32(bin[4:], uint32(len(bin))) + return bin, nil +} + // Entry is a resource key typically followed by a value or resource map. type Entry struct { size uint16 @@ -389,6 +599,34 @@ func (nt *Entry) UnmarshalBinary(bin []byte) error { return nil } +func (nt *Entry) MarshalBinary() ([]byte, error) { + bin := make([]byte, 8) + putu16(bin, nt.size) + putu16(bin[2:], nt.flags) + putu32(bin[4:], uint32(nt.key)) + + if nt.size == 16 { + bin = append(bin, make([]byte, 8+len(nt.values)*12)...) + putu32(bin[8:], uint32(nt.parent)) + putu32(bin[12:], uint32(len(nt.values))) + for i, val := range nt.values { + b, err := val.MarshalBinary() + if err != nil { + return nil, err + } + copy(bin[16+i*12:], b) + } + } else { + b, err := nt.values[0].data.MarshalBinary() + if err != nil { + return nil, err + } + bin = append(bin, b...) + } + + return bin, nil +} + type Value struct { name TableRef data *Data @@ -400,6 +638,17 @@ func (val *Value) UnmarshalBinary(bin []byte) error { return val.data.UnmarshalBinary(bin[4:]) } +func (val *Value) MarshalBinary() ([]byte, error) { + bin := make([]byte, 12) + putu32(bin, uint32(val.name)) + b, err := val.data.MarshalBinary() + if err != nil { + return nil, err + } + copy(bin[4:], b) + return bin, nil +} + type DataType uint8 // explicitly defined for clarity and resolvability with apt source