/* Hey EMACS -*- linux-c -*- */
/* $Id: ti92.c 1397 2005-07-20 10:55:03Z roms $ */

/*  libticalcs - Ti Calculator library, a part of the TiLP project
 *  Copyright (C) 1999-2004  Romain Lievin
 *
 *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
  This unit provides TI92 support.
*/

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

#include "gettext.h"

#include "headers.h"
#include "externs.h"
#include "update.h"
#include "packets.h"
#include "calc_err.h"
#include "cmd92.h"
#include "rom92f2.h"
#include "keys92p.h"
#include "pause.h"
#include "dirlist.h"
#include "printl.h"

// Screen coordinates of the TI92
#define TI92_ROWS  128
#define TI92_COLS  240

int ti92_supported_operations(void)
{
  return
      (OPS_ISREADY |
       OPS_SCREENDUMP |
       OPS_SEND_KEY | OPS_RECV_KEY | OPS_REMOTE |
       OPS_DIRLIST |
       OPS_SEND_BACKUP | OPS_RECV_BACKUP |
       OPS_SEND_VARS | OPS_RECV_VARS | OPS_ROMVERSION | OPS_ROMDUMP);
}

int ti92_send_key(uint16_t key)
{
  LOCK_TRANSFER();
  TRYF(cable->open());

  TRYF(ti92_send_KEY(key));
  TRYF(ti92_recv_ACK(NULL));

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return 0;
}

int ti92_isready(void)
{
  uint16_t status;

  printl2(0, _("Is calculator ready ?\n"));

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  TRYF(ti92_send_RDY());
  TRYF(ti92_recv_ACK(&status));

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return (status & 0x01) ? ERR_NOT_READY : 0;
}

int ti92_screendump(uint8_t ** bitmap, int mask_mode,
		    TicalcScreenCoord * sc)
{
  uint32_t max_cnt;
  int err;

  printl2(0, _("Receiving screendump...\n"));

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  sc->width = TI92_COLS;
  sc->height = TI92_ROWS;
  sc->clipped_width = TI92_COLS;
  sc->clipped_height = TI92_ROWS;

  if (*bitmap != NULL)
    free(*bitmap);
  (*bitmap) =
      (uint8_t *) malloc(TI92_COLS * TI92_ROWS * sizeof(uint8_t) / 8);
  if ((*bitmap) == NULL) {
    printl2(2, "Unable to allocate memory.\n");
    exit(0);
  }

  TRYF(ti92_send_SCR());
  TRYF(ti92_recv_ACK(NULL));

  err = ti92_recv_XDP(&max_cnt, *bitmap);	// pb with checksum
  if (err != ERR_CHECKSUM) {
  TRYF(err)};
  TRYF(ti92_send_ACK());

  printl2(0, _("Done.\n"));

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return 0;
}

int ti92_directorylist(TNode ** tree, uint32_t * memory)
{
  uint32_t unused;
  uint8_t buffer[65536];
  int err;
  TiVarEntry info;
  char folder_name[9] = "";
  TNode *vars, *apps;
  TNode *folder = NULL;

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  printl2(0, _("Directory listing...\n"));

  TRYF(ti92_send_REQ(0, TI92_RDIR, ""));
  TRYF(ti92_recv_ACK(NULL));

  TRYF(ti92_recv_VAR(&info.size, &info.type, info.name));

  *tree = t_node_new(NULL);
  vars = t_node_new(NULL);
  apps = t_node_new(NULL);
  t_node_append(*tree, vars);
  t_node_append(*tree, apps);

  for (;;) {
    TiVarEntry *ve = calloc(1, sizeof(TiVarEntry));
    TNode *node;

    TRYF(ti92_send_ACK());
    TRYF(ti92_send_CTS());

    TRYF(ti92_recv_ACK(NULL));
    TRYF(ti92_recv_XDP(&unused, buffer));
    memcpy(ve->name, buffer + 4, 8);	// skip 4 extra 00 uint8_t
    ve->name[8] = '\0';
    ve->type = buffer[12];
    ve->attr = buffer[13];
    ve->size = buffer[14] | (buffer[15] << 8) |
	(buffer[16] << 16) | (buffer[17] << 24);
    strcpy(ve->folder, "");

    tifiles_translate_varname(ve->name, ve->trans, ve->type);
    node = t_node_new(ve);

    if (ve->type == TI92_DIR) {
      strcpy(folder_name, ve->name);
      folder = t_node_append(vars, node);
    } else {
      strcpy(ve->folder, folder_name);
      t_node_append(folder, node);
    }

    printl2(0, _("Name: %8s | "), ve->name);
    printl2(0, _("Type: %8s | "), tifiles_vartype2string(ve->type));
    printl2(0, _("Attr: %i  | "), ve->attr);
    printl2(0, _("Size: %08X\n"), ve->size);

    TRYF(ti92_send_ACK());
    err = ti92_recv_CONT();
    if (err == ERR_EOT)
      break;
    TRYF(err);

    sprintf(update->label_text, _("Reading of '%s/%s'"),
	    ((TiVarEntry *) (folder->data))->trans, ve->trans);
    update_label();
    if (update->cancel)
      return -1;
  }

  TRYF(ti92_send_ACK());

  *memory = ticalc_dirlist_memused(*tree);

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return 0;
}

int ti92_recv_backup(const char *filename, int mask_mode)
{
  Ti9xBackup content = { 0 };
  uint32_t block_size;
  int block, err;
  uint32_t unused;
  uint16_t unused2;
  uint8_t *ptr;

  printl2(0, _("Receiving backup...\n"));

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  content.calc_type = CALC_TI92;
  sprintf(update->label_text, _("Receiving backup..."));
  update_label();

  // silent request
  TRYF(ti92_send_REQ(0, TI92_BKUP, "main\\backup"));
  TRYF(ti92_recv_ACK(&unused2));

  content.data_part = (uint8_t *) tifiles_calloc(128 * 1024, 1);
  content.type = TI92_BKUP;
  content.data_length = 0;

  for (block = 0;; block++) {
    sprintf(update->label_text, _("Receiving block %2i"), block);
    update_label();
    err = ti92_recv_VAR(&block_size, &content.type, content.rom_version);
    TRYF(ti92_send_ACK());

    if (err == ERR_EOT)
      break;
    TRYF(err);

    TRYF(ti92_send_CTS());
    TRYF(ti92_recv_ACK(NULL));

    ptr = content.data_part + content.data_length;
    TRYF(ti92_recv_XDP(&unused, ptr));
    memmove(ptr, ptr + 4, block_size);
    TRYF(ti92_send_ACK());
    content.data_length += block_size;
  }

  strcpy(content.comment, "Backup file received by TiLP");

  ti9x_write_backup_file(filename, &content);
  ti9x_free_backup_content(&content);

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return 0;
}

int ti92_send_backup(const char *filename, int mask_mode)
{
  Ti9xBackup content = { 0 };
  int i, nblocks;

  printl2(0, _("Sending backup...\n"));

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  sprintf(update->label_text, _("Sending backup..."));
  update_label();

  TRYF(ti9x_read_backup_file(filename, &content));

  TRYF(ti92_send_VAR(content.data_length, TI92_BKUP, content.rom_version));
  TRYF(ti92_recv_ACK(NULL));

  nblocks = content.data_length / 1024;
  for (i = 0; i <= nblocks; i++) {
    uint32_t length = (i != nblocks) ? 1024 : content.data_length % 1024;

    TRYF(ti92_send_VAR(length, TI92_BKUP, content.rom_version));
    TRYF(ti92_recv_ACK(NULL));

    TRYF(ti92_recv_CTS());
    TRYF(ti92_send_ACK());

    TRYF(ti92_send_XDP(length, content.data_part + 1024 * i));
    TRYF(ti92_recv_ACK(NULL));

    ((update->main_percentage)) = (float) i / nblocks;
    update_pbar();
    if (update->cancel)
      return -1;
  }

  TRYF(ti92_send_EOT());

  ti9x_free_backup_content(&content);

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return 0;
}

int ti92_recv_var(char *filename, int mask_mode, TiVarEntry * entry)
{
  static Ti9xRegular *content;
  uint16_t status;
  TiVarEntry *ve;
  char *fn;
  static int nvar = 0;
  uint32_t unused;
  uint8_t varname[18], utf8[35];

  printl2(0, _("Receiving variable(s)...\n"));

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  if ((mask_mode & MODE_RECEIVE_FIRST_VAR) ||
      (mask_mode & MODE_RECEIVE_SINGLE_VAR)) {
    content = ti9x_create_regular_content();
    nvar = 0;
  }

  content->calc_type = CALC_TI92;
  content->entries = (TiVarEntry *) tifiles_realloc(content->entries,
						    (nvar +
						     1) *
						    sizeof(TiVarEntry));
  ve = &(content->entries[nvar]);
  memcpy(ve, entry, sizeof(TiVarEntry));

  strcpy((char*)varname, entry->folder);
  strcat((char*)varname, "\\");
  strcat((char*)varname, entry->name);

  tifiles_translate_varname((char*)varname, (char*)utf8, entry->type);
  sprintf(update->label_text, _("Receiving '%s'"), utf8);
  update_label();

  TRYF(ti92_send_REQ(0, entry->type, (char*)varname));
  TRYF(ti92_recv_ACK(&status));
  if (status != 0)
    return ERR_MISSING_VAR;

  TRYF(ti92_recv_VAR(&ve->size, &ve->type, ve->name));
  TRYF(ti92_send_ACK());

  TRYF(ti92_send_CTS());
  TRYF(ti92_recv_ACK(NULL));

  ve->data = tifiles_calloc(ve->size + 4, 1);
  TRYF(ti92_recv_XDP(&unused, ve->data));
  memmove(ve->data, ve->data + 4, ve->size);
  TRYF(ti92_send_ACK());

  TRYF(ti92_recv_EOT());
  TRYF(ti92_send_ACK());

  if (++nvar > 1)
    strcpy(content->comment, "Group file received by TiLP");
  else
    strcpy(content->comment, "Single file received by TiLP");

  content->num_entries = nvar;
  if (mask_mode & MODE_RECEIVE_SINGLE_VAR) {	// single
    ti9x_write_regular_file(NULL, content, &fn);
    strcpy(filename, fn);
    tifiles_free(fn);
    ti9x_free_regular_content(content);
  } else if (mask_mode & MODE_RECEIVE_LAST_VAR) {	// group
    ti9x_write_regular_file(filename, content, NULL);
    ti9x_free_regular_content(content);
  }

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  PAUSE(PAUSE_BETWEEN_VARS);

  return 0;
}

int ti92_send_var(const char *filename, int mask_mode, char **actions)
{
  Ti9xRegular content = { 0 };
  int i;
  uint16_t status;

  printl2(0, _("Sending variable(s)...\n"));

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  sprintf(update->label_text, _("Sending variable(s)..."));
  update_label();

  TRYF(ti9x_read_regular_file(filename, &content));

  for (i = 0; i < content.num_entries; i++) {
    TiVarEntry *entry = &(content.entries[i]);
    uint8_t buffer[65536 + 4] = { 0 };
    uint8_t full_name[18], varname[18], utf8[35];

    if (actions == NULL)	// backup or old behaviour
      strcpy((char*)varname, entry->name);
    else if (actions[i][0] == ACT_SKIP) {
      printl2(0, _(" '%s' has been skipped !\n"), entry->name);
      continue;
    } else if (actions[i][0] == ACT_OVER)
      strcpy((char*)varname, actions[i] + 1);

    if (mask_mode & MODE_LOCAL_PATH)
      strcpy((char*)full_name, (char*)varname);
    else {
      strcpy((char*)full_name, entry->folder);
      strcat((char*)full_name, "\\");
      strcat((char*)full_name, (char*)varname);
    }

    tifiles_translate_varname((char*)full_name, (char*)utf8, entry->type);
    sprintf(update->label_text, _("Sending '%s'"), utf8);
    update_label();

    TRYF(ti92_send_VAR(entry->size, entry->type, (char*)varname));
    TRYF(ti92_recv_ACK(NULL));

    TRYF(ti92_recv_CTS());
    TRYF(ti92_send_ACK());

    memcpy(buffer + 4, entry->data, entry->size);
    TRYF(ti92_send_XDP(entry->size + 4, buffer));
    TRYF(ti92_recv_ACK(&status));

    TRYF(ti92_send_EOT());
    TRYF(ti92_recv_ACK(NULL));

    printl2(0, "\n");
  }

  ti9x_free_regular_content(&content);

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return 0;
}

int ti92_recv_var_2(char *filename, int mask_mode, TiVarEntry *entry)
{
	Ti9xRegular *content;
	uint32_t unused;
	uint8_t utf8[35];
	int nvar, err;
    char tipath[18];
    char *tiname;

	printl2(0, _("Receiving variable(s)...\n"));

	// open cable
	LOCK_TRANSFER();
	TRYF(cable->open());
	update_start();

	// create variable content and fills
    content = ti9x_create_regular_content();
	content->calc_type = ticalcs_calc_type;

	// receive packets
	for(nvar = 1;; nvar++)
	{
		TiVarEntry *ve;

		content->entries = (TiVarEntry *) tifiles_realloc(content->entries, nvar * sizeof(TiVarEntry));
		ve = &(content->entries[nvar-1]);
		strcpy(ve->folder, "main");	

		err = ti92_recv_VAR(&ve->size, &ve->type, tipath);
		TRYF(ti92_send_ACK());

		if(err == ERR_EOT)	// end of transmission
			goto exit;
		else
			content->num_entries = nvar;

		// from Christian (TI can send varname or fldname/varname)
        if ((tiname = strchr(tipath, '\\')) != NULL) 
		{
			*tiname = '\0';
            strcpy(ve->folder, tipath);
            strcpy(ve->name, tiname + 1);
        }
        else 
		{
            strcpy(ve->folder, "main");
            strcpy(ve->name, tipath);
        }

		tifiles_translate_varname((char*)ve->name, (char*)utf8, ve->type);
		sprintf(update->label_text, _("Receiving '%s'"), utf8);
		update_label();

		TRYF(ti92_send_CTS());
		TRYF(ti92_recv_ACK(NULL));

		ve->data = tifiles_calloc(ve->size + 4, 1);
		TRYF(ti92_recv_XDP(&unused, ve->data));
		memmove(ve->data, ve->data + 4, ve->size);
		TRYF(ti92_send_ACK());
	}

exit:
	// close cable
	TRYF(cable->close());
	UNLOCK_TRANSFER();
    
	// write file content
	nvar--;
	if(nvar > 1)
	{
		strcpy(content->comment, "Group file received by TiLP");
		strcat(filename, "group.92g");
		ti9x_write_regular_file(filename, content, NULL);
	}
	else
	{
		strcpy(content->comment, "Single file received by TiLP");
		strcat(filename, content->entries[0].name);
		strcat(filename, ".");
		strcat(filename, tifiles_vartype2file(content->entries[0].type));
		ti9x_write_regular_file(filename, content, NULL);
	}
    ti9x_free_regular_content(content);

	return 0;
}

int ti92_send_flash(const char *filename, int mask_mode)
{
  return ERR_VOID_FUNCTION;
}

int ti92_recv_flash(const char *filename, int mask_mode, TiVarEntry * ve)
{
  return ERR_VOID_FUNCTION;
}

#define DUMP_ROM92_FILE "dumprom.92p"
#define ROMSIZE (1024*1024)

int ti92_dump_rom(const char *filename, int mask_mode)
{
  int i, j, k;
  uint8_t data;
  time_t start, elapsed, estimated, remaining;
  char buffer[257];
  char tmp[257];
  int pad;
  FILE *f, *file;
  uint16_t checksum, sum;

  printl2(0, _("ROM dumping...\n"));

  // Copies ROM dump program into a file
  f = fopen(DUMP_ROM92_FILE, "wb");
  if (f == NULL)
    return ERR_FILE_OPEN;

  fwrite(romDump92f2, sizeof(unsigned char), romDumpSize92f2, f);

  fclose(f);

  // Transfer program to calc
  TRYF(ti92_send_var(DUMP_ROM92_FILE, MODE_SEND_ONE_VAR, NULL));
  unlink(DUMP_ROM92_FILE);

  // Launch calculator program by remote control
  sprintf(update->label_text, _("Launching..."));
  update_label();

  TRY(ti92_send_key(KEY92P_CLEAR));
  PAUSE(50);
  TRY(ti92_send_key(KEY92P_CLEAR));
  PAUSE(50);
  TRY(ti92_send_key('m'));
  TRY(ti92_send_key('a'));
  TRY(ti92_send_key('i'));
  TRY(ti92_send_key('n'));
  TRY(ti92_send_key('\\'));
  TRY(ti92_send_key('d'));
  TRY(ti92_send_key('u'));
  TRY(ti92_send_key('m'));
  TRY(ti92_send_key('p'));
  TRY(ti92_send_key('r'));
  TRY(ti92_send_key('o'));
  TRY(ti92_send_key('m'));
  TRY(ti92_send_key(KEY92P_LP));
  TRY(ti92_send_key(KEY92P_RP));
  TRY(ti92_send_key(KEY92P_ENTER));

  // Open file
  file = fopen(filename, "wb");
  if (file == NULL)
    return ERR_OPEN_FILE;

  LOCK_TRANSFER();
  TRYF(cable->open());
  update_start();

  // Receive it now blocks per blocks (1024 + CHK)
  update_start();
  sprintf(update->label_text, _("Receiving..."));
  update_label();

  start = time(NULL);

  for (i = 0, k = 0; i < mask_mode * 1024; i++) {
    sum = 0;
    update->total = 1024;

    for (j = 0; j < 1024; j++) {
      TRY(cable->get(&data));
      fprintf(file, "%c", data);
      sum += data;

      update->count = j;
      update_pbar();
      if (update->cancel)
	return -1;
    }

    TRY(cable->get(&data));
    checksum = data << 8;
    TRY(cable->get(&data));
    checksum |= data;
    if (sum != checksum)
      return ERR_CHECKSUM;
    TRY(cable->put(0xda));

    update->count = 1024 * mask_mode;
    update->main_percentage = (float) i / (1024 * mask_mode);
    if (update->cancel)
      return -1;

    elapsed = (long) difftime(time(NULL), start);
    estimated = (long) (elapsed * (float) (1024 * mask_mode) / i);
    remaining = (long) difftime(estimated, elapsed);
    sprintf(buffer, "%s", ctime(&remaining));
    sscanf(buffer, "%3s %3s %i %s %i", tmp, tmp, &pad, tmp, &pad);
    sprintf(update->label_text, _("Remaining (mm:ss): %s"), tmp + 3);
    update_label();
  }

  TRY(cable->put(0xcc));	// make ROM dumping program exit.
  fclose(file);

  TRYF(cable->close());
  UNLOCK_TRANSFER();

  return 0;
}

int ti92_get_idlist(char *id)
{
  return ERR_VOID_FUNCTION;
}


syntax highlighted by Code2HTML, v. 0.9.1