Esempio n. 1
0
enum thing layer_to_board_object_type(struct world *mzx_world)
{
  int dialog_result;
  struct element *elements[3];
  struct dialog di;
  int object_type = 0;
  const char *radio_button_strings[] =
  {
    "Custom Block",
    "Custom Floor",
    "Text"
  };

  // Prevent previous keys from carrying through.
  force_release_all_keys();

  set_context(CTX_BLOCK_TYPE);
  elements[0] = construct_radio_button(6, 4, radio_button_strings,
   3, 12, &object_type);
  elements[1] = construct_button(5, 11, "OK", 0);
  elements[2] = construct_button(15, 11, "Cancel", -1);

  construct_dialog(&di, "Object type", 26, 4, 28, 14,
   elements, 3, 0);

  dialog_result = run_dialog(mzx_world, &di);

  destruct_dialog(&di);
  pop_context();

  // Prevent UI keys from carrying through.
  force_release_all_keys();

  if(dialog_result)
    return NO_ID;

  switch(object_type)
  {
    case 0: return CUSTOM_BLOCK;
    case 1: return CUSTOM_FLOOR;
    case 2: return __TEXT;
  }

  return NO_ID;
}
Esempio n. 2
0
static void __check_for_updates(struct world *mzx_world, struct config_info *conf)
{
  int cur_host;
  char *update_host;
  bool try_next_host = true;
  bool ret = false;

  set_context(CTX_UPDATER);

  if(conf->update_host_count < 1)
  {
    error("No updater hosts defined! Aborting.", 1, 8, 0);
    goto err_out;
  }

  if(!swivel_current_dir(true))
    goto err_out;

  for(cur_host = 0; (cur_host < conf->update_host_count) && try_next_host; cur_host++)
  {
    char **list_entries, buffer[LINE_BUF_LEN], *url_base, *value;
    struct manifest_entry *removed, *replaced, *added, *e;
    int i = 0, entries = 0, buf_len, result;
    char update_branch[LINE_BUF_LEN];
    const char *version = VERSION;
    int list_entry_width = 0;
    enum host_status status;
    struct host *h = NULL;
    unsigned int retries;
    FILE *f;

    // Acid test: Can we write to this directory?
    f = fopen_unsafe(UPDATES_TXT, "w+b");
    if(!f)
    {
      error("Failed to create \"" UPDATES_TXT "\". Check permissions.", 1, 8, 0);
      goto err_chdir;
    }

    update_host = conf->update_hosts[cur_host];

    if(!reissue_connection(conf, &h, update_host))
      goto err_host_destroy;

    for(retries = 0; retries < MAX_RETRIES; retries++)
    {
      // Grab the file containing the names of the current Stable and Unstable
      status = host_recv_file(h, "/" UPDATES_TXT, f, "text/plain");
      rewind(f);

      if(status == HOST_SUCCESS)
        break;

      if(!reissue_connection(conf, &h, update_host))
        goto err_host_destroy;
    }

    if(retries == MAX_RETRIES)
    {
      snprintf(widget_buf, WIDGET_BUF_LEN, "Failed to download \""
       UPDATES_TXT "\" (err=%d).\n", status);
      widget_buf[WIDGET_BUF_LEN - 1] = 0;
      error(widget_buf, 1, 8, 0);
      goto err_host_destroy;
    }

    snprintf(update_branch, LINE_BUF_LEN, "Current-%s",
     conf->update_branch_pin);

    // Walk this list (of two, hopefully)
    while(true)
    {
      char *m = buffer, *key;
      value = NULL;

      // Grab a single line from the manifest
      if(!fgets(buffer, LINE_BUF_LEN, f))
        break;

      key = strsep(&m, ":\n");
      if(!key)
        break;

      value = strsep(&m, ":\n");
      if(!value)
        break;

      if(strcmp(key, update_branch) == 0)
        break;
    }

    fclose(f);
    unlink(UPDATES_TXT);

    /* There was no "Current-XXX: Version" found; we cannot proceed with the
     * update because we cannot compute an update URL below.
     */
    if(!value)
    {
      error("Failed to identify applicable update version.", 1, 8, 0);
      goto err_host_destroy;
    }

    /* There's likely to be a space prepended to the version number.
     * Skip it here.
     */
    if(value[0] == ' ')
      value++;

    /* We found the latest update version, but we should check to see if that
     * matches the version we're already using. The user may choose to receive
     * "stability" updates for their current major version, or upgrade to the
     * newest one.
     */
    if(strcmp(value, version) != 0)
    {
      struct element *elements[6];
      struct dialog di;

      buf_len = snprintf(widget_buf, WIDGET_BUF_LEN,
       "A new major version is available (%s)", value);
      widget_buf[WIDGET_BUF_LEN - 1] = 0;

      elements[0] = construct_label((55 - buf_len) >> 1, 2, widget_buf);

      elements[1] = construct_label(2, 4,
       "You can continue to receive updates for the version\n"
       "installed (if available), or you can upgrade to the\n"
       "newest major version (recommended).");

      elements[2] = construct_label(2, 8,
       "If you do not upgrade, this question will be asked\n"
       "again the next time you run the updater.\n");

      elements[3] = construct_button(9, 11, "Upgrade", 0);
      elements[4] = construct_button(21, 11, "Update Old", 1);
      elements[5] = construct_button(36, 11, "Cancel", 2);

      construct_dialog(&di, "New Major Version", 11, 6, 55, 14, elements, 6, 3);
      result = run_dialog(mzx_world, &di);
      destruct_dialog(&di);

      // User pressed Escape, abort all updates
      if(result < 0 || result == 2)
      {
        try_next_host = false;
        goto err_host_destroy;
      }

      // User pressed Upgrade, use new major
      if(result == 0)
        version = value;
    }

    /* We can now compute a unique URL base for the updater. This will
     * be composed of a user-selected version and a static platform-archicture
     * name.
     */
    url_base = cmalloc(LINE_BUF_LEN);
    snprintf(url_base, LINE_BUF_LEN, "/%s/" PLATFORM, version);
    debug("Update base URL: %s\n", url_base);

    /* The call to manifest_get_updates() destroys any existing manifest
     * file in this directory. Since we still allow user to abort after
     * this call, and downloading the updates may fail, we copy the
     * old manifest to a backup location and optionally restore it later.
     */
    if(!backup_original_manifest())
    {
      error("Failed to back up manifest. Check permissions.", 1, 8, 0);
      try_next_host = false;
      goto err_free_url_base;
    }

    for(retries = 0; retries < MAX_RETRIES; retries++)
    {
      bool m_ret;

      m_hide();

      draw_window_box(3, 11, 76, 13, DI_MAIN, DI_DARK, DI_CORNER, 1, 1);
      write_string("Computing manifest deltas (added, replaced, deleted)..",
       13, 12, DI_TEXT, 0);
      update_screen();

      m_ret = manifest_get_updates(h, url_base, &removed, &replaced, &added);

      clear_screen(32, 7);
      m_show();
      update_screen();

      if(m_ret)
        break;

      if(!reissue_connection(conf, &h, update_host))
        goto err_roll_back_manifest;
    }

    if(retries == MAX_RETRIES)
    {
      error("Failed to compute update manifests", 1, 8, 0);
      goto err_roll_back_manifest;
    }

    // At this point, we have a successful manifest, so we won't need another host
    try_next_host = false;

    if(!removed && !replaced && !added)
    {
      struct element *elements[3];
      struct dialog di;

      elements[0] = construct_label(2, 2, "This client is already current.");
      elements[1] = construct_button(7, 4, "OK", 0);
      elements[2] = construct_button(13, 4, "Try next host", 1);

      construct_dialog(&di, "No Updates", 22, 9, 35, 6, elements, 3, 1);
      result = run_dialog(mzx_world, &di);
      destruct_dialog(&di);

      if((result == 1) && (cur_host < conf->update_host_count))
        try_next_host = true;

      goto err_free_update_manifests;
    }

    for(e = removed; e; e = e->next, entries++)
      list_entry_width = MAX(list_entry_width, 2 + (int)strlen(e->name)+1+1);
    for(e = replaced; e; e = e->next, entries++)
      list_entry_width = MAX(list_entry_width, 2 + (int)strlen(e->name)+1+1);
    for(e = added; e; e = e->next, entries++)
      list_entry_width = MAX(list_entry_width, 2 + (int)strlen(e->name)+1+1);

    // We don't want the listbox to be too wide
    list_entry_width = MIN(list_entry_width, 60);

    list_entries = cmalloc(entries * sizeof(char *));

    for(e = removed; e; e = e->next, i++)
    {
      list_entries[i] = cmalloc(list_entry_width);
      snprintf(list_entries[i], list_entry_width, "- %s", e->name);
      list_entries[i][list_entry_width - 1] = 0;
    }

    for(e = replaced; e; e = e->next, i++)
    {
      list_entries[i] = cmalloc(list_entry_width);
      snprintf(list_entries[i], list_entry_width, "* %s", e->name);
      list_entries[i][list_entry_width - 1] = 0;
    }

    for(e = added; e; e = e->next, i++)
    {
      list_entries[i] = cmalloc(list_entry_width);
      snprintf(list_entries[i], list_entry_width, "+ %s", e->name);
      list_entries[i][list_entry_width - 1] = 0;
    }

    draw_window_box(19, 1, 59, 4, DI_MAIN, DI_DARK, DI_CORNER, 1, 1);
    write_string(" Task Summary ", 33, 1, DI_TITLE, 0);
    write_string("ESC   - Cancel   [+] Add   [-] Delete", 21, 2, DI_TEXT, 0);
    write_string("ENTER - Proceed  [*] Replace  ", 21, 3, DI_TEXT, 0);

    result = list_menu((const char **)list_entries, list_entry_width,
     NULL, 0, entries, ((80 - (list_entry_width + 9)) >> 1) + 1, 4);

    for(i = 0; i < entries; i++)
      free(list_entries[i]);
    free(list_entries);

    clear_screen(32, 7);
    update_screen();

    if(result < 0)
      goto err_free_update_manifests;

    /* Defer deletions until we restart; any of these files may still be
     * in use by this (old) process. Reduce the number of entries by the
     * number of removed items for the progress meter below.
     */
    for(e = removed; e; e = e->next, entries--)
      delete_hook(e->name);

    /* Since the operations for adding and replacing a file are identical,
     * we modify the replaced list and tack on the added list to the end.
     *
     * Either list may be NULL; in the case that `replaced' is NULL, simply
     * re-assign the `added' pointer. `added' being NULL has no effect.
     *
     * Later, we need only free the replaced list (see below).
     */
    if(replaced)
    {
      for(e = replaced; e->next; e = e->next)
        ;
      e->next = added;
    }
    else
      replaced = added;

    cancel_update = false;
    host_set_callbacks(h, NULL, recv_cb, cancel_cb);

    i = 1;
    for(e = replaced; e; e = e->next, i++)
    {
      for(retries = 0; retries < MAX_RETRIES; retries++)
      {
        char name[72];
        bool m_ret;

        if(!check_create_basedir(e->name))
          goto err_free_delete_list;

        final_size = (long)e->size;

        m_hide();
        snprintf(name, 72, "%s (%ldb) [%u/%u]", e->name, final_size, i, entries);
        meter(name, 0, final_size);
        update_screen();

        m_ret = manifest_entry_download_replace(h, url_base, e, delete_hook);

        clear_screen(32, 7);
        m_show();
        update_screen();

        if(m_ret)
          break;

        if(cancel_update)
        {
          error("Download was cancelled; update aborted.", 1, 8, 0);
          goto err_free_delete_list;
        }

        if(!reissue_connection(conf, &h, update_host))
          goto err_free_delete_list;
        host_set_callbacks(h, NULL, recv_cb, cancel_cb);
      }

      if(retries == MAX_RETRIES)
      {
        snprintf(widget_buf, WIDGET_BUF_LEN,
         "Failed to download \"%s\" (after %d attempts).", e->name, retries);
        widget_buf[WIDGET_BUF_LEN - 1] = 0;
        error(widget_buf, 1, 8, 0);
        goto err_free_delete_list;
      }
    }

    if(delete_list)
    {
      f = fopen_unsafe(DELETE_TXT, "wb");
      if(!f)
      {
        error("Failed to create \"" DELETE_TXT "\". Check permissions.", 1, 8, 0);
        goto err_free_delete_list;
      }

      for(e = delete_list; e; e = e->next)
      {
        fprintf(f, "%08x%08x%08x%08x%08x%08x%08x%08x %lu %s\n",
         e->sha256[0], e->sha256[1], e->sha256[2], e->sha256[3],
         e->sha256[4], e->sha256[5], e->sha256[6], e->sha256[7],
         e->size, e->name);
      }

      fclose(f);
    }

    try_next_host = false;
    ret = true;
err_free_delete_list:
    manifest_list_free(&delete_list);
    delete_list = delete_p = NULL;
err_free_update_manifests:
    manifest_list_free(&removed);
    manifest_list_free(&replaced);
err_roll_back_manifest:
    restore_original_manifest(ret);
err_free_url_base:
    free(url_base);
err_host_destroy:
    host_destroy(h);

    pop_context();
  } //end host for loop

err_chdir:
  swivel_current_dir_back(true);
err_out:

  /* At this point we found updates and we successfully updated
   * to them. Reload the program with the original argv.
   */
  if(ret)
  {
    const void *argv = process_argv;
    struct element *elements[2];
    struct dialog di;

    elements[0] = construct_label(2, 2,
     "This client will now attempt to restart itself.");
    elements[1] = construct_button(23, 4, "OK", 0);

    construct_dialog(&di, "Update Successful", 14, 9, 51, 6, elements, 2, 1);
    run_dialog(mzx_world, &di);
    destruct_dialog(&di);

    execv(process_argv[0], argv);
    perror("execv");

    error("Attempt to invoke self failed!", 1, 8, 0);
    return;
  }
}
Esempio n. 3
0
//--------------------------
//
// ( ) Copy block
// ( ) Copy block (repeated)
// ( ) Move block
// ( ) Clear block
// ( ) Flip block
// ( ) Mirror block
// ( ) Paint block
// ( ) Copy to...
// ( ) Copy to...
// ( ) Save as MZM
//
//    _OK_      _Cancel_
//
//--------------------------
boolean select_block_command(struct world *mzx_world, struct block_info *block,
 enum editor_mode mode)
{
  int dialog_result;
  struct element *elements[3];
  struct dialog di;
  int selection = 0;
  const char *radio_button_strings[] =
  {
    "Copy block",
    "Copy block (repeated)",
    "Move block",
    "Clear block",
    "Flip block",
    "Mirror block",
    "Paint block",
    NULL,
    NULL,
    "Save as MZM"
  };

  switch(mode)
  {
    default:
    case EDIT_BOARD:
      radio_button_strings[7] = "Copy to overlay";
      radio_button_strings[8] = "Copy to vlayer";
      break;

    case EDIT_OVERLAY:
      radio_button_strings[7] = "Copy to board";
      radio_button_strings[8] = "Copy to vlayer";
      break;

    case EDIT_VLAYER:
      radio_button_strings[7] = "Copy to board";
      radio_button_strings[8] = "Copy to overlay";
      break;
  }

  // Prevent previous keys from carrying through.
  force_release_all_keys();

  set_context(CTX_BLOCK_CMD);
  elements[0] = construct_radio_button(2, 2, radio_button_strings,
   10, 21, &selection);
  elements[1] = construct_button(5, 13, "OK", 0);
  elements[2] = construct_button(15, 13, "Cancel", -1);

  construct_dialog(&di, "Choose block command", 26, 3, 29, 16,
   elements, 3, 0);

  dialog_result = run_dialog(mzx_world, &di);
  pop_context();

  destruct_dialog(&di);

  // Prevent UI keys from carrying through.
  force_release_all_keys();

  if(dialog_result)
  {
    block->selected = false;
    return false;
  }

  // Translate selection to a real block command.
  block->selected = true;
  block->command = BLOCK_CMD_NONE;
  block->src_mode = mode;
  block->dest_mode = mode;

  switch(selection)
  {
    case 0:
      block->command = BLOCK_CMD_COPY;
      break;

    case 1:
      block->command = BLOCK_CMD_COPY_REPEATED;
      break;

    case 2:
      block->command = BLOCK_CMD_MOVE;
      break;

    case 3:
      block->command = BLOCK_CMD_CLEAR;
      break;

    case 4:
      block->command = BLOCK_CMD_FLIP;
      break;

    case 5:
      block->command = BLOCK_CMD_MIRROR;
      break;

    case 6:
      block->command = BLOCK_CMD_PAINT;
      break;

    case 7:
    {
      block->command = BLOCK_CMD_COPY;
      switch(mode)
      {
        case EDIT_BOARD:
          block->dest_mode = EDIT_OVERLAY;
          break;

        case EDIT_OVERLAY:
        case EDIT_VLAYER:
          block->dest_mode = EDIT_BOARD;
          break;
      }
      break;
    }

    case 8:
    {
      block->command = BLOCK_CMD_COPY;
      switch(mode)
      {
        case EDIT_BOARD:
        case EDIT_OVERLAY:
          block->dest_mode = EDIT_VLAYER;
          break;

        case EDIT_VLAYER:
          block->dest_mode = EDIT_OVERLAY;
          break;
      }
      break;
    }

    case 9:
      block->command = BLOCK_CMD_SAVE_MZM;
      break;
  }
  return true;
}