code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EDITOR_ROBO_ED_H
#define __EDITOR_ROBO_ED_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../rasm.h"
#include "../world_struct.h"

#define COMMAND_BUFFER_LEN 512

enum find_option
{
  FIND_OPTION_NONE,
  FIND_OPTION_FIND = 0,
  FIND_OPTION_REPLACE = 1,
  FIND_OPTION_REPLACE_ALL = 2
};

#ifdef CONFIG_DEBYTECODE

enum command_type
 {
  COMMAND_TYPE_COMMAND_START,
  COMMAND_TYPE_COMMAND_CONTINUE,
  COMMAND_TYPE_BLANK_LINE,
  COMMAND_TYPE_INVALID,
  COMMAND_TYPE_UNKNOWN,
};

struct color_code_pair
{
  enum arg_type_indexed arg_type_indexed;
  int offset;
  int length;
};

#else /* !CONFIG_DEBYTECODE */

enum validity_types
{
  valid,
  invalid_uncertain,
  invalid_discard,
  invalid_comment
};

#endif /* !CONFIG_DEBYTECODE */

struct robot_line
{
  int line_text_length;
  char *line_text;

#ifdef CONFIG_DEBYTECODE
  struct color_code_pair *color_codes;
  enum command_type command_type;
  int num_color_codes;
#else
  enum validity_types validity_status;
  int line_bytecode_length;
  char *line_bytecode;
  char arg_types[20];
  int num_args;
#endif

  struct robot_line *next;
  struct robot_line *previous;
};

struct robot_state
{
  int current_line;
  struct robot_line *current_rline;
  int total_lines;
  int size;
  int max_size;
  int include_ignores;
  int disassemble_base;
  int current_x;
  int color_code;
  int mark_mode;
  int mark_start;
  int mark_end;
  int show_line_numbers;
  int scr_line_start;
  int scr_line_middle;
  int scr_line_end;
  int scr_hide_mode;
  struct robot_line *base;
  struct robot_line *mark_start_rline;
  struct robot_line *mark_end_rline;
  char *ccodes;
  char *active_macro;
  char *command_buffer;
  char command_buffer_space[COMMAND_BUFFER_LEN];
  int macro_recurse_level;
  int macro_repeat_level;

#ifdef CONFIG_DEBYTECODE
  bool program_modified;
  struct robot *cur_robot;
#else
  enum validity_types default_invalid;
#endif

  struct world *mzx_world;
};

void robot_editor(struct world *mzx_world, struct robot *cur_robot);

EDITOR_LIBSPEC void init_macros(struct world *mzx_world);
EDITOR_LIBSPEC void free_extended_macros(struct world *mzx_world);

__M_END_DECLS

#endif // __EDITOR_ROBO_ED_H

repo_name: pgimeno/megazeux

license: mit


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "robot.h"

#include <string.h>

// TODO: This might have to be something different.
void create_blank_robot_direct(struct robot *cur_robot, int x, int y)
{
  memset(cur_robot, 0, sizeof(struct robot));
  cur_robot->robot_name[0] = 0;

#ifdef CONFIG_DEBYTECODE
  {
    const char empty_robot_program[] = "";
    int program_source_length = strlen(empty_robot_program) + 1;

    cur_robot->program_source = cmalloc(program_source_length);
    cur_robot->program_source_length = program_source_length;

    memcpy(cur_robot->program_source,
     empty_robot_program, program_source_length);
  }
#else
  cur_robot->program_bytecode = cmalloc(2);
  cur_robot->program_bytecode_length = 2;
  cur_robot->program_bytecode[0] = 0xFF;
  cur_robot->program_bytecode[1] = 0x00;
#endif

  cur_robot->xpos = x;
  cur_robot->ypos = y;
  cur_robot->robot_char = 2;
  cur_robot->bullet_type = 1;
  cur_robot->used = 1;
  cur_robot->cur_prog_line = 1;
}

void create_blank_scroll_direct(struct scroll *cur_scroll)
{
  char *message = cmalloc(3);

  memset(cur_scroll, 0, sizeof(struct scroll));

  cur_scroll->num_lines = 1;
  cur_scroll->mesg_size = 3;
  cur_scroll->mesg = message;
  cur_scroll->used = 1;
  message[0] = 0x01;
  message[1] = '\n';
  message[2] = 0x00;
}

void create_blank_sensor_direct(struct sensor *cur_sensor)
{
  memset(cur_sensor, 0, sizeof(struct sensor));
  cur_sensor->used = 1;
}

void clear_scroll_contents(struct scroll *cur_scroll)
{
  free(cur_scroll->mesg);
  cur_scroll->mesg = NULL;
  cur_scroll->used = 0;
}

void replace_scroll(struct board *src_board, struct scroll *src_scroll,
 int dest_id)
{
  struct scroll *cur_scroll = src_board->scroll_list[dest_id];
  clear_scroll_contents(cur_scroll);
  duplicate_scroll_direct(src_scroll, cur_scroll);
}

void replace_sensor(struct board *src_board, struct sensor *src_sensor,
 int dest_id)
{
  struct sensor *cur_sensor = src_board->sensor_list[dest_id];
  duplicate_sensor_direct(src_sensor, cur_sensor);
}

#ifdef CONFIG_DEBYTECODE

// For the editor: only copies the source and static parameters, doesn't
// worry about program or labels, but it needs the runtime stuff,
// for now (this should change later)

void duplicate_robot_direct_source(struct robot *cur_robot,
 struct robot *copy_robot, int x, int y)
{
  int program_source_length = cur_robot->program_source_length;

  // Copy all the base contents.
  memcpy(copy_robot, cur_robot, sizeof(struct robot));

  copy_robot->program_source = cmalloc(program_source_length);
  memcpy(copy_robot->program_source, cur_robot->program_source,
   program_source_length);

  // Bytecode starts as NULL.
  copy_robot->program_bytecode = NULL;

  // Give the robot a new, fresh stack
  copy_robot->stack = NULL;
  copy_robot->stack_size = 0;
  copy_robot->stack_pointer = 0;
  copy_robot->xpos = x;
  copy_robot->ypos = y;

  copy_robot->pos_within_line = 0;
  copy_robot->status = 0;
}

void replace_robot_source(struct board *src_board, struct robot *src_robot,
 int dest_id)
{
  char old_name[64];
  int x = (src_board->robot_list[dest_id])->xpos;
  int y = (src_board->robot_list[dest_id])->ypos;
  struct robot *cur_robot = src_board->robot_list[dest_id];

  strcpy(old_name, cur_robot->robot_name);

  clear_robot_contents(cur_robot);
  duplicate_robot_direct_source(src_robot, cur_robot, x, y);
  strcpy(cur_robot->robot_name, old_name);

  if(dest_id)
    change_robot_name(src_board, cur_robot, src_robot->robot_name);
}

int duplicate_robot_source(struct board *src_board, struct robot *cur_robot,
 int x, int y)
{
  int dest_id = find_free_robot(src_board);
  if(dest_id != -1)
  {
    struct robot *copy_robot = cmalloc(sizeof(struct robot));
    duplicate_robot_direct_source(cur_robot, copy_robot, x, y);
    add_robot_name_entry(src_board, copy_robot, copy_robot->robot_name);
    src_board->robot_list[dest_id] = copy_robot;
  }

  return dest_id;
}

#endif // CONFIG_DEBYTECODE


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EDITOR_ROBOT_H
#define __EDITOR_ROBOT_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../robot.h"

#include "../board_struct.h"
#include "../robot_struct.h"

void create_blank_robot_direct(struct robot *cur_robot, int x, int y);
void create_blank_scroll_direct(struct scroll *cur_croll);
void create_blank_sensor_direct(struct sensor *cur_sensor);
void clear_scroll_contents(struct scroll *cur_scroll);
void replace_scroll(struct board *src_board, struct scroll *src_scroll,
 int dest_id);
void replace_sensor(struct board *src_board, struct sensor *src_sensor,
 int dest_id);

#ifdef CONFIG_DEBYTECODE

void duplicate_robot_direct_source(struct robot *cur_robot,
 struct robot *copy_robot, int x, int y);
int duplicate_robot_source(struct board *src_board, struct robot *cur_robot,
 int x, int y);
void replace_robot_source(struct board *src_board, struct robot *src_robot,
 int dest_id);

#else // !CONFIG_DEBYTECODE

static inline void duplicate_robot_direct_source(struct robot *cur_robot,
 struct robot *copy_robot, int x, int y)
{
  duplicate_robot_direct(cur_robot, copy_robot, x, y);
}

static inline int duplicate_robot_source(struct board *src_board,
 struct robot *cur_robot, int x, int y)
{
  return duplicate_robot(src_board, cur_robot, x, y);
}

static inline void replace_robot_source(struct board *src_board,
 struct robot *src_robot, int dest_id)
{
  replace_robot(src_board, src_robot, dest_id);
}

#endif // !CONFIG_DEBYTECODE

__M_END_DECLS

#endif // __EDITOR_ROBOT_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

// Code to edit (via edit dialog) and test custom sound effects

#include <string.h>

#include "../helpsys.h"
#include "../sfx.h"
#include "../window.h"
#include "../data.h"

#include "sfx_edit.h"

// 8 char names per sfx

static const char *const sfx_names[NUM_SFX] =
{
  "Gem      ",
  "MagicGem ",
  "Health   ",
  "Ammo     ",
  "Coin     ",
  "Life     ",
  "Lo Bomb  ",
  "Hi Bomb  ",
  "Key      ",
  "FullKeys ",
  "Unlock   ",
  "Need Key ",
  "InvsWall ",
  "Forest   ",
  "GateLock ",
  "OpenGate ",
  "Invinco1 ",
  "Invinco2 ",
  "Invinco3 ",
  "DoorLock ",
  "OpenDoor ",
  "Ouch     ",
  "Lava     ",
  "Death    ",
  "GameOver ",
  "GateClse ",
  "Push     ",
  "Trnsport ",
  "Shoot    ",
  "Break    ",
  "NeedAmmo ",
  "Ricochet ",
  "NeedBomb ",
  "Place Lo ",
  "Place Hi ",
  "BombType ",
  "Explsion ",
  "Entrance ",
  "Pouch    ",
  "Potion   ",
  "EmtyChst ",
  "Chest    ",
  "Time Out ",
  "FireOuch ",
  "StolenGm ",
  "EnemyHit ",
  "DrgnFire ",
  "Scroll   ",
  "Goop     ",
  "Unused   "
};

//--------------------------
//
// ( ) Default internal SFX
// ( ) Custom SFX
//
//    _OK_      _Cancel_
//
//--------------------------

//(full screen)
//
//|--
//|
//|Explsion_________//_____|
//|MagicGem_________//_____|
//|etc.    _________//_____|
//|
//|
//|
//| Next Prev Done
//|
//|--

// 17 on pg 1/2, 16 on pg 3

void sfx_edit(struct world *mzx_world)
{
  // First, ask whether sfx should be INTERNAL or CUSTOM. If change is made
  // from CUSTOM to INTERNAL, verify deletion of CUSTOM sfx. If changed to
  // CUSTOM, copy over INTERNAL to CUSTOM. If changed to or left at CUSTOM,
  // go to sfx editing.
  struct dialog a_di, b_di;
  int i;
  int old_sfx_mode = mzx_world->custom_sfx_on;
  int page = 0;
  int dialog_result;
  int num_elements;
  const char *radio_strings[] =
  {
    "Default internal SFX", "Custom SFX"
  };
  struct element *a_elements[3] =
  {
    construct_radio_button(2, 2, radio_strings, 2, 20,
     &mzx_world->custom_sfx_on),
    construct_button(5, 5, "OK", 0),
    construct_button(15, 5, "Cancel", -1)
  };

  struct element *b_elements[21];

  set_context(97);

  construct_dialog(&a_di, "Choose SFX mode", 26, 7, 28, 8,
   a_elements, 3, 0);

  dialog_result = run_dialog(mzx_world, &a_di);

  if(!dialog_result)
  {
    // Custom to internal
    if((old_sfx_mode) && (!mzx_world->custom_sfx_on))
    {
      if(confirm(mzx_world, "Delete current custom SFX?"))
      {
        mzx_world->custom_sfx_on = 1;
      }
    }

    // Internal to custom
    if((!old_sfx_mode) && (mzx_world->custom_sfx_on))
    {
      char *offset = mzx_world->custom_sfx;
      for(i = 0; i < NUM_SFX; i++, offset += 69)
      {
        strcpy(offset, sfx_strs[i]);
      }
    }

    // Internal
    if(mzx_world->custom_sfx_on)
    {
      // Edit custom
      do
      {
        // Setup page
        num_elements = 17;

        if(page >= 2)
          num_elements = 16;

        for(i = 0; i < num_elements; i++)
        {
          b_elements[i] = construct_input_box(1, i + 2,
           sfx_names[(page * 17) + i], 68, 224,
           mzx_world->custom_sfx + ((i + (page * 17)) * 69));
        }

        b_elements[i] = construct_label(23, 20,
         "Press Alt+T to test a sound effect");
        b_elements[i + 1] = construct_button(16, 22, "Next", 1);
        b_elements[i + 2] = construct_button(36, 22, "Previous", 2);
        b_elements[i + 3] = construct_button(60, 22, "Done", 0);

        num_elements += 4;

        construct_dialog_ext(&b_di, "Edit custom SFX", 0, 0, 80, 25,
         b_elements, num_elements, 1, 0, 0, NULL);
        // Run dialog
        dialog_result = run_dialog(mzx_world, &b_di);
        destruct_dialog(&b_di);

        // Next/prev page?
        switch(dialog_result)
        {
          case 1:
          {
            page++;
            if(page > 2)
              page = 0;
            break;
          }

          case 2:
          {
            page--;
            if(page < 0)
              page = 2;
            break;
          }
        }
      } while(dialog_result > 0);
    }
  }

  destruct_dialog(&a_di);

  // Done!
  pop_context();
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EDITOR_SFX_EDIT_H
#define __EDITOR_SFX_EDIT_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"

void sfx_edit(struct world *mzx_world);

__M_END_DECLS

#endif // __EDITOR_SFX_EDIT_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "window.h"

#include "../board.h"
#include "../event.h"
#include "../graphics.h"
#include "../helpsys.h"
#include "../intake.h"
#include "../window.h"

#include "board.h"

#include <string.h>

#define list_button " \x1F "

#define check_on "[\xFB]"
#define check_off "[ ]"
#define color_blank ' '
#define color_wild '\x3F'
#define color_dot '\xFE'

//Foreground colors that look nice for each background color
static const char fg_per_bk[16] =
 { 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 0, 0, 15, 15, 0, 0 };

// For list/choice menus-

// List/choice menu colors- Box same as dialog, elements same as
// inactive question, current element same as active question, pointer
// same as text, title same as title.

// Put up a box listing choices. Move pointer and enter to pick. choice
// size is the size in bytes per choice in the array of choices. This size
// includes null terminator and any junk padding. All work is done on
// page given, nopage flipping. Choices are drawn using color_string.
// On ESC, returns negative current choice, unless current choice is 0,
// then returns -32767. Returns OUT_OF_WIN_MEM if out of screen storage space.

// A progress column is displayed directly to the right of the list, using
// the following characters. This is mainly used for mouse support.

// Mouse support- Click on a choice to move there auto, click on progress
// column arrows to move one line, click on progress meter itself to move
// there percentage-wise. If you click on the current choice, it exits.
// If you click on a choice to move there, the mouse cursor moves with it.
int list_menu(const char *const *choices, int choice_size, const char *title,
 int current, int num_choices, int xpos, int ypos)
{
  char key_buffer[64];
  int mouse_press;
  int key_position = 0;
  int last_keypress_time = 0;
  int ticks;
  int width = choice_size + 6;
  int key;
  int i;

  cursor_off();

  if(title && strlen(title) > (unsigned int)choice_size)
    width = (int)strlen(title) + 6;

  save_screen();

  // Display box
  draw_window_box(xpos, ypos + 2, xpos + width - 1, 22, DI_MAIN, DI_DARK,
   DI_CORNER, 1, 1);

  if(title)
  {
    // Add title
    write_string(title, xpos + 3, ypos + 2, DI_TITLE, 0);
    draw_char(' ', DI_TITLE, xpos + 2, ypos + 2);
    draw_char(' ', DI_TITLE, xpos + 3 + (Uint32)strlen(title), ypos + 2);
  }

  // Add pointer
  draw_char(arrow_char, DI_TEXT, xpos + 2, (ypos >> 1) + 12);

  // Add meter arrows
  if(num_choices > 1)
  {
    draw_char(pc_top_arrow, DI_PCARROW, xpos + 4 + choice_size, ypos + 3);
    draw_char(pc_bottom_arrow, DI_PCARROW, xpos + 4 + choice_size, 21);
  }

  do
  {
    // Fill over old menu
    draw_window_box(xpos + 3, ypos + 3, xpos + 3 + choice_size, 21,
     DI_DARK, DI_MAIN, DI_CORNER, 0, 1);

    // Draw current
    color_string(choices[current], xpos + 4, (ypos >> 1) + 12, DI_ACTIVE);

    // Draw above current
    for(i = 1; i < 9 - (ypos >> 1); i++)
    {
      if((current - i) < 0)
        break;
      color_string(choices[current - i], xpos + 4,
       (ypos >> 1) + 12 - i, DI_NONACTIVE);
    }

    // Draw below current
    for(i = 1; i < 9 - (ypos >> 1); i++)
    {
      if((current + i) >= num_choices)
        break;
      color_string(choices[current + i], xpos + 4,
       (ypos >> 1) + 12 + i, DI_NONACTIVE);
    }

    // Draw meter (xpos 9 + choice_size, ypos 4 thru 20)
    if(num_choices > 1)
    {
      for(i = 4; i < 21 - ypos; i++)
        draw_char(pc_filler, DI_PCFILLER, xpos + 4 + choice_size, ypos + i);
      // Add progress dot
      i = (current * (16 - ypos)) / (num_choices - 1);
      draw_char(pc_dot, DI_PCDOT, xpos + 4 + choice_size, ypos + i + 4);
    }

    update_screen();

    // Get keypress
    update_event_status_delay();

    // Act upon it
    key = get_key(keycode_internal);

    mouse_press = get_mouse_press_ext();

    if(mouse_press && (mouse_press <= MOUSE_BUTTON_RIGHT))
    {
      int mouse_x, mouse_y;
      get_mouse_position(&mouse_x, &mouse_y);

      // Clicking on- List, column, arrow, or nothing?
      if((mouse_x == xpos + 4 + choice_size) && (mouse_y > ypos + 3) &&
       (mouse_y < 21))
      {
        // Column
        int ny = mouse_y - (ypos + 4);
        current = (ny * (num_choices - 1)) / (16 - ypos);
      }
      else

      if((mouse_y > ypos + 3) && (mouse_y < 21) &&
       (mouse_x > xpos + 3) && (mouse_x < xpos + 3 + choice_size))
      {
        // List
        if(mouse_y == ypos + 12)
          key = IKEY_RETURN;

        current += mouse_y - (ypos + 12);
        if(current < 0)
          current = 0;
        if(current >= num_choices)
          current = num_choices - 1;
        // Move mouse with choices
        warp_mouse(mouse_x, ypos + 12);
      }
      else

      if((mouse_x == xpos + 4 + choice_size) && (mouse_y == ypos + 3))
      {
        // Top arrow
        if(current > 0)
          current--;
      }
      else

      if((mouse_x == xpos + 4 + choice_size) && (mouse_y == 21))
      {
        // Bottom arrow
        if(current < (num_choices - 1))
         current++;
      }
    }
    else

    if(mouse_press == MOUSE_BUTTON_WHEELUP)
    {
      key = IKEY_UP;
    }
    else

    if(mouse_press == MOUSE_BUTTON_WHEELDOWN)
    {
      key = IKEY_DOWN;
    }

    switch(key)
    {
      case IKEY_ESCAPE:
      {
        // ESC
        restore_screen();
        if(current == 0)
          return -32767;
        else
          return -current;
      }

      case IKEY_BACKSPACE:
      {
        // Goto .. if it's there
        for(i = 0; i < num_choices; i++)
        {
          if(!strncmp(choices[i], " .. ", 4))
            break;
        }

        if(i == num_choices)
          break;

        current = i;
        restore_screen();
        return current;
      }

      case IKEY_RETURN:
      {
        // Selected
        restore_screen();
        return current;
      }

      case IKEY_UP:
      {
        if(current > 0)
          current--;
        break;
      }

      case IKEY_DOWN:
      {
        if(current < (num_choices - 1))
          current++;
        break;
      }

      case IKEY_PAGEUP:
      {
        current -= 8;
        if(current < 0)
          current = 0;
        break;
      }

      case IKEY_PAGEDOWN:
      {
        current += 8;
        if(current >= num_choices)
          current = num_choices - 1;
        break;
      }

      case IKEY_HOME:
      {
        current = 0;
        break;
      }

      case IKEY_END:
      {
        current = num_choices - 1;
        break;
      }

      default:
      {
        // Not necessarily invalid. Might be an alphanumeric; seek the
        // sucker.

        if(((key >= IKEY_a) && (key <= IKEY_z)) ||
         ((key >= IKEY_0) && (key <= IKEY_9)))
        {
          ticks = get_ticks();
          if(((ticks - last_keypress_time) >= TIME_SUSPEND) ||
           (key_position == 63))
          {
            // Go back to zero
            key_position = 0;
          }
          last_keypress_time = ticks;

          key_buffer[key_position] = key;
          key_position++;
          key_buffer[key_position] = 0;

          if(get_shift_status(keycode_internal))
          {
            for(i = 0; i < num_choices; i++)
            {
              if((choices[i][0] == ' ') &&
               !strncasecmp(choices[i] + 1, key_buffer, key_position))
              {
                // It's good!
                current = i;
                break;
              }
            }
          }
          else
          {
            for(i = 0; i < num_choices; i++)
            {
              if(!strncasecmp(choices[i], key_buffer, key_position))
              {
                // It's good!
                current = i;
                break;
              }
            }
          }
        }
        break;
      }
    }
  } while(1);
}

// For color selection screen

#define color_sel_char '\xFE'
#define color_sel_wild '\x3F'

// Color selection screen colors- see char selection screen.

// Put up a color selection box. Returns selected, or negative selected
// for ESC, -512 for -0. Returns OUT_OF_WIN_MEM if out of window mem.
// Display is 16 across, 16 down. All work done on given page. Current
// uses bit 256 for the wild bit.

// Mouse support- Click on a color to select it. If it is the current color,
// it exits.

int color_selection(int current, int allow_wild)
{
  int x, y;
  int key;
  int selected;
  int currx, curry;

  // Save screen
  save_screen();

  if(context == 72)
    set_context(98);
  else
    set_context(context);

  // Ensure allow_wild is 0 or 1
  if(allow_wild)
    allow_wild = 1;

  // Calculate current x/y
  if((current & 256) && (allow_wild))
  {
    // Wild
    current -= 256;
    if(current < 16)
    {
      currx = current;
      curry = 16;
    }
    else
    {
      curry = current - 16;
      currx = 16;
    }
  }
  else

  if(current & 256)
  {
    // Wild when not allowed to be
    current -= 256;
    currx = current & 15;
    curry = current >> 4;
  }
  else
  {
    // Normal
    currx = current & 15;
    curry = current >> 4;
  }

  // Draw box
  draw_window_box(12, 2, 33 + allow_wild, 21 + allow_wild,
   DI_MAIN, DI_DARK, DI_CORNER, 1, 1);
  // Add title
  write_string(" Select a color ", 14, 2, DI_TITLE, 0);

  do
  {
    // Draw outer edge
    draw_window_box(14, 3, 31 + allow_wild, 20 + allow_wild,
     DI_DARK, DI_MAIN, DI_CORNER, 0, 0);
    // Draw colors
    for(y = 0; y < 16 + allow_wild; y++)
    {
      for(x = 0; x < 16 + allow_wild; x++)
      {
        if(x == 16)
        {
          if(y == 16)
            draw_char_ext(color_sel_wild, 135, 31, 20, 256, 0);
          else
            draw_char_ext(color_sel_wild, fg_per_bk[y] + y * 16,
             31, y + 4, 256, 0);
        }
        else

        if(y == 16)
        {
          if(x == 0)
            draw_char_ext(color_sel_wild, 128, 15, 20, 256, 0);
          else
            draw_char_ext(color_sel_wild, x, x + 15, 20, 256, 0);
        }
        else
        {
          draw_char_ext(color_sel_char, x + (y * 16), x + 15,
           y + 4, 256, 0);
        }
      }
    }

    // Add selection box
    draw_window_box(currx + 14, curry + 3, currx + 16,
     curry + 5, DI_MAIN, DI_MAIN, DI_MAIN, 0, 0);

    // Write number of color
    if((allow_wild) && ((currx == 16) || (curry == 16)))
    {
      // Convert wild
      if(currx == 16)
      {
        if(curry == 16)
          selected = 288;
        else
          selected = 272 + curry;
      }
      else
      {
        selected = 256 + currx;
      }
    }
    else
    {
      selected = currx + (curry * 16);
    }

    write_number(selected, DI_MAIN, 28 + allow_wild, 21 + allow_wild, 3, 0, 10);
    update_screen();

    // Get key

    update_event_status_delay();
    key = get_key(keycode_internal);

    if(get_mouse_press())
    {
      int mouse_x, mouse_y;
      get_mouse_position(&mouse_x, &mouse_y);

      if((mouse_x >= 15) && (mouse_x <= (30 + allow_wild)) &&
       (mouse_y >= 4) && (mouse_y <= (19 + allow_wild)))
      {
        int new_x = mouse_x - 15;
        int new_y = mouse_y - 4;
        if((currx == new_x) && (curry == new_y))
          key = IKEY_RETURN;

        currx = new_x;
        curry = new_y;
      }
    }

    // Process
    switch(key)
    {
      // ESC
      case IKEY_ESCAPE:
      case IKEY_SPACE:
      case IKEY_RETURN:
      {
        pop_context();
        // Selected
        restore_screen();
        if((allow_wild) && ((currx == 16) || (curry == 16)))
        {
          // Convert wild
          if(currx == 16)
          {
            if(curry == 16)
              current = 288;
            else
              current = 272 + curry;
          }
          else
          {
            current = 256 + currx;
          }
        }
        else
        {
          current = currx + curry * 16;
        }

        if(key == IKEY_ESCAPE)
        {
          if(current == 0)
            current = -512;
          else
            current =- current;
        }
        return current;
      }

      case IKEY_UP:
      {
        // Up
        if(curry > 0)
          curry--;

        break;
      }

      case IKEY_DOWN:
      {
        // Down
        if(curry < (15 + allow_wild))
          curry++;

        break;
      }

      case IKEY_LEFT:
      {
        // Left
        if(currx > 0)
          currx--;

        break;
      }

      case IKEY_RIGHT:
      {
        // Right
        if(currx < (15 + allow_wild))
          currx++;

        break;
      }

      case IKEY_HOME:
      {
        // Home
        currx = 0;
        curry = 0;
        break;
      }

      case IKEY_END:
      {
        // End
        currx = 15 + allow_wild;
        curry = 15 + allow_wild;
        break;
      }

      default:
      {
        break;
      }
    }
  } while(1);
}

// Short function to display a color as a colored box
void draw_color_box(int color, int q_bit, int x, int y, int x_limit)
{
  // If q_bit is set, there are unknowns
  if(q_bit)
  {
    if(color < 16)
    {
      // Unknown background
      // Use black except for black fg, which uses grey
      if(color == 0)
        color = 8;

      if(x < x_limit)
        draw_char_ext(color_wild, color, x, y, 256, 0);

      if(x + 1 < x_limit)
        draw_char_ext(color_dot, color, x + 1, y, 256, 0);

      if(x + 2 < x_limit)
        draw_char_ext(color_wild, color, x + 2, y, 256, 0);
    }
    else

    if(color < 32)
    {
      // Unkown foreground
      // Use foreground from array
      color -= 16;
      color = (color << 4) + fg_per_bk[color];

      if(x < x_limit)
        draw_char_ext(color_wild, color, x, y, 256, 0);

      if(x + 1 < x_limit)
        draw_char_ext(color_wild, color, x + 1, y, 256, 0);

      if(x + 2 < x_limit)
        draw_char_ext(color_wild, color, x + 2, y, 256, 0);
    }
    else
    {
      // Both unknown
      if(x < x_limit)
        draw_char(color_wild, 8, x, y);

      if(x + 1 < x_limit)
        draw_char(color_wild, 135, x + 1, y);

      if(x + 2 < x_limit)
        draw_char(color_wild, 127, x + 2, y);
    }
  }
  else
  {
    if(x < x_limit)
      draw_char_ext(color_blank, color, x, y, 256, 0);

    if(x + 1 < x_limit)
      draw_char_ext(color_dot, color, x + 1, y, 256, 0);

    if(x + 2 < x_limit)
      draw_char_ext(color_blank, color, x + 2, y, 256, 0);
  }
}

static void draw_check_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int color, int active)
{
  struct check_box *src = (struct check_box *)e;
  int x = di->x + e->x;
  int y = di->y + e->y;
  int i;

  for(i = 0; i < src->num_choices; i++)
  {
    if(src->results[i])
      write_string(check_on, x, y + i, DI_NONACTIVE, 0);
    else
      write_string(check_off, x, y + i, DI_NONACTIVE, 0);

    write_string(src->choices[i], x + 4, y + i,
     DI_NONACTIVE, 0);
  }

  if(active)
  {
    color_line(src->max_length + 4, x, y + src->current_choice,
     DI_ACTIVE);
  }
}

static void draw_char_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int color, int active)
{
  struct char_box *src = (struct char_box *)e;
  int x = di->x + e->x;
  int y = di->y + e->y;
  int question_len = (int)strlen(src->question) + di->pad_space;

  write_string(src->question, x, y, color, 0);
  draw_char_ext(*(src->result), DI_CHAR,
   x + question_len + 1, y, 0, 16);
  draw_char(' ', DI_CHAR, x + question_len, y);
  draw_char(' ', DI_CHAR, x + question_len + 2, y);
}

static void draw_color_box_element(struct world *mzx_world, struct dialog *di,
 struct element *e, int color, int active)
{
  struct color_box *src = (struct color_box *)e;
  int x = di->x + e->x;
  int y = di->y + e->y;
  int current_color = *(src->result);

  write_string(src->question, x, y, color, 0);
  draw_color_box(current_color & 0xFF, current_color >> 8,
   x + (int)strlen(src->question) + di->pad_space, y, 80);
}

static void draw_board_list(struct world *mzx_world, struct dialog *di,
 struct element *e, int color, int active)
{
  struct board_list *src = (struct board_list *)e;
  int x = di->x + e->x;
  int y = di->y + e->y;
  int current_board = *(src->result);

  struct board *src_board;
  char board_name[BOARD_NAME_SIZE] = "(none)";

  if(current_board || (!src->board_zero_as_none))
  {
    if(current_board <= mzx_world->num_boards)
    {
      src_board = mzx_world->board_list[current_board];
      strncpy(board_name, src_board->board_name, BOARD_NAME_SIZE - 1);
    }
    else
    {
      strncpy(board_name, "(no board)", BOARD_NAME_SIZE - 1);
    }
    board_name[BOARD_NAME_SIZE - 1] = '\0';
  }

  write_string(src->title, x, y, color, 0);
  fill_line(BOARD_NAME_SIZE + 1, x, y + 1, 32, DI_LIST);

  color_string(board_name, x + 1, y + 1, DI_LIST);

  // Draw button
  write_string(list_button, x + BOARD_NAME_SIZE + 1, y + 1,
   DI_ARROWBUTTON, 0);
}

static int key_check_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int key)
{
  struct check_box *src = (struct check_box *)e;

  switch(key)
  {
    case IKEY_SPACE:
    case IKEY_RETURN:
    {
      src->results[src->current_choice] ^= 1;
      break;
    }

    case IKEY_PAGEUP:
    {
      src->current_choice = 0;
      break;
    }

    case IKEY_LEFT:
    case IKEY_UP:
    {
      if(src->current_choice)
        src->current_choice--;

      break;
    }

    case IKEY_PAGEDOWN:
    {
      src->current_choice = src->num_choices - 1;
      break;
    }

    case IKEY_RIGHT:
    case IKEY_DOWN:
    {
      if(src->current_choice < (src->num_choices - 1))
        src->current_choice++;

      break;
    }

    default:
    {
      return key;
    }
  }

  return 0;
}

static int key_char_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int key)
{
  struct char_box *src = (struct char_box *)e;

  switch(key)
  {
    case IKEY_SPACE:
    case IKEY_RETURN:
    {
      int current_char =
       char_selection(*(src->result));

      if((current_char == 255) && !(src->allow_char_255) &&
       confirm(mzx_world, "CHAR IDs <128 set to 255 use their param as their char."))
        current_char = -1; //don't change the char if the user cancels.

      if(current_char >= 0)
        *(src->result) = current_char;
      break;
    }

    default:
    {
      int key_char = get_key(keycode_unicode);

      if(key_char >= 32)
      {
        *(src->result) = key_char;
        break;
      }

      return key;
    }
  }

  return 0;
}

static int key_color_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int key)
{
  struct color_box *src = (struct color_box *)e;

  switch(key)
  {
    case IKEY_SPACE:
    case IKEY_RETURN:
    {
      int current_color =
       color_selection(*(src->result), src->allow_wildcard);

      if(current_color >= 0)
        *(src->result) = current_color;
      break;
    }

    default:
    {
      return key;
    }
  }

  return 0;
}

static int key_board_list(struct world *mzx_world, struct dialog *di,
 struct element *e, int key)
{
  struct board_list *src = (struct board_list *)e;

  switch(key)
  {
    case IKEY_SPACE:
    case IKEY_RETURN:
    {
      int current_board =
       choose_board(mzx_world, *(src->result),
       src->title, src->board_zero_as_none);

      if(current_board >= 0)
        *(src->result) = current_board;

      break;
    }

    default:
    {
      return key;
    }
  }

  return 0;
}

static int click_check_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int mouse_button, int mouse_x, int mouse_y,
 int new_active)
{
  struct check_box *src = (struct check_box *)e;

  src->current_choice = mouse_y;
  src->results[src->current_choice] ^= 1;

  return 0;
}

static int click_char_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int mouse_button, int mouse_x, int mouse_y,
 int new_active)
{
  return IKEY_RETURN;
}

static int click_color_box(struct world *mzx_world, struct dialog *di,
 struct element *e, int mouse_button, int mouse_x, int mouse_y,
 int new_active)
{
  return IKEY_RETURN;
}

struct element *construct_check_box(int x, int y, const char **choices,
 int num_choices, int max_length, int *results)
{
  struct check_box *src = cmalloc(sizeof(struct check_box));
  src->current_choice = 0;
  src->choices = choices;
  src->num_choices = num_choices;
  src->results = results;
  src->max_length = max_length;
  construct_element(&(src->e), x, y, max_length + 4, num_choices,
   draw_check_box, key_check_box, click_check_box, NULL, NULL);

  return (struct element *)src;
}

struct element *construct_char_box(int x, int y, const char *question,
 int allow_char_255, int *result)
{
  struct char_box *src = cmalloc(sizeof(struct char_box));
  src->question = question;
  src->allow_char_255 = allow_char_255;
  src->result = result;
  construct_element(&(src->e), x, y, (int)strlen(question) + 4,
   1, draw_char_box, key_char_box, click_char_box, NULL, NULL);

  return (struct element *)src;
}

struct element *construct_color_box(int x, int y,
 const char *question, int allow_wildcard, int *result)
{
  struct color_box *src = cmalloc(sizeof(struct color_box));
  src->question = question;
  src->allow_wildcard = allow_wildcard;
  src->result = result;
  construct_element(&(src->e), x, y, (int)strlen(question) + 4,
   1, draw_color_box_element, key_color_box, click_color_box,
   NULL, NULL);

  return (struct element *)src;
}

static int click_board_list(struct world *mzx_world, struct dialog *di,
 struct element *e, int mouse_button, int mouse_x, int mouse_y,
 int new_active)
{
  return IKEY_RETURN;
}

struct element *construct_board_list(int x, int y,
 const char *title, int board_zero_as_none, int *result)
{
  struct board_list *src = cmalloc(sizeof(struct board_list));
  src->title = title;
  src->board_zero_as_none = board_zero_as_none;
  src->result = result;
  construct_element(&(src->e), x, y, BOARD_NAME_SIZE + 3,
   2, draw_board_list, key_board_list, click_board_list,
   NULL, NULL);

  return (struct element *)src;
}

int add_board(struct world *mzx_world, int current)
{
  struct board *new_board;
  char temp_board_str[BOARD_NAME_SIZE];
  // ..get a name...
  save_screen();
  draw_window_box(16, 12, 64, 14, 79, 64, 70, 1, 1);
  write_string("Name for new board:", 18, 13, 78, 0);
  temp_board_str[0] = 0;
  if(intake(mzx_world, temp_board_str, BOARD_NAME_SIZE - 1,
   38, 13, 15, 1, 0, NULL, 0, NULL) == IKEY_ESCAPE)
  {
    restore_screen();
    return -1;
  }
  if(mzx_world->num_boards == mzx_world->num_boards_allocated)
  {
    mzx_world->num_boards_allocated *= 2;
    mzx_world->board_list = crealloc(mzx_world->board_list,
     mzx_world->num_boards_allocated * sizeof(struct board *));
  }

  mzx_world->num_boards++;
  new_board = create_blank_board();
  mzx_world->board_list[current] = new_board;
  strncpy(new_board->board_name, temp_board_str, BOARD_NAME_SIZE - 1);
  new_board->board_name[BOARD_NAME_SIZE - 1] = '\0';

  // Link global robot!
  new_board->robot_list[0] = &mzx_world->global_robot;
  restore_screen();

  return current;
}

// Shell for list_menu()
int choose_board(struct world *mzx_world, int current, const char *title,
 int board0_none)
{
  char **board_names = ccalloc(mzx_world->num_boards + 1, sizeof(char *));
  int num_boards = mzx_world->num_boards;
  int i;

  // Go through - blank boards get a (no board) marker. t2 keeps track
  // of the number of boards.
  for(i = 0; i < num_boards; i++)
  {
    board_names[i] = cmalloc(BOARD_NAME_SIZE);

    if(mzx_world->board_list[i] == NULL)
      strncpy(board_names[i], "(no board)", BOARD_NAME_SIZE - 1);
    else
      strncpy(board_names[i], (mzx_world->board_list[i])->board_name,
       BOARD_NAME_SIZE - 1);

    board_names[i][BOARD_NAME_SIZE - 1] = '\0';
  }

  board_names[i] = cmalloc(BOARD_NAME_SIZE);

  if((current < 0) || (current >= mzx_world->num_boards))
    current = 0;

  // Add (new board) to bottom
  if(i < MAX_BOARDS)
  {
    strncpy(board_names[i], "(add board)", BOARD_NAME_SIZE - 1);
    board_names[i][BOARD_NAME_SIZE - 1] = '\0';
    i++;
  }

  // Change top to (none) if needed
  if(board0_none)
  {
    strncpy(board_names[0], "(no board)", BOARD_NAME_SIZE - 1);
    board_names[0][BOARD_NAME_SIZE - 1] = '\0';
  }

  // Run the list_menu()
  current = list_menu((const char **)board_names,
   BOARD_NAME_SIZE, title, current, i, 27, 0);

  // New board? (if select no board or add board)
  if((current == num_boards) ||
   ((current >= 0) && (mzx_world->board_list[current] == NULL)))
  {
    current = add_board(mzx_world, current);
  }

  for(i = 0; i < num_boards + 1; i++)
  {
    free(board_names[i]);
  }

  free(board_names);

  if(board0_none && (current == 0))
  {
    return NO_BOARD;
  }

  return current;
}

int choose_file(struct world *mzx_world, const char *const *wildcards,
 char *ret, const char *title, int dirs_okay)
{
  return file_manager(mzx_world, wildcards, NULL, ret, title, dirs_okay,
   0, NULL, 0, 0, 0);
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EDITOR_WINDOW_H
#define __EDITOR_WINDOW_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../window.h"
#include "../world_struct.h"

EDITOR_LIBSPEC int list_menu(const char *const *choices, int choice_size,
 const char *title, int current, int num_choices, int xpos, int ypos);
int color_selection(int current, int allow_wild);
void draw_color_box(int color, int q_bit, int x, int y, int x_limit);
struct element *construct_check_box(int x, int y, const char **choices,
 int num_choices, int max_length, int *results);
struct element *construct_char_box(int x, int y, const char *question,
 int allow_char_255, int *result);
struct element *construct_color_box(int x, int y,
 const char *question, int allow_wildcard, int *result);
struct element *construct_board_list(int x, int y,
 const char *title, int board_zero_as_none, int *result);
int add_board(struct world *mzx_world, int current);
int choose_board(struct world *mzx_world, int current, const char *title,
 int board0_none);
int choose_file(struct world *mzx_world, const char *const *wildcards,
 char *ret, const char *title, int dirs_okay);

__M_END_DECLS

#endif // __EDITOR_WINDOW_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 * Copyright (C) 1999 Charles Goetzman
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "world.h"

#include "../error.h"
#include "../graphics.h"
#include "../window.h"
#include "../world.h"
#include "../idput.h"

#include "board.h"
#include "robot.h"

#include <string.h>

static const unsigned char def_id_chars[455] =
{
  /* id_chars */
  32,178,219,6,0,255,177,255,233,254,255,254,255,178,177,176, /* 0-15 */
  254,255,0,0,176,24,25,26,27,0,0,160,4,4,3,9,                /* 16-31 */
  150,7,176,0,11,0,177,12,10,0,0,162,161,0,0,22,              /* 32-47 */
  196,0,7,255,255,255,255,159,0,18,29,0,206,0,0,0,            /* 48-63 */
  '?',178,0,151,152,153,154,32,0,42,0,0,255,255,0,0,          /* 64-79 */
  235,236,5,42,2,234,21,224,127,149,5,227,0,0,172,173,        /* 80-95 */
  '?',0, /* 96-97 */
  '?','?','?','?','?','?','?','?', /* 98-121 */
  '?','?','?','?','?','?','?','?',
  '?','?','?','?','?','?','?','?',
  0,0,0,226,232,0, /* 122-127 */
  /* thin_line */
  249,179,179,179, /* None, N, S, NS */
  196,192,218,195, /* E, NE, SE, NSE */
  196,217,191,180, /* W, NW, SW, NSW */
  196,193,194,197, /* EW, NEW, SEW, NSEW */
  /* thick_line */
  249,186,186,186, /* None, N, S, NS */
  205,200,201,204, /* E, NE, SE, NSE */
  205,188,187,185, /* W, NW, SW, NSW */
  205,202,203,206, /* EW, NEW, SEW, NSEW */
  /* ice_anim */
  32,252,253,255, /* Ice animation table */
  /* lava_anim */
  176,177,178, /* Lava animation table */
  /* low_ammo, hi_ammo */
  163, /* < 10 ammunition pic */
  164, /* > 9 ammunition pic */
  /* lit_bomb */
  171,170,169,168,167,166,165, /* Lit bomb animation */
  /* energizer_glow */
  1,9,3,11,15,11,3,9, /* Energizer Glow */
  /* explosion_colors */
  239,206,76,4, /* Explosion color table */
  /* horiz_door, vert_door */
  196, /* Horizontal door pic */
  179, /* Vertical door pic */
  /* cw_anim, ccw_anim */
  47,196,92,179, /* CW animation table */
  47,179,92,196, /* CCW animation table */
  /* open_door */
  47,47, /* Open 1/2 of type 0 */
  92,92, /* Open 1/2 of type 1 */
  92,92, /* Open 1/2 of type 2 */
  47,47, /* Open 1/2 of type 3 */
  179,196,179,196,179,196,179,196, /* Open full of all types */
  179,196,179,196,179,196,179,196, /* Open full of all types */
  47,47, /* Close 1/2 of type 0 */
  92,92, /* Close 1/2 of type 1 */
  92,92, /* Close 1/2 of type 2 */
  47,47, /* Close 1/2 of type 3 */
  /* transport_anims */
  45,94,126,94, /* North */
  86,118,95,118, /* South */
  93,41,62,41, /* East */
  91,40,60,40, /* West */
  94,62,118,60, /* All directions */
  /* thick_arrow, thin_arrow */
  30,31,16,17, /* Thick arrows (pusher-style) NSEW */
  24,25,26,27, /* Thin arrows (gun-style) NSEW */
  /* horiz_lazer, vert_lazer */
  130,196,131,196, /* Horizontal Lazer Anim Table */
  128,179,129,179, /* Vertical Lazer Anim Table */
  /* fire_anim, fire_colors */
  177,178,178,178,177,176, /* Fire animation */
  4,6,12,14,12,6, /* Fire colors */
  /* life_anim, life_colors */
  155,156,157,158, /* Free life animation */
  15,11,3,11, /* Free life colors */
  /* ricochet_panels */
  28,23, /* Ricochet pics */
  /* mine_anim */
  143,144, /* Mine animation */
  /* shooting_fire_anim, shooting_fire_colors */
  15,42, /* Shooting Fire Anim */
  12,14, /* Shooting Fire Colors */
  /* seeker_anim, seeker_colors */
  '/','-','\\','|', /* Seeker animation */
  11,14,10,12, /* Seeker colors */
  /* whirlpool_glow */
  11,3,9,15, /* Whirlpool colors (this ends at 306 bytes, where ver */
  /* 1.02 used to end) */
  /* bullet_char */
  145,146,147,148, /* Player */
  145,146,147,148, /* Neutral */
  145,146,147,148, /* Enemy */
  /* player_char */
  2, 2, 2, 2, /*N S E W */
  /* player_color, missile_color, bullet_color */
  27,
  8,
  15, /* Player */
  15, /* Neutral */
  15, /* Enemy */
  /* id_dmg */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100,  0, 0, 0, 0, 0, /* 16-31 */
  0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 32-47 */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 10, 5, 5, /* 48-63 */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 0, 10, 10, /* 64-79 */
  10, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 10, 10, /* 80-95 */
  0, 0, /* 96-97 */
  0, 0, 0, 0, 0, 0, 0, 0, /* 98-121 */
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0  /* 122-127 */
};

bool append_world(struct world *mzx_world, const char *file)
{
  int i, i2;
  int num_boards, old_num_boards = mzx_world->num_boards;
  int last_pos;
  int offset;
  int d_flag;
  struct board *cur_board;
  int board_width, board_height;
  char *level_id, *level_param;
  int input_version;
  FILE *fp;

  fp = try_load_world(file, false, &input_version, NULL);
  if(!fp)
    return false;

  fseek(fp, 4234, SEEK_SET);

  num_boards = fgetc(fp);

  if(num_boards == 0)
  {
    int sfx_size;
    // Sfx skip word size
    fseek(fp, 2, SEEK_CUR);

    // Skip sfx
    for(i = 0; i < NUM_SFX; i++)
    {
      sfx_size = fgetc(fp);
      fseek(fp, sfx_size, SEEK_CUR);
    }
    num_boards = fgetc(fp);
  }

  // Skip the names for now
  // Gonna wanna come back to here
  last_pos = ftell(fp);
  fseek(fp, num_boards * BOARD_NAME_SIZE, SEEK_CUR);

  if(num_boards + old_num_boards >= MAX_BOARDS)
    num_boards = MAX_BOARDS - old_num_boards;

  mzx_world->num_boards += num_boards;
  mzx_world->num_boards_allocated += num_boards;
  mzx_world->board_list =
   crealloc(mzx_world->board_list,
   sizeof(struct board *) * (old_num_boards + num_boards));

  // Append boards
  for(i = old_num_boards; i < old_num_boards + num_boards; i++)
  {
    mzx_world->board_list[i] = load_board_allocate(fp, 0, input_version);
    cur_board = mzx_world->board_list[i];
  }

  // Go back to where the names are
  fseek(fp, last_pos, SEEK_SET);
  for(i = old_num_boards; i < old_num_boards + num_boards; i++)
  {
    cur_board = mzx_world->board_list[i];
    fread(cur_board->board_name, BOARD_NAME_SIZE, 1, fp);

    if(cur_board)
    {
      // Also patch a pointer to the global robot
      if(cur_board->robot_list)
      {
        cur_board->robot_list[0] = &mzx_world->global_robot;
      }
      // Also optimize out null objects
      optimize_null_objects(cur_board);

      board_width = cur_board->board_width;
      board_height = cur_board->board_height;
      level_id = cur_board->level_id;
      level_param = cur_board->level_param;

      // ALSO offset all entrances and exits
      for(offset = 0; offset < board_width * board_height; offset++)
      {
        d_flag = flags[(int)level_id[offset]];

        if((d_flag & A_ENTRANCE) && (level_param[offset] != NO_BOARD))
        {
          level_param[offset] += old_num_boards;
        }
      }

      for(i2 = 0; i2 < 4; i2++)
      {
        if(cur_board->board_dir[i2] != NO_BOARD)
          cur_board->board_dir[i2] += old_num_boards;
      }
    }
  }

  // Remove any null boards
  optimize_null_boards(mzx_world);

  fclose(fp);
  return true;
}

// Create a new, blank, world, suitable for editing.
void create_blank_world(struct world *mzx_world)
{
  // Make default global data
  // Make a blank board
  int i;

  mzx_world->num_boards = 1;
  mzx_world->num_boards_allocated = 1;
  mzx_world->board_list = cmalloc(sizeof(struct board *));
  mzx_world->board_list[0] = create_blank_board();
  mzx_world->current_board_id = 0;
  mzx_world->current_board = mzx_world->board_list[0];

  mzx_world->edge_color = 8;
  mzx_world->first_board = 0;
  mzx_world->endgame_board = NO_ENDGAME_BOARD;
  mzx_world->death_board = NO_DEATH_BOARD;
  mzx_world->endgame_x = 0;
  mzx_world->endgame_y = 0;
  mzx_world->game_over_sfx = 1;
  mzx_world->death_x = 0;
  mzx_world->death_y = 0;
  mzx_world->starting_lives = 7;
  mzx_world->lives_limit = 99;
  mzx_world->starting_health = 100;
  mzx_world->health_limit = 200;
  mzx_world->enemy_hurt_enemy = 0;
  mzx_world->clear_on_exit = 0;
  mzx_world->only_from_swap = 0;
  memcpy(id_chars, def_id_chars, 324);
  memcpy(bullet_color, def_id_chars + 324, 3);
  memcpy(id_dmg, def_id_chars + 327, 128);

  create_blank_robot_direct(&mzx_world->global_robot, -1, -1);
  mzx_world->current_board->robot_list[0] = &mzx_world->global_robot;

  for(i = 0; i < 6; i++)
  {
    mzx_world->status_counters_shown[i][0] = 0;
  }

  mzx_world->name[0] = 0;

  set_update_done(mzx_world);

  set_screen_mode(0);
  smzx_palette_loaded(0);
  set_palette_intensity(100);

  ec_load_mzx();
  default_palette();
  default_global_data(mzx_world);
}

void set_update_done_current(struct world *mzx_world)
{
  struct board *current_board = mzx_world->current_board;
  int size = current_board->board_width * current_board->board_height;

  if(size > mzx_world->update_done_size)
  {
    if(mzx_world->update_done == NULL)
    {
     mzx_world->update_done = cmalloc(size);
    }
    else
    {
      mzx_world->update_done =
       crealloc(mzx_world->update_done, size);
    }

    mzx_world->update_done_size = size;
  }
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EDITOR_WORLD_H
#define __EDITOR_WORLD_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"

bool append_world(struct world *mzx_world, const char *file);
void create_blank_world(struct world *mzx_world);
void set_update_done_current(struct world *mzx_world);

__M_END_DECLS

#endif // __EDITOR_WORLD_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
// Error code

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "platform.h"
#include "helpsys.h"
#include "error.h"
#include "window.h"
#include "graphics.h"
#include "event.h"

// Error type names by type code:
static const char *const error_type_names[] =
{
  " WARNING: ",		// 0
  " ERROR: ",		// 1
  " FATAL ERROR: ",	// 2
  " CRITICAL ERROR: ",	// 3
};

// Call for an error OR a warning. Type=0 for a warning, 1 for a recoverable
// error, 2 for a fatal error. Options are (bits set in options and returned
// as action) FAIL=1, RETRY=2, EXIT TO DOS=4, OK=8 (OK is for usually only
// for warnings) Type = 3 for a critical error

int error(const char *string, unsigned int type, unsigned int options,
 unsigned int code)
{
  const char *type_name;
  int t1 = 9, ret = 0;
  char temp[5];
  int x;

  // Find the name of this error type.
  if(type >= sizeof(error_type_names) / sizeof(*error_type_names))
    type = 0;

  type_name = error_type_names[type];

  // If graphics couldn't initialize, print the error to stderr and abort.
  if(!has_video_initialized())
  {
    fprintf(stderr, "%s%s\n", type_name, string);
    exit(-1);
  }

  // Window
  set_context(code >> 8);
  m_hide();
  save_screen();

  dialog_fadein();

  draw_window_box(1, 10, 78, 14, 76, 64, 72, 1, 1);
  // Add title and error name
  x = 40 - (int)strlen(type_name)/2;
  write_string(type_name, x, 10, 78, 0);

  write_string(string, 40 - ((Uint32)strlen(string) / 2), 11, 79, 0);

  // Add options
  write_string("Press", 4, 13, 78, 0);

  if(options & 1)
  {
    write_string(", F for Fail", t1, 13, 78, 0);
    t1 += 12;
  }
  if(options & 2)
  {
    write_string(", R to Retry", t1, 13, 78, 0);
    t1 += 12;
  }
  if(options & 4)
  {
    write_string(", E to Exit", t1, 13, 78, 0);
    t1 += 11;
  }
  if(options & 8)
  {
    write_string(", O for OK", t1, 13, 78, 0);
    t1 += 10;
  }

  draw_char('.', 78, t1, 13);
  draw_char(':', 78, 9, 13);

  // Add code if not 0

  if(code != 0)
  {
    write_string(" Debug code:0000h ", 30, 14, 64, 0);
    sprintf(temp, "%x", code);
    write_string(temp, 46 - (Uint32)strlen(temp), 14, 64, 0);
  }

  update_screen();

  // Get key
  do
  {
    wait_event();
    t1 = get_key(keycode_internal);

    //Process
    switch(t1)
    {
      case IKEY_f:
        fail:
        if(!(options & 1)) break;
        ret = 1;
        break;
      case IKEY_r:
        retry:
        if(!(options & 2)) break;
        ret = 2;
        break;
      case IKEY_e:
        exit:
        if(!(options & 4)) break;
        ret = 4;
        break;
      case IKEY_o:
        ok:
        if(!(options & 8)) break;
        ret = 8;
        break;
      case IKEY_h:
        if(!(options & 16)) break;
        // Call help
        break;
      case IKEY_ESCAPE:
        // Escape. Order of options this applies to-
        // Fail, Ok, Retry, Exit.
        if(options & 1) goto fail;
        if(options & 8) goto ok;
        if(options & 2) goto retry;
        goto exit;
      case IKEY_RETURN:
        // Enter. Order of options this applies to-
        // OK, Retry, Fail, Exit.
        if(options & 8) goto ok;
        if(options & 2) goto retry;
        if(options & 1) goto fail;
        goto exit;
    }
  } while(ret == 0);

  pop_context();

  // Restore screen and exit appropriately
  dialog_fadeout();

  restore_screen();
  m_show();
  if(ret == 4) // Exit the program
  {
    platform_quit();
    exit(-1);
  }

  return ret;
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Declaration for ERROR.CPP */

#ifndef __ERROR_H
#define __ERROR_H

#include "compat.h"

__M_BEGIN_DECLS

//Call for an error OR a warning. Type=0 for a warning, 1 for a recoverable
//error, 2 for a fatal error. Options are (bits set in options and returned
//as action) FAIL=1, RETRY=2, EXIT TO DOS=4, OK=8, HELP=16 (OK is for usually
//only for warnings) Code is a specialized error code for debugging purposes.
//Type of 3 for a critical error.

CORE_LIBSPEC int error(const char *string, unsigned int type,
 unsigned int options, unsigned int code);

__M_END_DECLS

#endif // __ERROR_H


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "event.h"
#include "graphics.h"
#include "util.h"
#include "platform.h"

#include <stdlib.h>
#include <string.h>

#define KEY_REPEAT_START    250
#define KEY_REPEAT_RATE     33

#define MOUSE_REPEAT_START  200
#define MOUSE_REPEAT_RATE   10

static Uint32 last_update_time;

struct input_status input;

static Uint8 num_buffered_events = 1;

struct buffered_status *store_status(void)
{
  return &input.buffer[input.store_offset];
}

static const struct buffered_status *load_status(void)
{
  return (const struct buffered_status *)&input.buffer[input.load_offset];
}

static void bump_status(void)
{
  Uint16 last_store_offset = input.store_offset;

  input.store_offset = (input.store_offset + 1) % num_buffered_events;
  input.load_offset = (input.store_offset + 1) % num_buffered_events;

  // No event buffering; nothing to do
  if(input.store_offset == input.load_offset)
    return;

  // Some events can "echo" from the previous buffer
  memcpy(store_status(), &input.buffer[last_store_offset],
         sizeof(struct buffered_status));
}

void init_event(void)
{
  input.buffer = ccalloc(num_buffered_events, sizeof(struct buffered_status));
  input.load_offset = num_buffered_events - 1;
  input.store_offset = 0;
}

static Uint32 convert_internal_xt(enum keycode key)
{
  switch(key)
  {
    case IKEY_ESCAPE: return 0x01;
    case IKEY_F1: return 0x3B;
    case IKEY_F2: return 0x3C;
    case IKEY_F3: return 0x3D;
    case IKEY_F4: return 0x3E;
    case IKEY_F5: return 0x3F;
    case IKEY_F6: return 0x40;
    case IKEY_F7: return 0x41;
    case IKEY_F8: return 0x42;
    case IKEY_F9: return 0x43;
    case IKEY_F10: return 0x44;
    case IKEY_F11: return 0x57;
    case IKEY_F12: return 0x58;
    case IKEY_BACKQUOTE: return 0x29;
    case IKEY_1: return 0x02;
    case IKEY_2: return 0x03;
    case IKEY_3: return 0x04;
    case IKEY_4: return 0x05;
    case IKEY_5: return 0x06;
    case IKEY_6: return 0x07;
    case IKEY_7: return 0x08;
    case IKEY_8: return 0x09;
    case IKEY_9: return 0x0A;
    case IKEY_0: return 0x0B;
    case IKEY_MINUS: return 0x0C;
    case IKEY_EQUALS: return 0x0D;
    case IKEY_BACKSLASH: return 0x2B;
    case IKEY_BACKSPACE: return 0x0E;
    case IKEY_TAB: return 0x0F;
    case IKEY_q: return 0x10;
    case IKEY_w: return 0x11;
    case IKEY_e: return 0x12;
    case IKEY_r: return 0x13;
    case IKEY_t: return 0x14;
    case IKEY_y: return 0x15;
    case IKEY_u: return 0x16;
    case IKEY_i: return 0x17;
    case IKEY_o: return 0x18;
    case IKEY_p: return 0x19;
    case IKEY_LEFTBRACKET: return 0x1A;
    case IKEY_RIGHTBRACKET: return 0x1B;
    case IKEY_CAPSLOCK: return 0x3A;
    case IKEY_a: return 0x1E;
    case IKEY_s: return 0x1F;
    case IKEY_d: return 0x20;
    case IKEY_f: return 0x21;
    case IKEY_g: return 0x22;
    case IKEY_h: return 0x23;
    case IKEY_j: return 0x24;
    case IKEY_k: return 0x25;
    case IKEY_l: return 0x26;
    case IKEY_SEMICOLON: return 0x27;
    case IKEY_QUOTE: return 0x28;
    case IKEY_RETURN: return 0x1C;
    case IKEY_LSHIFT: return 0x2A;
    case IKEY_z: return 0x2C;
    case IKEY_x: return 0x2D;
    case IKEY_c: return 0x2E;
    case IKEY_v: return 0x2F;
    case IKEY_b: return 0x30;
    case IKEY_n: return 0x31;
    case IKEY_m: return 0x32;
    case IKEY_COMMA: return 0x33;
    case IKEY_PERIOD: return 0x34;
    case IKEY_SLASH: return 0x35;
    case IKEY_RSHIFT: return 0x36;
    case IKEY_LCTRL: return 0x1D;
    case IKEY_LSUPER: return 0x5B;
    case IKEY_LALT: return 0x38;
    case IKEY_SPACE: return 0x39;
    case IKEY_RALT: return 0x38;
    case IKEY_RSUPER: return 0x5C;
    case IKEY_MENU: return 0x5D;
    case IKEY_RCTRL: return 0x1D;
    case IKEY_SYSREQ: return 0x37;
    case IKEY_SCROLLOCK: return 0x46;
    case IKEY_BREAK: return 0xC5;
    case IKEY_INSERT: return 0x52;
    case IKEY_HOME: return 0x47;
    case IKEY_PAGEUP: return 0x49;
    case IKEY_DELETE: return 0x53;
    case IKEY_END: return 0x4F;
    case IKEY_PAGEDOWN: return 0x51;
    case IKEY_NUMLOCK: return 0x45;
    case IKEY_KP_DIVIDE: return 0x35;
    case IKEY_KP_MULTIPLY: return 0x37;
    case IKEY_KP_MINUS: return 0x4A;
    case IKEY_KP7: return 0x47;
    case IKEY_KP8: return 0x48;
    case IKEY_KP9: return 0x49;
    case IKEY_KP4: return 0x4B;
    case IKEY_KP5: return 0x4C;
    case IKEY_KP6: return 0x4D;
    case IKEY_KP_PLUS: return 0x4E;
    case IKEY_KP1: return 0x4F;
    case IKEY_KP2: return 0x50;
    case IKEY_KP3: return 0x51;
    case IKEY_KP_ENTER: return 0x1C;
    case IKEY_KP0: return 0x52;
    case IKEY_KP_PERIOD: return 0x53;
    case IKEY_UP: return 0x48;
    case IKEY_LEFT: return 0x4B;
    case IKEY_DOWN: return 0x50;
    case IKEY_RIGHT: return 0x4D;

    // All others are currently undefined; returns 0
    default: return 0;
  }
}

static enum keycode convert_xt_internal(Uint32 key, enum keycode *second)
{
  *second = IKEY_UNKNOWN;
  switch(key)
  {
    case 0x01: return IKEY_ESCAPE;
    case 0x3B: return IKEY_F1;
    case 0x3C: return IKEY_F2;
    case 0x3D: return IKEY_F3;
    case 0x3E: return IKEY_F4;
    case 0x3F: return IKEY_F5;
    case 0x40: return IKEY_F6;
    case 0x41: return IKEY_F7;
    case 0x42: return IKEY_F8;
    case 0x43: return IKEY_F9;
    case 0x44: return IKEY_F10;
    case 0x57: return IKEY_F11;
    case 0x58: return IKEY_F12;
    case 0x29: return IKEY_BACKQUOTE;
    case 0x02: return IKEY_1;
    case 0x03: return IKEY_2;
    case 0x04: return IKEY_3;
    case 0x05: return IKEY_4;
    case 0x06: return IKEY_5;
    case 0x07: return IKEY_6;
    case 0x08: return IKEY_7;
    case 0x09: return IKEY_8;
    case 0x0A: return IKEY_9;
    case 0x0B: return IKEY_0;
    case 0x0C: return IKEY_MINUS;
    case 0x0D: return IKEY_EQUALS;
    case 0x2B: return IKEY_BACKSLASH;
    case 0x0E: return IKEY_BACKSPACE;
    case 0x0F: return IKEY_TAB;
    case 0x10: return IKEY_q;
    case 0x11: return IKEY_w;
    case 0x12: return IKEY_e;
    case 0x13: return IKEY_r;
    case 0x14: return IKEY_t;
    case 0x15: return IKEY_y;
    case 0x16: return IKEY_u;
    case 0x17: return IKEY_i;
    case 0x18: return IKEY_o;
    case 0x19: return IKEY_p;
    case 0x1A: return IKEY_LEFTBRACKET;
    case 0x1B: return IKEY_RIGHTBRACKET;
    case 0x3A: return IKEY_CAPSLOCK;
    case 0x1E: return IKEY_a;
    case 0x1F: return IKEY_s;
    case 0x20: return IKEY_d;
    case 0x21: return IKEY_f;
    case 0x22: return IKEY_g;
    case 0x23: return IKEY_h;
    case 0x24: return IKEY_j;
    case 0x25: return IKEY_k;
    case 0x26: return IKEY_l;
    case 0x27: return IKEY_SEMICOLON;
    case 0x28: return IKEY_QUOTE;
    case 0x1C:
      *second = IKEY_KP_ENTER;
      return IKEY_RETURN;
    case 0x2A: return IKEY_LSHIFT;
    case 0x2C: return IKEY_z;
    case 0x2D: return IKEY_x;
    case 0x2E: return IKEY_c;
    case 0x2F: return IKEY_v;
    case 0x30: return IKEY_b;
    case 0x31: return IKEY_n;
    case 0x32: return IKEY_m;
    case 0x33: return IKEY_COMMA;
    case 0x34: return IKEY_PERIOD;
    case 0x35:
      *second = IKEY_KP_DIVIDE;
      return IKEY_SLASH;
    case 0x36: return IKEY_RSHIFT;
    case 0x1D:
      *second = IKEY_RCTRL;
      return IKEY_LCTRL;
    case 0x5B: return IKEY_LSUPER;
    case 0x38:
      *second = IKEY_RALT;
      return IKEY_LALT;
    case 0x39: return IKEY_SPACE;
    case 0x5C: return IKEY_RSUPER;
    case 0x5D: return IKEY_MENU;
    case 0x37:
      *second = IKEY_KP_MULTIPLY;
      return IKEY_SYSREQ;
    case 0x46: return IKEY_SCROLLOCK;
    case 0xC5: return IKEY_BREAK;
    case 0x52:
      *second = IKEY_KP0;
      return IKEY_INSERT;
    case 0x47:
      *second = IKEY_KP7;
      return IKEY_HOME;
    case 0x49:
      *second = IKEY_KP9;
      return IKEY_PAGEUP;
    case 0x53:
      *second = IKEY_KP_PERIOD;
      return IKEY_DELETE;
    case 0x4F:
      *second = IKEY_KP1;
      return IKEY_END;
    case 0x51:
      *second = IKEY_KP3;
      return IKEY_PAGEDOWN;
    case 0x45: return IKEY_NUMLOCK;
    case 0x4A: return IKEY_KP_MINUS;
    case 0x48:
      *second = IKEY_UP;
      return IKEY_KP8;
    case 0x4B:
      *second = IKEY_LEFT;
       return IKEY_KP4;
    case 0x4C: return IKEY_KP5;
    case 0x4D:
      *second = IKEY_RIGHT;
      return IKEY_KP6;
    case 0x4E: return IKEY_KP_PLUS;
    case 0x50:
      *second = IKEY_DOWN;
      return IKEY_KP2;
    default: return IKEY_UNKNOWN;
  }
}

static bool update_autorepeat(void)
{
  struct buffered_status *status = store_status();
  bool rval = false;

  // Repeat code
  Uint8 last_key_state = status->keymap[status->key_repeat];
  Uint8 last_mouse_state = status->mouse_repeat_state;

  if(last_key_state)
  {
    Uint32 new_time = get_ticks();
    Uint32 ms_difference = new_time - status->keypress_time;

    if(last_key_state == 1)
    {
      if(ms_difference > KEY_REPEAT_START)
      {
        status->keypress_time = new_time;
        status->keymap[status->key_repeat] = 2;
        status->key = status->key_repeat;
        status->unicode = status->unicode_repeat;
        rval = true;
      }
    }
    else
    {
      if(ms_difference > KEY_REPEAT_RATE)
      {
        status->keypress_time = new_time;
        status->key = status->key_repeat;
        status->unicode = status->unicode_repeat;
        rval = true;
      }
    }
  }
  else

  if(input.repeat_stack_pointer)
  {
    // Take repeat off the stack.
    input.repeat_stack_pointer--;
    status->key_repeat =
     input.key_repeat_stack[input.repeat_stack_pointer];
    status->unicode_repeat =
     input.unicode_repeat_stack[input.repeat_stack_pointer];

    status->keypress_time = get_ticks();
  }

  if(last_mouse_state)
  {
    Uint32 new_time = get_ticks();
    Uint32 ms_difference = new_time - status->mouse_time;

    if(status->mouse_drag_state == -1)
      status->mouse_drag_state = 0;
    else
      status->mouse_drag_state = 1;

    if(last_mouse_state == 1)
    {
      if(ms_difference > MOUSE_REPEAT_START)
      {
        status->mouse_time = new_time;
        status->mouse_repeat_state = 2;
        status->mouse_button = status->mouse_repeat;
        rval = true;
      }
    }
    else
    {
      if(ms_difference > MOUSE_REPEAT_RATE)
      {
        status->mouse_time = new_time;
        status->mouse_button = status->mouse_repeat;
        rval = true;
      }
    }
  }

  bump_status();
  return rval;
}

bool update_event_status(void)
{
  struct buffered_status *status = store_status();
  bool rval;

  status->key = IKEY_UNKNOWN;
  status->unicode = 0;
  status->mouse_moved = 0;
  status->mouse_button = 0;

  rval  = __update_event_status();
  rval |= update_autorepeat();

  return rval;
}

void wait_event(void)
{
  struct buffered_status *status = store_status();

  status->key = IKEY_UNKNOWN;
  status->unicode = 0;
  status->mouse_moved = 0;
  status->mouse_button = 0;

  __wait_event();
  update_autorepeat();
}

Uint32 update_event_status_delay(void)
{
  int rval = update_event_status();
  int delay_ticks;

  if(!last_update_time)
    last_update_time = get_ticks();

  delay_ticks = UPDATE_DELAY - (get_ticks() - last_update_time);

  last_update_time = get_ticks();

  if(delay_ticks < 0)
    delay_ticks = 0;

  delay(delay_ticks);
  return rval;
}

static enum keycode emit_keysym_wrt_numlock(enum keycode key)
{
  const struct buffered_status *status = load_status();

  if(status->numlock_status)
  {
    switch(key)
    {
      case IKEY_KP0:         return IKEY_0;
      case IKEY_KP1:         return IKEY_1;
      case IKEY_KP2:         return IKEY_2;
      case IKEY_KP3:         return IKEY_3;
      case IKEY_KP4:         return IKEY_4;
      case IKEY_KP5:         return IKEY_5;
      case IKEY_KP6:         return IKEY_6;
      case IKEY_KP7:         return IKEY_7;
      case IKEY_KP8:         return IKEY_8;
      case IKEY_KP9:         return IKEY_9;
      case IKEY_KP_PERIOD:   return IKEY_PERIOD;
      case IKEY_KP_ENTER:    return IKEY_RETURN;
      default: break;
    }
  }
  else
  {
    switch(key)
    {
      case IKEY_KP0:         return IKEY_INSERT;
      case IKEY_KP1:         return IKEY_END;
      case IKEY_KP2:         return IKEY_DOWN;
      case IKEY_KP3:         return IKEY_PAGEDOWN;
      case IKEY_KP4:         return IKEY_LEFT;
      case IKEY_KP5:         return IKEY_SPACE; // kinda arbitrary
      case IKEY_KP6:         return IKEY_RIGHT;
      case IKEY_KP7:         return IKEY_HOME;
      case IKEY_KP8:         return IKEY_UP;
      case IKEY_KP9:         return IKEY_PAGEUP;
      case IKEY_KP_PERIOD:   return IKEY_DELETE;
      case IKEY_KP_ENTER:    return IKEY_RETURN;
      default: break;
    }
  }
  return key;
}

Uint32 get_key(enum keycode_type type)
{
  const struct buffered_status *status = load_status();

  switch(type)
  {
    case keycode_pc_xt:
      return convert_internal_xt(status->key);

    case keycode_internal:
      return emit_keysym_wrt_numlock(status->key);

    default:
      return status->unicode;
  }
}

Uint32 get_key_status(enum keycode_type type, Uint32 index)
{
  const struct buffered_status *status = load_status();

  switch(type)
  {
    case keycode_pc_xt:
    {
      enum keycode first, second;
      first = convert_xt_internal(index, &second);
      return (status->keymap[first] || status->keymap[second]);
    }

    case keycode_internal:
      return status->keymap[index];

    default:
      return 0;
  }
}

Uint32 get_last_key(enum keycode_type type)
{
  const struct buffered_status *status = load_status();

  switch(type)
  {
    case keycode_pc_xt:
      return convert_internal_xt(status->key_pressed);

    case keycode_internal:
      return status->key_pressed;

    default:
      return 0;
  }
}

Uint32 get_last_key_released(enum keycode_type type)
{
  const struct buffered_status *status = load_status();

  switch(type)
  {
    case keycode_pc_xt:
      return convert_internal_xt(status->key_release);

    case keycode_internal:
      return status->key_release;

    default:
      return 0;
  }
}

void get_mouse_position(int *x, int *y)
{
  const struct buffered_status *status = load_status();
  *x = status->mouse_x;
  *y = status->mouse_y;
}

void get_real_mouse_position(int *x, int *y)
{
  const struct buffered_status *status = load_status();
  *x = status->real_mouse_x;
  *y = status->real_mouse_y;
}

Uint32 get_mouse_x(void)
{
  const struct buffered_status *status = load_status();
  return status->mouse_x;
}

Uint32 get_mouse_y(void)
{
  const struct buffered_status *status = load_status();
  return status->mouse_y;
}

Uint32 get_real_mouse_x(void)
{
  const struct buffered_status *status = load_status();
  return status->real_mouse_x;
}

Uint32 get_real_mouse_y(void)
{
  const struct buffered_status *status = load_status();
  return status->real_mouse_y;
}

Uint32 get_mouse_drag(void)
{
  const struct buffered_status *status = load_status();
  return status->mouse_drag_state;
}

Uint32 get_mouse_press(void)
{
  const struct buffered_status *status = load_status();

  if(status->mouse_button <= MOUSE_BUTTON_RIGHT)
    return status->mouse_button;

  return 0;
}

Uint32 get_mouse_press_ext(void)
{
  const struct buffered_status *status = load_status();
  return status->mouse_button;
}

Uint32 get_mouse_status(void)
{
  const struct buffered_status *status = load_status();
  return status->mouse_button_state;
}

void warp_mouse(Uint32 x, Uint32 y)
{
  int mx_real, my_real, mx = (x * 8) + 4, my = (y * 14) + 7;
  struct buffered_status *status = store_status();

  status->mouse_x = x;
  status->mouse_y = y;
  status->real_mouse_x = mx;
  status->real_mouse_y = my;

  set_screen_coords(mx, my, &mx_real, &my_real);
  real_warp_mouse(mx_real, my_real);
}

void warp_mouse_x(Uint32 x)
{
  struct buffered_status *status = store_status();
  int mx_real, my_real, mx = (x * 8) + 4;

  status->mouse_x = x;
  status->real_mouse_x = mx;

  set_screen_coords(mx, status->real_mouse_y, &mx_real, &my_real);
  real_warp_mouse(mx_real, my_real);
}

void warp_mouse_y(Uint32 y)
{
  struct buffered_status *status = store_status();
  int mx_real, my_real, my = (y * 14) + 7;

  status->mouse_y = y;
  status->real_mouse_y = my;

  set_screen_coords(status->real_mouse_x, my, &mx_real, &my_real);
  real_warp_mouse(mx_real, my_real);
}

void warp_real_mouse_x(Uint32 mx)
{
  struct buffered_status *status = store_status();
  int mx_real, my_real, x = mx / 8;

  status->mouse_x = x;
  status->real_mouse_x = mx;

  set_screen_coords(mx, status->real_mouse_y, &mx_real, &my_real);
  real_warp_mouse(mx_real, my_real);
}

void warp_real_mouse_y(Uint32 my)
{
  struct buffered_status *status = store_status();
  int mx_real, my_real, y = my / 14;

  status->mouse_y = y;
  status->real_mouse_y = my;

  set_screen_coords(status->real_mouse_x, my, &mx_real, &my_real);
  real_warp_mouse(mx_real, my_real);
}

void force_last_key(enum keycode_type type, int val)
{
  struct buffered_status *status = store_status();

  switch(type)
  {
    case keycode_pc_xt:
    {
      enum keycode second;
      status->key_pressed = convert_xt_internal(val, &second);
      break;
    }

    case keycode_internal:
    {
      status->key_pressed = (enum keycode)val;
      break;
    }

    default:
      break;
  }
}

bool get_alt_status(enum keycode_type type)
{
  return get_key_status(type, IKEY_LALT) ||
         get_key_status(type, IKEY_RALT);
}

bool get_shift_status(enum keycode_type type)
{
  return get_key_status(type, IKEY_LSHIFT) ||
         get_key_status(type, IKEY_RSHIFT);
}

bool get_ctrl_status(enum keycode_type type)
{
  return get_key_status(type, IKEY_LCTRL) ||
         get_key_status(type, IKEY_RCTRL);
}

void map_joystick_axis(int joystick, int axis, enum keycode min_key,
 enum keycode max_key)
{
  input.joystick_axis_map[joystick][axis][0] = min_key;
  input.joystick_axis_map[joystick][axis][1] = max_key;
}

void map_joystick_button(int joystick, int button, enum keycode key)
{
  input.joystick_button_map[joystick][button] = key;
}

void set_unfocus_pause(bool value)
{
  input.unfocus_pause = value;
}

void set_num_buffered_events(Uint8 value)
{
  num_buffered_events = MAX(1, value);
}

void key_press(struct buffered_status *status, enum keycode key,
 Uint16 unicode_key)
{
  status->keymap[key] = 1;
  status->key_pressed = key;
  status->key = key;
  status->unicode = unicode_key;
  status->key_repeat = key;
  status->unicode_repeat = unicode_key;
  status->keypress_time = get_ticks();
  status->key_release = IKEY_UNKNOWN;
}

void key_release(struct buffered_status *status, enum keycode key)
{
  status->keymap[key] = 0;
  status->key_repeat = IKEY_UNKNOWN;
  status->unicode_repeat = 0;
  status->key_release = key;
}


code:
/* MegaZeux
 *
 * Copyright (C) 2002 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __EVENT_H
#define __EVENT_H

#include "compat.h"

__M_BEGIN_DECLS

#include "platform.h"
#include "keysym.h"

#define UPDATE_DELAY 30

#define KEY_REPEAT_STACK_SIZE 32

#define MOUSE_BUTTON(x)         (1 << ((x) - 1))
#define MOUSE_BUTTON_LEFT       1
#define MOUSE_BUTTON_MIDDLE     2
#define MOUSE_BUTTON_RIGHT      3
#define MOUSE_BUTTON_WHEELUP    4
#define MOUSE_BUTTON_WHEELDOWN  5

struct buffered_status
{
  enum keycode key_pressed;
  enum keycode key;
  enum keycode key_repeat;
  enum keycode key_release;
  Uint16 unicode;
  Uint16 unicode_repeat;
  Uint32 keypress_time;
  Uint32 mouse_x;
  Uint32 mouse_y;
  Uint32 real_mouse_x;
  Uint32 real_mouse_y;
  Uint32 mouse_button;
  Uint32 mouse_repeat;
  Uint32 mouse_repeat_state;
  Uint32 mouse_time;
  Sint32 mouse_drag_state;
  Uint32 mouse_button_state;
  bool caps_status;
  bool numlock_status;
  bool mouse_moved;
  Sint8 axis[16][16];
  Uint8 keymap[512];
};

struct input_status
{
  struct buffered_status *buffer;
  Uint16 load_offset;
  Uint16 store_offset;

  enum keycode key_repeat_stack[KEY_REPEAT_STACK_SIZE];
  Uint16 unicode_repeat_stack[KEY_REPEAT_STACK_SIZE];
  Uint32 repeat_stack_pointer;

  enum keycode joystick_button_map[16][256];
  enum keycode joystick_axis_map[16][16][2];

  bool unfocus_pause;
};

enum keycode_type
{
  keycode_pc_xt,
  keycode_internal,
  keycode_unicode
};

CORE_LIBSPEC void init_event(void);

struct buffered_status *store_status(void);

CORE_LIBSPEC bool update_event_status(void);
CORE_LIBSPEC Uint32 update_event_status_delay(void);
CORE_LIBSPEC Uint32 get_key(enum keycode_type type);
CORE_LIBSPEC void get_mouse_position(int *x, int *y);
CORE_LIBSPEC void get_real_mouse_position(int *x, int *y);
CORE_LIBSPEC Uint32 get_mouse_press(void);
CORE_LIBSPEC Uint32 get_mouse_press_ext(void);
CORE_LIBSPEC Uint32 get_mouse_status(void);
CORE_LIBSPEC void warp_mouse(Uint32 x, Uint32 y);
CORE_LIBSPEC Uint32 get_mouse_drag(void);
CORE_LIBSPEC bool get_alt_status(enum keycode_type type);
CORE_LIBSPEC bool get_shift_status(enum keycode_type type);
CORE_LIBSPEC bool get_ctrl_status(enum keycode_type type);
CORE_LIBSPEC void initialize_joysticks(void);
CORE_LIBSPEC void key_press(struct buffered_status *status, enum keycode key,
 Uint16 unicode_key);
CORE_LIBSPEC void key_release(struct buffered_status *status, enum keycode key);

// Implemented by "drivers" (SDL, Wii, and NDS currently)
void __wait_event(void);
bool __update_event_status(void);

void wait_event(void);
Uint32 get_last_key(enum keycode_type type);
void force_last_key(enum keycode_type type, int val);
Uint32 get_key_status(enum keycode_type type, Uint32 index);
void warp_mouse_x(Uint32 x);
void warp_mouse_y(Uint32 y);
void warp_real_mouse_x(Uint32 x);
void warp_real_mouse_y(Uint32 y);
Uint32 get_mouse_x(void);
Uint32 get_mouse_y(void);
Uint32 get_real_mouse_x(void);
Uint32 get_real_mouse_y(void);
Uint32 get_last_key_released(enum keycode_type type);
void map_joystick_axis(int joystick, int axis, enum keycode min_key,
 enum keycode max_key);
void map_joystick_button(int joystick, int button, enum keycode key);
void set_unfocus_pause(bool value);
void set_num_buffered_events(Uint8 value);

void real_warp_mouse(Uint32 x, Uint32 y);

__M_END_DECLS

#endif // __EVENT_H


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007 Kevin Vance <kvance@kvance.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "event.h"
#include "platform.h"
#include "graphics.h"

#include "SDL.h"

extern struct input_status input;

static bool numlock_status_initialized;

static enum keycode convert_SDL_internal(SDLKey key)
{
  switch(key)
  {
    case SDLK_BACKSPACE: return IKEY_BACKSPACE;
    case SDLK_TAB: return IKEY_TAB;
    case SDLK_RETURN: return IKEY_RETURN;
    case SDLK_ESCAPE: return IKEY_ESCAPE;
    case SDLK_SPACE: return IKEY_SPACE;
    case SDLK_QUOTE: return IKEY_QUOTE;
    case SDLK_PLUS: return IKEY_EQUALS;
    case SDLK_COMMA: return IKEY_COMMA;
    case SDLK_MINUS: return IKEY_MINUS;
    case SDLK_PERIOD: return IKEY_PERIOD;
    case SDLK_SLASH: return IKEY_SLASH;
    case SDLK_0: return IKEY_0;
    case SDLK_1: return IKEY_1;
    case SDLK_2: return IKEY_2;
    case SDLK_3: return IKEY_3;
    case SDLK_4: return IKEY_4;
    case SDLK_5: return IKEY_5;
    case SDLK_6: return IKEY_6;
    case SDLK_7: return IKEY_7;
    case SDLK_8: return IKEY_8;
    case SDLK_9: return IKEY_9;
    case SDLK_SEMICOLON: return IKEY_SEMICOLON;
#if SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION <= 2
    case SDLK_EQUALS: return IKEY_EQUALS;
#endif
    case SDLK_LEFTBRACKET: return IKEY_LEFTBRACKET;
    case SDLK_BACKSLASH: return IKEY_BACKSLASH;
    case SDLK_RIGHTBRACKET: return IKEY_RIGHTBRACKET;
    case SDLK_BACKQUOTE: return IKEY_BACKQUOTE;
    case SDLK_a: return IKEY_a;
    case SDLK_b: return IKEY_b;
    case SDLK_c: return IKEY_c;
    case SDLK_d: return IKEY_d;
    case SDLK_e: return IKEY_e;
    case SDLK_f: return IKEY_f;
    case SDLK_g: return IKEY_g;
    case SDLK_h: return IKEY_h;
    case SDLK_i: return IKEY_i;
    case SDLK_j: return IKEY_j;
    case SDLK_k: return IKEY_k;
    case SDLK_l: return IKEY_l;
    case SDLK_m: return IKEY_m;
    case SDLK_n: return IKEY_n;
    case SDLK_o: return IKEY_o;
    case SDLK_p: return IKEY_p;
    case SDLK_q: return IKEY_q;
    case SDLK_r: return IKEY_r;
    case SDLK_s: return IKEY_s;
    case SDLK_t: return IKEY_t;
    case SDLK_u: return IKEY_u;
    case SDLK_v: return IKEY_v;
    case SDLK_w: return IKEY_w;
    case SDLK_x: return IKEY_x;
    case SDLK_y: return IKEY_y;
    case SDLK_z: return IKEY_z;
    case SDLK_DELETE: return IKEY_DELETE;
    case SDLK_KP0: return IKEY_KP0;
    case SDLK_KP1: return IKEY_KP1;
    case SDLK_KP2: return IKEY_KP2;
    case SDLK_KP3: return IKEY_KP3;
    case SDLK_KP4: return IKEY_KP4;
    case SDLK_KP5: return IKEY_KP5;
    case SDLK_KP6: return IKEY_KP6;
    case SDLK_KP7: return IKEY_KP7;
    case SDLK_KP8: return IKEY_KP8;
    case SDLK_KP9: return IKEY_KP9;
    case SDLK_KP_PERIOD: return IKEY_KP_PERIOD;
    case SDLK_KP_DIVIDE: return IKEY_KP_DIVIDE;
    case SDLK_KP_MULTIPLY: return IKEY_KP_MULTIPLY;
    case SDLK_KP_MINUS: return IKEY_KP_MINUS;
    case SDLK_KP_PLUS: return IKEY_KP_PLUS;
    case SDLK_KP_ENTER: return IKEY_KP_ENTER;
    case SDLK_UP: return IKEY_UP;
    case SDLK_DOWN: return IKEY_DOWN;
    case SDLK_RIGHT: return IKEY_RIGHT;
    case SDLK_LEFT: return IKEY_LEFT;
    case SDLK_INSERT: return IKEY_INSERT;
    case SDLK_F1: return IKEY_F1;
    case SDLK_F2: return IKEY_F2;
    case SDLK_F3: return IKEY_F3;
    case SDLK_F4: return IKEY_F4;
    case SDLK_F5: return IKEY_F5;
    case SDLK_F6: return IKEY_F6;
    case SDLK_F7: return IKEY_F7;
    case SDLK_F8: return IKEY_F8;
    case SDLK_F9: return IKEY_F9;
    case SDLK_F10: return IKEY_F10;
    case SDLK_F11: return IKEY_F11;
    case SDLK_F12: return IKEY_F12;
    case SDLK_NUMLOCK: return IKEY_NUMLOCK;
    case SDLK_CAPSLOCK: return IKEY_CAPSLOCK;
    case SDLK_SCROLLOCK: return IKEY_SCROLLOCK;
    case SDLK_LSHIFT: return IKEY_LSHIFT;
    case SDLK_LCTRL: return IKEY_LCTRL;
    case SDLK_RALT: return IKEY_RALT;
    case SDLK_LALT: return IKEY_LALT;
    case SDLK_LSUPER: return IKEY_LSUPER;
    case SDLK_RSUPER: return IKEY_RSUPER;
    case SDLK_SYSREQ: return IKEY_SYSREQ;
    case SDLK_BREAK: return IKEY_BREAK;
    case SDLK_MENU: return IKEY_MENU;
#ifndef CONFIG_PANDORA
    case SDLK_HOME: return IKEY_HOME;
    case SDLK_END: return IKEY_END;
    case SDLK_PAGEUP: return IKEY_PAGEUP;
    case SDLK_PAGEDOWN: return IKEY_PAGEDOWN;
    case SDLK_RSHIFT: return IKEY_RSHIFT;
    case SDLK_RCTRL: return IKEY_RCTRL;
#else /* CONFIG_PANDORA */
    case SDLK_HOME: return input.joystick_button_map[0][0];
    case SDLK_END: return input.joystick_button_map[0][1];
    case SDLK_PAGEUP: return input.joystick_button_map[0][2];
    case SDLK_PAGEDOWN: return input.joystick_button_map[0][3];
    case SDLK_RSHIFT: return input.joystick_button_map[0][4];
    case SDLK_RCTRL: return input.joystick_button_map[0][5];
#endif /* CONFIG_PANDORA */
    default: return IKEY_UNKNOWN;
  }
}

static bool process_event(SDL_Event *event)
{
  struct buffered_status *status = store_status();
  enum keycode ckey;

  /* SDL's numlock keyboard modifier handling seems to be broken on X11,
   * and it will only get numlock's status right on application init. We
   * can trust this value once, and then toggle based on user presses of
   * the numlock key.
   *
   * On Windows, KEYDOWN/KEYUP seem to be sent separately, to indicate
   * enabling or disabling of numlock. But on X11, both KEYDOWN/KEYUP are
   * sent for each toggle, so this must be handled differently.
   *
   * What a mess!
   */
  if(!numlock_status_initialized)
  {
    status->numlock_status = !!(SDL_GetModState() & KMOD_NUM);
    numlock_status_initialized = true;
  }

  switch(event->type)
  {
    case SDL_QUIT:
    {
      // Stuff an escape
      status->key = IKEY_ESCAPE;
      status->keymap[IKEY_ESCAPE] = 1;
      status->keypress_time = get_ticks();
      break;
    }

    case SDL_VIDEORESIZE:
    {
      resize_screen(event->resize.w, event->resize.h);
      break;
    }

    case SDL_MOUSEMOTION:
    {
      int mx_real = event->motion.x;
      int my_real = event->motion.y;
      int mx, my, min_x, min_y, max_x, max_y;
      get_screen_coords(mx_real, my_real, &mx, &my, &min_x,
       &min_y, &max_x, &max_y);

      if(mx > 639)
        SDL_WarpMouse(max_x, my_real);

      if(mx < 0)
        SDL_WarpMouse(min_x, my_real);

      if(my > 349)
        SDL_WarpMouse(mx_real, max_y);

      if(my < 0)
        SDL_WarpMouse(mx_real, min_y);

      status->real_mouse_x = mx;
      status->real_mouse_y = my;
      status->mouse_x = mx / 8;
      status->mouse_y = my / 14;
      status->mouse_moved = true;
      break;
    }

    case SDL_MOUSEBUTTONDOWN:
    {
      status->mouse_button = event->button.button;
      status->mouse_repeat = event->button.button;
      status->mouse_button_state |= SDL_BUTTON(event->button.button);
      status->mouse_repeat_state = 1;
      status->mouse_drag_state = -1;
      status->mouse_time = SDL_GetTicks();
      break;
    }

    case SDL_MOUSEBUTTONUP:
    {
      status->mouse_button_state &= ~SDL_BUTTON(event->button.button);
      status->mouse_repeat = 0;
      status->mouse_drag_state = 0;
      status->mouse_repeat_state = 0;
      break;
    }

    case SDL_KEYDOWN:
    {
      ckey = convert_SDL_internal(event->key.keysym.sym);
      if(!ckey)
      {
        if(event->key.keysym.unicode)
          ckey = IKEY_UNICODE;
        else
          break;
      }

      if((ckey == IKEY_RETURN) &&
       get_alt_status(keycode_internal) &&
       get_ctrl_status(keycode_internal))
      {
        toggle_fullscreen();
        break;
      }

      if(ckey == IKEY_CAPSLOCK)
      {
        status->caps_status = true;
      }

      if(ckey == IKEY_NUMLOCK)
      {
#ifdef __WIN32__
        status->numlock_status = true;
#endif
        break;
      }

      if(ckey == IKEY_F12)
      {
        dump_screen();
        break;
      }

      // Ignore alt + tab
      if((ckey == IKEY_TAB) && get_alt_status(keycode_internal))
      {
        break;
      }

      if(status->key_repeat &&
       (status->key_repeat != IKEY_LSHIFT) &&
       (status->key_repeat != IKEY_RSHIFT) &&
       (status->key_repeat != IKEY_LALT) &&
       (status->key_repeat != IKEY_RALT) &&
       (status->key_repeat != IKEY_LCTRL) &&
       (status->key_repeat != IKEY_RCTRL))
      {
        // Stack current repeat key if it isn't shift, alt, or ctrl
        if(input.repeat_stack_pointer != KEY_REPEAT_STACK_SIZE)
        {
          input.key_repeat_stack[input.repeat_stack_pointer] =
           status->key_repeat;
          input.unicode_repeat_stack[input.repeat_stack_pointer] =
           status->unicode_repeat;
          input.repeat_stack_pointer++;
        }
      }

      key_press(status, ckey, event->key.keysym.unicode);
      break;
    }

    case SDL_KEYUP:
    {
      ckey = convert_SDL_internal(event->key.keysym.sym);
      if(!ckey)
      {
        if(status->keymap[IKEY_UNICODE])
          ckey = IKEY_UNICODE;
        else
          break;
      }

      if(ckey == IKEY_NUMLOCK)
      {
#ifdef __WIN32__
        status->numlock_status = false;
#else
        status->numlock_status = !status->numlock_status;
#endif
        break;
      }

      if(ckey == IKEY_CAPSLOCK)
      {
        status->caps_status = false;
      }

      status->keymap[ckey] = 0;
      if(status->key_repeat == ckey)
      {
        status->key_repeat = IKEY_UNKNOWN;
        status->unicode_repeat = 0;
      }
      status->key_release = ckey;
      break;
    }

    case SDL_ACTIVEEVENT:
    {
      if(input.unfocus_pause)
      {
        // Pause while minimized
        if(event->active.state & (SDL_APPACTIVE | SDL_APPINPUTFOCUS))
        {
          // Wait for SDL_APPACTIVE with gain of 1
          do
          {
            SDL_WaitEvent(event);
          } while((event->type != SDL_ACTIVEEVENT) ||
           (event->active.state & ~(SDL_APPACTIVE | SDL_APPINPUTFOCUS)));
        }
      }
      break;
    }

    case SDL_JOYAXISMOTION:
    {
      int axis_value = event->jaxis.value;
      int digital_value = -1;
      int which = event->jaxis.which;
      int axis = event->jaxis.axis;
      Sint8 last_axis = status->axis[which][axis];
      enum keycode stuffed_key;

      if(axis_value > 10000)
        digital_value = 1;
      else

      if(axis_value < -10000)
        digital_value = 0;

      if(digital_value != -1)
      {
        stuffed_key =
          input.joystick_axis_map[which][axis][digital_value];

        if(stuffed_key)
        {
          if(status->keymap[stuffed_key] == 0)
            key_press(status, stuffed_key, stuffed_key);

          if(last_axis == (digital_value ^ 1))
          {
            key_release(status,
             input.joystick_axis_map[which][axis][last_axis]);
          }
        }
      }
      else if(last_axis != -1)
      {
        key_release(status,
          input.joystick_axis_map[which][axis][last_axis]);
      }

      status->axis[which][axis] = digital_value;
      break;
    }

    case SDL_JOYBUTTONDOWN:
    {
      int which = event->jbutton.which;
      int button = event->jbutton.button;
      enum keycode stuffed_key = input.joystick_button_map[which][button];

      if(stuffed_key && (status->keymap[stuffed_key] == 0))
        key_press(status, stuffed_key, stuffed_key);

      break;
    }

    case SDL_JOYBUTTONUP:
    {
      int which = event->jbutton.which;
      int button = event->jbutton.button;
      enum keycode stuffed_key = input.joystick_button_map[which][button];

      if(stuffed_key)
        key_release(status, stuffed_key);

      break;
    }

    default:
      return false;
  }

  return true;
}

bool __update_event_status(void)
{
  Uint32 rval = false;
  SDL_Event event;

  while(SDL_PollEvent(&event))
    rval |= process_event(&event);

  return rval;
}

void __wait_event(void)
{
  SDL_Event event;

  // FIXME: WaitEvent with MSVC hangs the render cycle, so this is, hopefully,
  //        a short-term fix.
  #ifdef MSVC_H
    SDL_PollEvent(&event);
  #else
    SDL_WaitEvent(&event);
  #endif

  process_event(&event);
}

void real_warp_mouse(Uint32 x, Uint32 y)
{
  SDL_WarpMouse(x, y);
}

void initialize_joysticks(void)
{
  int i, count;

  count = SDL_NumJoysticks();

  if(count > 16)
    count = 16;

  for(i = 0; i < count; i++)
  {
    SDL_JoystickOpen(i);
  }

  SDL_JoystickEventState(SDL_ENABLE);
}