/******************************************************************************
*
* Copyright (C) 2005-2017  Brian Berlin
* 
* 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 3 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, see <http://www.gnu.org/licenses/>
*
******************************************************************************
*
* FILENAME: states.c
*
* DESCRIPTION:  This handles saving and recall emulator states
*
* AUTHORS:  bberlin
*
* CHANGE LOG
*
*  VER     DATE     CHANGED BY   DESCRIPTION OF CHANGE
* ------  --------  ----------   ------------------------------------
* 0.2     12/10/05  bberlin      Creation
* 0.8.0   11/27/17  bberlin      Get media settings and rom file on 
*                                recall state (Fix GUI and back again)
* 0.9.0   01/23/19  bberlin      Change to use zip files, no more database
******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include "../win32/unistd.h"
#else
#include <unistd.h>
#endif
#include "../minizip/zip.h"
#include "../minizip/unzip.h"
#include "../core/gtia.h"
#include "../core/console.h"
#include "../core/memory.h"
#include "../core/pokeysound.h"
#include "logger.h"
#include "util.h"

#define ANTIC_CHIP 1
#define GTIA_CHIP  2
#define POKEY_CHIP 3
#define PIA_CHIP   4

extern KBYTE memory[0x10000];
extern unsigned int pokey_cpu_clock;

extern t_sio_interface sio;
extern ps_chip pokeyChip[MAXPOKEYS];

void states_recall_section_tag ( char *line, char *tag );
int states_recall_int ( char *line, char *tag, int *value );
int states_recall_string ( char *line, char *tag, char *value );
int states_recall_string_alloc ( char *line, char *tag, char **value );
void states_recall_dev_binaries ( unzFile zf, char *name, t_sio_device *dev );
void states_recall_binary ( unzFile zf, char *filename, KBYTE *data, int size );
void states_recall_binary_bin ( unzFile zf, char *filename, void *data, int size );
void states_recall_dev_info ( char *line, char *name, t_sio_device *dev );
void states_recall_media_info ( char *line, char *name, t_media *media );
void states_save_dev_info ( zipFile zf, char *name, t_sio_device *dev );
void states_save_dev_binaries ( zipFile zf, char *name, t_sio_device *dev );
void states_save_binary ( zipFile zf, char *filename, KBYTE *data, int size );
void states_save_binary_bin ( zipFile zf, char *filename, void *data, int size );
void states_save_media_info ( zipFile zf, char *name, t_media *media );
void states_append_int_value ( zipFile zf, const char *statement, int value );
void states_append_string_value ( zipFile zf, const char *statement, 
                                                    const char *value );
void state_append_section_title ( zipFile zf, char *title );

/******************************************************************************
**  Function   :  states_save
**                            
**  Objective  :  This function saves the machine state to the database
**
**  Parameters :  name   - name of state to save to
**                atari  - pointer to system struct to save
**                                                
**  return     :  Error code
**      
******************************************************************************/
int states_save ( char *name, t_atari *atari ) {

    unsigned int tmp_hex_data;
    unsigned int *temp_mem;
    char temp[50];
    char state_name[257];
    char state_path[270];
    int i, j, temp_int,status;
    int cart_bank_size;
    unsigned int portb, mem_image, be_switch;
    KBYTE sd_cmd_save[42];
    char *state_file;
    zipFile zf;
    zip_fileinfo zfi = { 0 };
    zipFile fp;

    /*
     * Create our zip file for current state
     */
    sprintf ( state_name, "%s.zip", name );
    util_make_path ( "states", state_name, state_path );
    state_file = util_create_program_path_alloc ( state_path );

    /* Ensure the states directory exists before trying to create the zip */
    {
        char states_dir[270];
        util_make_path ( (char *)util_get_program_path(), "states", states_dir );
        util_mkdir ( states_dir, UTIL_DEFAULT_DIR_MODE );
    }

    zf = zipOpen(state_file, APPEND_STATUS_CREATE);
    if ( zf == NULL ) {
        logger_log_message ( LOG_ERROR, "Unable to open state file for saving:", "" );
        logger_log_message ( LOG_ERROR, state_file, "" );
        free(state_file);
        return -1;
    }
    fp = zf;
 
    util_set_zip_time_info ( &zfi );
    sprintf ( state_name, "%s.conf", name );
    if ( ZIP_OK != zipOpenNewFileInZip(zf, state_name, &zfi, NULL, 0, NULL, 0, NULL, 
            Z_DEFLATED, Z_DEFAULT_COMPRESSION) ) {
        logger_log_message ( LOG_ERROR, "Unable to open state conf for saving:", "" );
        logger_log_message ( LOG_ERROR, state_name, "" );
        zipClose(zf, NULL);
        free(state_file);
        return -1;
    }
    
    /*
     * Save Cart and Machine and RAM
     */
    state_append_section_title ( fp, "general" );

    states_save_media_info ( zf, "cart", &atari->cart->media_info );
    states_save_media_info ( zf, "cart_right", &atari->cart_right->media_info );
    states_append_int_value ( fp, "machine", atari->machine_type );
    states_append_int_value ( fp, "tv_standard", atari->tv_standard );
    states_append_int_value ( fp, "frame_number", atari->frame_number );
    states_append_int_value ( fp, "ram_size", atari->ram_size );
    /*FIXME: Do we need media_loaded */
    states_append_int_value ( fp, "media_loaded", atari->cart->loaded );
    states_append_int_value ( fp, "basic_enable", atari->basic_enable );
    states_append_int_value ( fp, "option_key_pressed", atari->option_key_pressed );
    states_append_int_value ( fp, "start_key_pressed", atari->start_key_pressed );
    states_append_int_value ( fp, "main_media_type", console_get_main_media_type(atari,&temp_int) );
    states_append_int_value ( fp, "main_media_slot", temp_int );

    /*
     * Memory Stuff
     */
    state_append_section_title ( fp, "memory" );
    mem_get_state_values ( &portb, &mem_image, &be_switch  );
    cart_bank_size = mem_get_bank_image_size();
    states_append_int_value ( fp, "portb", portb );
    states_append_int_value ( fp, "mem_image", mem_image );
    states_append_int_value ( fp, "be_switch", be_switch );
    states_append_int_value ( fp, "cart_bank_size", cart_bank_size );
    {
        int hyb_mode, hyb_bank, hyb_16k_bank, sd_count, sd_phase;
        mem_get_hyb_state ( &hyb_mode, &hyb_bank, &hyb_16k_bank );
        mem_get_sd_cmd_state ( sd_cmd_save, &sd_count, &sd_phase );
        states_append_int_value ( fp, "hyb_bank_mode", hyb_mode );
        states_append_int_value ( fp, "hyb_bank", hyb_bank );
        states_append_int_value ( fp, "hyb_16k_bank", hyb_16k_bank );
        states_append_int_value ( fp, "sd_image_size", mem_get_sd_image_size() );
        states_append_int_value ( fp, "sd_cmd_count", sd_count );
        states_append_int_value ( fp, "sd_bfff_phase", sd_phase );
    }

    /*
     * Save 6502
     */
    state_append_section_title ( fp, "cpu" );
    states_append_int_value ( fp, "pc", atari->cpu->pc );
    states_append_int_value ( fp, "a_reg", atari->cpu->a_reg );
    states_append_int_value ( fp, "x_reg", atari->cpu->x_reg );
    states_append_int_value ( fp, "y_reg", atari->cpu->y_reg );
    states_append_int_value ( fp, "status", atari->cpu->status );
    states_append_int_value ( fp, "stack_ptr", atari->cpu->stack_ptr );
    states_append_int_value ( fp, "irq_assert", atari->cpu->irq_assert );
    states_append_int_value ( fp, "i_req", atari->cpu->i_req );
    states_append_int_value ( fp, "i_cnt", atari->cpu->i_cnt );

    /*
     * Save ANTIC
     */
    state_append_section_title ( fp, "antic" );
    states_append_int_value ( fp, "msc", atari->antic->msc );
    states_append_int_value ( fp, "dmactl", atari->antic->dmactl_reg );
    states_append_int_value ( fp, "chactl", atari->antic->chactl_reg );
    states_append_int_value ( fp, "dlistl", atari->antic->dlistl_reg );
    states_append_int_value ( fp, "dlisth", atari->antic->dlisth_reg );
    states_append_int_value ( fp, "hscroll", (atari->antic->hscroll_reg_value)>>1 );
    states_append_int_value ( fp, "vscroll", atari->antic->vscroll_reg_value );
    states_append_int_value ( fp, "pm_base", atari->antic->pm_base_address >> 8 );
    states_append_int_value ( fp, "ch_base", atari->antic->ch_base_address >> 8 );
    states_append_int_value ( fp, "vcount", atari->antic->vcount );
    tmp_hex_data = atari->antic->nmi_dli_en | atari->antic->nmi_vbi_en | atari->antic->nmi_reset_en;
    states_append_int_value ( fp, "nmi_en", tmp_hex_data );
    states_append_int_value ( fp, "nmi_status", atari->antic->nmi_status_reg );

    /*
     * Save GTIA
     */
    state_append_section_title ( fp, "gtia" );
    states_append_int_value ( fp, "hpos_p0", atari->gtia->hpos_p0_reg>>1 );
    states_append_int_value ( fp, "hpos_p1", atari->gtia->hpos_p1_reg>>1 );
    states_append_int_value ( fp, "hpos_p2", atari->gtia->hpos_p2_reg>>1 );
    states_append_int_value ( fp, "hpos_p3", atari->gtia->hpos_p3_reg>>1 );
    states_append_int_value ( fp, "hpos_m0", atari->gtia->hpos_m0_reg>>1 );
    states_append_int_value ( fp, "hpos_m1", atari->gtia->hpos_m1_reg>>1 );
    states_append_int_value ( fp, "hpos_m2", atari->gtia->hpos_m2_reg>>1 );
    states_append_int_value ( fp, "hpos_m3", atari->gtia->hpos_m3_reg>>1 );
    states_append_int_value ( fp, "size_p0", atari->gtia->w_reg[SIZEP0] );
    states_append_int_value ( fp, "size_p1", atari->gtia->w_reg[SIZEP1] );
    states_append_int_value ( fp, "size_p2", atari->gtia->w_reg[SIZEP2] );
    states_append_int_value ( fp, "size_p3", atari->gtia->w_reg[SIZEP3] );
    states_append_int_value ( fp, "size_m", atari->gtia->w_reg[SIZEM] );
    states_append_int_value ( fp, "graph_p0", atari->gtia->graph_p0_reg );
    states_append_int_value ( fp, "graph_p1", atari->gtia->graph_p1_reg );
    states_append_int_value ( fp, "graph_p2", atari->gtia->graph_p2_reg );
    states_append_int_value ( fp, "graph_p3", atari->gtia->graph_p3_reg );
    states_append_int_value ( fp, "graph_m", atari->gtia->graph_m_reg );
    states_append_int_value ( fp, "color_pf0", atari->gtia->color_pf0_reg );
    states_append_int_value ( fp, "color_pf1", atari->gtia->color_pf1_reg );
    states_append_int_value ( fp, "color_pf2", atari->gtia->color_pf2_reg );
    states_append_int_value ( fp, "color_pf3", atari->gtia->color_pf3_reg );
    states_append_int_value ( fp, "color_pm0", atari->gtia->color_pm0_reg );
    states_append_int_value ( fp, "color_pm1", atari->gtia->color_pm1_reg );
    states_append_int_value ( fp, "color_pm2", atari->gtia->color_pm2_reg );
    states_append_int_value ( fp, "color_pm3", atari->gtia->color_pm3_reg );
    states_append_int_value ( fp, "color_bk", atari->gtia->color_bk_reg );
    states_append_int_value ( fp, "m0pf", gtia_read_register(M0PF) );
    states_append_int_value ( fp, "m1pf", gtia_read_register(M1PF) );
    states_append_int_value ( fp, "m2pf", gtia_read_register(M2PF) );
    states_append_int_value ( fp, "m3pf", gtia_read_register(M3PF) );
    states_append_int_value ( fp, "p0pf", gtia_read_register(P0PF) );
    states_append_int_value ( fp, "p1pf", gtia_read_register(P1PF) );
    states_append_int_value ( fp, "p2pf", gtia_read_register(P2PF) );
    states_append_int_value ( fp, "p3pf", gtia_read_register(P3PF) );
    states_append_int_value ( fp, "m0pl", gtia_read_register(M0PL) );
    states_append_int_value ( fp, "m1pl", gtia_read_register(M1PL) );
    states_append_int_value ( fp, "m2pl", gtia_read_register(M2PL) );
    states_append_int_value ( fp, "m3pl", gtia_read_register(M3PL) );
    states_append_int_value ( fp, "p0pl", gtia_read_register(P0PL) );
    states_append_int_value ( fp, "p1pl", gtia_read_register(P1PL) );
    states_append_int_value ( fp, "p2pl", gtia_read_register(P2PL) );
    states_append_int_value ( fp, "p3pl", gtia_read_register(P3PL) );
    states_append_int_value ( fp, "trig0", atari->gtia->trig[0] );
    states_append_int_value ( fp, "trig1", atari->gtia->trig[1] );
    states_append_int_value ( fp, "trig2", atari->gtia->trig[2] );
    states_append_int_value ( fp, "trig3", atari->gtia->trig[3] );
    states_append_int_value ( fp, "prior", atari->gtia->prior_reg );
    tmp_hex_data = atari->gtia->latch_triggers | atari->gtia->player_dma | atari->gtia->missle_dma;
    states_append_int_value ( fp, "dma", tmp_hex_data );
    states_append_int_value ( fp, "vdelay", atari->gtia->vdelay_reg );
    states_append_int_value ( fp, "tv_standard", atari->gtia->pal_or_ntsc );
    states_append_int_value ( fp, "console_mask", atari->gtia->console_mask );
    states_append_int_value ( fp, "console", atari->gtia->console_reg );
    states_append_int_value ( fp, "start_pressed", atari->gtia->start_pressed );
    states_append_int_value ( fp, "select_pressed", atari->gtia->select_pressed );
    states_append_int_value ( fp, "option_pressed", atari->gtia->option_pressed );
    states_append_int_value ( fp, "internal_speaker", atari->gtia->internal_speaker );
    states_append_int_value ( fp, "keypad", atari->gtia->keypad );
    states_append_int_value ( fp, "pot_enable", atari->gtia->pot_enable );

    /*
     * Save POKEY
     */
    state_append_section_title ( fp, "pokey" );
    states_append_int_value ( fp, "audf1", atari->pokey->w_reg[AUDF1] );
    states_append_int_value ( fp, "audf2", atari->pokey->w_reg[AUDF2] );
    states_append_int_value ( fp, "audf3", atari->pokey->w_reg[AUDF3] );
    states_append_int_value ( fp, "audf4", atari->pokey->w_reg[AUDF4] );
    states_append_int_value ( fp, "audc1", atari->pokey->w_reg[AUDC1] );
    states_append_int_value ( fp, "audc2", atari->pokey->w_reg[AUDC2] );
    states_append_int_value ( fp, "audc3", atari->pokey->w_reg[AUDC3] );
    states_append_int_value ( fp, "audc4", atari->pokey->w_reg[AUDC4] );
    states_append_int_value ( fp, "audctl", atari->pokey->w_reg[AUDCTL] );
    states_append_int_value ( fp, "stimer", atari->pokey->w_reg[STIMER] );
    states_append_int_value ( fp, "sk_status", atari->pokey->sk_status );
    states_append_int_value ( fp, "sk_ctl", atari->pokey->sk_ctl );
    states_append_int_value ( fp, "serial_out_time", atari->pokey->serial_out_time );
    states_append_int_value ( fp, "serial_out", atari->pokey->serial_out );
    states_append_int_value ( fp, "serial_in", atari->pokey->serial_in );
    states_append_int_value ( fp, "irq_status", atari->pokey->irq_status );
    states_append_int_value ( fp, "irq_enable", atari->pokey->irq_enable );
    states_append_int_value ( fp, "kbcode", atari->pokey->kbcode );
    states_append_int_value ( fp, "pot0", atari->pokey->r_reg[POT0] );
    states_append_int_value ( fp, "pot1", atari->pokey->r_reg[POT1] );
    states_append_int_value ( fp, "pot2", atari->pokey->r_reg[POT2] );
    states_append_int_value ( fp, "pot3", atari->pokey->r_reg[POT3] );
    states_append_int_value ( fp, "pot4", atari->pokey->r_reg[POT4] );
    states_append_int_value ( fp, "pot5", atari->pokey->r_reg[POT5] );
    states_append_int_value ( fp, "pot6", atari->pokey->r_reg[POT6] );
    states_append_int_value ( fp, "pot7", atari->pokey->r_reg[POT7] );
    states_append_int_value ( fp, "allpot", atari->pokey->r_reg[ALLPOT] );
    states_append_int_value ( fp, "current_key_code", atari->pokey->current_key_code );
    states_append_int_value ( fp, "pot_scanline", atari->pokey->pot_scanline );
    states_append_int_value ( fp, "serial_mode_status", atari->pokey->serial_mode_status );
    states_append_int_value ( fp, "serial_hw_status", atari->pokey->serial_hw_status );
    states_append_int_value ( fp, "serial_irq_time", atari->pokey->serial_irq_time );
    states_append_int_value ( fp, "random_last_clock", atari->pokey->random_last_clock );
    states_append_int_value ( fp, "random_poly9", atari->pokey->random_poly9 );
    states_append_int_value ( fp, "random_poly17", atari->pokey->random_poly17 );
    states_append_int_value ( fp, "timer_irq_next_clock", atari->pokey->timer_irq_next_clock );
    states_append_int_value ( fp, "timer_irq_next_valid", atari->pokey->timer_irq_next_clock_valid );
    states_append_int_value ( fp, "timer1_irq_armed", atari->pokey->timer_irq[0].armed );
    states_append_int_value ( fp, "timer1_irq_reload_cycles", atari->pokey->timer_irq[0].reload_cycles );
    states_append_int_value ( fp, "timer1_irq_next_fire_clock", atari->pokey->timer_irq[0].next_fire_clock );
    states_append_int_value ( fp, "timer2_irq_armed", atari->pokey->timer_irq[1].armed );
    states_append_int_value ( fp, "timer2_irq_reload_cycles", atari->pokey->timer_irq[1].reload_cycles );
    states_append_int_value ( fp, "timer2_irq_next_fire_clock", atari->pokey->timer_irq[1].next_fire_clock );
    states_append_int_value ( fp, "timer4_irq_armed", atari->pokey->timer_irq[2].armed );
    states_append_int_value ( fp, "timer4_irq_reload_cycles", atari->pokey->timer_irq[2].reload_cycles );
    states_append_int_value ( fp, "timer4_irq_next_fire_clock", atari->pokey->timer_irq[2].next_fire_clock );
    sprintf ( temp, "%u", pokey_cpu_clock );
    states_append_string_value ( fp, "pokey_cpu_clock", temp );

    /*
     * Pokey Sound
     */
    states_append_int_value ( fp, "poly4_start", pokeyChip[0].clockPolyStart[POLY4_SEL] );
    states_append_int_value ( fp, "poly17_start", pokeyChip[0].clockPolyStart[POLY17_SEL] );
    states_append_int_value ( fp, "poly9_start", pokeyChip[0].clockPolyStart[POLY9_SEL] );
    states_append_int_value ( fp, "poly5_start", pokeyChip[0].clockPoly5Start );
    states_append_int_value ( fp, "clock114time", pokeyChip[0].clock114Time );
    states_append_int_value ( fp, "clock28time", pokeyChip[0].clock28Time );
    states_append_int_value ( fp, "volChanBits", pokeyChip[0].volChanBits );

    for ( i = 0; i < 4; ++i ) {
        sprintf ( temp, "chan%d.toggleClk", i );
        states_append_int_value ( fp, temp, pokeyChip[0].chan[i].toggleClk );
        sprintf ( temp, "chan%d.toggle", i );
        states_append_int_value ( fp, temp, pokeyChip[0].chan[i].toggle );
        sprintf ( temp, "chan%d.lastEventTime", i );
        states_append_int_value ( fp, temp, pokeyChip[0].chan[i].lastEventTime );
        sprintf ( temp, "chan%d.ffMainOut", i );
        states_append_int_value ( fp, temp, pokeyChip[0].chan[i].ffMainOut );
        sprintf ( temp, "chan%d.ffPoly5Out", i );
        states_append_int_value ( fp, temp, pokeyChip[0].chan[i].ffPoly5Out );
        sprintf ( temp, "chan%d.ffFilterOut", i );
        states_append_int_value ( fp, temp, pokeyChip[0].chan[i].ffFilterOut );
    }

    /*
     * Save PIA
     */
    state_append_section_title ( fp, "pia" );
    states_append_int_value ( fp, "input_a", atari->pia->input[PORTA] );
    states_append_int_value ( fp, "input_b", atari->pia->input[PORTB] );
    states_append_int_value ( fp, "port_control_a", atari->pia->port_control[PORTA] );
    states_append_int_value ( fp, "port_control_b", atari->pia->port_control[PORTB] );
    states_append_int_value ( fp, "port_a", atari->pia->port_value[PORTA] );
    states_append_int_value ( fp, "port_b", atari->pia->port_value[PORTB] );
    states_append_int_value ( fp, "direction_a", atari->pia->direction[PORTA] );
    states_append_int_value ( fp, "direction_b", atari->pia->direction[PORTB] );
    states_append_int_value ( fp, "pactl", atari->pia->port_control[PACTL&0x1] );
    states_append_int_value ( fp, "pbctl", atari->pia->port_control[PBCTL&0x1] );
    states_append_int_value ( fp, "portb_mem_ctl", mem_get_portb_value () );

    /*
     * SIO
     */
    state_append_section_title ( fp, "sio" );
    temp_int = MAX_SIO_DEVICES + 1; // default to cassette
    for ( i = 0; i < MAX_SIO_DEVICES; ++i ) {
        if ( atari->sio->current_device == &atari->sio->drives[i].dev )
            temp_int = i;
    }
    states_append_int_value ( fp, "current_dev", temp_int );
    states_append_int_value ( fp, "motor_on_time", atari->sio->motor_on_time );
    states_append_int_value ( fp, "motor_on", atari->sio->motor_on );
    states_append_int_value ( fp, "cmd_index", atari->sio->cmd_index );

    /* Disk Devices */
    for ( i = 0; i < MAX_SIO_DEVICES; ++i ) {
        sprintf ( temp, "drive%d.write_protect", i );
        states_append_int_value ( fp, temp, atari->sio->drives[i].write_protect );
        sprintf ( temp, "drive%d.sector_size", i );
        states_append_int_value ( fp, temp, atari->sio->drives[i].sector_size );
        sprintf ( temp, "drive%d.dsk_type", i );
        states_append_int_value ( fp, temp, atari->sio->drives[i].dsk_type );

        sprintf ( temp, "drive%d", i );
        states_save_dev_info ( zf, temp, &atari->sio->drives[i].dev );
        states_save_media_info ( zf, temp, &atari->sio->drives[i].media_info );
    }

    /* Cassette */
    states_append_int_value ( fp, "cassette.record_pressed", atari->sio->cassette.record_pressed );
    states_append_int_value ( fp, "cassette.motor_gap_delay", atari->sio->cassette.motor_gap_delay );
    states_append_string_value ( fp, "cassette.description", atari->sio->cassette.description );
    states_save_dev_info ( zf, "cassette", &atari->sio->cassette.dev );
    states_save_media_info ( zf, "cassette", &(atari->sio->cassette.media_info) );

    //fclose ( fp );
    zipCloseFileInZip(zf);

    /*
     * Now update with the actual BLOBS
     */
    sprintf ( state_name, "%s.stack.bin", name );
    //util_make_path ( "states", state_name, state_path );
    //cpu_stack = util_create_program_path_alloc ( state_path );
    states_save_binary ( zf, state_name, atari->cpu->stack, 0x100 );
    //free(cpu_stack);

    sprintf ( state_name, "%s.memory.bin", name );
    states_save_binary ( zf, state_name, memory, 0x10000 );

    sprintf ( state_name, "%s.cart_banks.bin", name );
    states_save_binary ( zf, state_name, mem_get_bank_image(), cart_bank_size );

    /* Hidden RAM behind cartridge */
    sprintf ( state_name, "%s.mem_image.bin", name );
    states_save_binary ( zf, state_name, mem_get_mem_image(),
                                         mem_get_mem_image_size() );

    /* Hybrid SD backing image and command machine */
    if ( mem_get_sd_image_size() > 0 ) {
        sprintf ( state_name, "%s.sd_image.bin", name );
        states_save_binary ( zf, state_name, mem_get_sd_image(),
                                             mem_get_sd_image_size() );
        sprintf ( state_name, "%s.sd_cmd.bin", name );
        states_save_binary ( zf, state_name, sd_cmd_save,
                             sizeof(sd_cmd_save) / sizeof(sd_cmd_save[0]) );
    }

    /*
     * For the XL/XE, save OS, BASIC, and Banked RAM seperately
     */
    if ( atari->machine_type == MACHINE_TYPE_XL ) {

        /* Bank */
        temp_mem = malloc ( 0x10000 * sizeof(KBYTE) );
        for ( i = 0; i < 0x10000; ++i ) {
            temp_mem[i] = read_xe_bank_state(i);
        }
        sprintf ( state_name, "%s.xe_banks.bin", name );
        states_save_binary ( zf, state_name, temp_mem, 0x10000 );

        /* BIOS */
        for ( i = 0; i < 0x4000; ++i ) {
            temp_mem[i] = read_os_rom(i);
        }
        sprintf ( state_name, "%s.bios.bin", name );
        states_save_binary ( zf, state_name, temp_mem, 0x4000 );

        /* Basic */
        for ( i = 0; i < 0x2000; ++i ) {
            temp_mem[i] = read_basic_rom(i);
        }
        sprintf ( state_name, "%s.basic.bin", name );
        states_save_binary ( zf, state_name, temp_mem, 0x2000 );

        free ( temp_mem );

    } /* end if an XL/XE system */

    /* SIO Cmd Frame */
    sprintf ( state_name, "%s.siocmdframe.bin", name );
    states_save_binary ( zf, state_name, atari->sio->cmd_frame, 0x6 );

    /* SIO Devices */
    for ( i = 0; i < MAX_SIO_DEVICES; ++i ) {

        sprintf ( state_name, "%s.drive%d", name, i );
        states_save_dev_binaries ( zf, state_name, &atari->sio->drives[i].dev );
    }

    /* Cassette */
    sprintf ( state_name, "%s.cassette", name );
    states_save_dev_binaries ( zf, state_name, &atari->sio->cassette.dev );

    t_cas_device tmpCas;
    memcpy ( tmpCas.blk, atari->sio->cassette.blk, sizeof(t_cas_block)*MAX_CAS_BLOCKS );
    for ( i = 0; i < MAX_CAS_BLOCKS; ++i ) {
        for ( j = 0; j < atari->sio->cassette.dev.buffer_size; ++j ) {
            if ( tmpCas.blk[i].data_ptr == &atari->sio->cassette.dev.f_buffer[j] ) {
                tmpCas.blk[i].data_ptr = (unsigned int *)(j);
                break;
            }
        }
        if ( j == atari->sio->cassette.dev.buffer_size )
            tmpCas.blk[i].data_ptr = (unsigned int *)(-1);
    }

    sprintf ( state_name, "%s.cassette_blk.bin", name );
    states_save_binary_bin ( zf, state_name, tmpCas.blk, 
                                     sizeof(t_cas_block) * (MAX_CAS_BLOCKS) );
    /*
     * Now save to a zip file
     */
    if ( zipClose(zf, NULL) != ZIP_OK ) {
        logger_log_message ( LOG_ERROR, "Failure while closing state file", "" );
        logger_log_message ( LOG_ERROR, state_file, "" );
    }


    free(state_file);

    return 0;

} /* end states_save */

/******************************************************************************
**  Function   :  states_recall
**                            
**  Objective  :  This function loads the machine state from the database
**
**  Parameters :  name   - name of state to recall
**                atari  - pointer to system struct to save
**                                                
**  return     :  Error code
**      
******************************************************************************/
int states_recall ( const char *name, t_atari *atari ) {

    char statement[1024];
    KBYTE *temp_mem;
    int i, j, status, start, tmp_slot, tmp_type;
    char *state_file, *state_name, *state_path;
    char *buffer;
    char line[512];
    char temp[1024];
    char tag[60];
    int tmp_int, portb = 0, mem_image = 0, be_switch = 0;
    int cart_bank_size = 0x20000;
    int hyb_bank_mode = 0, hyb_bank = 0, hyb_16k_bank = 0;
    int sd_image_size = 0, sd_cmd_count = 0, sd_bfff_phase = 0;
    KBYTE sd_cmd_buf[42] = { 0 };

    unzFile zf;
    unz_file_info file_info;

    /*
     * Open the zip containing the desired state
     */
    state_name = malloc(strlen(name) + 5);
    sprintf ( state_name, "%s.zip", name );
    state_path = malloc(strlen(state_name) + strlen("states") + 2);
    util_make_path ( "states", state_name, state_path );
    state_file = util_create_program_path_alloc ( state_path );
    zf = unzOpen(state_file);
    free(state_file);
    free(state_path);
    free(state_name);
    if ( zf == NULL ) {
        logger_log_message ( LOG_ERROR, "Unable to open state file for recall:", "" );
        logger_log_message ( LOG_ERROR, (char *)name, "" );
        return -1;
    }

    /*
     * Open the conf file from the zip
     */
    sprintf ( temp, "%s.conf", name );
    if ( unzLocateFile(zf, temp, 2) != UNZ_OK || unzOpenCurrentFile(zf) ) {
        logger_log_message ( LOG_ERROR, "Unable to find state file for recall:", "" );
        logger_log_message ( LOG_ERROR, (char *)name, "" );
        unzClose(zf);
        return -1;
    }

    /*
     * Read conf file into buffer
     */
    unzGetCurrentFileInfo(zf, &file_info, NULL, 0, NULL, 0, NULL, 0);
    buffer = malloc(file_info.uncompressed_size + 1);
    tmp_int = unzReadCurrentFile(zf, buffer, file_info.uncompressed_size);
    buffer[file_info.uncompressed_size] = 0;

    /* 
     * Go through buffer and read lines 
     */
    i = 0;
    while ( i < file_info.uncompressed_size ) {

        for ( j = 0; j < 512 && buffer[i++] && buffer[i] != '\n'; ++j ) {
            line[j] = buffer[i];
        }

        /* If no newline found, log it */
        if ( j == 512 ) {
            line[511] = '\0';
            logger_log_message ( LOG_ERROR, "Unable to find newline:", "" );
            logger_log_message ( LOG_ERROR, state_name, "" );
        }
        else 
            line[j] = '\0';

        /* Search for section tag */
        states_recall_section_tag ( line, tag );

        /* General */
        if ( strstr(tag, "general") ) {
            states_recall_media_info ( line, "cart", &atari->cart->media_info );
            states_recall_media_info ( line, "cart_right", &atari->cart_right->media_info );
            if ( states_recall_int ( line, "machine", &tmp_int ) )
                atari->machine_type = tmp_int;
            states_recall_int ( line, "tv_standard", &(atari->tv_standard) );
            states_recall_int ( line, "frame_number", &atari->frame_number ); /* frame number has "ram" */
            if ( states_recall_int ( line, "ram_size", &(atari->ram_size) ) ) {

                console_reset ( atari->machine_type, atari->tv_standard, 
                                atari->ram_size );
            }
            states_recall_int ( line, "media_loaded", &(atari->cart->loaded) );
            states_recall_int ( line, "basic_enable", &(atari->basic_enable) );
            states_recall_int ( line, "option_key_pressed", &(atari->option_key_pressed) );
            states_recall_int ( line, "start_key_pressed", &(atari->start_key_pressed) );
            states_recall_int ( line, "main_media_type", &tmp_type );
            if ( states_recall_int ( line, "main_media_slot", &tmp_slot ) )
                console_set_main_media_from_type ( atari, tmp_type, tmp_slot);
        }

        /* Memory */
        if ( strstr(tag, "memory") ) {
            states_recall_int ( line, "portb", &portb );
            states_recall_int ( line, "mem_image", &mem_image );
            states_recall_int ( line, "be_switch", &be_switch );
            states_recall_int ( line, "cart_bank_size", &cart_bank_size );
            states_recall_int ( line, "hyb_bank_mode", &hyb_bank_mode );
            states_recall_int ( line, "hyb_bank", &hyb_bank );
            states_recall_int ( line, "hyb_16k_bank", &hyb_16k_bank );
            states_recall_int ( line, "sd_image_size", &sd_image_size );
            states_recall_int ( line, "sd_cmd_count", &sd_cmd_count );
            states_recall_int ( line, "sd_bfff_phase", &sd_bfff_phase );
        }

        /* CPU */
        if ( strstr(tag, "cpu") ) {
            states_recall_int ( line, "pc", &(atari->cpu->pc) );
            states_recall_int ( line, "a_reg", &(atari->cpu->a_reg) );
            states_recall_int ( line, "x_reg", &(atari->cpu->x_reg) );
            states_recall_int ( line, "y_reg", &(atari->cpu->y_reg) );
            states_recall_int ( line, "status", &(atari->cpu->status) );
            states_recall_int ( line, "stack_ptr", &(atari->cpu->stack_ptr) );
            states_recall_int ( line, "irq_assert", &atari->cpu->irq_assert );
            states_recall_int ( line, "i_req", &atari->cpu->i_req );
            states_recall_int ( line, "i_cnt", &atari->cpu->i_cnt );
        }

        /* ANTIC */
        if ( strstr(tag, "antic") ) {
            states_recall_int ( line, "msc", &(atari->antic->msc) );
            if ( states_recall_int ( line, "dmactl", &(tmp_int) ) )
                antic_write_register ( DMACTL, tmp_int );
            if ( states_recall_int ( line, "chactl", &(tmp_int) ) )
                antic_write_register ( CHACTL, tmp_int );
            if ( states_recall_int ( line, "dlistl", &(tmp_int) ) )
                antic_write_register ( DLISTL, tmp_int );
            if ( states_recall_int ( line, "dlisth", &(tmp_int) ) )
                antic_write_register ( DLISTH, tmp_int );
            if ( states_recall_int ( line, "hscroll", &(tmp_int) ) )
                antic_write_register ( HSCROL, tmp_int );
            if ( states_recall_int ( line, "vscroll", &(tmp_int) ) )
                antic_write_register ( VSCROL, tmp_int );
            if ( states_recall_int ( line, "pm_base", &(tmp_int) ) )
                antic_write_register ( PMBASE, tmp_int );
            if ( states_recall_int ( line, "ch_base", &(tmp_int) ) )
                antic_write_register ( CHBASE, tmp_int );
            states_recall_int ( line, "vcount", &(atari->antic->vcount) );
            if ( states_recall_int ( line, "nmi_en", &(tmp_int) ) )
                antic_write_register ( NMIEN, tmp_int );
            states_recall_int ( line, "nmi_status", &(atari->antic->nmi_status_reg) );
        }

        /* GTIA */
        if ( strstr(tag, "gtia") ) {

            if ( states_recall_int ( line, "hpos_p0", &tmp_int ) )
                gtia_write_register ( HPOSP0, tmp_int );
            if ( states_recall_int ( line, "hpos_p1", &tmp_int ) )
                gtia_write_register ( HPOSP1, tmp_int );
            if ( states_recall_int ( line, "hpos_p2", &tmp_int ) )
                gtia_write_register ( HPOSP2, tmp_int );
            if ( states_recall_int ( line, "hpos_p3", &tmp_int ) )
                gtia_write_register ( HPOSP3, tmp_int );
            if ( states_recall_int ( line, "hpos_m0", &tmp_int ) )
                gtia_write_register ( HPOSM0, tmp_int );
            if ( states_recall_int ( line, "hpos_m1", &tmp_int ) )
                gtia_write_register ( HPOSM1, tmp_int );
            if ( states_recall_int ( line, "hpos_m2", &tmp_int ) )
                gtia_write_register ( HPOSM2, tmp_int );
            if ( states_recall_int ( line, "hpos_m3", &tmp_int ) )
                gtia_write_register ( HPOSM3, tmp_int );
            if ( states_recall_int ( line, "size_p0", &tmp_int ) )
                gtia_write_register ( SIZEP0, tmp_int );
            if ( states_recall_int ( line, "size_p1", &tmp_int ) )
                gtia_write_register ( SIZEP1, tmp_int );
            if ( states_recall_int ( line, "size_p2", &tmp_int ) )
                gtia_write_register ( SIZEP2, tmp_int );
            if ( states_recall_int ( line, "size_p3", &tmp_int ) )
                gtia_write_register ( SIZEP3, tmp_int );
            if ( states_recall_int ( line, "size_m", &tmp_int ) )
                gtia_write_register ( SIZEM, tmp_int );
            if ( states_recall_int ( line, "graph_p0", &tmp_int ) )
                gtia_write_register ( GRAFP0, tmp_int );
            if ( states_recall_int ( line, "graph_p1", &tmp_int ) )
                gtia_write_register ( GRAFP1, tmp_int );
            if ( states_recall_int ( line, "graph_p2", &tmp_int ) )
                gtia_write_register ( GRAFP2, tmp_int );
            if ( states_recall_int ( line, "graph_p3", &tmp_int ) )
                gtia_write_register ( GRAFP3, tmp_int );
            if ( states_recall_int ( line, "graph_m", &tmp_int ) )
                gtia_write_register ( GRAFM, tmp_int );
            if ( states_recall_int ( line, "color_pf0", &tmp_int ) )
                gtia_write_register ( COLPF0, tmp_int );
            if ( states_recall_int ( line, "color_pf1", &tmp_int ) )
                gtia_write_register ( COLPF1, tmp_int );
            if ( states_recall_int ( line, "color_pf2", &tmp_int ) )
                gtia_write_register ( COLPF2, tmp_int );
            if ( states_recall_int ( line, "color_pf3", &tmp_int ) )
                gtia_write_register ( COLPF3, tmp_int );
            if ( states_recall_int ( line, "color_pm0", &tmp_int ) )
                gtia_write_register ( COLPM0, tmp_int );
            if ( states_recall_int ( line, "color_pm1", &tmp_int ) )
                gtia_write_register ( COLPM1, tmp_int );
            if ( states_recall_int ( line, "color_pm2", &tmp_int ) )
                gtia_write_register ( COLPM2, tmp_int );
            if ( states_recall_int ( line, "color_pm3", &tmp_int ) )
                gtia_write_register ( COLPM3, tmp_int );
            if ( states_recall_int ( line, "color_bk", &tmp_int ) )
                gtia_write_register ( COLBK, tmp_int );
            if ( states_recall_int ( line, "m0pf", &tmp_int ) )
                gtia_write_collision ( M0PF, atari->gtia->m0pf_reg = tmp_int );
            if ( states_recall_int ( line, "m1pf", &tmp_int ) )
                gtia_write_collision ( M1PF, atari->gtia->m1pf_reg = tmp_int );
            if ( states_recall_int ( line, "m2pf", &tmp_int ) )
                gtia_write_collision ( M2PF, atari->gtia->m2pf_reg = tmp_int );
            if ( states_recall_int ( line, "m3pf", &tmp_int ) )
                gtia_write_collision ( M3PF, atari->gtia->m3pf_reg = tmp_int );
            if ( states_recall_int ( line, "p0pf", &tmp_int ) )
                gtia_write_collision ( P0PF, atari->gtia->p0pf_reg = tmp_int );
            if ( states_recall_int ( line, "p1pf", &tmp_int ) )
                gtia_write_collision ( P1PF, atari->gtia->p1pf_reg = tmp_int );
            if ( states_recall_int ( line, "p2pf", &tmp_int ) )
                gtia_write_collision ( P2PF, atari->gtia->p2pf_reg = tmp_int );
            if ( states_recall_int ( line, "p3pf", &tmp_int ) )
                gtia_write_collision ( P3PF, atari->gtia->p3pf_reg = tmp_int );
            if ( states_recall_int ( line, "m0pl", &tmp_int ) )
                gtia_write_collision ( M0PL, atari->gtia->m0pl_reg = tmp_int );
            if ( states_recall_int ( line, "m1pl", &tmp_int ) )
                gtia_write_collision ( M1PL, atari->gtia->m1pl_reg = tmp_int );
            if ( states_recall_int ( line, "m2pl", &tmp_int ) )
                gtia_write_collision ( M2PL, atari->gtia->m2pl_reg = tmp_int );
            if ( states_recall_int ( line, "m3pl", &tmp_int ) )
                gtia_write_collision ( M3PL, atari->gtia->m3pl_reg = tmp_int );
            if ( states_recall_int ( line, "p0pl", &tmp_int ) )
                gtia_write_collision ( P0PL, atari->gtia->p0pl_reg = tmp_int );
            if ( states_recall_int ( line, "p1pl", &tmp_int ) )
                gtia_write_collision ( P1PL, atari->gtia->p1pl_reg = tmp_int );
            if ( states_recall_int ( line, "p2pl", &tmp_int ) )
                gtia_write_collision ( P2PL, atari->gtia->p2pl_reg = tmp_int );
            if ( states_recall_int ( line, "p3pl", &tmp_int ) )
                gtia_write_collision ( P3PL, atari->gtia->p3pl_reg = tmp_int );
            states_recall_int ( line, "trig0", &atari->gtia->trig[0] );
            states_recall_int ( line, "trig1", &atari->gtia->trig[1] );
            states_recall_int ( line, "trig2", &atari->gtia->trig[2] );
            states_recall_int ( line, "trig3", &atari->gtia->trig[3] );
            if ( states_recall_int ( line, "prior", &tmp_int ) )
                gtia_write_register ( PRIOR, tmp_int );
            if ( states_recall_int ( line, "dma", &tmp_int ) )
                gtia_write_register ( GRACTL, tmp_int );
            if ( states_recall_int ( line, "vdelay", &tmp_int ) )
                gtia_write_register ( VDELAY, tmp_int );
            states_recall_int ( line, "tv_standard", &atari->gtia->pal_or_ntsc );
            states_recall_int ( line, "console_mask", &atari->gtia->console_mask );
            states_recall_int ( line, "console", &atari->gtia->console_reg );
            states_recall_int ( line, "start_pressed", &atari->gtia->start_pressed );
            states_recall_int ( line, "select_pressed", &atari->gtia->select_pressed );
            states_recall_int ( line, "option_pressed", &atari->gtia->option_pressed );
            states_recall_int ( line, "internal_speaker", &atari->gtia->internal_speaker );
            states_recall_int ( line, "keypad", &atari->gtia->keypad );
            states_recall_int ( line, "pot_enable", &atari->gtia->pot_enable );

        } /* end if tag gtia */

        /* POKEY */
        if ( strstr(tag, "pokey") ) {

            states_recall_int ( line, "audf1", &atari->pokey->w_reg[AUDF1] );
            states_recall_int ( line, "audf2", &atari->pokey->w_reg[AUDF2] );
            states_recall_int ( line, "audf3", &atari->pokey->w_reg[AUDF3] );
            states_recall_int ( line, "audf4", &atari->pokey->w_reg[AUDF4] );
            states_recall_int ( line, "audc1", &atari->pokey->w_reg[AUDC1] );
            states_recall_int ( line, "audc2", &atari->pokey->w_reg[AUDC2] );
            states_recall_int ( line, "audc3", &atari->pokey->w_reg[AUDC3] );
            states_recall_int ( line, "audc4", &atari->pokey->w_reg[AUDC4] );
            states_recall_int ( line, "audctl", &atari->pokey->w_reg[AUDCTL] );
            states_recall_int ( line, "stimer", &atari->pokey->w_reg[STIMER] );
            states_recall_int ( line, "sk_status", &atari->pokey->sk_status );
            states_recall_int ( line, "sk_ctl", &atari->pokey->sk_ctl );
            states_recall_int ( line, "serial_out_time", &atari->pokey->serial_out_time );
            states_recall_int ( line, "serial_out", &atari->pokey->serial_out );
            states_recall_int ( line, "serial_in", &atari->pokey->serial_in );
            states_recall_int ( line, "irq_status", &atari->pokey->irq_status );
            states_recall_int ( line, "irq_enable", &atari->pokey->irq_enable );
            states_recall_int ( line, "kbcode", &atari->pokey->kbcode );
            states_recall_int ( line, "pot0", &atari->pokey->r_reg[POT0] );
            states_recall_int ( line, "pot1", &atari->pokey->r_reg[POT1] );
            states_recall_int ( line, "pot2", &atari->pokey->r_reg[POT2] );
            states_recall_int ( line, "pot3", &atari->pokey->r_reg[POT3] );
            states_recall_int ( line, "pot4", &atari->pokey->r_reg[POT4] );
            states_recall_int ( line, "pot5", &atari->pokey->r_reg[POT5] );
            states_recall_int ( line, "pot6", &atari->pokey->r_reg[POT6] );
            states_recall_int ( line, "pot7", &atari->pokey->r_reg[POT7] );
            states_recall_int ( line, "allpot", &atari->pokey->r_reg[ALLPOT] );
            states_recall_int ( line, "current_key_code", &atari->pokey->current_key_code );
            states_recall_int ( line, "pot_scanline", &atari->pokey->pot_scanline );
            states_recall_int ( line, "serial_mode_status", &atari->pokey->serial_mode_status );
            states_recall_int ( line, "serial_hw_status", &atari->pokey->serial_hw_status );
            states_recall_int ( line, "serial_irq_time", &atari->pokey->serial_irq_time );
            states_recall_int ( line, "random_last_clock", (int *)&atari->pokey->random_last_clock );
            states_recall_int ( line, "random_poly9", (int *)&atari->pokey->random_poly9 );
            states_recall_int ( line, "random_poly17", (int *)&atari->pokey->random_poly17 );
            states_recall_int ( line, "timer_irq_next_clock", (int *)&atari->pokey->timer_irq_next_clock );
            states_recall_int ( line, "timer_irq_next_valid", &atari->pokey->timer_irq_next_clock_valid );
            states_recall_int ( line, "timer1_irq_armed", &atari->pokey->timer_irq[0].armed );
            states_recall_int ( line, "timer1_irq_reload_cycles", (int *)&atari->pokey->timer_irq[0].reload_cycles );
            states_recall_int ( line, "timer1_irq_next_fire_clock", (int *)&atari->pokey->timer_irq[0].next_fire_clock );
            states_recall_int ( line, "timer2_irq_armed", &atari->pokey->timer_irq[1].armed );
            states_recall_int ( line, "timer2_irq_reload_cycles", (int *)&atari->pokey->timer_irq[1].reload_cycles );
            states_recall_int ( line, "timer2_irq_next_fire_clock", (int *)&atari->pokey->timer_irq[1].next_fire_clock );
            states_recall_int ( line, "timer4_irq_armed", &atari->pokey->timer_irq[2].armed );
            states_recall_int ( line, "timer4_irq_reload_cycles", (int *)&atari->pokey->timer_irq[2].reload_cycles );
            states_recall_int ( line, "timer4_irq_next_fire_clock", (int *)&atari->pokey->timer_irq[2].next_fire_clock );
            if ( states_recall_string ( line, "pokey_cpu_clock", temp ) )
                pokey_cpu_clock = strtoul ( temp, NULL, 0 );

            states_recall_int ( line, "poly4_start", &pokeyChip[0].clockPolyStart[POLY4_SEL] );
            states_recall_int ( line, "poly17_start", &pokeyChip[0].clockPolyStart[POLY17_SEL] );
            states_recall_int ( line, "poly9_start", &pokeyChip[0].clockPolyStart[POLY9_SEL] );
            states_recall_int ( line, "poly5_start", &pokeyChip[0].clockPoly5Start );
            states_recall_int ( line, "clock114time", &pokeyChip[0].clock114Time );
            states_recall_int ( line, "clock28time", &pokeyChip[0].clock28Time );
            states_recall_int ( line, "volChanBits", &pokeyChip[0].volChanBits );

            for ( j = 0; j < 4; ++j ) {
                sprintf ( temp, "chan%d.toggleClk", j );
                states_recall_int ( line, temp, &pokeyChip[0].chan[j].toggleClk );
                sprintf ( temp, "chan%d.toggle", j );
                states_recall_int ( line, temp, &pokeyChip[0].chan[j].toggle );
                sprintf ( temp, "chan%d.lastEventTime", j );
                states_recall_int ( line, temp, &pokeyChip[0].chan[j].lastEventTime );
                sprintf ( temp, "chan%d.ffMainOut", j );
                states_recall_int ( line, temp, &pokeyChip[0].chan[j].ffMainOut );
                sprintf ( temp, "chan%d.ffPoly5Out", j );
                states_recall_int ( line, temp, &pokeyChip[0].chan[j].ffPoly5Out );
                sprintf ( temp, "chan%d.ffFilterOut", j );
                states_recall_int ( line, temp, &pokeyChip[0].chan[j].ffFilterOut );
            }

            /* Update Main Pokey sound chip */

            pokeyChip[0].lastClock = pokey_cpu_clock;
            pokeyChip[0].inInit = !(atari->pokey->sk_ctl & 0x3) ? 1 : 0;
            pokey_sound_volume_update(0);
            pokeyChip[0].reg[AUDF1] = atari->pokey->w_reg[AUDF1];
            pokeyChip[0].reg[AUDF2] = atari->pokey->w_reg[AUDF2];
            pokeyChip[0].reg[AUDF3] = atari->pokey->w_reg[AUDF3];
            pokeyChip[0].reg[AUDF4] = atari->pokey->w_reg[AUDF4];
            pokeyChip[0].reg[AUDC1] = atari->pokey->w_reg[AUDC1];
            pokeyChip[0].reg[AUDC2] = atari->pokey->w_reg[AUDC2];
            pokeyChip[0].reg[AUDC3] = atari->pokey->w_reg[AUDC3];
            pokeyChip[0].reg[AUDC4] = atari->pokey->w_reg[AUDC4];
            pokeyChip[0].reg[AUDCTL] = atari->pokey->w_reg[AUDCTL];
            pokeyChip[0].reg[STIMER] = atari->pokey->w_reg[STIMER];

            // Update Channels
            for ( j = 0; j < 4; j++ ) {
                pokey_sound_determine_timer (0, j, &(pokeyChip[0].chan[j].baseClock), 
                                                   &(pokeyChip[0].chan[j].counter));
                pokey_sound_determine_poly (0, j, &(pokeyChip[0].chan[j].polySel), 
                                                  &(pokeyChip[0].chan[j].poly5En));

                pokey_sound_determine_filter (0, j, &(pokeyChip[0].chan[j].filterEn));
                pokeyChip[0].chan[j].volume = (pokeyChip[0].reg[(j << 1) | (AUDC1_C)] & 0xf) << 1;
                pokeyChip[0].chan[j].volumeOnly = (pokeyChip[0].reg[(j << 1) | (AUDC1_C)] & 0x10) ? 1 : 0;
                pokeyChip[0].chan[j].enabled =  pokey_is_channel_enabled ( 0, j, 1 );
            }

        } /* end if tag pokey */

        /* PIA */
        if ( strstr(tag, "pia") ) {
            states_recall_int ( line, "input_a", &atari->pia->input[PORTA] );
            states_recall_int ( line, "input_b", &atari->pia->input[PORTB] );
            states_recall_int ( line, "port_control_a", &atari->pia->port_control[PORTA] );
            states_recall_int ( line, "port_control_b", &atari->pia->port_control[PORTB] );
            states_recall_int ( line, "port_a", &atari->pia->port_value[PORTA] );
            states_recall_int ( line, "port_b", &atari->pia->port_value[PORTB] );
            states_recall_int ( line, "direction_a", &atari->pia->direction[PORTA] );
            states_recall_int ( line, "direction_b", &atari->pia->direction[PORTB] );
            states_recall_int ( line, "pactl", &atari->pia->port_control[PACTL&0x1] );
            states_recall_int ( line, "pbctl", &atari->pia->port_control[PBCTL&0x1] );
            /* PORTB memory banking only exists on the XL/XE.  Applying it on a
             * 5200 (or 800) clobbers ANTIC's read map for $50-$57 and $C0-$FF
             * - e.g. it remaps the cart ROM that a custom character set lives
             * in, blanking score/timer text after a recall.  Match the gate
             * used by mem_assign_ops() so only the XL/XE restores it. */
            if ( states_recall_int ( line, "portb_mem_ctl", &tmp_int ) &&
                 atari->machine_type == MACHINE_TYPE_XL )
                mem_assign_portb_ops ( atari->cpu, tmp_int );
        }

        /* SIO */
        if ( strstr(tag, "sio") ) {

            if ( states_recall_int( line, "current_dev", &tmp_int ) ) {

                if ( tmp_int < 0 || tmp_int >= MAX_SIO_DEVICES )
                    atari->sio->current_device = &atari->sio->cassette.dev;
                else
                    atari->sio->current_device = &atari->sio->drives[tmp_int].dev;
            }
            states_recall_int ( line, "motor_on_time", &atari->sio->motor_on_time );
            states_recall_int ( line, "motor_on", &atari->sio->motor_on );
            states_recall_int ( line, "cmd_index", &atari->sio->cmd_index );

            /* Disk Devices */
            for ( j = 0; j < MAX_SIO_DEVICES; ++j ) {
                sprintf ( temp, "drive%d.write_protect", j );
                states_recall_int ( line, temp, &atari->sio->drives[j].write_protect );
                sprintf ( temp, "drive%d.sector_size", j );
                states_recall_int ( line, temp, &atari->sio->drives[j].sector_size );
                sprintf ( temp, "drive%d.dsk_type", j );
                states_recall_int ( line, temp, &atari->sio->drives[j].dsk_type );

                sprintf ( temp, "drive%d", j );
                states_recall_dev_info ( line, temp, &atari->sio->drives[j].dev );
                states_recall_media_info ( line, temp, &atari->sio->drives[j].media_info );
            }

            /* Cassette */
            states_recall_int ( line, "cassette.record_pressed", 
                                      &atari->sio->cassette.record_pressed );
            states_recall_int ( line, "cassette.motor_gap_delay", 
                                      &atari->sio->cassette.motor_gap_delay );
            states_recall_string ( line, "cassette.description", 
                                         atari->sio->cassette.description );
            states_recall_dev_info ( line, "cassette", &atari->sio->cassette.dev );
            states_recall_media_info ( line, "cassette", &atari->sio->cassette.media_info );

        }

    } /* end while reading file from zip */

    unzCloseCurrentFile(zf);
    free(buffer);

    /*
     * Now grab binary files
     */
    sprintf ( temp, "%s.stack.bin", name );
    states_recall_binary ( zf, temp, atari->cpu->stack, 0x100 );
 
    sprintf ( temp, "%s.memory.bin", name );
    states_recall_binary ( zf, temp, memory, 0x10000 );

    if ( cart_bank_size < 0 )
        cart_bank_size = 0x20000;
    memory_init_bank ( cart_bank_size );

    /* be_switch latch - after memory_init_bank() reset it to power-on */
    mem_set_state_values( portb, mem_image, be_switch );

    sprintf ( temp, "%s.cart_banks.bin", name );
    states_recall_binary ( zf, temp, mem_get_bank_image(), cart_bank_size );

    /* Hidden RAM behind cartridge */
    sprintf ( temp, "%s.mem_image.bin", name );
    states_recall_binary ( zf, temp, mem_get_mem_image(),
                                     mem_get_mem_image_size() );

    /* Hybrid active bank latch - after memory_init_bank() reset it */
    mem_set_hyb_state ( hyb_bank_mode, hyb_bank, hyb_16k_bank );

    /* Hybrid SD backing image and command machine.  Always clear first so a
     * non-hybrid (or SD-less) state does not inherit a prior cart's SD data. */
    memory_clear_sd ();
    if ( sd_image_size > 0 ) {
        memory_init_sd ( sd_image_size );
        sprintf ( temp, "%s.sd_image.bin", name );
        states_recall_binary ( zf, temp, mem_get_sd_image(), sd_image_size );
        sprintf ( temp, "%s.sd_cmd.bin", name );
        states_recall_binary ( zf, temp, sd_cmd_buf,
                               sizeof(sd_cmd_buf) / sizeof(sd_cmd_buf[0]) );
        mem_set_sd_cmd_state ( sd_cmd_buf, sd_cmd_count, sd_bfff_phase );
    }

    /*
     * For the XL/XE, save OS, BASIC, and Banked RAM seperately
     */
    if ( atari->machine_type == MACHINE_TYPE_XL ) {

        temp_mem = malloc ( 0x10000 * sizeof(KBYTE) );

        /* Bank */
        sprintf ( temp, "%s.xe_banks.bin", name );
        states_recall_binary ( zf, temp, temp_mem, 0x10000 );
        for ( i = 0; i < 0x10000; ++i ) {
            write_xe_bank_state(i, temp_mem[i]);
        }

        /* BIOS */
        sprintf ( temp, "%s.bios.bin", name );
        states_recall_binary ( zf, temp, temp_mem, 0x4000 );
        for ( i = 0; i < 0x4000; ++i ) {
            write_os_rom(i, temp_mem[i]);
        }

        /* Basic */
        sprintf ( temp, "%s.basic.bin", name );
        states_recall_binary ( zf, temp, temp_mem, 0x2000 );
        for ( i = 0; i < 0x2000; ++i ) {
            write_basic_rom(i, temp_mem[i]);
        }

        free ( temp_mem );

    } /* end if an XL/XE system */

    /* SIO Cmd Frame */
    sprintf ( temp, "%s.siocmdframe.bin", name );
    states_recall_binary ( zf, temp, atari->sio->cmd_frame, 0x6 );

    /* SIO Devices */
    for ( i = 0; i < MAX_SIO_DEVICES; ++i ) {

        sprintf ( temp, "%s.drive%d", name, i );
        states_recall_dev_binaries ( zf, temp, &atari->sio->drives[i].dev );
        if ( !atari->sio->drives[i].dev.f_buffer )
            atari->sio->drives[i].dev.buffer_size = 0;
    }

    /* SIO Cassette */
    sprintf ( temp, "%s.cassette", name );
    states_recall_dev_binaries ( zf, temp, &atari->sio->cassette.dev );
    if ( !atari->sio->cassette.dev.f_buffer )
        atari->sio->cassette.dev.buffer_size = 0;

    sprintf ( temp, "%s.cassette_blk.bin", name );
    states_recall_binary_bin ( zf, temp, (unsigned int *)atari->sio->cassette.blk, 
                                     sizeof(t_cas_block) * (MAX_CAS_BLOCKS) );
    for ( i = 0; i < MAX_CAS_BLOCKS; ++i ) {
        if ( (int)atari->sio->cassette.blk[i].data_ptr == -1 )
            atari->sio->cassette.blk[i].data_ptr = 0;
        else
            atari->sio->cassette.blk[i].data_ptr = 
                &atari->sio->cassette.dev.f_buffer[(int)atari->sio->cassette.blk[i].data_ptr];
    }

    /* Close the file */
    unzClose(zf);

    sprintf ( statement, "states_recall: Loaded state \"%s\"", name );
    logger_log_message ( LOG_INFO, statement, "" );

    return 0;

} /* end states_recall */

/******************************************************************************
**  Function   :  states_save_quick_state
**                            
**  Objective  :  This function saves the state to the selected slot
**
**  Parameters :  slot   - name of file to save state to
**                atari  - pointer to system struct to save
**                                                
**  return     :  Error code
**      
******************************************************************************/
int states_save_quick_state ( int slot, t_atari *atari ) {

    char name[10];
    int status;

    sprintf ( name, "QS%d", slot );

    status =  states_save ( name, atari );

    return status;

} /* end states_save_quick_state */

/******************************************************************************
**  Function   :  states_recall_quick_state
**                            
**  Objective  :  This function loads the state from a slot
**
**  Parameters :  slot   - slot to use when accessing the state
**                atari  - pointer to system struct to load
**                                                
**  return     :  Error code
**      
******************************************************************************/
int states_recall_quick_state ( int slot, t_atari *atari ) {

    char name[10];
    char state_path[50];
    char *state_file = 0;
    int status;

    sprintf ( name, "QS%d", slot );

    status = states_recall ( name, atari );

    free ( state_file );

    return status;

} /* end states_recall_quick_state */

/******************************************************************************
**  Function   :  states_get_quick_state_existance
**                            
**  Objective  :  This function determines which quick states are in the db
**
**  Parameters :  exists - an array to store 1 for exists, 0 otherwise.  Note
**                         internally this is zero based, but 1 based outside.
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_get_quick_state_existance ( int *exists ) {

    char state_name[20];
    char state_path[30];
    char *state_file;
    int i, status;

    for ( i = 0; i < 9; ++i ) {

        sprintf ( state_name, "QS%d.zip", i+1 );
        util_make_path ( "states", state_name, state_path );
        state_file = util_create_program_path_alloc ( state_path );

        if( access( state_file, F_OK ) != -1 ) {
            exists[i] = 1;
        } else {
            exists[i] = 0;
        }

        free(state_file);
    }

} /* end states_get_quick_state_existance */

/******************************************************************************
**  Function   :  states_recall_section_tag
**                            
**  Objective  :  This function checks the input line for containing a section
**                tag and places it in the input tag string
**
**  Parameters :  line - single line to check from the state file
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_recall_section_tag ( char *line, char *tag ) {

    char *start;
    char *end;

    if ( (start = strchr(line,'[')) && (end = strchr(line, ']')) ) {
        if ( end > start ) {
            strncpy(tag, start+1, (end-start-1));
            tag[end-start-1] = '\0';
        }
    }

} /* end states_recall_section_tag */

/******************************************************************************
**  Function   :  states_recall_int
**                states_recall_string
**                            
**  Objective  :  These functions checks the input line for containing the 
**                input tag, if so get the value and return 1.  If not found
**                return 0;
**
**  Parameters :  line  - single line to check from the state file
**                tag   - tag to search for
**                value - ouput pointer to place value in
**                                                
**  return     :  1 if tag found, 0 if tag not found
**      
******************************************************************************/
int states_recall_int ( char *line, char *tag, int *value ) {

    char *split, *tag_place;

    if ((split = strchr(line, '=')) && (tag_place = strstr(line,tag)) && 
                    (tag_place < split) ) {
        *value = atoi(split+1);
        return 1;
    }

    return 0;

} /* end states_recall_int */

int states_recall_string ( char *line, char *tag, char *value ) {

    char *split, *tag_place;

    if ((split = strchr(line, '=')) && (tag_place = strstr(line,tag)) && 
                    (tag_place < split) ) {
        strcpy ( value, split+2 );
        return 1;
    }

    return 0;

} /* end states_recall_string */

int states_recall_string_alloc ( char *line, char *tag, char **value ) {

    char *split, *tag_place;

    if ((split = strchr(line, '=')) && (tag_place = strstr(line,tag)) && 
                    (tag_place < split) ) {
        if ( strlen(split+2) )
            util_alloc_string ( value, split+2 );
        else 
            *value = NULL;
        return 1;
    }

    return 0;

} /* end states_recall_string_alloc */

/******************************************************************************
**  Function   :  states_recall_dev_binaries
**                            
**  Objective  :  This function allocates memory for sio devices and hunts for
**                the data in the input zip file
**
**  Parameters :  zf   - opened zip file to search for bin files
**                name - which disk or cassette we are searching for
**                dev  - sio device struct to put recalled data in
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_recall_dev_binaries ( unzFile zf, char *name, t_sio_device *dev ) {

    char state_name[350];

    if ( dev->buffer_size ) {
        free(dev->f_buffer);
        dev->f_buffer = calloc ( dev->buffer_size, sizeof(KBYTE) );
        sprintf ( state_name, "%sfbuffer.bin", name );
        states_recall_binary ( zf, state_name, dev->f_buffer, dev->buffer_size );
        if ( (int)dev->current_data == -1 )
            dev->current_data = 0;
        else
            dev->current_data = &dev->f_buffer[(int)dev->current_data];
    }

    sprintf ( state_name, "%sheader.bin", name );
    states_recall_binary ( zf, state_name, dev->header, 16 );

    sprintf ( state_name, "%sdata_frame.bin", name );
    states_recall_binary ( zf, state_name, dev->data_frame, 260 );

} /* end states_recall_dev_binaries */

/******************************************************************************
**  Function   :  states_recall_binary
**                            
**  Objective  :  This function searches for and opens the binary file inside
**                of the zip file and reads contents into data buffer.
**
**  Parameters :  zf       - opened zip file to search for bin files
**                filename - file inside zip to open 
**                data     - buffer to read data into
**                size     - amount of data to read
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_recall_binary ( unzFile zf, char *filename, KBYTE *data, int size ) {

    int i;
    unsigned char *tmp_data = malloc(size);

    if ( unzLocateFile(zf, filename, 2) != UNZ_OK || unzOpenCurrentFile(zf) ) {
        logger_log_message ( LOG_ERROR, "Unable to open state file for recall:", "" );
        logger_log_message ( LOG_ERROR, filename, "" );
        return;
    }

    unzReadCurrentFile(zf, tmp_data, size);

    for ( i = 0; i < size; ++i )
        data[i] = tmp_data[i];

    unzCloseCurrentFile(zf);

    free ( tmp_data );

} /* end states_recall_binary */
 
void states_recall_binary_bin ( unzFile zf, char *filename, void *data, int size ) {

    if ( unzLocateFile(zf, filename, 2) != UNZ_OK || unzOpenCurrentFile(zf) ) {
        logger_log_message ( LOG_ERROR, "Unable to open state file for recall:", "" );
        logger_log_message ( LOG_ERROR, filename, "" );
        return;
    }

    unzReadCurrentFile(zf, data, size);
    unzCloseCurrentFile(zf);

} /* end states_recall_binary */

/******************************************************************************
**  Function   :  states_recall_dev_info
**                            
**  Objective  :  This function searches the input line from the state file
**                for any device data, if any found, will read into struct
**                struct
**
**  Parameters :  line - string with line to search on
**                name - device name
**                dev  - device struct to read any found data into
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_recall_dev_info ( char *line, char *name, t_sio_device *dev ) {

    char temp[350];
    int tmp_int;

    sprintf ( temp, "%s.dev.filename", name );
    states_recall_string ( line, temp, dev->filename );
    sprintf ( temp, "%s.dev.buffer_size", name );
    states_recall_int ( line, temp, &dev->buffer_size );
    sprintf ( temp, "%s.dev.num_blocks", name );
    states_recall_int ( line, temp, &dev->num_blocks );
    sprintf ( temp, "%s.dev.current_block", name );
    states_recall_int ( line, temp, &dev->current_block );
    sprintf ( temp, "%s.dev.current_byte", name );
    states_recall_int ( line, temp, &dev->current_byte );
    sprintf ( temp, "%s.dev.expected_bytes", name );
    states_recall_int ( line, temp, &dev->expected_bytes );
    sprintf ( temp, "%s.dev.header_size", name );
    states_recall_int ( line, temp, &dev->header_size );
    sprintf ( temp, "%s.dev.baud", name );
    states_recall_int ( line, temp, &dev->baud );
    sprintf ( temp, "%s.dev.action_time", name );
    states_recall_int ( line, temp, &dev->action_time );
    sprintf ( temp, "%s.dev.status", name );
    states_recall_int ( line, temp, (int *)&(dev->status) );
    sprintf ( temp, "%s.dev.type", name );
    states_recall_int ( line, temp, (int *)&dev->type );
    sprintf ( temp, "%s.dev.current_data", name );
    states_recall_int ( line, temp, (int *)&dev->current_data );
}

/******************************************************************************
**  Function   :  states_save_dev_info
**                            
**  Objective  :  This function adds the sio device data into the state file
**                that is currently open in the zip file
**
**  Parameters :  zf   - opened zip file with currently open state file.
**                name - device name
**                dev  - device struct to get data from to write
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_save_dev_info ( zipFile zf, char *name, t_sio_device *dev ) {

    char temp[350];
    int i;

    if ( !dev->f_buffer )
        dev->buffer_size = 0;

    sprintf ( temp, "%s.dev.filename", name );
    states_append_string_value ( zf, temp, dev->filename );
    sprintf ( temp, "%s.dev.buffer_size", name );
    states_append_int_value ( zf, temp, dev->buffer_size );
    sprintf ( temp, "%s.dev.num_blocks", name );
    states_append_int_value ( zf, temp, dev->num_blocks );
    sprintf ( temp, "%s.dev.current_block", name );
    states_append_int_value ( zf, temp, dev->current_block );
    sprintf ( temp, "%s.dev.current_byte", name );
    states_append_int_value ( zf, temp, dev->current_byte );
    sprintf ( temp, "%s.dev.expected_bytes", name );
    states_append_int_value ( zf, temp, dev->expected_bytes );
    sprintf ( temp, "%s.dev.header_size", name );
    states_append_int_value ( zf, temp, dev->header_size );
    sprintf ( temp, "%s.dev.baud", name );
    states_append_int_value ( zf, temp, dev->baud );
    sprintf ( temp, "%s.dev.action_time", name );
    states_append_int_value ( zf, temp, dev->action_time );
    sprintf ( temp, "%s.dev.status", name );
    states_append_int_value ( zf, temp, dev->status );
    sprintf ( temp, "%s.dev.type", name );
    states_append_int_value ( zf, temp, dev->type );
    sprintf ( temp, "%s.dev.current_data", name );
    for ( i = 0; i < dev->buffer_size; ++i ) {
        if ( dev->current_data == &dev->f_buffer[i] )
            break;
    }
    if ( i == dev->buffer_size )
        states_append_int_value ( zf, temp, -1 );
    else
        states_append_int_value ( zf, temp, i );
}

/******************************************************************************
**  Function   :  states_recall_media_info
**                            
**  Objective  :  This function recalls the media info data from the state file
**                that is currently open in the zip file
**
**  Parameters :  line  - current text line in current zip
**                name  - device name
**                media - media struct to get data from to write
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_recall_media_info ( char *line, char *name, t_media *media ) {

    char temp[350];
    char temp_var[100];
    int i;

    sprintf ( temp, "%s.media_info.title", name );
    states_recall_string_alloc ( line, temp, &media->title );
    sprintf ( temp, "%s.media_info.file", name );
    states_recall_string_alloc ( line, temp, &media->file );
    sprintf ( temp, "%s.media_info.image_size", name );
    states_recall_int ( line, temp, &media->image_size );
    sprintf ( temp, "%s.media_info.mapping", name );
    states_recall_int ( line, temp, &media->mapping );
    sprintf ( temp, "%s.media_info.media_type", name );
    states_recall_int ( line, temp, &media->media_type );
    sprintf ( temp, "%s.media_info.machine_type", name );
    states_recall_int ( line, temp, &media->machine_type );
    sprintf ( temp, "%s.media_info.write_protect", name );
    states_recall_int ( line, temp, &media->write_protect );
    sprintf ( temp, "%s.media_info.crc", name );
    if (states_recall_string ( line, temp, temp_var ))
        media->crc = strtoul ( temp_var, NULL, 0 );
    sprintf ( temp, "%s.media_info.collections", name );
    if (states_recall_string ( line, temp, temp_var ))
        media->collections = strtoul ( temp_var, NULL, 0 );
    sprintf ( temp, "%s.media_info.md5", name );
    states_recall_string_alloc ( line, temp, &media->md5 );
    sprintf ( temp, "%s.media_info.sha1", name );
    states_recall_string_alloc ( line, temp, &media->sha1 );
}

/******************************************************************************
**  Function   :  states_save_media_info
**                            
**  Objective  :  This function adds the media info data into the state file
**                that is currently open in the zip file
**
**  Parameters :  zf    - opened zip file with currently open state file.
**                name  - device name
**                media - media struct to get data from to write
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_save_media_info ( zipFile zf, char *name, t_media *media ) {

    char temp[350];
    char temp_var[100];
    int i;

    sprintf ( temp, "%s.media_info.title", name );
    states_append_string_value ( zf, temp, media->title );
    sprintf ( temp, "%s.media_info.file", name );
    states_append_string_value ( zf, temp, media->file );
    sprintf ( temp, "%s.media_info.image_size", name );
    states_append_int_value ( zf, temp, media->image_size );
    sprintf ( temp, "%s.media_info.mapping", name );
    states_append_int_value ( zf, temp, media->mapping );
    sprintf ( temp, "%s.media_info.media_type", name );
    states_append_int_value ( zf, temp, media->media_type );
    sprintf ( temp, "%s.media_info.machine_type", name );
    states_append_int_value ( zf, temp, media->machine_type );
    sprintf ( temp, "%s.media_info.write_protect", name );
    states_append_int_value ( zf, temp, media->write_protect );
    sprintf ( temp_var, "%lu", media->crc );
    sprintf ( temp, "%s.media_info.crc", name );
    states_append_string_value ( zf, temp, temp_var );
    sprintf ( temp_var, "%lu", media->collections );
    sprintf ( temp, "%s.media_info.collections", name );
    states_append_string_value ( zf, temp, temp_var );
    sprintf ( temp, "%s.media_info.md5", name );
    states_append_string_value ( zf, temp, media->md5 );
    sprintf ( temp, "%s.media_info.sha1", name );
    states_append_string_value ( zf, temp, media->sha1 );
}

/******************************************************************************
**  Function   :  states_save_dev_binaries
**                            
**  Objective  :  This function stores the sio device data into binary files
**                inside the zip file.
**
**  Parameters :  zf   - opened zip file to store binaries in
**                name - device name
**                dev  - device struct to get data from to write
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_save_dev_binaries ( zipFile zf, char *name, t_sio_device *dev ) {

    char state_name[350];

    if ( dev->f_buffer ) {
        sprintf ( state_name, "%sfbuffer.bin", name );
        states_save_binary ( zf, state_name, dev->f_buffer, dev->buffer_size );
    }

    sprintf ( state_name, "%sheader.bin", name );
    states_save_binary ( zf, state_name, dev->header, 16 );

    sprintf ( state_name, "%sdata_frame.bin", name );
    states_save_binary ( zf, state_name, dev->data_frame, 260 );
}

/******************************************************************************
**  Function   :  states_save_binary
**                            
**  Objective  :  This function stores input buffer into a binary file inside
**                the zip file.
**
**  Parameters :  zf       - opened zip file to store binary in
**                filename - filename to call created binary inside zip
**                data     - buffer of binary data to store
**                size     - size of buffer to store
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_save_binary ( zipFile zf, char *filename, KBYTE *data, int size ) {

    int i;
    unsigned char *tmp_data = malloc(size);
    zip_fileinfo zfi = { 0 };

    util_set_zip_time_info ( &zfi );
    if ( ZIP_OK != zipOpenNewFileInZip(zf, filename, &zfi, NULL, 0, NULL, 0, NULL, 
            Z_DEFLATED, Z_DEFAULT_COMPRESSION) ) {
        logger_log_message ( LOG_ERROR, "Unable to write state binary for saving:", "" );
        logger_log_message ( LOG_ERROR, filename, "" );
        return;
    }

    for ( i = 0; i < size; ++i )
        tmp_data[i] = (unsigned char)data[i];
    
    zipWriteInFileInZip(zf, tmp_data, size);

    zipCloseFileInZip(zf);

    free (tmp_data);

} /* end states_save_binary */

void states_save_binary_bin ( zipFile zf, char *filename, void *data, int size ) {

    zip_fileinfo zfi = { 0 };

    util_set_zip_time_info ( &zfi );
    if ( ZIP_OK != zipOpenNewFileInZip(zf, filename, &zfi, NULL, 0, NULL, 0, NULL, 
            Z_DEFLATED, Z_DEFAULT_COMPRESSION) ) {
        logger_log_message ( LOG_ERROR, "Unable to write state binary for saving:", "" );
        logger_log_message ( LOG_ERROR, filename, "" );
        return;
    }

    zipWriteInFileInZip(zf, data, size);
    zipCloseFileInZip(zf);

} /* end states_save_binary */
/******************************************************************************
**  Function   :  states_append_int_value
**                states_append_string_value
**                            
**  Objective  :  This function appends the value to the conf file
**
**  Parameters :  zf        - zip file with open file to write to
**                statement - the tag to go in the file
**                value     - value to append to file
**                                                
**  return     :  NONE
**      
******************************************************************************/
void states_append_int_value ( zipFile zf, const char *statement, int value ) {

    char temp[300];
    int size;

    //fprintf ( fp, "%s = %d\n", statement, value );
    sprintf ( temp, "%s = %d\n", statement, value );
    zipWriteInFileInZip(zf, temp, strlen(temp));

} /* end states_append_int_value */

void states_append_string_value ( zipFile zf, const char *statement, 
                                                    const char *value ) {

    char temp[400];
    int size;

    //fprintf ( fp, "%s = %d\n", statement, value );
    if ( !value ) 
        sprintf ( temp, "%s = ""\n", statement );
    else
        sprintf ( temp, "%s = %s\n", statement, value );
    zipWriteInFileInZip(zf, temp, strlen(temp));

} /* end states_append_string_value */

void state_append_section_title ( zipFile zf, char *title ) {

    char temp[300];

    //fprintf ( fp, "\n[%s]\n", title );
    sprintf ( temp, "\n[%s]\n", title );
    zipWriteInFileInZip(zf, temp, strlen(temp));

} /* end state_append_section_title */
