/* tape_block.h: individual tape block types
   Copyright (c) 2003-2007 Philip Kendall

   $Id: tape_block.h,v 1.13 2007/02/02 16:35:43 pak21 Exp $

   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

   Author contact information:

   E-mail: philip-fuse@shadowmagic.org.uk

*/

#ifndef LIBSPECTRUM_TAPE_BLOCK_H
#define LIBSPECTRUM_TAPE_BLOCK_H

#ifndef LIBSPECTRUM_INTERNALS_H
#include "internals.h"
#endif				/* #ifndef LIBSPECTRUM_INTERNALS_H */

/*
 * The individual block types
 */

/* A standard ROM loading block */
typedef struct libspectrum_tape_rom_block {
  
  size_t length;		/* How long is this block */
  libspectrum_byte *data;	/* The actual data */
  libspectrum_dword pause;	/* Pause after block (milliseconds) */

  /* Private data */

  libspectrum_tape_state_type state;

  size_t edge_count;		/* Number of pilot pulses to go */

  size_t bytes_through_block;
  size_t bits_through_byte;	/* How far through the data are we? */

  libspectrum_byte current_byte; /* The current data byte; gets shifted out
				    as we read bits from it */
  libspectrum_dword bit_tstates; /* How long is an edge for the current bit */

} libspectrum_tape_rom_block;

/* A turbo loading block */
typedef struct libspectrum_tape_turbo_block {

  size_t length;		/* Length of data */
  size_t bits_in_last_byte;	/* How many bits are in the last byte? */
  libspectrum_byte *data;	/* The actual data */
  libspectrum_dword pause;	/* Pause after data (in ms) */

  libspectrum_dword pilot_length; /* Length of pilot pulse (in tstates) */
  size_t pilot_pulses;		/* Number of pilot pulses */

  libspectrum_dword sync1_length, sync2_length; /* Length of the sync pulses */
  libspectrum_dword bit0_length, bit1_length; /* Length of (re)set bits */

  /* Private data */

  libspectrum_tape_state_type state;

  size_t edge_count;		/* Number of pilot pulses to go */

  size_t bytes_through_block;
  size_t bits_through_byte;	/* How far through the data are we? */

  libspectrum_byte current_byte; /* The current data byte; gets shifted out
				    as we read bits from it */
  libspectrum_dword bit_tstates; /* How long is an edge for the current bit */

} libspectrum_tape_turbo_block;

/* A pure tone block */
typedef struct libspectrum_tape_pure_tone_block {

  libspectrum_dword length;
  size_t pulses;

  /* Private data */

  size_t edge_count;

} libspectrum_tape_pure_tone_block;

/* A list of pulses of different lengths */
typedef struct libspectrum_tape_pulses_block {

  size_t count;
  libspectrum_dword *lengths;

  /* Private data */

  size_t edge_count;

} libspectrum_tape_pulses_block;

/* A block of just of data */
typedef struct libspectrum_tape_pure_data_block {

  size_t length;		/* Length of data */
  size_t bits_in_last_byte;	/* How many bits are in the last byte? */
  libspectrum_byte *data;	/* The actual data */
  libspectrum_dword pause;	/* Pause after data (in ms) */

  libspectrum_dword bit0_length, bit1_length; /* Length of (re)set bits */

  /* Private data */

  libspectrum_tape_state_type state;

  size_t bytes_through_block;
  size_t bits_through_byte;	/* How far through the data are we? */

  libspectrum_byte current_byte; /* The current data byte; gets shifted out
				    as we read bits from it */
  libspectrum_dword bit_tstates; /* How long is an edge for the current bit */

} libspectrum_tape_pure_data_block;

/* A raw data block */
typedef struct libspectrum_tape_raw_data_block {

  size_t length;		/* Length of data */
  size_t bits_in_last_byte;	/* How many bits are in the last byte? */
  libspectrum_byte *data;	/* The actual data */
  libspectrum_dword pause;	/* Pause after data (in ms) */

  libspectrum_dword bit_length; /* Bit length. *Not* pulse length! */

  /* Private data */

  libspectrum_tape_state_type state;

  size_t bytes_through_block;
  size_t bits_through_byte;	/* How far through the data are we? */

  libspectrum_byte last_bit;	/* The last bit which was read */
  libspectrum_dword bit_tstates; /* How long is an edge for the current bit */

} libspectrum_tape_raw_data_block;

struct libspectrum_tape_generalised_data_symbol {

  libspectrum_tape_generalised_data_symbol_edge_type edge_type;
  libspectrum_word *lengths;

};

struct libspectrum_tape_generalised_data_symbol_table {

  libspectrum_dword symbols_in_block;
  libspectrum_byte max_pulses;
  libspectrum_word symbols_in_table;

  libspectrum_tape_generalised_data_symbol *symbols;

};

typedef struct libspectrum_tape_generalised_data_block {

  libspectrum_dword pause;	/* Pause after data (in ms) */

  libspectrum_tape_generalised_data_symbol_table pilot_table, data_table;

  libspectrum_byte *pilot_symbols;
  libspectrum_word *pilot_repeats;

  size_t bits_per_data_symbol;
  libspectrum_byte *data;

  /* Private data */

  libspectrum_tape_state_type state;

  libspectrum_dword run;
  libspectrum_word symbols_through_run;
  libspectrum_byte edges_through_symbol;

  libspectrum_byte current_symbol;
  size_t symbols_through_stream;

  libspectrum_byte current_byte;
  size_t bits_through_byte;
  size_t bytes_through_stream;

} libspectrum_tape_generalised_data_block;

/* A pause block */
typedef struct libspectrum_tape_pause_block {

  libspectrum_dword length;

} libspectrum_tape_pause_block;

/* A group start block */
typedef struct libspectrum_tape_group_start_block {

  char *name;

} libspectrum_tape_group_start_block;

/* No group end block needed as it contains no data */

/* A jump block */
typedef struct libspectrum_tape_jump_block {

  int offset;

} libspectrum_tape_jump_block;

/* A loop start block */
typedef struct libspectrum_tape_loop_start_block {

  int count;

} libspectrum_tape_loop_start_block;

/* No loop end block needed as it contains no data */

/* A select block */
typedef struct libspectrum_tape_select_block {

  /* Number of selections */
  size_t count;

  /* Offset of each selection, and a description of each */
  int *offsets;
  char **descriptions;

} libspectrum_tape_select_block;

/* No `stop tape if in 48K mode' block as it contains no data */

/* A comment block */
typedef struct libspectrum_tape_comment_block {

  char *text;

} libspectrum_tape_comment_block;

/* A message block */
typedef struct libspectrum_tape_message_block {

  int time;
  char *text;

} libspectrum_tape_message_block;

/* An archive info block */
typedef struct libspectrum_tape_archive_info_block {

  /* Number of strings */
  size_t count;

  /* ID for each string */
  int *ids;

  /* Text of each string */
  char **strings;

} libspectrum_tape_archive_info_block;

/* A hardware info block */
typedef struct libspectrum_tape_hardware_block {

  /* Number of entries */
  size_t count;

  /* For each entry, a type, an ID and a value */
  int *types, *ids, *values;

} libspectrum_tape_hardware_block;

/* A custom block */
typedef struct libspectrum_tape_custom_block {

  /* Description of this block */
  char *description;

  /* And the data for it; currently, no attempt is made to interpret
     this data */
  size_t length; libspectrum_byte *data;

} libspectrum_tape_custom_block;

/* No block needed for concatenation block, as it isn't stored */

/* Block types not present in the TZX format follow here */

/* A Z80Em or CSW audio block */
typedef struct libspectrum_tape_rle_pulse_block {

  size_t length;
  size_t index;
  libspectrum_byte *data;
  long scale;

} libspectrum_tape_rle_pulse_block;

/*
 * The generic tape block
 */

struct libspectrum_tape_block {

  libspectrum_tape_type type;

  union {
    libspectrum_tape_rom_block rom;
    libspectrum_tape_turbo_block turbo;
    libspectrum_tape_pure_tone_block pure_tone;
    libspectrum_tape_pulses_block pulses;
    libspectrum_tape_pure_data_block pure_data;
    libspectrum_tape_raw_data_block raw_data;

    libspectrum_tape_generalised_data_block generalised_data;

    libspectrum_tape_pause_block pause;
    libspectrum_tape_group_start_block group_start;
    /* No group end block needed as it contains no data */
    libspectrum_tape_jump_block jump;
    libspectrum_tape_loop_start_block loop_start;
    /* No loop end block needed as it contains no data */

    libspectrum_tape_select_block select;

    /* No `stop tape if in 48K mode' block as it contains no data */

    libspectrum_tape_comment_block comment;
    libspectrum_tape_message_block message;
    libspectrum_tape_archive_info_block archive_info;
    libspectrum_tape_hardware_block hardware;

    libspectrum_tape_custom_block custom;

    libspectrum_tape_rle_pulse_block rle_pulse;
  } types;

};

/* Functions needed by both tape.c and tape_block.c */
libspectrum_error
libspectrum_tape_pure_data_next_bit( libspectrum_tape_pure_data_block *block );
libspectrum_error
libspectrum_tape_raw_data_next_bit( libspectrum_tape_raw_data_block *block );

#endif				/* #ifndef LIBSPECTRUM_TAPE_BLOCK_H */

