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 "char_ed.h"

#include "../helpsys.h"
#include "../window.h"
#include "../graphics.h"
#include "../data.h"
#include "../event.h"
#include "../world.h"

#include "graphics.h"

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

//----------------------------------------------
//  +----------------+ Current char-     (#000)
//  |................|    ..........0..........
//  |................| %%      Move cursor
//  |................| -+      Change char
//  |................| Space   Toggle pixel
//  |................| Enter   Select char
//  |................| Del     Clear char
//  |................| N       'Negative'
//  |................| Alt+%%  Shift char
//  |................| M       'Mirror'
//  |................| F       'Flip'
//  |................| F2      Copy to buffer
//  |................| F3      Copy from buffer
//  |................| Tab     Draw (off)
//  |................| F4      Revert to ASCII
//  +----------------+ F5      Revert to MZX
//----------------------------------------------

//Mouse- Menu=Command
//       Grid=Toggle
//       "Current Char-"=Enter
//       "..0.."=Change char

static char char_copy_buffer[8 * 14 * 32];
static int char_copy_width = 8;
static int char_copy_height = 14;
static char current_char = 1;
static int current_width = 1;
static int current_height = 1;
static int highlight_width = 1;
static int highlight_height = 1;
static int num_files = 1;

static int char_copy_use_offset = 0;

static void fill_region(char *buffer, int x, int y,
 int buffer_width, int buffer_height, int check, int draw)
{
  int offset = x + (y * buffer_width);
  if((x >= 0) && (x < buffer_width) &&
   (y >= 0) && (y < buffer_height) &&
   (buffer[offset] == check))
  {
    buffer[offset] = draw;

    fill_region(buffer, x - 1, y, buffer_width,
     buffer_height, check, draw);
    fill_region(buffer, x + 1, y, buffer_width,
     buffer_height, check, draw);
    fill_region(buffer, x, y - 1, buffer_width,
     buffer_height, check, draw);
    fill_region(buffer, x, y + 1, buffer_width,
     buffer_height, check, draw);
  }
}

static void expand_buffer(char *buffer, int width, int height,
 int cwidth, int cheight, int offset, int smzx)
{
  char char_buffer[32 * 14];
  char *buffer_ptr = buffer;
  char *cbuffer_ptr = char_buffer;
  int current_char = offset;
  int cx, cy, x, y, i, shift;
  int cskip = 32 - cwidth;
  char *last_ptr, *last_ptr2;

  for(cy = 0; cy < cheight; cy++, current_char += cskip)
  {
    for(cx = 0; cx < cwidth; cx++, current_char++,
     cbuffer_ptr += 14)
    {
      ec_read_char(current_char, cbuffer_ptr);
    }
  }

  if(smzx)
  {
    int skip = (width - 1) * 4;
    int skip2 = width * 4 * 14;

    for(cbuffer_ptr = char_buffer, y = 0; y < height; y++)
    {
      last_ptr = buffer_ptr;
      for(x = 0; x < width; x++)
      {
        last_ptr2 = buffer_ptr;
        for(i = 0; i < 14; i++, cbuffer_ptr++,
         buffer_ptr += skip)
        {
          for(shift = 6; shift >= 0; shift -= 2, buffer_ptr++)
          {
            *buffer_ptr = ((*cbuffer_ptr) >> shift) & 3;
          }
        }
        buffer_ptr = last_ptr2 + 4;
      }

      buffer_ptr = last_ptr + skip2;
    }
  }
  else
  {
    int skip = (width - 1) * 8;
    int skip2 = width * 8 * 14;

    for(cbuffer_ptr = char_buffer, y = 0; y < height; y++)
    {
      last_ptr = buffer_ptr;
      for(x = 0; x < width; x++)
      {
        last_ptr2 = buffer_ptr;
        for(i = 0; i < 14; i++, cbuffer_ptr++,
         buffer_ptr += skip)
        {
          for(shift = 7; shift >= 0; shift--, buffer_ptr++)
          {
            *buffer_ptr = ((*cbuffer_ptr) >> shift) & 1;
          }
        }
        buffer_ptr = last_ptr2 + 8;
      }

      buffer_ptr = last_ptr + skip2;
    }
  }
}

static void collapse_buffer(char *buffer, int width, int height,
 int cwidth, int cheight, int offset, int smzx)
{
  char char_buffer[32 * 14];
  char *buffer_ptr = buffer;
  char *cbuffer_ptr = char_buffer;
  int current_char = offset;
  int cx, cy, x, y, i, shift;
  int cskip = 32 - cwidth;
  char *last_ptr, *last_ptr2;
  int current_row;

  if(smzx)
  {
    int skip = (width - 1) * 4;
    int skip2 = width * 4 * 14;

    for(y = 0; y < height; y++)
    {
      last_ptr = buffer_ptr;
      for(x = 0; x < width; x++)
      {
        last_ptr2 = buffer_ptr;
        for(i = 0; i < 14; i++, cbuffer_ptr++,
         buffer_ptr += skip)
        {
          current_row = 0;
          for(shift = 6; shift >= 0; shift -= 2, buffer_ptr++)
          {
            current_row |= (*buffer_ptr) << shift;
          }
          *cbuffer_ptr = current_row;
        }
        buffer_ptr = last_ptr2 + 4;
      }

      buffer_ptr = last_ptr + skip2;
    }
  }
  else
  {
    int skip = (width - 1) * 8;
    int skip2 = width * 8 * 14;

    for(y = 0; y < height; y++)
    {
      last_ptr = buffer_ptr;
      for(x = 0; x < width; x++)
      {
        last_ptr2 = buffer_ptr;
        for(i = 0; i < 14; i++, cbuffer_ptr++,
         buffer_ptr += skip)
        {
          current_row = 0;
          for(shift = 7; shift >= 0; shift--, buffer_ptr++)
          {
            current_row |= (*buffer_ptr) << shift;
          }
          *cbuffer_ptr = current_row;
        }
        buffer_ptr = last_ptr2 + 8;
      }

      buffer_ptr = last_ptr + skip2;
    }
  }

  for(cbuffer_ptr = char_buffer, cy = 0; cy < cheight;
   cy++, current_char += cskip)
  {
    for(cx = 0; cx < cwidth; cx++, current_char++,
     cbuffer_ptr += 14)
    {
      ec_change_char(current_char, cbuffer_ptr);
    }
  }
}

static void draw_multichar(char *buffer, int start_x, int start_y,
 int width, int height, int current_x, int current_y,
 int highlight_width, int highlight_height)
{
  char *buffer_ptr = buffer;
  int offset = start_x + (start_y * 80);
  int buffer_width = width * 8;
  int x, y;

  if((height == 1) && (width <= 3))
  {
    int skip = 80 - (buffer_width * 2);
    char chars[2] = { 250, 219 };

    for(y = 0; y < 14; y++, offset += skip)
    {
      for(x = 0; x < (width * 8); x++, offset += 2, buffer_ptr++)
      {
        draw_char_linear(135, chars[(int)(*buffer_ptr)], offset);
        draw_char_linear(135, chars[(int)(*buffer_ptr)], offset + 1);
      }
    }

    for(y = 0; y < highlight_height; y++)
    {
      color_line(highlight_width * 2, start_x + (current_x * 2),
       start_y + current_y + y, 0x1B);
    }
  }
  else
  {
    char half_chars[4] = { 32, 223, 220, 219 };
    int skip = 80 - buffer_width;
    int skip2 = buffer_width;
    int current_char;

    for(y = 0; y < (height * 7); y++, offset += skip,
     buffer_ptr += skip2)
    {
      for(x = 0; x < skip2; x++, offset++, buffer_ptr++)
      {
        current_char = (*buffer_ptr) |
         (*(buffer_ptr + skip2) << 1);
        draw_char_linear(135, half_chars[current_char], offset);
      }
    }

    // Start at the top
    y = highlight_height;
    if(current_y & 1)
    {
      char colors[4] = { 0x81, 0x71, 0x8B, 0x7B };
      buffer_ptr = buffer + ((current_y - 1) * buffer_width)
       + current_x;
      for(x = 0; x < highlight_width; x++, offset++, buffer_ptr++)
      {
        current_char =
         *(buffer_ptr) | (*(buffer_ptr + buffer_width) << 1);
        draw_char(half_chars[2], colors[current_char], start_x +
         x + current_x, start_y + (current_y / 2));
      }
      current_y++;
      y--;
    }

    while(y > 1)
    {
      buffer_ptr = buffer + (current_y * buffer_width) + current_x;
      color_line(highlight_width, start_x + current_x,
       start_y + (current_y / 2), 0x1B);
      current_y += 2;
      y -= 2;
    }

    if(y)
    {
      char colors[4] = { 0x81, 0x8B, 0x71, 0x7B };
      buffer_ptr = buffer + (current_y * buffer_width) + current_x;
      for(x = 0; x < highlight_width; x++, offset++, buffer_ptr++)
      {
        current_char =
         *(buffer_ptr) | (*(buffer_ptr + buffer_width) << 1);
        draw_char(half_chars[1], colors[current_char], start_x +
         x + current_x, start_y + (current_y / 2));
      }
    }
  }
}

static void draw_multichar_smzx(char *buffer, int start_x, int start_y,
 int width, int height, int current_x, int current_y,
 int highlight_width, int highlight_height)
{
  char *buffer_ptr = buffer;
  char current_color;
  char base_colors[4] = { 0x8, 0x2, 0x3, 0x7 };
  char highlight_colors[4] = { 0x1, 0x4, 0x5, 0xB };
  int offset = start_x + (start_y * 80);
  int buffer_width = width * 4;

  int x, y;

  if((height == 1) && (width <= 3))
  {
    int skip = 80 - (buffer_width * 4);
    int skip2 = buffer_width - highlight_width;

    int current_pixel;
    for(y = 0; y < 14; y++, offset += skip)
    {
      for(x = 0; x < buffer_width; x++, buffer_ptr++)
      {
        current_pixel = *buffer_ptr;

        if(current_pixel)
        {
          write_string("\xDB\xDB\xDB\xDB", start_x + (x * 4),
           start_y + y, base_colors[current_pixel], 1);
        }
        else
        {
          write_string("\xFA\xFA\xFA\xFA", start_x + (x * 4),
           start_y + y, 0x87, 1);
        }
      }
    }

    buffer_ptr = buffer + current_x + ((current_y) * buffer_width);

    for(y = 0; y < highlight_height; y++,
     buffer_ptr += skip2)
    {
      for(x = 0; x < highlight_width; x++, buffer_ptr++)
      {
        current_pixel = *buffer_ptr;

        if(current_pixel)
        {
          write_string("\xDB\xDB\xDB\xDB", start_x +
           ((x + current_x) * 4), start_y + current_y + y,
           highlight_colors[current_pixel], 1);
        }
        else
        {
          write_string("\xFA\xFA\xFA\xFA", start_x +
           ((x + current_x) * 4), start_y + y + current_y,
           0x1B, 1);
        }
      }
    }
  }
  else
  {
    int skip = 80 - (buffer_width * 2);
    int skip2 = buffer_width - highlight_width;
    int skip3 = 80 - (highlight_width * 2);

    for(y = 0; y < (height * 7); y++, offset += skip,
     buffer_ptr += buffer_width)
    {
      for(x = 0; x < buffer_width; x++, offset += 2, buffer_ptr++)
      {
        current_color = (base_colors[(int)(*buffer_ptr)]) |
         ((base_colors[(int)(*(buffer_ptr + buffer_width))]) << 4);
        draw_char_linear(current_color, 223, offset);
        draw_char_linear(current_color, 223, offset + 1);
      }
    }

    offset = start_x + (current_x * 2) +
     ((start_y + (current_y / 2)) * 80);

    // Start at the top
    y = highlight_height;
    if(current_y & 1)
    {
      buffer_ptr = buffer + current_x +
       ((current_y & ~1) * buffer_width);

      for(x = 0; x < highlight_width; x++, offset += 2,
       buffer_ptr++)
      {
        current_color = (base_colors[(int)(*buffer_ptr)]) |
         ((highlight_colors[(int)(*(buffer_ptr + buffer_width))]) << 4);
        draw_char_linear(current_color, 223, offset);
        draw_char_linear(current_color, 223, offset + 1);
      }

      buffer_ptr += (skip2 * 2);
      offset += skip3;
      y--;

      current_y++;
    }

    while(y > 1)
    {
      buffer_ptr = buffer + current_x +
       ((current_y & ~1) * buffer_width);

      for(x = 0; x < highlight_width; x++, offset += 2,
       buffer_ptr++)
      {
        current_color = (highlight_colors[(int)(*buffer_ptr)]) |
         ((highlight_colors[(int)(*(buffer_ptr + buffer_width))]) << 4);
        draw_char_linear(current_color, 223, offset);
        draw_char_linear(current_color, 223, offset + 1);
      }
      buffer_ptr += (skip2 * 2);
      offset += skip3;
      current_y += 2;
      y -= 2;
    }

    if(y)
    {
      buffer_ptr = buffer + current_x +
       ((current_y & ~1) * buffer_width);

      for(x = 0; x < highlight_width; x++, offset += 2,
       buffer_ptr++)
      {
        current_color = (highlight_colors[(int)(*buffer_ptr)]) |
         ((base_colors[(int)(*(buffer_ptr + buffer_width))]) << 4);
        draw_char_linear(current_color, 223, offset);
        draw_char_linear(current_color, 223, offset + 1);
      }
    }
  }
}

static void replace_filenum(char *src, char *dest, int num)
{
  char *src_ptr = src;
  char *dest_ptr = dest;

  while(*src_ptr)
  {
    if(*src_ptr == '#')
    {
      sprintf(dest_ptr, "%d", num);
      dest_ptr += strlen(dest_ptr);
    }
    else
    {
      *dest_ptr = *src_ptr;
      dest_ptr++;
    }

    src_ptr++;
  }

  *dest_ptr = 0;
}

int char_editor(struct world *mzx_world)
{
  int x = 0;
  int y = 0;
  int draw = 0;
  int draw_new;
  int chars_width, chars_height;
  int chars_x, chars_y;
  int info_height;
  int info_x, info_y;
  int buffer_width = current_width * 8;
  int buffer_height = current_height * 14;
  int buffer_size = buffer_width * buffer_height;
  int dialog_width, dialog_height;
  int dialog_x, dialog_y;
  int chars_show_width;
  char *buffer = cmalloc(8 * 14 * 32);
  char *previous = cmalloc(8 * 14 * 32);
  char mini_buffer[32];
  int last_x = -1, last_y = 0;
  int block_x = 0;
  int block_y = 0;
  int block_start_x = 0;
  int block_start_y = 0;
  int block_width = buffer_width;
  int block_height = buffer_height;
  int block_mode = 0;
  int shifted = 0;
  int offset;
  int i, i2, key;
  int pad_height;
  int small_chars;
  int screen_mode = get_screen_mode();
  int current_pixel = 0;
  char smzx_colors[4] = { 0x8, 0x2, 0x3, 0x7 };
  unsigned char saved_rgb[4][3];

  if(screen_mode)
  {
    if(screen_mode != 1)
      set_screen_mode(1);

    for(i = 0; i < 4; i++)
    {
      get_rgb(i + 2, saved_rgb[i], saved_rgb[i] + 1,
       saved_rgb[i] + 2);
    }

    set_rgb(2, 35, 35, 35);
    set_rgb(3, 28, 28, 28);
    set_rgb(4, 14, 42, 56);
    set_rgb(5, 7, 21, 49);
    update_palette();
  }

  set_context(79);

  m_show();
  // Get char
  expand_buffer(buffer, current_width,
   current_height, highlight_width, highlight_height,
   current_char, screen_mode);
  memcpy(previous, buffer, buffer_size);

  save_screen();

  do
  {
    buffer_width = current_width * 8;
    buffer_height = current_height * 14;
    buffer_size = buffer_width * buffer_height;

    if((current_height == 1) &&
     (current_width <= 3))
    {
      small_chars = 0;
      set_mouse_mul(8, 14);
      chars_width = buffer_width * 2;
      chars_height = buffer_height;
    }
    else
    {
      small_chars = 1;
      set_mouse_mul(8, 7);
      chars_width = buffer_width;
      chars_height = buffer_height / 2;
    }

    if(screen_mode)
      buffer_width /= 2;

    if(x >= buffer_width)
      x = buffer_width - 1;

    if(y >= buffer_height)
      y = buffer_height - 1;

    dialog_width = chars_width + 4 + 27;
    dialog_height = chars_height + 4;
    pad_height = current_height;
    if(highlight_height > pad_height)
      pad_height = highlight_height;

    info_height = 15 + pad_height;

    if(dialog_height < (info_height + 2))
      dialog_height = info_height + 2;

    if(dialog_width == 79)
      dialog_width++;

    dialog_x = 40 - (dialog_width / 2);
    dialog_y = 12 - (dialog_height / 2);

    chars_x = dialog_x + 3;
    chars_y = dialog_y + (dialog_height / 2) - (chars_height / 2);
    info_x = chars_x + chars_width + 2;
    info_y = dialog_y + (dialog_height / 2) - (info_height / 2);

    chars_show_width = 22 - current_width;

    restore_screen();
    save_screen();
    draw_window_box(dialog_x, dialog_y,
     dialog_x + dialog_width - 1,
     dialog_y + dialog_height - 1, 143, 128, 135, 1, 1);
    // Draw in static elements
    draw_window_box(chars_x - 1, chars_y - 1,
     chars_x + chars_width, chars_y + chars_height,
     128, 143, 135, 0, 0);
    write_string("Current char-\t(#000)",
     info_x, info_y, 143, 1);

    if(screen_mode)
    {
      write_string
      (
        "\x12\x1d\t Move cursor\n"
        "-+\t Change char\n"
        "Space   Place pixel\n"
        "Enter   Select char\n"
        "Del\tClear char\n"
        "N\t  'Negative'\n"
        "Alt+\x12\x1d  Shift char\n"
        "M\t  'Mirror'\n"
        "F\t  'Flip'\n"
        "F2\t Copy to buffer\n"
        "F3\t Copy from buffer\n"
        "Tab\tDraw\n"
        "1-4\tSelect\n"
        "F5\t Revert to SMZX",
         info_x, info_y + pad_height + 1, 143, 1
      );

      if(current_pixel)
      {
        write_string("\xDB\xDB\xDB\xDB", info_x + 16,
         info_y + pad_height + 13, smzx_colors[current_pixel], 1);
      }
      else
      {
        write_string("\xFA\xFA\xFA\xFA", info_x + 16,
         info_y + pad_height + 13, 0x87, 1);
      }
    }
    else
    {
      write_string
      (
        "\x12\x1d\t Move cursor\n"
        "-+\t Change char\n"
        "Space   Toggle pixel\n"
        "Enter   Select char\n"
        "Del\tClear char\n"
        "N\t  'Negative'\n"
        "Alt+\x12\x1d  Shift char\n"
        "M\t  'Mirror'\n"
        "F\t  'Flip'\n"
        "F2\t Copy to buffer\n"
        "F3\t Copy from buffer\n"
        "Tab\tDraw\n"
        "F4\t Revert to ASCII\n"
        "F5\t Revert to MZX",
         info_x, info_y + pad_height + 1, 143, 1
      );
    }

    // Update char
    if(block_mode)
    {
      if(screen_mode)
      {
        draw_multichar_smzx(buffer, chars_x, chars_y,
         current_width, current_height, block_start_x,
         block_start_y, block_width, block_height);
      }
      else
      {
        draw_multichar(buffer, chars_x, chars_y,
         current_width, current_height, block_start_x,
         block_start_y, block_width, block_height);
      }
    }
    else
    {
      if(screen_mode)
      {
        draw_multichar_smzx(buffer, chars_x, chars_y,
         current_width, current_height, x, y, 1, 1);
      }
      else
      {
        draw_multichar(buffer, chars_x, chars_y,
         current_width, current_height, x, y, 1, 1);
      }
    }

    switch(draw)
    {
      case 0:
        write_string("(off)   ", info_x + 14,
         info_y + pad_height + 12, 143, 0);
        break;

      case 1:
        write_string("(set)   ", info_x + 14,
         info_y + pad_height + 12, 143, 0);
        break;

      case 2:
        write_string("(clear) ", info_x + 14,
         info_y + pad_height + 12, 143, 0);
        break;
    }

    // Current character
    write_number(current_char, 143, info_x + 20, info_y, 3, 0, 10);

    for(i = 0, offset = 0; i < highlight_height; i++)
    {
      for(i2 = 0; i2 < highlight_width; i2++, offset++)
      {
        mini_buffer[offset] = current_char + i2 + (i * 32);
      }
    }

    for(i = 0, offset = 0; i < current_height; i++)
    {
      for(i2 = 0; i2 < current_width; i2++, offset++)
      {
        draw_char_ext(mini_buffer[offset], 143,
         i2 + info_x, info_y + i + 1, 0, 16);
      }
    }

    for(i = 0; i < highlight_height; i++)
    {
      for(i2 = -(chars_show_width / 2);
       i2 < -(chars_show_width / 2) + chars_show_width; i2++)
      {
        if((i2 >= 0) && (i2 < highlight_width) &&
         (i < highlight_height))
        {
          draw_char_ext(current_char + i2 + (i * 32),
           143, i2 + info_x + current_width + 11,
           info_y + i + 1, 0, 16);
        }
        else
        {
          draw_char_ext(current_char + i2 + (i * 32),
           128, i2 + info_x + current_width + 11,
           info_y + i + 1, 0, 16);
        }
      }
    }

    update_screen();
    update_event_status_delay();

    key = get_key(keycode_internal);

    draw_new = 0;

    if(get_shift_status(keycode_internal) || (block_mode == 2))
    {
      if(!shifted)
      {
        block_x = x;
        block_y = y;
        block_start_x = x;
        block_start_y = y;
        block_mode = 1;
        block_width = 1;
        block_height = 1;
        shifted = 1;
      }
      else
      {
        block_start_x = block_x;
        block_start_y = block_y;

        if(block_start_x > x)
        {
          block_start_x = x;
          block_width = block_x - x + 1;
        }
        else
        {
          block_width = x - block_x + 1;
        }

        if(block_start_y > y)
        {
          block_start_y = y;
          block_height = block_y - y + 1;
        }
        else
        {
          block_height = y - block_y + 1;
        }
      }
    }
    else
    {
      shifted = 0;
      block_mode = 0;

      block_width = buffer_width;
      block_height = buffer_height;
      block_start_x = 0;
      block_start_y = 0;
    }

    if(get_mouse_drag())
    {
      int mouse_x, mouse_y;
      int draw_pixel;
      get_mouse_position(&mouse_x, &mouse_y);

      if((mouse_x >= chars_x) &&
       (mouse_x < chars_x + chars_width) &&
       (mouse_y >= chars_y) &&
       (mouse_y < chars_y + chars_height))
      {
        // Grid.
        if(small_chars)
        {
          get_real_mouse_position(&mouse_x, &mouse_y);

          x = (mouse_x / 8) - chars_x;
          y = (mouse_y / 7) - (chars_y * 2);
        }
        else
        {
          x = (mouse_x - chars_x) / 2;
          y = mouse_y - chars_y;
        }

        if(screen_mode)
        {
          x /= 2;
          draw_pixel = current_pixel;
        }
        else
        {
          draw_pixel = 1;
        }

        if(!block_mode)
        {
          if(get_mouse_status() & MOUSE_BUTTON(MOUSE_BUTTON_LEFT))
          {
            int dx = x - last_x;
            int dy = y - last_y;
            int first_x, first_y;
            int draw_x, draw_y;

            if(((last_x != x) || (last_y != y)) && (last_x != -1))
            {
              if(abs(dx) > abs(dy))
              {
                if(dx < 0)
                {
                  first_x = x;
                  first_y = y;
                  dx *= -1;
                  dy *= -1;
                }
                else
                {
                  first_x = last_x;
                  first_y = last_y;
                }

                for(i = 0; i <= dx; i++)
                {
                  draw_y = (dy * i / dx) + first_y;
                  buffer[(first_x + i) + (buffer_width * draw_y)] =
                   draw_pixel;
                }
              }
              else
              {
                if(dy < 0)
                {
                  first_x = x;
                  first_y = y;
                  dx *= -1;
                  dy *= -1;
                }
                else
                {
                  first_x = last_x;
                  first_y = last_y;
                }

                for(i = 0; i <= dy; i++)
                {
                  draw_x = (dx * i / dy) + first_x;
                  buffer[draw_x + (buffer_width * (first_y + i))] =
                   draw_pixel;
                }
              }
            }
            else
            {
              buffer[x + (buffer_width * y)] = draw_pixel;
            }
          }
          else
          {
            if(screen_mode)
              current_pixel = buffer[x + (buffer_width * y)];
            else
              buffer[x + (buffer_width * y)] = 0;
          }

          last_x = x;
          last_y = y;

          collapse_buffer(buffer, current_width,
           current_height, highlight_width, highlight_height,
           current_char, screen_mode);
        }
      }
    }
    else
    {
      last_x = -1;
    }

    switch(key)
    {
      case IKEY_LEFT:
      {
        if(get_alt_status(keycode_internal))
        {
          char *buffer_ptr = buffer +
           block_start_x + (block_start_y * buffer_width);
          int wrap;

          memcpy(previous, buffer, buffer_size);

          for(i = 0; i < block_height; i++,
           buffer_ptr += buffer_width)
          {
            wrap = buffer_ptr[0];
            memmove(buffer_ptr, buffer_ptr + 1,
             block_width - 1);
            buffer_ptr[block_width - 1] = wrap;
          }

          collapse_buffer(buffer, current_width,
           current_height, highlight_width, highlight_height,
           current_char, screen_mode);
        }
        else
        {
          x--;
          if(x < 0)
            x = buffer_width - 1;

          if(draw)
            draw_new = 1;
        }
        break;
      }

      case IKEY_RIGHT:
      {
        if(get_alt_status(keycode_internal))
        {
          char *buffer_ptr = buffer +
           block_start_x + (block_start_y * buffer_width);
          int wrap;

          memcpy(previous, buffer, buffer_size);

          for(i = 0; i < block_height; i++,
           buffer_ptr += buffer_width)
          {
            wrap = buffer_ptr[block_width - 1];
            memmove(buffer_ptr + 1, buffer_ptr,
             block_width - 1);
            buffer_ptr[0] = wrap;
          }

          collapse_buffer(buffer, current_width,
           current_height, highlight_width, highlight_height,
           current_char, screen_mode);
        }
        else
        {
          x++;
          if(x >= buffer_width)
            x = 0;

          if(draw)
            draw_new = 1;
        }
        break;
      }

      case IKEY_UP:
      {
        if(get_alt_status(keycode_internal))
        {
          char *buffer_ptr = buffer +
           block_start_x + (block_start_y * buffer_width);
          char *wrap_row = cmalloc(block_width);

          memcpy(previous, buffer, buffer_size);
          memcpy(wrap_row, buffer_ptr, block_width);

          for(i = 0; i < block_height - 1; i++,
           buffer_ptr += buffer_width)
          {
            memcpy(buffer_ptr, buffer_ptr + buffer_width,
             block_width);
          }

          memcpy(buffer_ptr, wrap_row, block_width);
          collapse_buffer(buffer, current_width,
           current_height, highlight_width, highlight_height,
           current_char, screen_mode);

          free(wrap_row);
        }
        else
        {
          y--;
          if(y < 0)
            y = buffer_height - 1;

          if(draw)
            draw_new = 1;
        }
        break;
      }

      case IKEY_DOWN:
      {
        if(get_alt_status(keycode_internal))
        {
          char *buffer_ptr = buffer +
           block_start_x + ((block_start_y + (block_height - 1)) *
           buffer_width);
          char *wrap_row = cmalloc(block_width);

          memcpy(previous, buffer, buffer_size);
          memcpy(wrap_row, buffer_ptr, block_width);

          for(i = 0; i < block_height - 1; i++,
           buffer_ptr -= buffer_width)
          {
            memcpy(buffer_ptr, buffer_ptr - buffer_width,
             block_width);
          }

          memcpy(buffer_ptr, wrap_row, block_width);
          collapse_buffer(buffer, current_width,
           current_height, highlight_width, highlight_height,
           current_char, screen_mode);

          free(wrap_row);
        }
        else
        {
          y++;
          if(y >= buffer_height)
            y = 0;

          if(draw)
            draw_new = 1;
        }
        break;
      }

      case IKEY_KP_MINUS:
      case IKEY_MINUS:
      {
        current_char -= highlight_width;
        expand_buffer(buffer, current_width, current_height,
         highlight_width, highlight_height, current_char, screen_mode);
        memcpy(previous, buffer, buffer_size);
        break;
      }

      case IKEY_KP_PLUS:
      case IKEY_EQUALS:
      {
        current_char += highlight_width;
        expand_buffer(buffer, current_width, current_height,
         highlight_width, highlight_height, current_char, screen_mode);
        memcpy(previous, buffer, buffer_size);
        break;
      }

      case IKEY_SPACE:
      {
        memcpy(previous, buffer, buffer_size);
        if(screen_mode)
          buffer[x + (y * buffer_width)] = current_pixel;
        else
          buffer[x + (y * buffer_width)] ^= 1;

        collapse_buffer(buffer, current_width,
         current_height, highlight_width, highlight_height,
         current_char, screen_mode);
        break;
      }

      case IKEY_RETURN:
      {
        int new_char =
         char_selection_ext(current_char, 1, &highlight_width,
         &highlight_height);
        int highlight_size = highlight_width * highlight_height;
        int search_order[16];
        int factors[6];
        int num_factors = 0;
        int low, high;
        int subdivision = 0;

        // The first choice should be about the square root,
        // biased high.
        search_order[0] = (int)(sqrt(highlight_size) + 0.75);

        // Subsequent choices should expand outwards
        low = search_order[0];
        high = search_order[0];
        i = 1;
        while((low > 1) || (high < 6))
        {
          if(high < 6)
          {
            high++;
            search_order[i] = high;
            i++;
          }

          if(low > 1)
          {
            low--;
            search_order[i] = low;
            i++;
          }
        }

        for(i = 0; i < 6; i++)
        {
          if(!(highlight_size % search_order[i]) &&
           (highlight_size / search_order[i] <= 3))
          {
            factors[num_factors] = search_order[i];
            num_factors++;
          }
        }

        if(num_factors > 1)
        {
          struct element **elements =
           cmalloc(sizeof(struct element *) * num_factors);
          char **radio_button_strings =
           cmalloc(sizeof(char *) * num_factors);
          char **radio_button_substrings =
           cmalloc(sizeof(char *) * num_factors);
          struct dialog di;

          for(i = 0; i < num_factors; i++)
          {
            radio_button_substrings[i] = cmalloc(32);
            radio_button_strings[i] = radio_button_substrings[i];
            sprintf(radio_button_strings[i], "%d x %d",
             factors[i], highlight_size / factors[i]);
          }

          elements[0] =
           construct_radio_button(8, 2,
           (const char **)radio_button_strings,
           num_factors, 5, &subdivision);
          elements[1] = construct_button(10, num_factors + 3,
           "OK", 0);

          construct_dialog(&di, "Choose subdivision", 28,
           8, 24, 6 + num_factors, elements, 2, 0);

          run_dialog(mzx_world, &di);
          destruct_dialog(&di);

          for(i = 0; i < num_factors; i++)
            free(radio_button_substrings[i]);
          free(radio_button_substrings);
          free(radio_button_strings);
          free(elements);
        }

        current_width = factors[subdivision];
        current_height = highlight_size / factors[subdivision];

        if(new_char >= 0)
          current_char = new_char;

        expand_buffer(buffer, current_width, current_height,
         highlight_width, highlight_height, current_char, screen_mode);

        memcpy(previous, buffer, buffer_size);
        break;
      }

      case IKEY_q:
      {
        key = IKEY_ESCAPE;
        break;
      }

      case IKEY_DELETE:
      {
        char *buffer_ptr = buffer +
         block_start_x + (block_start_y * buffer_width);
        memcpy(previous, buffer, buffer_size);

        for(i = 0; i < block_height; i++,
         buffer_ptr += buffer_width)
        {
          memset(buffer_ptr, 0, block_width);
        }
        collapse_buffer(buffer, current_width,
         current_height, highlight_width, highlight_height,
         current_char, screen_mode);
        break;
      }

      case IKEY_F2:
      {
        char *buffer_ptr = buffer +
         block_start_x + (block_start_y * buffer_width);
        char *dest_ptr = char_copy_buffer;

        if(block_mode)
          char_copy_use_offset = 1;
        else
          char_copy_use_offset = 0;

        char_copy_width = block_width;
        char_copy_height = block_height;

        if(get_ctrl_status(keycode_internal))
        {
          for(i = 0; i < block_height; i++,
           buffer_ptr += buffer_width, dest_ptr += block_width)
          {
            memcpy(dest_ptr, buffer_ptr, block_width);
            memset(buffer_ptr, 0, block_width);
          }
        }
        else
        {
          for(i = 0; i < block_height; i++,
           buffer_ptr += buffer_width, dest_ptr += block_width)
          {
            memcpy(dest_ptr, buffer_ptr, block_width);
          }
        }
        break;
      }

      case IKEY_F3:
      {
        int copy_width = char_copy_width;
        int copy_height = char_copy_height;
        int dest_x = x;
        int dest_y = y;
        char *src_ptr = char_copy_buffer;
        char *buffer_ptr;

        if(!char_copy_use_offset)
        {
          dest_x = 0;
          dest_y = 0;
        }

        buffer_ptr = buffer +
         dest_x + (dest_y * buffer_width);

        memcpy(previous, buffer, buffer_size);

        if((dest_x + copy_width) > buffer_width)
          copy_width = buffer_width - dest_x;

        if((dest_y + copy_height) > buffer_height)
          copy_height = buffer_height - dest_y;

        for(i = 0; i < copy_height; i++,
         buffer_ptr += buffer_width, src_ptr += char_copy_width)
        {
          memcpy(buffer_ptr, src_ptr, copy_width);
        }

        collapse_buffer(buffer, current_width,
         current_height, highlight_width, highlight_height,
         current_char, screen_mode);
        break;
      }

      case IKEY_m:
      {
        int temp, i2;
        int start_offset = block_start_x +
         (block_start_y * buffer_width);
        int previous_offset;
        int end_offset;

        memcpy(previous, buffer, buffer_size);

        for(i = 0; i < block_height; i++)
        {
          previous_offset = start_offset;
          end_offset = start_offset + block_width - 1;
          for(i2 = 0; i2 < (block_width / 2);
           i2++, start_offset++, end_offset--)
          {
            temp = buffer[start_offset];
            buffer[start_offset] = buffer[end_offset];
            buffer[end_offset] = temp;
          }
          start_offset = previous_offset + buffer_width;
        }

        collapse_buffer(buffer, current_width,
         current_height, highlight_width, highlight_height,
         current_char, screen_mode);

        break;
      }

      case IKEY_f:
      {
        if(get_alt_status(keycode_internal))
        {
          // Flood fill
          int check = buffer[x + (y * buffer_width)];
          int fill = check ^ 1;

          memcpy(previous, buffer, buffer_size);

          if(screen_mode)
            fill = current_pixel;

          if(check != fill)
          {
            fill_region(buffer, x, y, buffer_width, buffer_height,
             check, fill);
            collapse_buffer(buffer, current_width,
             current_height, highlight_width, highlight_height,
             current_char, screen_mode);
          }
        }
        else
        {
          char *temp_buffer = cmalloc(sizeof(char) * block_width);
          int start_offset = block_start_x +
           (block_start_y * buffer_width);
          int end_offset = start_offset +
           ((block_height - 1) * buffer_width);

          memcpy(previous, buffer, buffer_size);

          for(i = 0; i < (block_height / 2); i++,
           start_offset += buffer_width, end_offset -= buffer_width)
          {
            memcpy(temp_buffer, buffer + start_offset,
             block_width);
            memcpy(buffer + start_offset, buffer + end_offset,
             block_width);
            memcpy(buffer + end_offset, temp_buffer,
             block_width);
          }

          collapse_buffer(buffer, current_width,
           current_height, highlight_width, highlight_height,
           current_char, screen_mode);

          free(temp_buffer);
        }

        break;
      }

      case IKEY_TAB:
      {
        if(draw)
        {
          draw = 0;
        }
        else
        {
          if(get_shift_status(keycode_internal))
            draw = 2;
          else
            draw = 1;

          draw_new = 1;
        }

        break;
      }

      case IKEY_n:
      {
        char *buffer_ptr = buffer +
         block_start_x + (block_start_y * buffer_width);
        memcpy(previous, buffer, buffer_size);

        if(screen_mode)
        {
          for(i = 0; i < block_height; i++,
           buffer_ptr += (buffer_width - block_width))
          {
            for(i2 = 0; i2 < block_width; i2++, buffer_ptr++)
            {
              *buffer_ptr = 3 - *buffer_ptr;
            }
          }
        }
        else
        {
          for(i = 0; i < block_height; i++,
           buffer_ptr += (buffer_width - block_width))
          {
            for(i2 = 0; i2 < block_width; i2++, buffer_ptr++)
            {
              *buffer_ptr ^= 1;
            }
          }
        }

        collapse_buffer(buffer, current_width,
         current_height, highlight_width, highlight_height,
         current_char, screen_mode);
        break;
      }

      case IKEY_u:
      {
        // Undo
        if(get_alt_status(keycode_internal))
        {
          char *swap = cmalloc(sizeof(char) * buffer_size);
          memcpy(swap, buffer, buffer_size);
          memcpy(buffer, previous, buffer_size);
          memcpy(previous, swap, buffer_size);
          collapse_buffer(buffer, current_width,
           current_height, highlight_width, highlight_height,
           current_char, screen_mode);
          free(swap);
        }
        break;
      }

      case IKEY_F4:
      {
        int char_offset = current_char;
        int skip = 32 - highlight_width;

        memcpy(previous, buffer, buffer_size);

        for(i = 0; i < highlight_height; i++,
         char_offset += skip)
        {
          for(i2 = 0; i2 < highlight_width; i2++,
           char_offset++)
          {
            char_offset = char_offset % 254; // Wrap away from the protected set
            ec_load_char_ascii(char_offset);
          }
        }

        expand_buffer(buffer, current_width, current_height,
         highlight_width, highlight_height, current_char, screen_mode);

        break;
      }

      case IKEY_F5:
      {
        int char_offset = current_char;
        int skip = 32 - highlight_width;

        memcpy(previous, buffer, buffer_size);

        for(i = 0; i < highlight_height; i++,
         char_offset += skip)
        {
          for(i2 = 0; i2 < highlight_width; i2++,
           char_offset++)
          {
            char_offset = char_offset % 254; // Wrap away from the protected set
            ec_load_char_mzx(char_offset);
          }
        }

        expand_buffer(buffer, current_width, current_height,
         highlight_width, highlight_height, current_char, screen_mode);

        break;
      }

#ifdef CONFIG_HELPSYS
      case IKEY_F1:
      {
        m_show();
        help_system(mzx_world);
        break;
      }
#endif

      case IKEY_HOME:
      {
        x = 0;
        y = 0;
        break;
      }

      case IKEY_END:
      {
        x = buffer_width - 1;
        y = buffer_height - 1;
        break;
      }

      case IKEY_x:
      {
        if(get_alt_status(keycode_internal))
        {
          // Character set
          const char *chr_ext[] = { ".CHR", NULL };
          char export_string[256] = { 0, };
          char export_name[256];
          int char_offset = current_char;
          int char_size = current_width * current_height;
          int current_file = 0;

          struct element *elements[] =
          {
            construct_number_box(3, 20, "Offset:  ",
             0, 255, 0, &char_offset),
            construct_number_box(28, 20, "First: ",
             0, 255, 0, &current_file),
            construct_number_box(52, 20, "Count: ",
             1, 256, 0, &num_files)
          };

          if(!file_manager(mzx_world, chr_ext, ".chr", export_string,
           "Export character set(s)", 1, 1, elements, 3, 2, 0))
          {
            int num_files_present = num_files;

            if(strchr(export_string, '#') == NULL)
              num_files_present = 1;

            for(i = 0; i < num_files_present; i++, current_file++,
             char_offset += char_size)
            {
              replace_filenum(export_string, export_name,
               current_file);

              add_ext(export_name, ".chr");
              ec_save_set_var(export_name, char_offset,
               char_size);
            }
          }
        }
        break;
      }

      case IKEY_i:
      {
        if(get_alt_status(keycode_internal))
        {
          // Character set
          const char *chr_ext[] = { ".CHR", NULL };
          char import_string[256] = { 0, };
          char import_name[256];
          int char_offset = current_char;
          int current_file = 0;
          int char_size;

          struct element *elements[] =
          {
            construct_number_box(3, 20, "Offset:  ",
             0, 255, 0, &char_offset),
            construct_number_box(28, 20, "First: ",
             0, 255, 0, &current_file),
            construct_number_box(52, 20, "Count: ",
             1, 256, 0, &num_files)
          };

          if(!file_manager(mzx_world, chr_ext, NULL, import_string,
           "Import character set(s)", 1, 2, elements, 3, 2, 0))
          {
            int num_files_present = num_files;

            if(strchr(import_string, '#') == NULL)
              num_files_present = 1;

            for(i = 0; i < num_files_present; i++, current_file++)
            {
              replace_filenum(import_string, import_name,
               current_file);

              add_ext(import_name, ".chr");
              char_size =
               ec_load_set_var(import_name, char_offset);

              if(char_size != -1)
                char_offset += char_size;
            }

          expand_buffer(buffer, current_width, current_height,
           highlight_width, highlight_height, current_char, screen_mode);
          }
        }
        break;
      }

      case IKEY_b:
      {
        if(get_alt_status(keycode_internal))
        {
          block_mode = 2;
          block_x = x;
          block_y = y;
          block_start_x = x;
          block_start_y = y;
          block_width = 1;
          block_height = 1;
          shifted = 1;
        }

        break;
      }

      case IKEY_ESCAPE:
      {
        if(block_mode == 2)
        {
          block_mode = 0;
          key = 0;
        }
        break;
      }

      case IKEY_KP1:
      case IKEY_1:
      {
        current_pixel = 0;
        break;
      }

      case IKEY_KP2:
      case IKEY_2:
      {
        current_pixel = 2;
        break;
      }

      case IKEY_KP3:
      case IKEY_3:
      {
        current_pixel = 1;
        break;
      }

      case IKEY_KP4:
      case IKEY_4:
      {
        current_pixel = 3;
        break;
      }

      case IKEY_INSERT:
      {
        current_pixel = buffer[x + (y * buffer_width)];
        break;
      }

    }

    if(draw_new)
    {
      memcpy(previous, buffer, buffer_size);

      switch(draw)
      {
        case 1:
        {
          if(screen_mode)
            buffer[x + (y * buffer_width)] = current_pixel;
          else
            buffer[x + (y * buffer_width)] = 1;
          break;
        }

        case 2:
        {
          buffer[x + (y * buffer_width)] = 0;
          break;
        }
      }
      collapse_buffer(buffer, current_width,
       current_height, highlight_width, highlight_height,
       current_char, screen_mode);
    }
  } while(key != IKEY_ESCAPE);

  set_mouse_mul(8, 14);

  restore_screen();
  update_screen();
  pop_context();

  if(screen_mode)
  {
    for(i = 0; i < 4; i++)
    {
      set_rgb(i + 2, saved_rgb[i][0], saved_rgb[i][1],
       saved_rgb[i][2]);
    }

    update_palette();
    set_screen_mode(screen_mode);
  }

  free(previous);
  free(buffer);

  return current_char;
}


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
 */

// Declaration

#ifndef __EDITOR_CHAR_ED_H
#define __EDITOR_CHAR_ED_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"

int char_editor(struct world *mzx_world);
int char_editor_ext(struct world *mzx_world);
int smzx_char_editor(struct world *mzx_world);
void fill_region_smzx(char *matrix, int x, int y, int check, int draw);

__M_END_DECLS

#endif // __EDITOR_CHAR_ED_H

repo_name: pgimeno/megazeux

license: mit


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2008 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * 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 "configure.h"

#include "../counter.h"
#include "../configure.h"

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

struct editor_config_entry
{
  char option_name[OPTION_NAME_LEN];
  editor_config_function change_option;
};

// Default colors for color coding:
// 0 current line - 11
// 1 immediates - 10
// 2 characters - 14
// 3 colors - color box or 2
// 4 directions - 3
// 5 things - 11
// 6 params - 2
// 7 strings - 14
// 8 equalities - 0
// 9 conditions - 15
// 10 items - 11
// 11 extras - 7
// 12 commands and command fragments - 15

static void config_ccode_colors(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[4] = (char)strtol(value, NULL, 10);
}

static void config_ccode_commands(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[13] = (char)strtol(value, NULL, 10);
}

static void config_ccode_conditions(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[10] = (char)strtol(value, NULL, 10);
}

static void config_ccode_current_line(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[0] = (char)strtol(value, NULL, 10);
}

static void config_ccode_directions(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[5] = (char)strtol(value, NULL, 10);
}

static void config_ccode_equalities(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[9] = (char)strtol(value, NULL, 10);
}

static void config_ccode_extras(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[8] = (char)strtol(value, NULL, 10);
}

static void config_ccode_on(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_coding_on = strtol(value, NULL, 10);
}

static void config_ccode_immediates(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  int new_color = strtol(value, NULL, 10);
  conf->color_codes[1] = new_color;
  conf->color_codes[2] = new_color;
}

static void config_ccode_items(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[11] = (char)strtol(value, NULL, 10);
}

static void config_ccode_params(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[7] = (char)strtol(value, NULL, 10);
}

static void config_ccode_strings(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[8] = (char)strtol(value, NULL, 10);
}

static void config_ccode_things(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->color_codes[6] = (char)strtol(value, NULL, 10);
}

static void config_default_invald(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  if(!strcasecmp(value, "ignore"))
  {
    conf->default_invalid_status = 1;
  }
  else

  if(!strcasecmp(value, "delete"))
  {
    conf->default_invalid_status = 2;
  }
  else

  if(!strcasecmp(value, "comment"))
  {
    conf->default_invalid_status = 3;
  }
}

static void config_macro(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  char *macro_name = name + 6;

  if(isdigit((int)macro_name[0]) && !macro_name[1] && !extended_data)
  {
    int macro_num = macro_name[0] - 0x31;
    value[63] = 0;
    strcpy(conf->default_macros[macro_num], value);
  }
  else
  {
    if(extended_data)
      add_ext_macro(conf, macro_name, extended_data, value);
  }
}

static void bedit_hhelp(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->bedit_hhelp = strtol(value, NULL, 10);
}

static void redit_hhelp(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->redit_hhelp = strtol(value, NULL, 10);
}

static void backup_count(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->backup_count = strtol(value, NULL, 10);
}

static void backup_interval(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->backup_interval = strtol(value, NULL, 10);
}

static void backup_name(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  strncpy(conf->backup_name, value, 256);
}

static void backup_ext(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  strncpy(conf->backup_ext, value, 256);
}

static void config_editor_space_toggles(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->editor_space_toggles = strtol(value, NULL, 10);
}

static void config_editor_enter_splits(struct editor_config_info *conf,
 char *name, char *value, char *extended_data)
{
  conf->editor_enter_splits = strtol(value, NULL, 10);
}

/* FAT NOTE: This is searched as a binary tree, the nodes must be
 *           sorted alphabetically, or they risk being ignored.
 */
static const struct editor_config_entry editor_config_options[] =
{
  { "backup_count", backup_count },
  { "backup_ext", backup_ext },
  { "backup_interval", backup_interval },
  { "backup_name", backup_name },
  { "board_editor_hide_help", bedit_hhelp },
  { "ccode_colors", config_ccode_colors },
  { "ccode_commands", config_ccode_commands },
  { "ccode_conditions", config_ccode_conditions },
  { "ccode_current_line", config_ccode_current_line },
  { "ccode_directions", config_ccode_directions },
  { "ccode_equalities", config_ccode_equalities },
  { "ccode_extras", config_ccode_extras },
  { "ccode_immediates", config_ccode_immediates },
  { "ccode_items", config_ccode_items },
  { "ccode_params", config_ccode_params },
  { "ccode_strings", config_ccode_strings },
  { "ccode_things", config_ccode_things },
  { "color_coding_on", config_ccode_on },
  { "default_invalid_status", config_default_invald },
  { "editor_enter_splits", config_editor_enter_splits },
  { "editor_space_toggles", config_editor_space_toggles },
  { "macro_*", config_macro },
  { "robot_editor_hide_help", redit_hhelp },
};

static const int num_editor_config_options =
 sizeof(editor_config_options) / sizeof(struct editor_config_entry);

static const struct editor_config_entry *find_editor_option(char *name,
 const struct editor_config_entry options[], int num_options)
{
  int cmpval, top = num_options - 1, middle, bottom = 0;
  const struct editor_config_entry *base = options;

  while(bottom <= top)
  {
    middle = (top + bottom) / 2;
    cmpval = match_function_counter(name, (base[middle]).option_name);

    if(cmpval > 0)
      bottom = middle + 1;
    else

    if(cmpval < 0)
      top = middle - 1;
    else
      return base + middle;
  }

  return NULL;
}

static const struct editor_config_info default_editor_options =
{
  // Board editor options
  0,                            // editor_space_toggles
  0,                            // board_editor_hide_help

  // Robot editor options
  true,
  { 11, 10, 10, 14, 255, 3, 11, 2, 14, 0, 15, 11, 7, 15, 1, 2, 3 },
  1,                            // color_coding_on
  1,                            // default_invalid_status
  0,                            // robot_editor_hide_help

  // Backup options
  3,                            // backup_count
  60,                           // backup_interval
  "backup",                     // backup_name
  ".mzx",                       // backup_ext

  // Macro options
  { "char ", "color ", "goto ", "send ", ": playershot^" },
  0,                            // num_extended_macros
  0,
  NULL,
};

static void editor_config_change_option(void *conf, char *name, char *value,
 char *extended_data)
{
  const struct editor_config_entry *current_option = find_editor_option(name,
   editor_config_options, num_editor_config_options);

  if(current_option)
    current_option->change_option(conf, name, value, extended_data);
}

void set_editor_config_from_file(struct editor_config_info *conf,
 const char *conf_file_name)
{
  __set_config_from_file(editor_config_change_option, conf, conf_file_name);
}

void default_editor_config(struct editor_config_info *conf)
{
  memcpy(conf, &default_editor_options, sizeof(struct editor_config_info));
}

void set_editor_config_from_command_line(struct editor_config_info *conf,
 int *argc, char *argv[])
{
  __set_config_from_command_line(editor_config_change_option, conf, argc, argv);
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2008 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * 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_CONFIGURE_H
#define __EDITOR_CONFIGURE_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "macro.h"

struct editor_config_info
{
  // Board editor options
  int editor_space_toggles;
  int bedit_hhelp;

  // Robot editor options
  bool editor_enter_splits;
  char color_codes[32];
  int color_coding_on;
  int default_invalid_status;
  int redit_hhelp;

  // Backup options
  int backup_count;
  int backup_interval;
  char backup_name[256];
  char backup_ext[256];

  // Macro options
  char default_macros[5][64];
  int num_extended_macros;
  int num_macros_allocated;
  struct ext_macro **extended_macros;
};

typedef void (* editor_config_function)(struct editor_config_info *conf,
 char *name, char *value, char *extended_data);

void set_editor_config_from_file(struct editor_config_info *conf,
 const char *conf_file_name);
void default_editor_config(struct editor_config_info *conf);
void set_editor_config_from_command_line(struct editor_config_info *conf,
 int *argc, char *argv[]);

__M_END_DECLS

#endif // __EDITOR_CONFIGURE_H


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2008 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * 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 "debug.h"

#include "../graphics.h"
#include "../counter.h"
#include "../window.h"
#include "../intake.h"
#include "../world.h"

#include <string.h>

#define CVALUE_COL_OFFSET 63

static void copy_substring_escaped(struct string *str, char *buf,
 unsigned int size)
{
  unsigned int i, j;

  for(i = 0, j = 0; j < str->length && i < size; i++, j++)
  {
    if(str->value[j] == '\\')
    {
      buf[i++] = '\\';
      buf[i] = '\\';
    }
    else if(str->value[j] == '\n')
    {
      buf[i++] = '\\';
      buf[i] = 'n';
    }
    else if(str->value[j] == '\r')
    {
      buf[i++] = '\\';
      buf[i] = 'r';
    }
    else if(str->value[j] == '\t')
    {
      buf[i++] = '\\';
      buf[i] = 't';
    }
    else
      buf[i] = str->value[j];
  }

  buf[i] = 0;
}

static void unescape_string(char *buf)
{
  size_t i, j, len = strlen(buf);

  for(i = 0, j = 0; j < len + 1; i++, j++)
  {
    if(buf[j] != '\\')
    {
      buf[i] = buf[j];
      continue;
    }

    j++;

    if(buf[j] == 'n')
      buf[i] = '\n';
    else if(buf[j] == 'r')
      buf[i] = '\r';
    else if(buf[j] == 't')
      buf[i] = '\t';
    else if(buf[j] == '\\')
      buf[i] = '\\';
    else
      buf[i] = buf[j];
  }
}

void __debug_counters(struct world *mzx_world)
{
  // +1 for mzx_speed
  int num_vars = mzx_world->num_counters + mzx_world->num_strings + 1;
  char **var_list = ccalloc(num_vars, sizeof(char *));
  int dialog_result;
  size_t cp_len;
  int selected = 0;
  int i, i2;
  struct dialog di;
  struct element *elements[3];

  m_show();

  for(i = 0; i < mzx_world->num_counters; i++)
  {
    var_list[i] = cmalloc(76);
    cp_len = strlen(mzx_world->counter_list[i]->name);
    memset(var_list[i], ' ', 75);

    if(cp_len > CVALUE_COL_OFFSET)
      cp_len = CVALUE_COL_OFFSET;

    memcpy(var_list[i], mzx_world->counter_list[i]->name, cp_len);

    var_list[i][cp_len] = ' ';
    sprintf(var_list[i] + CVALUE_COL_OFFSET, "%d",
     mzx_world->counter_list[i]->value);
  }

  // mzx_speed isn't a real counter, so special case here
  var_list[i] = cmalloc(76);
  memset(var_list[i], ' ', 75);
  memcpy(var_list[i], "mzx_speed", 9);
  sprintf(var_list[i] + CVALUE_COL_OFFSET, "%d", mzx_world->mzx_speed);
  i++;

  for(i2 = 0; i2 < mzx_world->num_strings; i2++, i++)
  {
    var_list[i] = cmalloc(76);
    cp_len = strlen(mzx_world->string_list[i2]->name);
    memset(var_list[i], ' ', 75);

    if(cp_len > 16)
      cp_len = 16;

    memcpy(var_list[i],
     mzx_world->string_list[i2]->name, cp_len);

    var_list[i][cp_len] = ' ';

    copy_substring_escaped(mzx_world->string_list[i2], var_list[i] + 17, 58);
  }

  do
  {
    elements[0] = construct_list_box(2, 2, (const char **)var_list,
     num_vars, 19, 75, 0, &selected, false);
    elements[1] = construct_button(23, 22, "Export", 1);
    elements[2] = construct_button(45, 22, "Done", -1);

    construct_dialog_ext(&di, "Debug Variables", 0, 0,
     80, 25, elements, 3, 0, 0, 0, NULL);

    dialog_result = run_dialog(mzx_world, &di);

    if(dialog_result == 0)
    {
      char new_value[70];
      char name[70] = "Edit ";
      int edit_type = 0;
      int offset = selected;

      if(selected > mzx_world->num_counters)
      {
        offset -= mzx_world->num_counters + 1;
        edit_type = 1;

        snprintf(name + 5, 70 - 5, "string %s",
         mzx_world->string_list[offset]->name);

        copy_substring_escaped(mzx_world->string_list[offset], new_value, 68);
      }
      else
      {
        if(selected == mzx_world->num_counters)
        {
          edit_type = -1;
          strncpy(name + 5, "counter mzx_speed", 70 - 5);
          sprintf(new_value, "%d", mzx_world->mzx_speed);
        }
        else
        {
          snprintf(name + 5, 70 - 5, "counter %s",
           mzx_world->counter_list[offset]->name);
          sprintf(new_value, "%d",
           mzx_world->counter_list[offset]->value);
        }
      }

      name[69] = 0;
      save_screen();
      draw_window_box(4, 12, 76, 14, DI_DEBUG_BOX, DI_DEBUG_BOX_DARK,
       DI_DEBUG_BOX_CORNER, 1, 1);
      write_string(name, 6, 12, DI_DEBUG_LABEL, 0);

      if(intake(mzx_world, new_value, 68, 6, 13, 15, 1, 0,
       NULL, 0, NULL) != IKEY_ESCAPE)
      {
        if(edit_type > 0)
        {
          struct string src;

          unescape_string(new_value);
          memset(&src, 0, sizeof(struct string));
          src.length = strlen(new_value);
          src.value = new_value;

          set_string(mzx_world,
           mzx_world->string_list[offset]->name, &src, 0);

          copy_substring_escaped(mzx_world->string_list[offset],
           var_list[selected] + 17, 58);
        }
        else
        {
          int counter_value = strtol(new_value, NULL, 10);
          if(edit_type == -1)
          {
            if(counter_value < 1)
              counter_value = 1;
            if(counter_value > 16)
              counter_value = 16;
            mzx_world->mzx_speed = counter_value;
          }
          else
          {
            set_counter(mzx_world,
             mzx_world->counter_list[offset]->name, counter_value, 0);
          }

          sprintf(var_list[offset] + CVALUE_COL_OFFSET, "%d", counter_value);
        }
      }

      restore_screen();
    }
    else

    if(dialog_result == 1)
    {
      char export_name[128];
      const char *txt_ext[] = { ".TXT", NULL };

      export_name[0] = 0;

      if(!new_file(mzx_world, txt_ext, ".txt", export_name,
       "Export counters/strings", 1))
      {
        FILE *fp;

        fp = fopen_unsafe(export_name, "wb");

        for(i = 0; i < mzx_world->num_counters; i++)
        {
          fprintf(fp, "set \"%s\" to %d\n",
           mzx_world->counter_list[i]->name,
           mzx_world->counter_list[i]->value);
        }

        fprintf(fp, "set \"mzx_speed\" to %d\n", mzx_world->mzx_speed);

        for(i = 0; i < mzx_world->num_strings; i++)
        {
          fprintf(fp, "set \"%s\" to \"",
           mzx_world->string_list[i]->name);

          fwrite(mzx_world->string_list[i]->value,
           mzx_world->string_list[i]->length, 1, fp);

          fprintf(fp, "\"\n");
        }

        fclose(fp);
      }
    }

    destruct_dialog(&di);

  } while(dialog_result != -1);

  m_hide();

  for(i=0;i<num_vars;i++)
    free(var_list[i]);

  free(var_list);
}

void __draw_debug_box(struct world *mzx_world, int x, int y, int d_x, int d_y)
{
  struct board *src_board = mzx_world->current_board;
  int i;
  int robot_mem = 0;

  draw_window_box(x, y, x + 19, y + 5, DI_DEBUG_BOX, DI_DEBUG_BOX_DARK,
   DI_DEBUG_BOX_CORNER, 0, 1);

  write_string
  (
    "X/Y:        /     \n"
    "Board:            \n"
    "Robot mem:      kb\n",
    x + 1, y + 1, DI_DEBUG_LABEL, 0
  );

  write_number(d_x, DI_DEBUG_NUMBER, x + 8, y + 1, 5, 0, 10);
  write_number(d_y, DI_DEBUG_NUMBER, x + 14, y + 1, 5, 0, 10);
  write_number(mzx_world->current_board_id, DI_DEBUG_NUMBER,
   x + 18, y + 2, 0, 1, 10);

  for(i = 0; i < src_board->num_robots_active; i++)
  {
    robot_mem +=
#ifdef CONFIG_DEBYTECODE
     (src_board->robot_list_name_sorted[i])->program_source_length;
#else
     (src_board->robot_list_name_sorted[i])->program_bytecode_length;
#endif
  }

  write_number((robot_mem + 512) / 1024, DI_DEBUG_NUMBER,
   x + 12, y + 3, 5, 0, 10);

  if(*(src_board->mod_playing) != 0)
  {
    if(strlen(src_board->mod_playing) > 18)
    {
      char tempc = src_board->mod_playing[18];
      src_board->mod_playing[18] = 0;
      write_string(src_board->mod_playing, x + 1, y + 4,
       DI_DEBUG_NUMBER, 0);
      src_board->mod_playing[18] = tempc;
    }
    else
    {
      write_string(src_board->mod_playing, x + 1, y + 4,
       DI_DEBUG_NUMBER, 0);
    }
  }
  else
  {
    write_string("(no module)", x + 2, y + 4, DI_DEBUG_NUMBER, 0);
  }
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2008 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * 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 __DEBUG_H
#define __DEBUG_H

#include "../compat.h"

__M_BEGIN_DECLS

#include "../world_struct.h"

void __debug_counters(struct world *mzx_world);
void __draw_debug_box(struct world *mzx_world, int x, int y, int d_x, int d_y);

__M_END_DECLS

#endif // __DEBUG_H