Sonic CD shows Sega logo
[picodrive.git] / platform / uiq3 / engine / vid.cpp
1 // EmuScan routines for Pico, also simple text and shape drawing routines.\r
2 \r
3 // (c) Copyright 2006, notaz\r
4 // All Rights Reserved\r
5 \r
6 #include "vid.h"\r
7 #include "../Engine.h"\r
8 #include "../../../pico/picoInt.h"\r
9 #include "blit.h"\r
10 #include "debug.h"\r
11 \r
12 \r
13 // global stuff\r
14 extern TPicoConfig *currentConfig;\r
15 extern TPicoAreaConfigEntry areaConfig[];\r
16 extern const char *actionNames[];\r
17 \r
18 // main framebuffer\r
19 static void *screenbuff = 0; // pointer to real device video memory\r
20 //static\r
21 extern "C" { unsigned char *framebuff = 0; }  // temporary buffer\r
22 const int framebuffsize  = (8+320)*(8+240+8)*2+8*2; // actual framebuffer size (in bytes+to support new rendering mode)\r
23 \r
24 // drawer function pointers\r
25 static void (*drawTextFps)(const char *text) = 0;\r
26 static void (*drawTextNotice)(const char *text) = 0;\r
27 \r
28 // blitter\r
29 static void (*vidBlit)(int full) = 0;\r
30 \r
31 // colors\r
32 const unsigned short color_red     = 0x022F;\r
33 const unsigned short color_red_dim = 0x0004;\r
34 const unsigned short color_green   = 0x01F1;\r
35 const unsigned short color_blue    = 0x0F11;\r
36 const unsigned short color_grey    = 0x0222;\r
37 \r
38 // other\r
39 int txtheight_fit = 138;\r
40 \r
41 // bitmasks\r
42 static const unsigned long mask_numbers[] = {\r
43         0x12244800, // 47 2F /\r
44         0x69999600, // 48 30 0\r
45         0x26222200, // 49 31 1\r
46         0x69168F00, // 50 32 2\r
47         0x69219600, // 51 33 3\r
48         0x266AF200, // 52 34 4\r
49         0xF8E11E00, // 53 35 5\r
50         0x68E99600, // 54 36 6\r
51         0x71222200, // 55 37 7\r
52         0x69699600, // 56 38 8\r
53         0x69719600, // 57 39 9\r
54         0x04004000, // 58 3A :\r
55         0x04004400, // 59 3B ;\r
56         0x01242100, // 60 3C <\r
57         0x00707000, // 61 3D =\r
58         0x04212400, // 62 3E >\r
59         0x69240400, // 63 3F ?\r
60         0x00000000, // 64 40 @ [used instead of space for now]\r
61         0x22579900, // 65 41 A\r
62         0xE9E99E00, // 66 42 B\r
63         0x69889600, // 67 43 C\r
64         0xE9999E00, // 68 44 D\r
65         0xF8E88F00, // 69 45 E\r
66         0xF8E88800, // 70 46 F\r
67         0x698B9700, // 71 47 G\r
68         0x99F99900, // 72 48 H\r
69         0x44444400, // 73 49 I\r
70         0x11119600, // 74 4A J\r
71         0x9ACCA900, // 75 4B K\r
72         0x88888F00, // 76 4C L\r
73         0x9F999900, // 77 4D M\r
74         0x9DDBB900, // 78 4E N\r
75         0x69999600, // 79 4F O\r
76         0xE99E8800, // 80 50 P\r
77         0x6999A500, // 81 51 Q\r
78         0xE99E9900, // 82 52 R\r
79         0x69429600, // 83 53 S\r
80         0x72222200, // 84 54 T\r
81         0x99999600, // 85 55 U\r
82         0x55552200, // 86 56 V\r
83         0x9999F900, // 87 57 W\r
84         0x55225500, // 88 58 X\r
85         0x55222200, // 89 59 Y\r
86         0xF1248F00, // 90 5A Z\r
87 };\r
88 \r
89 \r
90 ////////////////////////////////\r
91 // Cram functions\r
92 \r
93 static int EmuCramNull(int cram)\r
94 {\r
95         User::Panic(_L("Cram called!!"), 0);\r
96         return cram;\r
97 }\r
98 \r
99 \r
100 ////////////////////////////////\r
101 // PicoScan functions\r
102 \r
103 static int EmuScan8(unsigned int num, void *sdata)\r
104 {\r
105         DrawLineDest = framebuff + 328*(num+1) + 328*8 + 8;\r
106 \r
107         return 0;\r
108 }\r
109 \r
110 \r
111 static int EmuScanFit0(unsigned int num, void *sdata)\r
112 {\r
113         // 0.75, 168 lines\r
114 \r
115         static int u = 0, num2 = 0;\r
116         if(!num) u = num2 = 0;\r
117 \r
118         DrawLineDest = framebuff + 328*(++num2) + 328*8 + 8;\r
119 \r
120         u += 6666;\r
121 \r
122         if(u < 10000) {\r
123 //              u += 7500;\r
124                 return 1;\r
125         }\r
126 \r
127         u -= 10000;\r
128 \r
129         return 0;\r
130 }\r
131 \r
132 \r
133 ////////////////////////////////\r
134 // text drawers\r
135 // warning: text must be at least 1px away from screen borders\r
136 \r
137 static void drawTextM2(int x, int y, const char *text)\r
138 {\r
139         unsigned char *vidmem = framebuff + 328*8 + 8;\r
140         int charmask, i, cx = x, cy;\r
141         unsigned char *l, *le;\r
142 \r
143         // darken the background (left border)\r
144         for(l=vidmem+(cx-1)+(y-1)*328, le=l+8*328; l < le; l+=328) *l = 0xE0;\r
145 \r
146         for(const char *p=text; *p; p++) {\r
147                 cy = y;\r
148                 charmask = *(mask_numbers + (*p - 0x2F));\r
149 \r
150                 for(l = vidmem+cx+(y-1)*328, le = l+8*328; l < le; l+=328-4) {\r
151                         *l = 0xE0; l++; *l = 0xE0; l++;\r
152                         *l = 0xE0; l++; *l = 0xE0; l++;\r
153                         *l = 0xE0;\r
154                 }\r
155 \r
156                 for(i=0; i < 24; i++) {\r
157                         if(charmask&0x80000000) *( vidmem + (cx+(i&3)) + (cy+(i>>2))*328 ) = 0xf0;\r
158                         charmask <<= 1;\r
159                 }\r
160                 cx += 5;\r
161         }\r
162 }\r
163 \r
164 \r
165 static void drawTextM2Fat(int x, int y, const char *text)\r
166 {\r
167         unsigned char *vidmem = framebuff + 328*8 + 8;\r
168         int charmask, i, cx = x&~1, cy;\r
169         unsigned short *l, *le;\r
170 \r
171         // darken the background (left border)\r
172         for(l=(unsigned short *)(vidmem+(cx-2)+(y-1)*328), le=l+8*328/2; l < le; l+=328/2) *l = 0xE0;\r
173 \r
174         for(const char *p=text; *p; p++) {\r
175                 cy = y;\r
176                 for(l = (unsigned short *)(vidmem+cx+(y-1)*328), le = l+8*328/2; l < le; l+=328/2) {\r
177                         l += 4;\r
178                         *l-- = 0xe0e0; *l-- = 0xe0e0; *l-- = 0xe0e0; *l-- = 0xe0e0; *l = 0xe0e0; \r
179                 }\r
180 \r
181                 charmask = *(mask_numbers + (*p - 0x2F));\r
182 \r
183                 for(i=0; i < 24; i++) {\r
184                         if(charmask&0x80000000) *(unsigned short *)( vidmem + cx+(i&3)*2 + (cy+(i>>2))*328 ) = 0xf0f0;\r
185                         charmask <<= 1;\r
186                 }\r
187                 cx += 5*2;\r
188         }\r
189 }\r
190 \r
191 \r
192 static void drawTextFpsCenter0(const char *text)\r
193 {\r
194         if(!text) return;\r
195         drawTextM2(214, 216, text);\r
196 }\r
197 \r
198 static void drawTextFpsFit0(const char *text)\r
199 {\r
200         if(!text) return;\r
201         drawTextM2Fat((Pico.video.reg[12]&1) ? 256-32 : 224-32, 160, text);\r
202 }\r
203 \r
204 static void drawTextFpsFit2_0(const char *text)\r
205 {\r
206         if(!text) return;\r
207         drawTextM2Fat((Pico.video.reg[12]&1) ? 256-32 : 224-32, 216, text);\r
208 }\r
209 \r
210 static void drawTextFps0(const char *text)\r
211 {\r
212         if(!text) return;\r
213         drawTextM2((Pico.video.reg[12]&1) ? 256 : 224, 216, text);\r
214 }\r
215 \r
216 static void drawTextNoticeCenter0(const char *text)\r
217 {\r
218         if(!text) return;\r
219         drawTextM2(2, 216, text);\r
220 }\r
221 \r
222 static void drawTextNoticeFit0(const char *text)\r
223 {\r
224         if(!text) return;\r
225         drawTextM2Fat(2, 160, text);\r
226 }\r
227 \r
228 static void drawTextNoticeFit2_0(const char *text)\r
229 {\r
230         if(!text) return;\r
231         drawTextM2Fat(2, 216, text);\r
232 }\r
233 \r
234 static void drawTextNotice0(const char *text)\r
235 {\r
236         if(!text) return;\r
237         drawTextM2(2, 216, text);\r
238 }\r
239 \r
240 \r
241 // -----------------------------------------------------------------\r
242 \r
243 static int localPal[0x100];\r
244 \r
245 static void fillLocalPal(void)\r
246 {\r
247         Pico.m.dirtyPal = 0;\r
248 \r
249         if (PicoOpt&0x10) {\r
250                 // 8bit fast renderer\r
251                 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
252                 return;\r
253         }\r
254 \r
255         // 8bit accurate renderer\r
256         if(Pico.video.reg[0xC]&8) { // shadow/hilight mode\r
257                 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
258                 vidConvCpyRGB32sh(localPal+0x40, Pico.cram, 0x40);\r
259                 vidConvCpyRGB32hi(localPal+0x80, Pico.cram, 0x40);\r
260                 blockcpy(localPal+0xc0, localPal+0x40, 0x40*4);\r
261                 localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
262                 localPal[0xf0] = 0x00ee0000;\r
263         } else if (rendstatus & 0x20) { // mid-frame palette changes\r
264                 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
265                 vidConvCpyRGB32(localPal+0x40, HighPal, 0x40);\r
266                 vidConvCpyRGB32(localPal+0x80, HighPal+0x40, 0x40);\r
267         } else {\r
268                 vidConvCpyRGB32(localPal, Pico.cram, 0x40);\r
269         }\r
270 }\r
271 \r
272 \r
273 // note: the internal 8 pixel border is taken care by asm code\r
274 static void vidBlit_90(int full)\r
275 {\r
276         unsigned char *ps = framebuff+328*8;\r
277         unsigned long *pd = (unsigned long *) screenbuff;\r
278 \r
279         if (Pico.m.dirtyPal) fillLocalPal();\r
280 \r
281         if(Pico.video.reg[12]&1)\r
282                 vidConvCpy_90(pd, ps, localPal, 320/8);\r
283         else {\r
284                 if(full) vidClear(pd, 32);\r
285                 pd += 256*32;\r
286                 vidConvCpy_90(pd, ps, localPal, 256/8);\r
287                 if(full) vidClear(pd + 256*256, 32);\r
288         }\r
289 }\r
290 \r
291 \r
292 static void vidBlit_270(int full)\r
293 {\r
294         unsigned char *ps = framebuff+328*8;\r
295         unsigned long *pd = (unsigned long *) screenbuff;\r
296 \r
297         if (Pico.m.dirtyPal) fillLocalPal();\r
298 \r
299         if(Pico.video.reg[12]&1)\r
300                 vidConvCpy_270(pd, ps, localPal, 320/8);\r
301         else {\r
302                 if(full) vidClear(pd, 32);\r
303                 pd += 256*32;\r
304                 ps -= 64;     // the blitter starts copying from the right border, so we need to adjust\r
305                 vidConvCpy_270(pd, ps, localPal, 256/8);\r
306                 if(full) vidClear(pd + 256*256, 32);\r
307         }\r
308 }\r
309 \r
310 \r
311 static void vidBlitCenter_0(int full)\r
312 {\r
313         unsigned char *ps = framebuff+328*8+8;\r
314         unsigned long *pd = (unsigned long *) screenbuff;\r
315 \r
316         if (Pico.m.dirtyPal) fillLocalPal();\r
317 \r
318         if(Pico.video.reg[12]&1) ps += 32;\r
319         vidConvCpy_center_0(pd, ps, localPal);\r
320         if(full) vidClear(pd + 224*256, 96);\r
321 }\r
322 \r
323 \r
324 static void vidBlitCenter_180(int full)\r
325 {\r
326         unsigned char *ps = framebuff+328*8+8;\r
327         unsigned long *pd = (unsigned long *) screenbuff;\r
328 \r
329         if (Pico.m.dirtyPal) fillLocalPal();\r
330 \r
331         if(Pico.video.reg[12]&1) ps += 32;\r
332         vidConvCpy_center_180(pd, ps, localPal);\r
333         if(full) vidClear(pd + 224*256, 96);\r
334 }\r
335 \r
336 \r
337 static void vidBlitFit_0(int full)\r
338 {\r
339         if (Pico.m.dirtyPal) fillLocalPal();\r
340 \r
341         if(Pico.video.reg[12]&1)\r
342                  vidConvCpy_center2_40c_0(screenbuff, framebuff+328*8, localPal, 168);\r
343         else vidConvCpy_center2_32c_0(screenbuff, framebuff+328*8, localPal, 168);\r
344         if(full) vidClear((unsigned long *)screenbuff + 168*256, 320-168);\r
345 }\r
346 \r
347 \r
348 static void vidBlitFit_180(int full)\r
349 {\r
350         if (Pico.m.dirtyPal) fillLocalPal();\r
351 \r
352         if(Pico.video.reg[12]&1)\r
353              vidConvCpy_center2_40c_180(screenbuff, framebuff+328*8, localPal, 168);\r
354         else vidConvCpy_center2_32c_180(screenbuff, framebuff+328*8-64, localPal, 168);\r
355         if(full) vidClear((unsigned long *)screenbuff + 168*256, 320-168);\r
356 }\r
357 \r
358 \r
359 static void vidBlitFit2_0(int full)\r
360 {\r
361         if (Pico.m.dirtyPal) fillLocalPal();\r
362 \r
363         if(Pico.video.reg[12]&1)\r
364              vidConvCpy_center2_40c_0(screenbuff, framebuff+328*8, localPal, 224);\r
365         else vidConvCpy_center2_32c_0(screenbuff, framebuff+328*8, localPal, 224);\r
366         if(full) vidClear((unsigned long *)screenbuff + 224*256, 96);\r
367 }\r
368 \r
369 \r
370 static void vidBlitFit2_180(int full)\r
371 {\r
372         if (Pico.m.dirtyPal) fillLocalPal();\r
373 \r
374         if(Pico.video.reg[12]&1)\r
375              vidConvCpy_center2_40c_180(screenbuff, framebuff+328*8, localPal, 224);\r
376         else vidConvCpy_center2_32c_180(screenbuff, framebuff+328*8-64, localPal, 224);\r
377         if(full) vidClear((unsigned long *)screenbuff + 224*256, 96);\r
378 }\r
379 \r
380 \r
381 static void vidBlitCfg(void)\r
382 {\r
383         unsigned short *ps = (unsigned short *) framebuff;\r
384         unsigned long *pd = (unsigned long *) screenbuff;\r
385         int i;\r
386 \r
387         // hangs randomly (due to repeated ldms/stms?)\r
388         //for (int i = 1; i < 320; i++, ps += 240, pd += 256)\r
389         //      vidConvCpyRGB32(pd, ps, 240);\r
390 \r
391         for (i = 0; i < 320; i++, pd += 16)\r
392                 for (int u = 0; u < 240; u++, ps++, pd++)\r
393                         *pd = ((*ps & 0xf) << 20) | ((*ps & 0xf0) << 8) | ((*ps & 0xf00) >> 4);\r
394 }\r
395 \r
396 \r
397 ////////////////////////////////\r
398 // main functions\r
399 \r
400 int vidInit(void *vidmem, int reinit)\r
401 {\r
402         if(!reinit) {\r
403                 // prepare framebuffer\r
404                 screenbuff = vidmem;\r
405                 framebuff = (unsigned char *) malloc(framebuffsize);\r
406 \r
407                 if(!screenbuff) return KErrNotSupported;\r
408                 if(!framebuff)  return KErrNoMemory;\r
409 \r
410                 memset(framebuff, 0, framebuffsize);\r
411 \r
412                 // Cram function: go and hack Pico so it never gets called\r
413                 PicoCram = EmuCramNull;\r
414         }\r
415 \r
416         // select suitable blitters\r
417         vidBlit = vidBlit_270;\r
418         PicoScan = EmuScan8;\r
419         drawTextFps = drawTextFps0;\r
420         drawTextNotice = drawTextNotice0;\r
421 \r
422         memset(localPal, 0, 0x100*4);\r
423         localPal[0xe0] = 0x00000000; // reserved pixels for OSD\r
424         localPal[0xf0] = 0x00ee0000;\r
425 \r
426         // setup all orientation related stuff\r
427         if(currentConfig->iScreenRotation == TPicoConfig::PRot0) {\r
428                 if(currentConfig->iScreenMode == TPicoConfig::PMCenter) {\r
429                         vidBlit = vidBlitCenter_0;\r
430                         drawTextFps = drawTextFpsCenter0;\r
431                         drawTextNotice = drawTextNoticeCenter0;\r
432                 } else if(currentConfig->iScreenMode == TPicoConfig::PMFit2) {\r
433                         vidBlit = vidBlitFit2_0;\r
434                         drawTextFps = drawTextFpsFit2_0;\r
435                         drawTextNotice = drawTextNoticeFit2_0;\r
436                 } else {\r
437                         vidBlit = vidBlitFit_0;\r
438                         drawTextFps = drawTextFpsFit0;\r
439                         drawTextNotice = drawTextNoticeFit0;\r
440                         PicoScan = EmuScanFit0;\r
441                 }\r
442         } else if(currentConfig->iScreenRotation == TPicoConfig::PRot90) {\r
443                 vidBlit = vidBlit_90;\r
444         } else if(currentConfig->iScreenRotation == TPicoConfig::PRot180) {\r
445                 if(currentConfig->iScreenMode == TPicoConfig::PMCenter) {\r
446                         vidBlit = vidBlitCenter_180;\r
447                         drawTextFps = drawTextFpsCenter0;\r
448                         drawTextNotice = drawTextNoticeCenter0;\r
449                 } else if(currentConfig->iScreenMode == TPicoConfig::PMFit2) {\r
450                         vidBlit = vidBlitFit2_180;\r
451                         drawTextFps = drawTextFpsFit2_0;\r
452                         drawTextNotice = drawTextNoticeFit2_0;\r
453                 } else {\r
454                         vidBlit = vidBlitFit_180;\r
455                         drawTextFps = drawTextFpsFit0;\r
456                         drawTextNotice = drawTextNoticeFit0;\r
457                         PicoScan = EmuScanFit0;\r
458                 }\r
459         } else if(currentConfig->iScreenRotation == TPicoConfig::PRot270) {\r
460                 vidBlit = vidBlit_270;\r
461         }\r
462 \r
463         vidBlit(1);\r
464         PicoOpt |= 0x100;\r
465         Pico.m.dirtyPal = 1;\r
466 \r
467         return 0;\r
468 }\r
469 \r
470 void vidFree()\r
471 {\r
472         free(framebuff);\r
473         framebuff = 0;\r
474 }\r
475 \r
476 void vidDrawFrame(char *noticeStr, char *fpsStr, int num)\r
477 {\r
478         DrawLineDest = framebuff + 328*8 + 8;\r
479 \r
480 //      PicoFrame(); // moved to main loop\r
481         if(currentConfig->iFlags & 2)\r
482                 drawTextFps(fpsStr);\r
483         drawTextNotice(noticeStr);\r
484 \r
485         vidBlit(!num); // copy full frame once a second\r
486 }\r
487 \r
488 // -----------------------------------------------------------------\r
489 \r
490 static void drawText0(int x, int y, const char *text, long color)\r
491 {\r
492         unsigned short *vidmem=(unsigned short *)framebuff;\r
493         int charmask, i, cx = x, cy;\r
494         unsigned short *l, *le, dmask=0x0333;\r
495 \r
496         // darken the background (left border)\r
497         for(l=vidmem+(cx-1)+(y-1)*240, le=vidmem+(cx-1)+(y+7)*240; l < le; l+=240)\r
498                 *l = (*l >> 2) & dmask;\r
499 \r
500         for(const char *p=text; *p; p++) {\r
501                 cy = y;\r
502                 charmask = *(mask_numbers + (*p - 0x2F));\r
503 \r
504                 for(l = vidmem+cx+(y-1)*240, le = vidmem+cx+(y+7)*240; l < le; l+=240-4) {\r
505                         *l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;\r
506                         *l = (*l >> 2) & dmask; l++; *l = (*l >> 2) & dmask; l++;\r
507                         *l = (*l >> 2) & dmask;\r
508                 }\r
509 \r
510                 for(i=0; i < 24; i++) {\r
511                         // draw dot. Is this fast?\r
512                         if(charmask&0x80000000) *( vidmem + (cx+(i&3)) + (cy+(i>>2))*240 ) = color;\r
513                         charmask <<= 1;\r
514                 }\r
515                 cx += 5;\r
516         }\r
517 }\r
518 \r
519 // draws rect with width - 1 and height - 1\r
520 static void drawRect(const TRect &rc, unsigned short color)\r
521 {\r
522         unsigned short *vidmem=(unsigned short *)framebuff;\r
523 \r
524         if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) {\r
525                 int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1;\r
526                 int stepY = rc.iTl.iY < rc.iBr.iY ? 1 : -1;\r
527                 \r
528                 for(int x = rc.iTl.iX;; x += stepX) {\r
529                         *(vidmem + rc.iTl.iY*240 + x) = *(vidmem + (rc.iBr.iY - stepY)*240 + x) = color;\r
530                         if(x == rc.iBr.iX - stepX) break;\r
531                 }\r
532                 \r
533                 for(int y = rc.iTl.iY;; y += stepY) {\r
534                         *(vidmem + y*240 + rc.iTl.iX) = *(vidmem + y*240 + rc.iBr.iX - stepX) = color;\r
535                         if(y == rc.iBr.iY - stepY) break;\r
536                 }\r
537         }\r
538 }\r
539 \r
540 // draws fullsize filled rect\r
541 static void drawRectFilled(const TRect rc, unsigned short color)\r
542 {\r
543         unsigned short *vidmem=(unsigned short *)framebuff;\r
544 \r
545         if(rc.iTl.iX - rc.iBr.iX && rc.iTl.iY - rc.iBr.iY) {\r
546                 int stepX = rc.iTl.iX < rc.iBr.iX ? 1 : -1;\r
547                 int stepY = rc.iTl.iY < rc.iBr.iY ? 1 : -1;\r
548                 \r
549                 for(int y = rc.iTl.iY;; y += stepY) {\r
550                         for(int x = rc.iTl.iX;; x += stepX) {\r
551                                 *(vidmem + y*240 + x) = *(vidmem + y*240 + x) = color;\r
552                                 if(x == rc.iBr.iX) break;\r
553                         }\r
554                         if(y == rc.iBr.iY) break;\r
555                 }\r
556         }\r
557 }\r
558 \r
559 // direction: -1 left, 1 right\r
560 static void drawArrow0(TPoint p, int direction, unsigned short color)\r
561 {\r
562         unsigned short *vidmem=(unsigned short *)framebuff;\r
563         int width = 15;\r
564         int x = p.iX;\r
565         int y = p.iY;\r
566 \r
567         for(; width > 0; x+=direction, y++, width -=2)\r
568                 for(int i=0; i < width; i++)\r
569                         *(vidmem + x + y*240 + i*240) = color;\r
570 }\r
571 \r
572 static char *vidGetScanName(int scan)\r
573 {\r
574         static char buff[32];\r
575 \r
576         if((scan >= '0' && scan <= '9') || (scan >= 'A' && scan <= 'Z')) {\r
577                 buff[0] = (char) scan; buff[1] = 0;\r
578         } else {\r
579                 switch(scan) {\r
580                         case 0x01: strcpy(buff, "BSPACE");   break;\r
581                         case 0x03: strcpy(buff, "OK");       break;\r
582                         case 0x05: strcpy(buff, "SPACE");    break;\r
583                         case 0x0e: strcpy(buff, "AST");      break;\r
584                         case 0x0f: strcpy(buff, "HASH");     break;\r
585                         case 0x12: strcpy(buff, "SHIFT");    break;\r
586                         case 0x19: strcpy(buff, "ALT");      break;\r
587                         case 0x79: strcpy(buff, "PLUS");     break;\r
588                         case 0x7a: strcpy(buff, "DOT");      break;\r
589                         case 0xa5: strcpy(buff, "JOG@UP");   break;\r
590                         case 0xa6: strcpy(buff, "JOG@DOWN"); break;\r
591                         case 0xb5: strcpy(buff, "INET");     break;\r
592                         case 0xd4: strcpy(buff, "JOG@PUSH"); break;\r
593                         case 0xd5: strcpy(buff, "BACK");     break;\r
594                         default:  sprintf(buff, "KEY@%02X", scan); break;\r
595                 }\r
596         }\r
597 \r
598         return buff;\r
599 }\r
600 \r
601 void vidKeyConfigFrame(const TUint whichAction)\r
602 {\r
603         int i;\r
604         char buttonNames[128];\r
605         buttonNames[0] = 0;\r
606         memset(framebuff, 0, framebuffsize);\r
607 \r
608         unsigned long currentActCode = 1 << whichAction;\r
609 \r
610         // draw all "buttons" in reverse order\r
611         const TPicoAreaConfigEntry *e = areaConfig + 1; i = 0;\r
612         while(e->rect != TRect(0,0,0,0)) { e++; i++; }\r
613         for(e--, i--; e->rect != TRect(0,0,0,0); e--, i--)\r
614                 drawRect(e->rect, (currentConfig->iAreaBinds[i] & currentActCode) ? color_red : color_red_dim);\r
615 \r
616         // action name control\r
617         drawRectFilled(TRect(72, 2, 168, 20), color_grey); // 96x14\r
618         drawArrow0(TPoint(80, 3), -1, color_green);\r
619         drawArrow0(TPoint(160, 3), 1, color_green);\r
620 \r
621         drawText0(86, 9, actionNames[whichAction], color_red);\r
622 \r
623         // draw active button names if there are any\r
624         for(i = 0; i < 256; i++) {\r
625                 if(currentConfig->iKeyBinds[i] & currentActCode) {\r
626                         if(buttonNames[0]) strcat(buttonNames, ";@");\r
627                         strcat(buttonNames, vidGetScanName(i));\r
628                 }\r
629         }\r
630 \r
631         if(buttonNames[0]) {\r
632                 buttonNames[61] = 0; // only 60 chars fit\r
633                 drawText0(6, 48, buttonNames, color_blue);\r
634         }\r
635 \r
636         vidBlitCfg();\r
637 }\r
638 \r
639 void vidDrawNotice(const char *txt)\r
640 {\r
641         if(framebuff) {\r
642                 drawTextNotice(txt);\r
643                 vidBlit(1);\r
644         }\r
645 }\r