stubs for item usage
[awish.git] / src / game.c
blob5996e8ed7bc58eb9d710d72df4a954d3a55d109f
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
5 #include "game.h"
7 #include "resfile.h"
8 #include "video.h"
9 #include "mainloop.h"
10 #include "vm.h"
11 #include "gameglobals.h"
15 ////////////////////////////////////////////////////////////////////////////////
16 #define MAP_WIDTH (44)
17 #define MAP_HEIGHT (41)
18 #define VIS_WIDTH (15)
19 #define VIS_HEIGHT (17)
20 #define VIS_X (10)
21 #define VIS_Y (20)
24 ////////////////////////////////////////////////////////////////////////////////
25 static const char *itemNames[] = {
26 "",
27 "key",
28 "fan",
29 "dynamite",
30 "drill",
31 "umbrella",
32 "monster",
33 "rocket",
34 "apple",
35 "vial",
36 "gloves"
40 enum {
41 ITEM_NOTHING,
42 ITEM_KEY,
43 ITEM_FAN,
44 ITEM_DYNAMITE,
45 ITEM_DRILL,
46 ITEM_UMBRELLA,
47 ITEM_MONSTER,
48 ITEM_ROCKET,
49 ITEM_APPLE,
50 ITEM_VIAL,
51 ITEM_GLOVES,
52 ITEM_MAX
56 typedef struct {
57 int type; // 0: inactive
58 int x, y; // in map
59 int frame; // animation frame
60 int fframe, lframe; // first and last anim frame
61 int animated;
62 int tid;
63 } Item;
66 ////////////////////////////////////////////////////////////////////////////////
67 static int curLevel;
68 static char levelname[257];
69 static int lnamex;
70 static Uint8 levelback;
71 static Uint8 bmap[MAP_HEIGHT][MAP_WIDTH];
72 static Uint8 fmap[MAP_HEIGHT][MAP_WIDTH];
73 static Uint8 xflags[MAP_WIDTH];
74 static Item items[256];
75 static int itemCount;
76 static int scrX, scrY;
77 static int profX, profY;
78 //static Item profItem;
79 static int fkeys[10];
80 static int inMinimap;
83 ////////////////////////////////////////////////////////////////////////////////
84 static void frmGameKey (SDL_KeyboardEvent *key) {
85 if (key->type == SDL_KEYDOWN) {
86 switch (key->keysym.sym) {
87 case SDLK_ESCAPE:
88 if (inMinimap) {
89 inMinimap = 0;
90 vmPaused = 0;
91 vmGVars[GVAR_KEY_MINIMAP] = 0;
93 break;
94 case SDLK_F12:
95 vmGVars[GVAR_KEY_QUIT] = 1;
96 break;
97 case SDLK_p:
98 vmGVars[GVAR_KEY_MINIMAP] = 1;
99 break;
100 default: ;
104 switch (key->keysym.sym) {
105 case SDLK_LEFT: case SDLK_KP4: vmGVars[GVAR_KEY_LEFT] = (key->type == SDL_KEYDOWN); break;
106 case SDLK_RIGHT: case SDLK_KP6: vmGVars[GVAR_KEY_RIGHT] = (key->type == SDL_KEYDOWN); break;
107 case SDLK_UP: case SDLK_KP8: vmGVars[GVAR_KEY_UP] = (key->type == SDL_KEYDOWN); break;
108 case SDLK_DOWN: case SDLK_KP2: vmGVars[GVAR_KEY_DOWN] = (key->type == SDL_KEYDOWN); break;
109 case SDLK_SPACE: case SDLK_KP0: vmGVars[GVAR_KEY_TAKE] = (key->type == SDL_KEYDOWN); break;
110 case SDLK_RETURN: case SDLK_KP_PERIOD: vmGVars[GVAR_KEY_USE] = (key->type == SDL_KEYDOWN); break;
112 case SDLK_f: vmGVars[GVAR_KEY_FALL_CHEAT] = (key->type == SDL_KEYDOWN); break;
113 case SDLK_w: vmGVars[GVAR_KEY_WALK_CHEAT] = (key->type == SDL_KEYDOWN); break;
114 case SDLK_KP_MINUS: vmGVars[GVAR_KEY_PLEV_CHEAT] = (key->type == SDL_KEYDOWN); break;
115 case SDLK_KP_PLUS: vmGVars[GVAR_KEY_NLEV_CHEAT] = (key->type == SDL_KEYDOWN); break;
117 case SDLK_F1: fkeys[1] = (key->type == SDL_KEYDOWN); break;
118 case SDLK_F2: fkeys[2] = (key->type == SDL_KEYDOWN); break;
119 case SDLK_F3: fkeys[3] = (key->type == SDL_KEYDOWN); break;
120 case SDLK_F4: fkeys[4] = (key->type == SDL_KEYDOWN); break;
121 case SDLK_F5: fkeys[5] = (key->type == SDL_KEYDOWN); break;
122 case SDLK_F6: fkeys[6] = (key->type == SDL_KEYDOWN); break;
123 case SDLK_F7: fkeys[7] = (key->type == SDL_KEYDOWN); break;
124 case SDLK_F8: fkeys[8] = (key->type == SDL_KEYDOWN); break;
125 case SDLK_F9: fkeys[9] = (key->type == SDL_KEYDOWN); break;
126 default: ;
131 ////////////////////////////////////////////////////////////////////////////////
132 static int loadLevelInternal (ResFile *resfile, int lidx) {
133 Uint8 *lvl;
134 int sz, res = -1, pos;
136 vmGVars[GVAR_MAP_WIDTH] = MAP_WIDTH;
137 vmGVars[GVAR_MAP_HEIGHT] = MAP_HEIGHT;
138 vmGVars[GVAR_VIS_WIDTH] = VIS_WIDTH;
139 vmGVars[GVAR_VIS_HEIGHT] = VIS_HEIGHT;
141 if ((lvl = loadResFile(resfile, lidx+9, &sz)) == NULL) return -1;
142 if (sz < 3700) goto quit;
143 if (lvl[0] > 25) goto quit;
144 memset(levelname, 0, sizeof(levelname));
145 if (lvl[0] > 0) memcpy(levelname, lvl+1, lvl[0]);
146 lnamex = (320-strlen(levelname)*8)/2;
147 levelback = lvl[26];
148 if (levelback < 1 || levelback > 7) levelback = 1;
149 profX = lvl[27]; //if (profX > 0) --profX;
150 profY = lvl[28];
151 vmSetTVar(0, TVAR_POS_X, profX);
152 vmSetTVar(0, TVAR_POS_Y, profY);
153 scrX = lvl[29]; //if (scrX > 0) --scrX;
154 scrY = lvl[30]; if (scrY > 0) --scrY;
155 vmGVars[GVAR_SCR_X] = scrX;
156 vmGVars[GVAR_SCR_Y] = scrY;
157 pos = 31;
158 memset(bmap, 0, sizeof(bmap));
159 memset(fmap, 0, sizeof(fmap));
160 memset(xflags, 0, sizeof(xflags));
161 for (int x = 1; x < MAP_WIDTH; ++x) {
162 for (int y = 0; y < MAP_HEIGHT; ++y) {
163 bmap[y][x] = lvl[pos++];
166 for (int x = 1; x < MAP_WIDTH; ++x) {
167 for (int y = 0; y < MAP_HEIGHT; ++y) {
168 fmap[y][x] = lvl[pos++];
170 pos += 3; // skip unknown bytes
173 //fprintf(stderr, "pos: 0x%08x\n", pos);
174 itemCount = lvl[pos++]+1; // at least one item is always here
175 vmGVars[GVAR_ITEM_COUNT] = itemCount;
176 //fprintf(stderr, "ic=%d\n", itemCount);
177 //fprintf(stderr, "LEVEL #%d\n", lidx+1);
178 for (int f = 0; f < itemCount; ++f) {
179 int tid;
181 items[f].x = lvl[pos++]; //--items[f].x;
182 items[f].y = lvl[pos++]; //--items[f].y;
183 items[f].fframe = lvl[pos++];
184 items[f].lframe = lvl[pos++];
185 items[f].frame = lvl[pos++];
186 items[f].animated = lvl[pos++];
187 //if (items[f].animated > 1) fprintf(stderr, "item #%d: animated=%d\n", f, items[f].animated);
188 if (!items[f].animated) items[f].frame = items[f].fframe;
189 //if (lvl[pos] != 0xff) fprintf(stderr, "item #%d: NOT FF!\n", f);
190 ++pos; // 0xff
192 #define DIE(msg,ofs) { fprintf(stderr, "LEVEL #%d, item #%d (%s): %s (%d)\n", lidx+1, f, itemNames[lvl[pos+4]], msg, lvl[pos+ofs]); abort(); }
193 if (lvl[pos-1] != 0xff) DIE("FF", 0);
194 if (lvl[pos+4] == 6) {
195 if (lidx == 18 && lvl[pos+0] == 3) goto skip;
196 if (f == 3 && lidx == 20 && lvl[pos+0] == 7) goto skip;
197 if (f == 4 && lidx == 20 && lvl[pos+0] == 3) goto skip;
198 if (lidx == 23 && lvl[pos+0] == 6) goto skip;
199 if (lidx == 24 && lvl[pos+0] == 1) goto skip;
200 if (lidx == 26 && lvl[pos+0] == 0) goto skip;
201 if (lidx == 32 && lvl[pos+0] == 6) goto skip;
202 if (f == 2 && lidx == 37 && lvl[pos+0] == 2) goto skip;
203 if (f == 3 && lidx == 37 && lvl[pos+0] == 4) goto skip;
204 if (f == 4 && lidx == 37 && lvl[pos+0] == 6) goto skip;
205 if (f == 6 && lidx == 37 && lvl[pos+0] == 7) goto skip;
206 if (f == 8 && lidx == 37 && lvl[pos+0] == 3) goto skip;
207 if (f == 10 && lidx == 37 && lvl[pos+0] == 0) goto skip;
208 if (f == 12 && lidx == 37 && lvl[pos+0] == 0) goto skip;
209 if (f == 16 && lidx == 37 && lvl[pos+0] == 4) goto skip;
210 if (f == 17 && lidx == 37 && lvl[pos+0] == 0) goto skip;
211 if (f == 18 && lidx == 37 && lvl[pos+0] == 0) goto skip;
212 if (f == 19 && lidx == 37 && lvl[pos+0] == 0) goto skip;
213 if (lidx == 43 && lvl[pos+0] == 0) goto skip;
214 if (lidx == 48 && lvl[pos+0] == 6) goto skip;
215 if (lidx == 55 && lvl[pos+0] == 4) goto skip;
216 if (lidx == 59 && lvl[pos+0] == 8) goto skip;
217 if (lidx == 60 && lvl[pos+0] == 7) goto skip;
218 if (lidx == 63 && lvl[pos+0] == 1) goto skip;
219 if (lidx == 65 && lvl[pos+0] == 2) goto skip;
220 if (lidx == 66 && lvl[pos+0] == 3) goto skip;
221 if (f == 9 && lidx == 67 && lvl[pos+0] == 0) goto skip;
222 if (f == 10 && lidx == 67 && lvl[pos+0] == 7) goto skip;
223 if (lidx == 69 && lvl[pos+0] == 2) goto skip;
224 if (lvl[pos+0] != 9) DIE("MONSTER+0 is not 9", 0);
225 } else {
226 if (lidx == 12 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
227 if (lidx == 23 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
228 if (lidx == 25 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
229 if (lidx == 26 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
230 if (lidx == 27 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
231 if (lidx == 48 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
232 if (lidx == 55 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
233 if (lidx == 60 && lvl[pos+4] == 2 && lvl[pos+0] == 1) goto skip;
234 if (lvl[pos+0] != 0) DIE("non-MONSTER+0 is not 0", 0);
236 skip: ;
237 switch (lvl[pos+4]) {
238 case 1: case 4: case 5: case 8: case 10:
239 if (lvl[pos+1] != 0) DIE("00", 1);
240 break;
241 case 2:
242 if (lvl[pos+1] != 2) DIE("01", 1);
243 break;
244 case 3:
245 if (lvl[pos+1] != 6) DIE("02", 1);
246 break;
247 case 6:
248 if (lvl[pos+1] != 10) DIE("03", 1);
249 break;
250 case 7:
251 if (lvl[pos+1] != 2) DIE("04", 1);
252 break;
253 case 9:
254 if (lvl[pos+1] != 10) DIE("05", 1);
255 break;
256 default: abort();
258 if (lvl[pos+2] != 0) DIE("06", 2);
259 if (lvl[pos+3] != 0) DIE("07", 3);
260 if (lvl[pos+5] != 0) DIE("08", 5);
262 pos += 4; // dunno
263 items[f].type = lvl[pos++];
264 ++pos; // dunno
267 if (items[f].type == 6) { if (lvl[pos-6] != 9) lvl[pos-6] = 255; else lvl[pos-6] = 0; }
268 if (lvl[pos-6] == 0 || lvl[pos-4] == 0 || lvl[pos-3] == 0 || lvl[pos-1] == 0) {
269 //if (items[f].type == 1 && lvl[pos-5] == 0) goto skip;
270 if (items[f].type == 2 && lvl[pos-5] == 2) goto skip;
271 if (items[f].type == 3 && lvl[pos-5] == 6) goto skip;
272 //if (items[f].type == 4 && lvl[pos-5] == 0) goto skip;
273 //if (items[f].type == 5 && lvl[pos-5] == 0) goto skip;
274 if (items[f].type == 6 && lvl[pos-5] == 10) goto skip;
275 if (items[f].type == 7 && lvl[pos-5] == 2) goto skip;
276 //if (items[f].type == 8 && lvl[pos-5] == 0) goto skip;
277 if (items[f].type == 9 && lvl[pos-5] == 10) goto skip;
278 //if (items[f].type == 10 && lvl[pos-5] == 0) goto skip;
280 fprintf(stderr, "item #%d: %s (%d)", f, itemNames[items[f].type], items[f].type);
281 for (int c = -6; c < 0; ++c) fprintf(stderr, " %d", lvl[pos+c]);
282 fprintf(stderr, "\n");
283 skip: ;
286 //fprintf(stderr, "%d: x=%d; y=%d; type=%d\n", f, items[f].x, items[f].y, items[f].type);
287 // spawn thread
288 tid = vmNewThread(CODE_ENTRY_ITEM);
289 if (tid < 0) goto quit;
290 vmSetTVar(tid, TVAR_ITEM_ID, items[f].type);
291 vmSetTVar(tid, TVAR_ANIMATED, items[f].animated);
292 vmSetTVar(tid, TVAR_AFIRST_FRAME, items[f].fframe);
293 vmSetTVar(tid, TVAR_ALAST_FRAME, items[f].lframe);
294 vmSetTVar(tid, TVAR_AFRAME, items[f].frame);
295 if (items[f].type == 6) vmSetTVar(tid, TVAR_AFRAME, lvl[pos-6]);
296 vmSetTVar(tid, TVAR_POS_X, items[f].x);
297 vmSetTVar(tid, TVAR_POS_Y, items[f].y);
298 vmSetTVar(tid, TVAR_POS_TX, 0);
299 vmSetTVar(tid, TVAR_POS_TY, 0);
300 vmSetTVar(tid, TVAR_SPR_BANK, CONST_BANK_ITEMS);
301 vmSetTVar(tid, TVAR_SPR_NUM, items[f].frame);
302 vmSetTVar(tid, TVAR_SPR_DIR, 0);
304 curLevel = lidx;
305 res = 0;
306 quit:
307 free(lvl);
308 return res;
312 ////////////////////////////////////////////////////////////////////////////////
313 static inline Uint8 fgTile (int x, int y) {
314 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? fmap[y][x]&0x0f : 0;
318 static inline Uint8 bgTile (int x, int y) {
319 if (y == -666) return (x >= 0 && x < MAP_WIDTH) ? xflags[x] : 0;
320 return (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) ? bmap[y][x]&0x0f : 0;
324 static int mapGet (int tid, int fg, int x, int y) {
325 return fg ? fgTile(x, y) : bgTile(x, y);
329 static inline void setFGTile (int x, int y, Uint8 tile) {
330 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) fmap[y][x] = (fmap[y][x]&0xf0)|(tile&0x0f);
334 static inline void setBGTile (int x, int y, Uint8 tile) {
335 if (y == -666 && x >= 0 && x < MAP_WIDTH) xflags[x] = tile;
336 if (x >= 0 && y >= 0 && x < MAP_WIDTH && y < MAP_HEIGHT) bmap[y][x] = (bmap[y][x]&0xf0)|(tile&0x0f);
340 static void mapSet (int tid, int fg, int x, int y, int tile) {
341 if (fg) setFGTile(x, y, tile); else setBGTile(x, y, tile);
345 ////////////////////////////////////////////////////////////////////////////////
346 // cliprect should be set
347 static void levelDrawMap (SDL_Surface *frame) {
348 if (!inMinimap) {
349 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
351 for (int dy = 0; dy < VIS_HEIGHT; ++dy) {
352 for (int dx = 0; dx < VIS_WIDTH; ++dx) {
353 int x = scrX+dx, y = scrY+dy;
354 Uint8 b = bgTile(x, y), f = fgTile(x, y);
356 if (b) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_BG_TILES].spr[b-1][0]);
357 if (f) blitSurface2x(frame, VIS_X+dx*20, VIS_Y+dy*10, banks[CONST_BANK_FG_TILES].spr[f-1][0]);
360 } else {
361 for (int dy = 0; dy < MAP_HEIGHT; ++dy) {
362 for (int dx = 1; dx < MAP_WIDTH; ++dx) {
363 Uint8 b = bgTile(dx, dy), f = fgTile(dx, dy), t = 8;
365 if (f == 0) {
366 if (b >= 1 && b <= 8) t = b-1; else t = 8;
367 } else {
368 t = f+8;
370 if (banks[CONST_BANK_MAP_TILES].spr[t][0]) {
371 blitSurface2x(frame, VIS_X+dx*6, VIS_Y+dy*4, banks[CONST_BANK_MAP_TILES].spr[t][0]);
379 static void levelDrawSprites (SDL_Surface *frame) {
380 if (!inMinimap) {
381 int scrX = vmGVars[GVAR_SCR_X], scrY = vmGVars[GVAR_SCR_Y];
383 for (int cc = 0; cc <= 1; ++cc) {
384 for (int f = cc; f <= vmLastThread(); ++f) {
385 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
386 int b = vmGetTVar(f, TVAR_SPR_BANK);
387 int s = vmGetTVar(f, TVAR_SPR_NUM);
388 int d = vmGetTVar(f, TVAR_SPR_DIR);
390 if (cc == 0 && b == CONST_BANK_ITEMS) continue;
391 if (cc == 1 && b != CONST_BANK_ITEMS) continue;
392 if (d >= 0 && d <= 1 && s >= 0 && b >= 0 && b <= 255 && s < banks[b].count && banks[b].spr[s][d]) {
393 SDL_Surface *sf = banks[b].spr[s][d];
394 int x = vmGetTVar(f, TVAR_POS_X)-scrX;
395 int y = vmGetTVar(f, TVAR_POS_Y)-scrY;
396 int tx = vmGetTVar(f, TVAR_POS_TX);
397 int ty = vmGetTVar(f, TVAR_POS_TY);
398 int si = vmGetTVar(f, TVAR_SPR_ITEM);
400 if (b == CONST_BANK_IM_PROF || b == CONST_BANK_PROF) tx -= 5;
401 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2, sf);
402 if (si > 0 && si < banks[CONST_BANK_ITEMS].count) {
403 sf = banks[CONST_BANK_ITEMS].spr[si][0];
404 blitSurface2x(frame, VIS_X+x*20+tx, VIS_Y+y*10+ty-sf->h/2-24, sf);
410 } else {
411 blitSurface2x(frame, VIS_X+(vmGetTVar(0, TVAR_POS_X)+0)*6, VIS_Y+(vmGetTVar(0, TVAR_POS_Y)-4)*4, banks[CONST_BANK_MAP_ITEMS].spr[0][0]);
412 for (int f = 1; f < VM_MAX_THREADS; ++f) {
413 if (vmIsThreadAlive(f) && !vmIsSuspendedThread(f)) {
414 int i = vmGetTVar(f, TVAR_ITEM_ID);
415 if (i > 0 && i < ITEM_MAX) {
416 SDL_Surface *sf = banks[CONST_BANK_MAP_ITEMS].spr[i][0];
417 int x = vmGetTVar(f, TVAR_POS_X);
418 int y = vmGetTVar(f, TVAR_POS_Y);
420 blitSurface2x(frame, VIS_X+(x+0)*6, VIS_Y+(y-2)*4, sf);
428 static void drawSpriteBank (SDL_Surface *frame, SpriteBank *bank) {
429 SDL_Rect dst;
430 int x = 0, y = 0, maxh = 0;
432 SDL_SetClipRect(frame, NULL);
433 dst.x = 0;
434 dst.y = 0;
435 dst.w = frame->w;
436 dst.h = frame->h;
437 SDL_FillRect(frame, &dst, palette[3]);
439 for (int num = 0; num < bank->count; ++num) {
440 SDL_Surface *s = bank->spr[num][0];
442 if (s != NULL) {
443 char buf[16];
444 int w = s->w/2;
446 sprintf(buf, "%d", num);
447 if (w < strlen(buf)*8+2) w = strlen(buf)*8+2;
448 if (x+w > 320) {
449 x = 0;
450 y += maxh;
451 maxh = 0;
453 if (maxh < s->h/2+1) maxh = s->h/2+1;
454 blitSurface2x(frame, x, y, s);
455 drawString(frame, buf, x, y, 1);
456 x += w+1;
462 static void frmGameDraw (SDL_Surface *frame) {
463 SDL_Rect rc;
464 char buf[8];
465 int pi;
467 SDL_SetClipRect(frame, NULL);
468 if (!inMinimap) {
469 blitSurface(frame, 0, 0, backs[levelback]);
470 } else {
471 blitSurface(frame, 0, 0, backs[1]);
473 drawString(frame, levelname, lnamex, 0, 0x76);
475 pi = vmGVars[GVAR_PROF_ITEM];
476 if (vmIsThreadAlive(pi) && vmIsSuspendedThread(pi)) {
477 pi = vmGetTVar(pi, TVAR_ITEM_ID);
478 if (pi > 0 && pi < 11) {
479 //blitSurface2x(frame, 0, 0, itemSpr[profItem.frame]);
480 drawString(frame, itemNames[pi], 0, 0, 1);
484 sprintf(buf, "%d", curLevel+1);
485 drawString(frame, buf, 320-strlen(buf)*8, 0, 1);
487 rc.x = VIS_X*2;
488 rc.y = VIS_Y*2;
489 rc.w = VIS_WIDTH*20*2;
490 rc.h = VIS_HEIGHT*10*2;
491 SDL_SetClipRect(frame, &rc);
493 levelDrawMap(frame);
494 levelDrawSprites(frame);
496 for (int f = 84; f <= 90; ++f) if (fkeys[f-83]) drawSpriteBank(frame, &banks[f]);
500 ////////////////////////////////////////////////////////////////////////////////
501 int loadLevel (int lidx) {
502 if (lidx < 0 || lidx > 73) return -1;
503 return loadLevelInternal(&resfile, lidx);
507 void activateMinimap (void) {
508 inMinimap = 1;
509 vmPaused = 1;
513 ////////////////////////////////////////////////////////////////////////////////
514 void setMainLoopGame (void) {
515 frameCB = frmGameDraw;
516 keyCB = frmGameKey;
517 vmMapGetCB = mapGet;
518 vmMapSetCB = mapSet;
520 vmPaused = 0;
521 inMinimap = 0;
522 curLevel = -1;
523 memset(fkeys, 0, sizeof(fkeys));