2 * misc.c: Miscellaneous helpful functions.
12 void free_cfg(config_item
*cfg
)
16 for (i
= cfg
; i
->type
!= C_END
; i
++)
17 if (i
->type
== C_STRING
)
23 * The Mines (among others) game descriptions contain the location of every
24 * mine, and can therefore be used to cheat.
26 * It would be pointless to attempt to _prevent_ this form of
27 * cheating by encrypting the description, since Mines is
28 * open-source so anyone can find out the encryption key. However,
29 * I think it is worth doing a bit of gentle obfuscation to prevent
30 * _accidental_ spoilers: if you happened to note that the game ID
31 * starts with an F, for example, you might be unable to put the
32 * knowledge of those mines out of your mind while playing. So,
33 * just as discussions of film endings are rot13ed to avoid
34 * spoiling it for people who don't want to be told, we apply a
35 * keyless, reversible, but visually completely obfuscatory masking
36 * function to the mine bitmap.
38 void obfuscate_bitmap(unsigned char *bmp
, int bits
, int decode
)
40 int bytes
, firsthalf
, secondhalf
;
42 unsigned char *seedstart
;
44 unsigned char *targetstart
;
50 * My obfuscation algorithm is similar in concept to the OAEP
51 * encoding used in some forms of RSA. Here's a specification
54 * + We have a `masking function' which constructs a stream of
55 * pseudorandom bytes from a seed of some number of input
58 * + We pad out our input bit stream to a whole number of
59 * bytes by adding up to 7 zero bits on the end. (In fact
60 * the bitmap passed as input to this function will already
61 * have had this done in practice.)
63 * + We divide the _byte_ stream exactly in half, rounding the
64 * half-way position _down_. So an 81-bit input string, for
65 * example, rounds up to 88 bits or 11 bytes, and then
66 * dividing by two gives 5 bytes in the first half and 6 in
69 * + We generate a mask from the second half of the bytes, and
70 * XOR it over the first half.
72 * + We generate a mask from the (encoded) first half of the
73 * bytes, and XOR it over the second half. Any null bits at
74 * the end which were added as padding are cleared back to
75 * zero even if this operation would have made them nonzero.
77 * To de-obfuscate, the steps are precisely the same except
78 * that the final two are reversed.
80 * Finally, our masking function. Given an input seed string of
81 * bytes, the output mask consists of concatenating the SHA-1
82 * hashes of the seed string and successive decimal integers,
86 bytes
= (bits
+ 7) / 8;
87 firsthalf
= bytes
/ 2;
88 secondhalf
= bytes
- firsthalf
;
90 steps
[decode ?
1 : 0].seedstart
= bmp
+ firsthalf
;
91 steps
[decode ?
1 : 0].seedlen
= secondhalf
;
92 steps
[decode ?
1 : 0].targetstart
= bmp
;
93 steps
[decode ?
1 : 0].targetlen
= firsthalf
;
95 steps
[decode ?
0 : 1].seedstart
= bmp
;
96 steps
[decode ?
0 : 1].seedlen
= firsthalf
;
97 steps
[decode ?
0 : 1].targetstart
= bmp
+ firsthalf
;
98 steps
[decode ?
0 : 1].targetlen
= secondhalf
;
100 for (i
= 0; i
< 2; i
++) {
101 SHA_State base
, final
;
102 unsigned char digest
[20];
104 int digestpos
= 20, counter
= 0;
107 SHA_Bytes(&base
, steps
[i
].seedstart
, steps
[i
].seedlen
);
109 for (j
= 0; j
< steps
[i
].targetlen
; j
++) {
110 if (digestpos
>= 20) {
111 sprintf(numberbuf
, "%d", counter
++);
113 SHA_Bytes(&final
, numberbuf
, strlen(numberbuf
));
114 SHA_Final(&final
, digest
);
117 steps
[i
].targetstart
[j
] ^= digest
[digestpos
++];
121 * Mask off the pad bits in the final byte after both steps.
124 bmp
[bits
/ 8] &= 0xFF & (0xFF00 >> (bits
% 8));
128 /* err, yeah, these two pretty much rely on unsigned char being 8 bits.
129 * Platforms where this is not the case probably have bigger problems
130 * than just making these two work, though... */
131 char *bin2hex(const unsigned char *in
, int inlen
)
133 char *ret
= snewn(inlen
*2 + 1, char), *p
= ret
;
136 for (i
= 0; i
< inlen
*2; i
++) {
138 if (i
% 2 == 0) v
>>= 4;
139 *p
++ = "0123456789abcdef"[v
& 0xF];
145 unsigned char *hex2bin(const char *in
, int outlen
)
147 unsigned char *ret
= snewn(outlen
, unsigned char);
150 debug(("hex2bin: in '%s'", in
));
152 memset(ret
, 0, outlen
*sizeof(unsigned char));
153 for (i
= 0; i
< outlen
*2; i
++) {
158 if (c
>= '0' && c
<= '9')
160 else if (c
>= 'a' && c
<= 'f')
162 else if (c
>= 'A' && c
<= 'F')
167 ret
[i
/ 2] |= v
<< (4 * (1 - (i
% 2)));
172 /* vim: set shiftwidth=4 tabstop=8: */