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 memset(ret
, 0, outlen
*sizeof(unsigned char));
151 for (i
= 0; i
< outlen
*2; i
++) {
156 if (c
>= '0' && c
<= '9')
158 else if (c
>= 'a' && c
<= 'f')
160 else if (c
>= 'A' && c
<= 'F')
165 ret
[i
/ 2] |= v
<< (4 * (1 - (i
% 2)));
170 void game_mkhighlight_specific(frontend
*fe
, float *ret
,
171 int background
, int highlight
, int lowlight
)
177 * Drop the background colour so that the highlight is
178 * noticeably brighter than it while still being under 1.
180 max
= ret
[background
*3];
181 for (i
= 1; i
< 3; i
++)
182 if (ret
[background
*3+i
] > max
)
183 max
= ret
[background
*3+i
];
184 if (max
* 1.2F
> 1.0F
) {
185 for (i
= 0; i
< 3; i
++)
186 ret
[background
*3+i
] /= (max
* 1.2F
);
189 for (i
= 0; i
< 3; i
++) {
191 ret
[highlight
* 3 + i
] = ret
[background
* 3 + i
] * 1.2F
;
193 ret
[lowlight
* 3 + i
] = ret
[background
* 3 + i
] * 0.8F
;
197 void game_mkhighlight(frontend
*fe
, float *ret
,
198 int background
, int highlight
, int lowlight
)
200 frontend_default_colour(fe
, &ret
[background
* 3]);
201 game_mkhighlight_specific(fe
, ret
, background
, highlight
, lowlight
);
204 static void memswap(void *av
, void *bv
, int size
)
207 char *a
= av
, *b
= bv
;
210 int thislen
= min(size
, sizeof(tmpbuf
));
211 memcpy(tmpbuf
, a
, thislen
);
212 memcpy(a
, b
, thislen
);
213 memcpy(b
, tmpbuf
, thislen
);
220 void shuffle(void *array
, int nelts
, int eltsize
, random_state
*rs
)
222 char *carray
= (char *)array
;
225 for (i
= nelts
; i
-- > 1 ;) {
226 int j
= random_upto(rs
, i
+1);
228 memswap(carray
+ eltsize
* i
, carray
+ eltsize
* j
, eltsize
);
232 void draw_rect_outline(drawing
*dr
, int x
, int y
, int w
, int h
, int colour
)
234 int x0
= x
, x1
= x
+w
-1, y0
= y
, y1
= y
+h
-1;
246 draw_polygon(dr
, coords
, 4, -1, colour
);
249 void draw_rect_corners(drawing
*dr
, int cx
, int cy
, int r
, int col
)
251 draw_line(dr
, cx
- r
, cy
- r
, cx
- r
, cy
- r
/2, col
);
252 draw_line(dr
, cx
- r
, cy
- r
, cx
- r
/2, cy
- r
, col
);
253 draw_line(dr
, cx
- r
, cy
+ r
, cx
- r
, cy
+ r
/2, col
);
254 draw_line(dr
, cx
- r
, cy
+ r
, cx
- r
/2, cy
+ r
, col
);
255 draw_line(dr
, cx
+ r
, cy
- r
, cx
+ r
, cy
- r
/2, col
);
256 draw_line(dr
, cx
+ r
, cy
- r
, cx
+ r
/2, cy
- r
, col
);
257 draw_line(dr
, cx
+ r
, cy
+ r
, cx
+ r
, cy
+ r
/2, col
);
258 draw_line(dr
, cx
+ r
, cy
+ r
, cx
+ r
/2, cy
+ r
, col
);
261 void move_cursor(int button
, int *x
, int *y
, int maxw
, int maxh
, int wrap
)
265 case CURSOR_UP
: dy
= -1; break;
266 case CURSOR_DOWN
: dy
= 1; break;
267 case CURSOR_RIGHT
: dx
= 1; break;
268 case CURSOR_LEFT
: dx
= -1; break;
272 *x
= (*x
+ dx
+ maxw
) % maxw
;
273 *y
= (*y
+ dy
+ maxh
) % maxh
;
275 *x
= min(max(*x
+dx
, 0), maxw
- 1);
276 *y
= min(max(*y
+dy
, 0), maxh
- 1);
280 /* Used in netslide.c and sixteen.c for cursor movement around edge. */
282 int c2pos(int w
, int h
, int cx
, int cy
)
285 return cx
; /* top row, 0 .. w-1 (->) */
287 return w
+ cy
; /* R col, w .. w+h -1 (v) */
289 return w
+ h
+ (w
-cx
-1); /* bottom row, w+h .. w+h+w-1 (<-) */
291 return w
+ h
+ w
+ (h
-cy
-1); /* L col, w+h+w .. w+h+w+h-1 (^) */
293 assert(!"invalid cursor pos!");
294 return -1; /* not reached */
297 int c2diff(int w
, int h
, int cx
, int cy
, int button
)
301 assert(IS_CURSOR_MOVE(button
));
303 /* Obvious moves around edge. */
305 diff
= (button
== CURSOR_RIGHT
) ? +1 : (button
== CURSOR_LEFT
) ? -1 : diff
;
307 diff
= (button
== CURSOR_RIGHT
) ? -1 : (button
== CURSOR_LEFT
) ? +1 : diff
;
309 diff
= (button
== CURSOR_UP
) ? +1 : (button
== CURSOR_DOWN
) ? -1 : diff
;
311 diff
= (button
== CURSOR_UP
) ? -1 : (button
== CURSOR_DOWN
) ? +1 : diff
;
313 if (button
== CURSOR_LEFT
&& cx
== w
&& (cy
== 0 || cy
== h
-1))
314 diff
= (cy
== 0) ? -1 : +1;
315 if (button
== CURSOR_RIGHT
&& cx
== -1 && (cy
== 0 || cy
== h
-1))
316 diff
= (cy
== 0) ? +1 : -1;
317 if (button
== CURSOR_DOWN
&& cy
== -1 && (cx
== 0 || cx
== w
-1))
318 diff
= (cx
== 0) ? -1 : +1;
319 if (button
== CURSOR_UP
&& cy
== h
&& (cx
== 0 || cx
== w
-1))
320 diff
= (cx
== 0) ? +1 : -1;
322 debug(("cx,cy = %d,%d; w%d h%d, diff = %d", cx
, cy
, w
, h
, diff
));
326 void pos2c(int w
, int h
, int pos
, int *cx
, int *cy
)
330 pos
= (pos
+ max
) % max
;
333 *cx
= pos
; *cy
= -1; return;
337 *cx
= w
; *cy
= pos
; return;
341 *cx
= w
-pos
-1; *cy
= h
; return;
345 *cx
= -1; *cy
= h
-pos
-1; return;
347 assert(!"invalid pos, huh?"); /* limited by % above! */
350 void draw_text_outline(drawing
*dr
, int x
, int y
, int fonttype
,
351 int fontsize
, int align
,
352 int text_colour
, int outline_colour
, char *text
)
354 if (outline_colour
> -1) {
355 draw_text(dr
, x
-1, y
, fonttype
, fontsize
, align
, outline_colour
, text
);
356 draw_text(dr
, x
+1, y
, fonttype
, fontsize
, align
, outline_colour
, text
);
357 draw_text(dr
, x
, y
-1, fonttype
, fontsize
, align
, outline_colour
, text
);
358 draw_text(dr
, x
, y
+1, fonttype
, fontsize
, align
, outline_colour
, text
);
360 draw_text(dr
, x
, y
, fonttype
, fontsize
, align
, text_colour
, text
);
363 /* vim: set shiftwidth=4 tabstop=8: */