fix small poroblems
[vg.git] / vg_input.h
1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
2 #ifndef VG_INPUT_H
3 #define VG_INPUT_H
4
5 #include "vg/vg_loader.h"
6
7 #define VG_MAX_CONTROLLERS 4
8
9 static float controller_deadzone = 0.05f;
10 typedef u32 vg_input_op;
11 typedef vg_input_op *vg_input_program;
12
13 enum vg_input_type {
14 k_vg_input_type_button_u8,
15 k_vg_input_type_axis_f32,
16 k_vg_input_type_joy_v2f
17 };
18
19 enum vg_input_op {
20 /* data source */
21 vg_keyboard,
22 vg_mouse,
23 vg_joy_button,
24 vg_joy_axis,
25 vg_joy_ls,
26 vg_joy_rs,
27
28 /* modes */
29 vg_mode_mul,
30 vg_mode_sub,
31 vg_mode_add,
32 vg_mode_absmax,
33 vg_mode_max,
34
35 /* control */
36 vg_index,
37 vg_end,
38 vg_gui_visible,
39
40 /* math */
41 vg_normalize
42 };
43
44 struct{
45 const u8 *sdl_keys;
46 u32 sdl_mouse;
47
48 struct vg_controller{
49 SDL_GameController *handle; /* handle for controller. NULL if unused */
50 SDL_JoystickID instance_id; /* uid used in events */
51
52 float axises[ SDL_CONTROLLER_AXIS_MAX ];
53 u32 buttons[ SDL_CONTROLLER_BUTTON_MAX ];
54 }
55 controllers[4];
56
57 int active_controller_index; /* most recent controller (by button press)
58 will be -1 if no controllers active */
59
60 /* what the user is currently using. the keyboard and controller are still
61 * active simultaneously, but this reflects what the UI should show */
62 enum input_method{
63 k_input_method_kbm,
64 k_input_method_controller
65 }
66 display_input_method;
67 SDL_GameControllerType display_input_type;
68 }
69 static vg_input = { .active_controller_index = -2 };
70
71 static u8 vg_getkey( SDL_Keycode kc )
72 {
73 SDL_Scancode sc = SDL_GetScancodeFromKey( kc );
74 return vg_input.sdl_keys[sc];
75 }
76
77 /*
78 * takes SDL device index, and tries to open that on any free channel
79 */
80 static int vg_open_gamecontroller( Sint32 index )
81 {
82 struct vg_controller *controller = NULL;
83 int vg_id = 0;
84 const char *name = SDL_GameControllerNameForIndex( index );
85 SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
86
87 if( instance_id == -1 ){
88 vg_error( ". Invalid device index (vg_open_gamecontroller)\n" );
89 return -1;
90 }
91
92 for( int j=0; j<VG_MAX_CONTROLLERS; j++ ){
93 struct vg_controller *esta = &vg_input.controllers[j];
94
95 if( esta->handle ){
96 if( esta->instance_id == instance_id ){
97 vg_warn( " . SDL_JoystickID[%d] is already in open at index #%d\n",
98 esta->instance_id, j );
99 return -1;
100 }
101 }
102 else{
103 if( !controller ){
104 controller = &vg_input.controllers[j];
105 vg_id = j;
106 }
107 }
108 }
109
110 if( controller ){
111 controller->handle = SDL_GameControllerOpen( index );
112 controller->instance_id = instance_id;
113
114 if( controller->handle ){
115 vg_success(
116 " . opened SDL_JoystickID[%d] as controller '%s' at index #%d\n",
117 instance_id, name, vg_id );
118
119 for( u32 i=0; i< SDL_CONTROLLER_BUTTON_MAX; i++ )
120 controller->buttons[i] = 0;
121
122 for( u32 i=0; i< SDL_CONTROLLER_AXIS_MAX; i++ )
123 controller->axises[i] = 0.0f;
124
125 if( vg_input.active_controller_index == -2 ){
126 vg_input.active_controller_index = vg_id;
127 vg_input.display_input_method = k_input_method_controller;
128 vg_input.display_input_type =
129 SDL_GameControllerGetType( controller->handle );
130 }
131
132 return vg_id;
133 }
134 else{
135 vg_error( ". Failed to attach game controller '%s'. Reason: %s\n",
136 name, SDL_GetError() );
137 return -1;
138 }
139 }
140 else{
141 vg_error( ". Too many controllers open! ignoring '%s'\n", name );
142 return -1;
143 }
144 }
145
146 static void vg_input_device_event( SDL_Event *ev )
147 {
148 if( ev->type == SDL_CONTROLLERDEVICEADDED ){
149 int is_controller = SDL_IsGameController( ev->cdevice.which );
150 const char *name = SDL_JoystickNameForIndex( ev->cdevice.which );
151
152 Sint32 index = ev->cdevice.which;
153 SDL_JoystickID instance_id = SDL_JoystickGetDeviceInstanceID( index );
154 vg_info( "SDL_CONTROLLERDEVICEADDED | device index: %d, name: '%s'\n",
155 index, name );
156
157 if( is_controller ){
158 vg_open_gamecontroller( index );
159 }
160 }
161 else if( ev->type == SDL_CONTROLLERDEVICEREMOVED ){
162 vg_info( "SDL_CONTROLLERDEVICEREMOVED | instance_id: %d\n",
163 ev->cdevice.which );
164
165 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
166 struct vg_controller *controller = &vg_input.controllers[i];
167
168 if( controller->handle ){
169 if( controller->instance_id == ev->cdevice.which ){
170 vg_info( " . closing controller at index #%d\n", i );
171 SDL_GameControllerClose( controller->handle );
172 controller->handle = NULL;
173 controller->instance_id = -1;
174
175 if( vg_input.active_controller_index == i ){
176 vg_input.active_controller_index = -1;
177 vg_input.display_input_method = k_input_method_kbm;
178 vg_info( "display_input: k_input_method_kbm\n" );
179 }
180 break;
181 }
182 }
183 }
184 }
185 }
186
187 static void vg_input_controller_event( SDL_Event *ev )
188 {
189 if( ev->type == SDL_CONTROLLERAXISMOTION ){
190 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
191 struct vg_controller *esta = &vg_input.controllers[i];
192
193 if( ev->caxis.which == esta->instance_id ){
194 float value = (float)ev->caxis.value / 32767.0f;
195
196 if( ev->caxis.axis == SDL_CONTROLLER_AXIS_LEFTX ||
197 ev->caxis.axis == SDL_CONTROLLER_AXIS_LEFTY ||
198 ev->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTX ||
199 ev->caxis.axis == SDL_CONTROLLER_AXIS_RIGHTY )
200 {
201 float deadz = vg_clampf( controller_deadzone, 0.0f, 0.999f ),
202 high = vg_maxf( 0.0f, fabsf(value) - deadz );
203
204 value = vg_signf(value) * (high / (1.0f-deadz));
205 }
206
207 esta->axises[ ev->caxis.axis ] = value;
208 break;
209 }
210 }
211 }
212 else if( ev->type == SDL_CONTROLLERBUTTONDOWN ){
213 struct vg_controller *active = NULL;
214
215 if( vg_input.active_controller_index >= 0 )
216 active = &vg_input.controllers[vg_input.active_controller_index];
217
218 if( !active || (ev->cbutton.which != active->instance_id) ){
219 active = NULL;
220 vg_input.active_controller_index = -1;
221 vg_input.display_input_method = k_input_method_kbm;
222
223 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
224 if( vg_input.controllers[i].instance_id == ev->cbutton.which ){
225 active = &vg_input.controllers[i];
226 vg_input.active_controller_index = i;
227 vg_input.display_input_type =
228 SDL_GameControllerGetType(active->handle);
229 break;
230 }
231 }
232
233 if( active ){
234 vg_info( "Switching active controller index to #%d\n",
235 vg_input.active_controller_index );
236 }
237 else{
238 vg_error( "Input out of range (SDL_JoystickID#%d)\n",
239 ev->cbutton.which );
240 }
241 }
242
243 if( active ){
244 if( vg_input.display_input_method != k_input_method_controller ){
245 vg_input.display_input_method = k_input_method_controller;
246 vg_info( "display_input: k_input_method_controller\n" );
247 }
248 active->buttons[ ev->cbutton.button ] = 1;
249 }
250 }
251 else if( ev->type == SDL_CONTROLLERBUTTONUP ){
252 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
253 struct vg_controller *esta = &vg_input.controllers[i];
254
255 if( ev->cbutton.which == esta->instance_id ){
256 esta->buttons[ ev->cbutton.button ] = 0;
257 break;
258 }
259 }
260 }
261 }
262
263 static void vg_process_inputs(void)
264 {
265 int count;
266 vg_input.sdl_keys = SDL_GetKeyboardState( &count );
267 vg_input.sdl_mouse = SDL_GetMouseState(NULL,NULL);
268
269 if( vg_input.display_input_method != k_input_method_kbm ){
270 /* check for giving keyboard priority */
271 for( int i=0; i<count; i++ ){
272 if( vg_input.sdl_keys[i] ){
273 vg_input.display_input_method = k_input_method_kbm;
274 vg_info( "display_input: k_input_method_kbm (keyboard %d)\n", i );
275 break;
276 }
277 }
278
279 /* check for giving mouse priority */
280 if( vg_input.sdl_mouse &
281 (SDL_BUTTON(SDL_BUTTON_LEFT)|SDL_BUTTON(SDL_BUTTON_RIGHT)|
282 SDL_BUTTON(SDL_BUTTON_MIDDLE)) )
283 {
284 vg_input.display_input_method = k_input_method_kbm;
285 vg_info( "display_input: k_input_method_kbm (mouse)\n" );
286 }
287 }
288 }
289
290 static void async_vg_input_init( void *payload, u32 size )
291 {
292 VG_VAR_F32( controller_deadzone, flags=VG_VAR_PERSISTENT );
293
294 vg_info( "Checking for controllers\n" );
295 SDL_GameControllerAddMappingsFromFile( "gamecontrollerdb.txt" );
296
297 int joy_count = SDL_NumJoysticks();
298 for( int i=0; i<joy_count; i++ ) {
299 const char *name = SDL_JoystickNameForIndex( i );
300 int is_controller = SDL_IsGameController(i);
301
302 vg_info( "%d: %s [controller: %d]\n", i, name, is_controller );
303
304 if( is_controller ){
305 vg_open_gamecontroller( i );
306 }
307 }
308 }
309
310 static void vg_input_init(void)
311 {
312 vg_async_call( async_vg_input_init, NULL, 0 );
313 }
314
315 static void vg_input_free(void)
316 {
317 for( int i=0; i<VG_MAX_CONTROLLERS; i++ ){
318 struct vg_controller *controller = &vg_input.controllers[i];
319
320 if( controller->handle ){
321 SDL_GameControllerClose( controller->handle );
322 controller->handle = NULL;
323 }
324 }
325 }
326
327 struct vg_controller *vg_active_controller(void){
328 if( vg_input.active_controller_index >= 0 )
329 return &vg_input.controllers[vg_input.active_controller_index];
330 else
331 return NULL;
332 }
333
334 static u8 vg_controller_button( SDL_GameControllerButton button ){
335 struct vg_controller *c = vg_active_controller();
336 if( c ) return c->buttons[ button ];
337 else return 0;
338 }
339
340 static f32 vg_controller_axis( SDL_GameControllerAxis axis ){
341 struct vg_controller *c = vg_active_controller();
342 if( c ) return c->axises[ axis ];
343 else return 0;
344 }
345
346 static void vg_input_apply_to_u8( vg_input_op mode, u8 data, u8 *inout_result ){
347 if ( mode == vg_mode_absmax ) *inout_result |= data;
348 else if( mode == vg_mode_mul ) *inout_result &= data;
349 else vg_fatal_error( "mode not supported for destination type (%d)", mode );
350 }
351
352 static void vg_input_apply_to_f32( vg_input_op mode, f32 data,
353 f32 *inout_result ){
354 if ( mode == vg_mode_absmax ){
355 if( fabsf(data) > fabsf(*inout_result) )
356 *inout_result = data;
357 }
358 else if( mode == vg_mode_max ) *inout_result = vg_maxf(*inout_result,data);
359 else if( mode == vg_mode_mul ) *inout_result *= (f32)data;
360 else if( mode == vg_mode_sub ) *inout_result -= (f32)data;
361 else if( mode == vg_mode_add ) *inout_result += (f32)data;
362 else vg_fatal_error( "mode not supported for destination type (%d)", mode );
363 }
364
365 /*
366 * Run an input program. out_result must point to memory with sufficient
367 * storage respective to the size set by type.
368 */
369 static void vg_exec_input_program( enum vg_input_type type, vg_input_op *ops,
370 void *out_result ){
371 u8 *out_button = NULL;
372 f32 *out_joy = NULL;
373
374 if( type == k_vg_input_type_button_u8 ){
375 out_button = out_result;
376 *out_button = 0;
377 }
378 else if( type == k_vg_input_type_axis_f32 ){
379 out_joy = out_result;
380 out_joy[0] = 0.0f;
381 }
382 else if( type == k_vg_input_type_joy_v2f ){
383 out_joy = out_result;
384 out_joy[0] = 0.0f;
385 out_joy[1] = 0.0f;
386 }
387
388 /* computer state */
389 vg_input_op mode = vg_mode_absmax;
390 u32 pc = 0, index = 0;
391
392 next_code:;
393 vg_input_op op = ops[ pc ++ ];
394
395 if( (op >= vg_mode_mul) && (op <= vg_mode_max) )
396 mode = op;
397 else if( (op == vg_keyboard) || (op == vg_mouse) || (op == vg_joy_button) ){
398 u8 state = 0;
399
400 if( op == vg_keyboard )
401 state = vg_getkey(ops[pc ++]);
402 else if( op == vg_mouse )
403 state = (vg_input.sdl_mouse & SDL_BUTTON(ops[pc ++]))?1:0;
404 else
405 state = vg_controller_button(ops[pc ++]);
406
407 if( type == k_vg_input_type_button_u8 )
408 vg_input_apply_to_u8( mode, state, out_button );
409 else
410 vg_input_apply_to_f32( mode, (f32)state, &out_joy[index] );
411 }
412 else if( op == vg_joy_axis ){
413 f32 state = vg_controller_axis( ops[pc ++] );
414 if( type == k_vg_input_type_button_u8 )
415 vg_input_apply_to_u8( mode, state>0.5f?1:0, out_button );
416 else
417 vg_input_apply_to_f32( mode, state, &out_joy[index] );
418 }
419 else if( (op == vg_joy_ls) || (op == vg_joy_rs) ){
420 if( type == k_vg_input_type_joy_v2f ){
421 vg_input_apply_to_f32( mode,
422 vg_controller_axis( op==vg_joy_ls? SDL_CONTROLLER_AXIS_LEFTX:
423 SDL_CONTROLLER_AXIS_RIGHTX),
424 &out_joy[0] );
425 vg_input_apply_to_f32( mode,
426 vg_controller_axis( op==vg_joy_ls? SDL_CONTROLLER_AXIS_LEFTY:
427 SDL_CONTROLLER_AXIS_RIGHTY),
428 &out_joy[1] );
429 }
430 }
431 else if( op == vg_index )
432 index = ops[pc ++];
433 else if( op == vg_end )
434 return;
435 else if( op == vg_normalize )
436 v2_normalize( out_joy );
437 else if( op == vg_gui_visible )
438 pc ++;
439 else
440 vg_fatal_error( "unknown op\n" );
441
442 goto next_code;
443 }
444
445 /*
446 * Get vendor specific button glyphs based on SDL button ID
447 */
448 static const char *controller_button_str( SDL_GameControllerButton button ){
449 static const char *controller_glyphs[ SDL_CONTROLLER_BUTTON_MAX ][2] = {
450 /* xbox/generic playstation */
451 [ SDL_CONTROLLER_BUTTON_A ] = { "\x1e\x85","\x1e\x82" },
452 [ SDL_CONTROLLER_BUTTON_B ] = { "\x1e\x86","\x1e\x81" },
453 [ SDL_CONTROLLER_BUTTON_X ] = { "\x1e\x83","\x1e\x7f" },
454 [ SDL_CONTROLLER_BUTTON_Y ] = { "\x1e\x84","\x1e\x80" },
455 [ SDL_CONTROLLER_BUTTON_LEFTSTICK ] = { "\x87", "\x87" },
456 [ SDL_CONTROLLER_BUTTON_RIGHTSTICK ] = { "\x8b", "\x8b" },
457 [ SDL_CONTROLLER_BUTTON_LEFTSHOULDER ] = { "\x91", "\x91" },
458 [ SDL_CONTROLLER_BUTTON_RIGHTSHOULDER ]= { "\x92", "\x92" },
459 [ SDL_CONTROLLER_BUTTON_DPAD_LEFT ] = { "\x1e\x93","\x1e\x93" },
460 [ SDL_CONTROLLER_BUTTON_DPAD_UP ] = { "\x1e\x94","\x1e\x94" },
461 [ SDL_CONTROLLER_BUTTON_DPAD_RIGHT ] = { "\x1e\x95","\x1e\x95" },
462 [ SDL_CONTROLLER_BUTTON_DPAD_DOWN ] = { "\x1e\x96","\x1e\x96" },
463 [ SDL_CONTROLLER_BUTTON_GUIDE ] = { "\x91", "\x91" },
464 };
465
466 if( vg_input.display_input_type == SDL_CONTROLLER_TYPE_PS3 ||
467 vg_input.display_input_type == SDL_CONTROLLER_TYPE_PS4 ||
468 vg_input.display_input_type == SDL_CONTROLLER_TYPE_PS5 )
469 {
470 return controller_glyphs[ button ][ 1 ];
471 }
472 else if( vg_input.display_input_type ==
473 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO ||
474 vg_input.display_input_type ==
475 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT ||
476 vg_input.display_input_type ==
477 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR ||
478 vg_input.display_input_type ==
479 SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT )
480 {
481 return NULL;
482 }
483 else
484 return controller_glyphs[ button ][ 0 ];
485 }
486
487 /*
488 * Cat keyboard key string. special_glyphs include SR glyphs
489 */
490 static void vg_keyboard_key_string( vg_str *str, u32 key, int special_glyphs ){
491 if( (key >= SDLK_a) && (key <= SDLK_z) ){
492 key = (key-SDLK_a)+(u32)'A';
493
494 if( special_glyphs ){
495 vg_strcatch( str, '\x1f' );
496 vg_strcatch( str, key );
497 vg_strcatch( str, ' ' );
498 }
499 else
500 vg_strcatch( str, key );
501 }
502 else if( (key == SDLK_LSHIFT) || (key == SDLK_RSHIFT) )
503 vg_strcat( str, special_glyphs? "\x9e": "shift" );
504 else if( (key == SDLK_LCTRL) || (key == SDLK_RCTRL) )
505 vg_strcat( str, special_glyphs? "\x9f": "ctrl" );
506 else if( (key == SDLK_LALT) || (key == SDLK_RALT) )
507 vg_strcat( str, special_glyphs? "\xa0": "alt" );
508 else if( key == SDLK_SPACE )
509 vg_strcat( str, special_glyphs? "\xa1": "space" );
510 else if( (key == SDLK_RETURN) || (key == SDLK_RETURN2) )
511 vg_strcat( str, special_glyphs? "\xa2": "return" );
512 else if( key == SDLK_ESCAPE )
513 vg_strcat( str, special_glyphs? "\xa3": "escape" );
514 else if( key == SDLK_RIGHT )
515 vg_strcat( str, special_glyphs? "\x1f\x95 ": "right" );
516 else if( key == SDLK_LEFT )
517 vg_strcat( str, special_glyphs? "\x1f\x93 ": "left" );
518 else if( key == SDLK_UP )
519 vg_strcat( str, special_glyphs? "\x1f\x94 ": "up" );
520 else if( key == SDLK_DOWN )
521 vg_strcat( str, special_glyphs? "\x1f\x96 ": "down" );
522 else {
523 vg_strcat( str, "keyboard key #" );
524 vg_strcati32( str, key );
525 }
526 }
527
528 /*
529 * Cat mouse button string. special_glyphs include SR glyphs
530 */
531 static void vg_mouse_button_string( vg_str *str, u32 button,
532 int special_glyphs ){
533 if ( button == SDL_BUTTON_LEFT )
534 vg_strcat( str, special_glyphs? "\x99": "left mouse" );
535 else if( button == SDL_BUTTON_RIGHT )
536 vg_strcat( str, special_glyphs? "\x9a": "right mouse" );
537 else if( button == SDL_BUTTON_MIDDLE )
538 vg_strcat( str, special_glyphs? "\x9c": "middle mouse" );
539 else{
540 vg_strcat( str, "mouse button #" );
541 vg_strcati32( str, button );
542 }
543 }
544
545 /*
546 * Cat string represeinting single axis
547 */
548 static void vg_joy_axis_string( vg_str *str,
549 SDL_GameControllerAxis axis, int special_glyphs ){
550 if( axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT )
551 vg_strcat( str, special_glyphs?"\x8f":"left trigger" );
552 else if( axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT )
553 vg_strcat( str, special_glyphs?"\x90":"right trigger" );
554 else if( axis == SDL_CONTROLLER_AXIS_LEFTX )
555 vg_strcat( str, special_glyphs?"\x88":"left stick horizontal" );
556 else if( axis == SDL_CONTROLLER_AXIS_LEFTY )
557 vg_strcat( str, special_glyphs?"\x89":"left stick vertical" );
558 else if( axis == SDL_CONTROLLER_AXIS_RIGHTX )
559 vg_strcat( str, special_glyphs?"\x8c":"right stick horizontal" );
560 else if( axis == SDL_CONTROLLER_AXIS_RIGHTY )
561 vg_strcat( str, special_glyphs?"\x8d":"right stick vertical" );
562 else{
563 vg_strcat( str, "axis " );
564 vg_strcati32( str, axis );
565 }
566 }
567
568 /*
569 * Cat string represeinting whole joystick
570 */
571 static void vg_joy_string( vg_str *str, vg_input_op op, int special_glyphs ){
572 if( op == vg_joy_ls )
573 vg_strcat( str, special_glyphs? "\x87": "left stick" );
574 else
575 vg_strcat( str, special_glyphs? "\x8b": "right stick" );
576 }
577
578 /*
579 * Convert an input program into a readable string
580 */
581 static void vg_input_string( vg_str *str, vg_input_op *ops, int glyphs ){
582 u32 pc = 0;
583 int applicable = 0, visible = 1;
584
585 next_code:;
586 vg_input_op op = ops[ pc ++ ];
587
588 if( (op == vg_keyboard) || (op == vg_mouse) ){
589 if( (vg_input.display_input_method == k_input_method_kbm) && visible ){
590 applicable = 1;
591
592 if( op == vg_keyboard )
593 vg_keyboard_key_string( str, ops[pc], glyphs );
594 else
595 vg_mouse_button_string( str, ops[pc], glyphs );
596 }
597 else applicable = 0;
598 pc ++;
599 }
600 else if( (op == vg_joy_button) || (op == vg_joy_axis) ){
601 if( (vg_input.display_input_method == k_input_method_controller)
602 && visible ){
603 applicable = 1;
604
605 if( op == vg_joy_button )
606 vg_strcat( str, controller_button_str(ops[pc]) );
607 else
608 vg_joy_axis_string( str, ops[pc], glyphs );
609 }
610 else applicable = 0;
611 pc ++;
612 }
613 else if( (op == vg_joy_ls) || (op == vg_joy_rs) ){
614 if( (vg_input.display_input_method == k_input_method_controller)
615 && visible ){
616 applicable = 1;
617 vg_joy_string( str, op, glyphs );
618 }
619 else applicable = 0;
620 }
621 else if( op == vg_mode_mul ){
622 if( applicable && visible )
623 vg_strcat( str, " + " );
624 }
625 else if( op == vg_index )
626 pc ++;
627 else if( op == vg_gui_visible )
628 visible = ops[pc++];
629 else if( op == vg_end )
630 return;
631
632 goto next_code;
633 }
634
635 #endif