Let's de-obfuscate it.
Indenting:
main(_) {
_^448 && main(-~_);
putchar(--_%64
? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
: 10);
}
Introducing variables to untangle this mess:
main(int i) {
if(i^448)
main(-~i);
if(--i % 64) {
char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
} else {
putchar(10); // newline
}
}
Note that -~i == i+1
because of twos-complement. Therefore, we have
main(int i) {
if(i != 448)
main(i+1);
i--;
if(i % 64 == 0) {
putchar('\n');
} else {
char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
}
}
Now, note that a[b]
is the same as b[a]
, and apply the -~ == 1+
change again:
main(int i) {
if(i != 448)
main(i+1);
i--;
if(i % 64 == 0) {
putchar('\n');
} else {
char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
putchar(32 | (b & 1));
}
}
Converting the recursion to a loop and sneaking in a bit more simplification:
// please don't pass any command-line arguments
main() {
int i;
for(i=447; i>=0; i--) {
if(i % 64 == 0) {
putchar('\n');
} else {
char t = __TIME__[7 - i/8%8];
char a = ">'txiZ^(~z?"[t - 48] + 1;
int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
if((i & 2) == 0)
shift /= 8;
shift = shift % 8;
char b = a >> shift;
putchar(32 | (b & 1));
}
}
}
This outputs one character per iteration. Every 64th character, it outputs a newline. Otherwise, it uses a pair of data tables to figure out what to output, and puts either character 32 (a space) or character 33 (a !
). The first table (">'txiZ^(~z?"
) is a set of 10 bitmaps describing the appearance of each character, and the second table (";;;====~$::199"
) selects the appropriate bit to display from the bitmap.
The second table
Let's start by examining the second table, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
. i/64
is the line number (6 to 0) and i*2&8
is 8 iff i
is 4, 5, 6 or 7 mod 8.
if((i & 2) == 0) shift /= 8; shift = shift % 8
selects either the high octal digit (for i%8
= 0,1,4,5) or the low octal digit (for i%8
= 2,3,6,7) of the table value. The shift table ends up looking like this:
row col val
6 6-7 0
6 4-5 0
6 2-3 5
6 0-1 7
5 6-7 1
5 4-5 7
5 2-3 5
5 0-1 7
4 6-7 1
4 4-5 7
4 2-3 5
4 0-1 7
3 6-7 1
3 4-5 6
3 2-3 5
3 0-1 7
2 6-7 2
2 4-5 7
2 2-3 3
2 0-1 7
1 6-7 2
1 4-5 7
1 2-3 3
1 0-1 7
0 6-7 4
0 4-5 4
0 2-3 3
0 0-1 7
or in tabular form
00005577
11775577
11775577
11665577
22773377
22773377
44443377
Note that the author used the null terminator for the first two table entries (sneaky!).
This is designed after a seven-segment display, with 7
s as blanks. So, the entries in the first table must define the segments that get lit up.
The first table
__TIME__
is a special macro defined by the preprocessor. It expands to a string constant containing the time at which the preprocessor was run, in the form "HH:MM:SS"
. Observe that it contains exactly 8 characters. Note that 0-9 have ASCII values 48 through 57 and :
has ASCII value 58. The output is 64 characters per line, so that leaves 8 characters per character of __TIME__
.
7 - i/8%8
is thus the index of __TIME__
that is presently being output (the 7-
is needed because we are iterating i
downwards). So, t
is the character of __TIME__
being output.
a
ends up equalling the following in binary, depending on the input t
:
0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000
Each number is a bitmap describing the segments that are lit up in our seven-segment display. Since the characters are all 7-bit ASCII, the high bit is always cleared. Thus, 7
in the segment table always prints as a blank. The second table looks like this with the 7
s as blanks:
000055
11 55
11 55
116655
22 33
22 33
444433
So, for example, 4
is 01101010
(bits 1, 3, 5, and 6 set), which prints as
----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--
To show we really understand the code, let's adjust the output a bit with this table:
00
11 55
11 55
66
22 33
22 33
44
This is encoded as "?;;?==? '::799\x07"
. For artistic purposes, we'll add 64 to a few of the characters (since only the low 6 bits are used, this won't affect the output); this gives "?{{?}}?gg::799G"
(note that the 8th character is unused, so we can actually make it whatever we want). Putting our new table in the original code:
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
we get
!! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !! !! !! !! !! !! !!
!! !! !!
just as we expected. It's not as solid-looking as the original, which explains why the author chose to use the table he did.
Best Answer
The article on the official dev site is out of date. Starting with SDK Tools r17 they changed the way the ProGuard configuration works. Basically they split what used to be
proguard.cfg
into two files (and also simply made them .txt files):proguard-android.txt
andproguard-project.txt
.proguard-android.txt
resides in your{SDK dir}\tools\proguard
directory andproguard-project.txt
is in each of your project directories.They split the file so that general configuration/flags that the Android team deems pretty standard will be in the
proguard-android.txt
file and can be updated (basically silently) with each SDK Tools revision. And then any developer/project specific flags, the developer can add to theproguard-project.txt
file.This is why you noticed everything in your
proguard-project.txt
file was commented out, because in general most people don't have any project specific needs, they just want to run ProGuard with standard settings and be done with it.For reference (a very good explanation): http://tools.android.com/recent/proguardimprovements
Step-by-Step (day by day :P)
(Using Eclipse IDE) To enable ProGuard and it's shrinking and obfuscation using only the default settings:
project.properties
uncomment the line that says:If you created the project before r17 then you might not see the same things. If that's the case I suggest you create a new dummy project (after updating SDK Tools) and just copy over the
proguard-project.txt
andproject.properties
files.That's it! ProGuard will now be enabled and it will run, but only on release builds (when you use Android Tools and export a signed/unsigned apk).
When you make a release build you'll notice that a new ProGuard folder will be created in your project folder. Within this folder you find four files with stuff like what classes/methods were kept or removed. Also note that when you obfuscate, you'll also obfuscate your stacktraces in the event of errors.
Edit: (9/4/12) Just wanted to update this answer to reflect changes in Tools r20.
The Android team has now added one additional proguard configuration file. It is
proguard-android-optimize.txt
and it also resides in the{SDK dir}\tools\proguard
directory. This file is the same as the originalproguard-android.txt
config file, except it has proguard optimization turned on.This additional file was added for convenience for those who wanted to turn on optimizations (maybe so they can do stuff like strip out Log calls, which requires optimizations be turned on). Now you can simply point to this file instead in your
project.properties
file like so:As before, don't try to alter any of the
proguard-android...
config files. Any project specific flags should go in theproguard-project.txt
file in your project folder.