/******************************************************************************
*
* Copyright (C) 2007-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: cart.c
*
* DESCRIPTION:  This contains functions for handling carts for each system
*
* AUTHORS:  bberlin
*
* CHANGE LOG
*
*  VER     DATE     CHANGED BY   DESCRIPTION OF CHANGE
* ------  --------  ----------   ------------------------------------
* 0.5.0   02/05/07  bberlin      Creation
******************************************************************************/
#include <stdio.h>
#include <string.h>
#include "cart.h"
#include "memory.h"
#include "console.h"

e_cart_type cart_find_16k_mapping ( unsigned long crc );

t_cart cart;
t_cart cart_right;

/******************************************************************************
**  Function   :  cart_init / exit
**                            
**  Objective  :  These functions handle initialization and exit.  
**
**  Parameters :  NONE
**                                                
**  return     :  NONE
**      
******************************************************************************/ 
void cart_init ( void ) {

    media_init ( &cart.media_info );
    media_init ( &cart_right.media_info );

} /* cart_init */

void cart_exit ( void ) {

    media_exit ( &cart.media_info );
    media_exit ( &cart_right.media_info );

} /* cart_exit */

/******************************************************************************
**  Function   :  cart_open
**                            
**  Objective  :  This function will load the cart rom into memory
**
**  Parameters :  file   - file name of file containing image 
**                buffer - contains the rom contents
**                size   - size of the buffer
**                crc    - checksum of the rom
**                mapping- rom to memory mapping type
**                launch - whether we are booting the image or just attaching
**                                                
**  return     :  0 if success                   
**                1 if failure                  
******************************************************************************/ 
int cart_open ( char *file, unsigned char *buffer, int size, unsigned long crc, 
                                                   e_cart_type mapping  ) {

    /*
     * First check for 5200 Bounty Bob
     */
    if ( crc == 0x74e6a611 )      {  /* $4000 */
        memory_init_bank ( 0x8000 );
        memory_write_bank_bytes ( 0x0000, buffer, 0x4000 );
        memory_write_rom_bytes ( 0x4000, buffer, 0x1000 );
        cart.media_info.mapping = CART_40K;
    }
    else if ( crc == 0xefa4915d ) {  /* $5000 */
        memory_write_bank_bytes ( 0x4000, buffer, 0x4000 );
        memory_write_rom_bytes ( 0x5000, buffer, 0x1000 );
        cart.media_info.mapping = CART_40K;
    }
    else if ( crc == 0x02931055  ) { /* $A000 */
        memory_write_rom_bytes ( 0x8000, buffer, 0x2000 );
        memory_write_rom_bytes ( 0xa000, buffer, 0x2000 );
        cart.media_info.mapping = CART_40K;
    }

    /*
     * Now check for 16KBE or 32KBE since size if variable
     *    Bosconian (M.U.L.E. and Berks Four are 64k, handled below)
     */
    else if ( mapping == CART_16K_BE ) {
        memory_init_bank ( size );
        memory_write_bank_bytes ( 0x0000, buffer, size );
        memory_write_rom_bytes ( 0x4000, buffer + size-0x8000, 0x8000 );
        cart.media_info.mapping = CART_16K_BE;
    }
    else if ( (mapping == CART_32K_BE ||
                crc==0xc44858e5 || crc==0xcedcd5d9 )
            && size > 0x8000 ) {
        memory_init_bank ( size );
        memory_write_bank_bytes ( 0x0000, buffer, size );
        memory_write_rom_bytes ( 0x4000, buffer + size-0x8000, 0x8000 );
        cart.media_info.mapping = CART_32K_BE;
    }

    /*
     * Now check for 4k and 8k roms
     */
    else if ( size < 5000 ) {
        memory_write_rom_bytes ( 0x8000, buffer, 0x1000 );
        memory_write_rom_bytes ( 0x9000, buffer, 0x1000 );
        memory_write_rom_bytes ( 0xa000, buffer, 0x1000 );
        memory_write_rom_bytes ( 0xb000, buffer, 0x1000 );
        cart.media_info.mapping = CART_4K;
    }
    else if ( size < 10000 ) {
        if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
            memory_write_rom_bytes ( 0x8000, buffer, 0x2000 );
            memory_write_rom_bytes ( 0xa000, buffer, 0x2000 );
            cart.media_info.mapping = CART_8K;
        }
        else if ( mapping == CART_PC_RIGHT ) {
            memory_write_rom_bytes ( 0x8000, buffer, 0x2000 );
            cart.media_info.mapping = CART_PC_RIGHT;
        }
        else {
            memory_write_rom_bytes ( 0xa000, buffer, 0x2000 );
            if ( mapping == CART_PHOENIX )
                cart.media_info.mapping = CART_PHOENIX;
            else
                cart.media_info.mapping = CART_PC_8K;
        }
    }

    /*
     * Now check for 16k roms
     */
    else if ( size < 20000 ) {
        if ( !mapping ) {
            /* One/two chip distinction is a 5200 concern; the 8-bit path below
             * overrides this anyway, so keep its prior two chip default. */
            if ( console_get_machine_type() == MACHINE_TYPE_5200 )
                mapping = cart_find_16k_mapping ( crc );
            else
                mapping = CART_16K_TWO_CHIP;
        }

        if ( mapping == CART_16K_ONE_CHIP ) {
            memory_write_rom_bytes ( 0x4000, buffer, 0x4000 );
            memory_write_rom_bytes ( 0x8000, buffer, 0x4000 );
            cart.media_info.mapping = CART_16K_ONE_CHIP;
        }
        else {
            memory_write_rom_bytes ( 0x4000, buffer, 0x2000 );
            memory_write_rom_bytes ( 0x6000, buffer, 0x2000 );
            memory_write_rom_bytes ( 0x8000, buffer+0x2000, 0x2000 );
            memory_write_rom_bytes ( 0xa000, buffer+0x2000, 0x2000 );
            cart.media_info.mapping = CART_16K_TWO_CHIP;
        }

        if ( console_get_machine_type() != MACHINE_TYPE_5200 ) {
            if ( mapping == CART_OSS ) {
                memory_write_rom_bytes ( 0xa000, buffer, 0x1000 );
                memory_write_rom_bytes ( 0xb000, buffer+0x3000, 0x1000 );
                cart.media_info.mapping = CART_OSS;
            }
            else if ( mapping == CART_OSS2 ) {
                memory_write_rom_bytes ( 0xa000, buffer+0x1000, 0x1000 );
                memory_write_rom_bytes ( 0xb000, buffer, 0x1000 );
                cart.media_info.mapping = CART_OSS2;
            }
            else {
                memory_write_rom_bytes ( 0x8000, buffer, 0x4000 );
                if ( mapping == CART_BLIZZARD )
                    cart.media_info.mapping = CART_BLIZZARD;
                else
                    cart.media_info.mapping = CART_PC_16K;
            }
        }
    }

    /*
     * Now check for 5200 Bounty Bob 40k
     */
    else if ( size > 40000  && size < 42000 ) {
        if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
            /*
             * Two file layouts exist for 5200 40K Bounty Bob:
             *   Merged:   [4000_banks 16KB][5000_banks 16KB][fixed 8KB]
             *   No-Intro: [fixed 8KB][4000_banks 16KB][5000_banks 16KB]
             * Detect by CART byte at $BFFC: 0x00 at offset 0x1FFC = No-Intro.
             */
            unsigned char *banks4, *banks5, *fixed;
            if ( buffer[0x1FFC] == 0x00 ) {
                fixed  = buffer;
                banks4 = buffer + 0x2000;
                banks5 = buffer + 0x6000;
            } else {
                banks4 = buffer;
                banks5 = buffer + 0x4000;
                fixed  = buffer + 0x8000;
            }

            memory_init_bank ( 0x8000 );
            memory_write_bank_bytes ( 0x0000, banks4, 0x4000 );
            memory_write_bank_bytes ( 0x4000, banks5, 0x4000 );

            memory_write_rom_bytes ( 0x4000, banks4, 0x1000 );
            memory_write_rom_bytes ( 0x5000, banks5, 0x1000 );

            memory_write_rom_bytes ( 0x8000, fixed, 0x2000 );
            memory_write_rom_bytes ( 0xa000, fixed, 0x2000 );
            cart.media_info.mapping = CART_40K;
        }
        else {
            memory_init_bank ( 0x8000 );
            memory_write_bank_bytes ( 0x0000, buffer, 0x8000 );

            memory_write_rom_bytes ( 0x8000, buffer, 0x1000 );
            memory_write_rom_bytes ( 0x9000, buffer+0x4000, 0x1000 );

            memory_write_rom_bytes ( 0xa000, buffer+0x8000, 0x2000 );
            cart.media_info.mapping = CART_PC_40K;
        }
    }

    /*
     * Now check for 64k
     */
    else if ( size > 62000  && size < 68000 ) {
        memory_init_bank ( 0x10000 );
        memory_write_bank_bytes ( 0x0000, buffer, 0x10000 );
        if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
            memory_write_rom_bytes ( 0x4000, buffer + size-0x8000, 0x8000 );
            cart.media_info.mapping = CART_32K_BE;
        }
        else {
            if ( mapping == CART_MEGA ) {
                memory_write_rom_bytes ( 0x8000, buffer, 0x4000 );
                cart.media_info.mapping = CART_MEGA;
            }
            if ( mapping == CART_WILL || mapping == CART_SDX ||
                 mapping == CART_EXP || mapping == CART_DIAMOND ) {
                memory_write_rom_bytes ( 0xa000, buffer, 0x2000 );
                cart.media_info.mapping = CART_WILL;
            }
            else {
                memory_write_rom_bytes ( 0x8000, buffer, 0x2000 );
                memory_write_rom_bytes ( 0xa000, buffer + (size-0x2000), 0x2000 );
                if ( mapping == CART_XEGS )
                    cart.media_info.mapping = CART_XEGS;
                else
                    cart.media_info.mapping = CART_SWXEGS;
            }
        }
    }

    /*
     * Now check for 512k hybrid
     */
    else if ( size > 512000 && size < 540000 ) {
        if ( strstr(file, ".hyb") || mapping==CART_512K_HYBRID ) {
            cart.media_info.mapping = CART_512K_HYBRID;
            memory_init_bank ( size );
            memory_write_bank_bytes ( 0x0000, buffer, size );
            memory_write_rom_bytes ( 0x8000, buffer + size-0x4000, 0x4000 );
        }
        else if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
            memory_init_bank ( size );
            memory_write_bank_bytes ( 0x0000, buffer, size );
            memory_write_rom_bytes ( 0x4000, buffer + size-0x8000, 0x8000 );
            cart.media_info.mapping = CART_32K_BE;
        }
    }

    /*
     * Now check for 128k
     */
    else if ( size > 130000  && size < 132000 ) {
        memory_init_bank ( 0x20000 );
        memory_write_bank_bytes ( 0x0000, buffer, 0x20000 );
        if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
            memory_write_rom_bytes ( 0x4000, buffer + size-0x8000, 0x8000 );
            cart.media_info.mapping = CART_32K_BE;
        }
        else if ( mapping == CART_MEGA ) {
            memory_write_rom_bytes ( 0x8000, buffer, 0x4000 );
            cart.media_info.mapping = CART_MEGA;
        }
        else if ( mapping == CART_ATMAX ) {
            memory_write_rom_bytes ( 0xa000, buffer, 0x2000 );
            cart.media_info.mapping = CART_ATMAX;
        }
        else if ( mapping == CART_ATRAX ) {
            memory_write_rom_bytes ( 0xa000, buffer, 0x2000 );
            cart.media_info.mapping = CART_ATRAX;
        }
        else {
            memory_write_rom_bytes ( 0x8000, buffer, 0x2000 );
            memory_write_rom_bytes ( 0xa000, buffer + (size-0x2000), 0x2000 );
            if ( mapping == CART_XEGS )
                cart.media_info.mapping = CART_XEGS;
            else
                cart.media_info.mapping = CART_SWXEGS;
        }
    }

    /*
     * Now check for 1024K
     */
    else if ( size > 1040000  && size < 1050000 ) {
        memory_init_bank ( 0x100000 );
        memory_write_bank_bytes ( 0x0000, buffer, 0x100000 );
        if ( mapping == CART_ATMAX ) {
            memory_write_rom_bytes ( 0xa000, buffer + (size-0x2000), 0x2000 );
            cart.media_info.mapping = CART_ATMAX;
        }
    }

    /*
     * Must be a standard 32k
     */
    else if ( size > 31000 && size < 34000 ) {
        if ( console_get_machine_type() == MACHINE_TYPE_5200 ) {
            memory_write_rom_bytes ( 0x4000, buffer, 0x8000 );
            cart.media_info.mapping = CART_32K;
        }
        else if ( mapping == CART_MEGA ) {
            memory_init_bank ( 0x8000 );
            memory_write_bank_bytes ( 0x0000, buffer, 0x8000 );
            memory_write_rom_bytes ( 0x8000, buffer, 0x4000 );
            cart.media_info.mapping = CART_MEGA;
        }
        else if ( mapping == CART_WILL ) {
            memory_write_rom_bytes ( 0xa000, buffer, 0x2000 );
            cart.media_info.mapping = CART_WILL;
        }
        else if ( mapping == CART_XEGS ) {
            memory_init_bank ( 0x8000 );
            memory_write_bank_bytes ( 0x0000, buffer, 0x8000 );
            memory_write_rom_bytes ( 0x8000, buffer, 0x2000 );
            memory_write_rom_bytes ( 0xa000, buffer+0x6000, 0x2000 );
            cart.media_info.mapping = CART_XEGS;
        }
        else {
            memory_init_bank ( 0x8000 );
            memory_write_bank_bytes ( 0x0000, buffer, 0x8000 );
            memory_write_rom_bytes ( 0x8000, buffer, 0x2000 );
            memory_write_rom_bytes ( 0xa000, buffer+0x6000, 0x2000 );
            cart.media_info.mapping = CART_PC_32K;
        }
    }
    else {
        return -1;
    }

    return 0;

} /* end cart_open */

/******************************************************************************
**  Function   :  cart_find_16k_mapping
**                            
**  Objective  :  This function determine which mapping (one chip or two chip)
**                to use if none is specified (for the 5200).
**
**                The default is one chip, which covers nearly all conversions,
**                hacks, homebrews and loose ROMs.  The two chip carts are a
**                bounded, known set (the original commercial 5200 titles), so
**                we enumerate those as exceptions here.  Any genuine two chip
**                ROM not listed can still be forced via its profile mapping.
**
**  Parameters :  crc - rom checksum to check
**
**  return     :  One chip or Two chip mapping
******************************************************************************/
e_cart_type cart_find_16k_mapping ( unsigned long crc ) {

    if ( crc == 0x4019ecec ||     /* Astro Chase                    */
         crc == 0xb3b8e314 ||     /* Battlezone                     */
         crc == 0x04807705 ||     /* Buck Rogers - Planet of Zoom   */
         crc == 0x536a70fe ||     /* Centipede                      */
         crc == 0xf1f42bbd ||     /* Congo Bongo                    */
         crc == 0xfd541c80 ||     /* Countermeasure                 */
         crc == 0xbd52623b ||     /* Defender                       */
         crc == 0x6a687f9c ||     /* Dig Dug                        */
         crc == 0x04b299a4 ||     /* Frisky Tom                     */
         crc == 0x0af19345 ||     /* Frogger II - Threeedeep!       */
         crc == 0xcfd4a7f9 ||     /* Gyruss                         */
         crc == 0xd9ae4518 ||     /* James Bond 007                 */
         crc == 0xbfd30c01 ||     /* Joust                          */
         crc == 0x59983c40 ||     /* Jr. Pac-Man                    */
         crc == 0x2c676662 ||     /* Jungle Hunt                    */
         crc == 0xecfa624f ||     /* Kangaroo                       */
         crc == 0x84df4925 ||     /* Looney Tunes Hotel             */
         crc == 0x931a454a ||     /* Microgammon SB                 */
         crc == 0xc597c087 ||     /* Miniature Golf                 */
         crc == 0x2a640143 ||     /* Montezuma's Revenge            */
         crc == 0x752f5efd ||     /* Ms. Pac-Man                    */
         crc == 0x8873ef51 ||     /* Pac-Man                        */
         crc == 0xabc2d1e4 ||     /* Pole Position                  */
         crc == 0xa18a9a40 ||     /* Popeye                         */
         crc == 0xaea6d2c2 ||     /* QIX                            */
         crc == 0x0f996184 ||     /* RealSports Basketball          */
         crc == 0x4336c2cc ||     /* RealSports Football            */
         crc == 0xecbd1853 ||     /* RealSports Soccer              */
         crc == 0x10f33c90 ||     /* RealSports Tennis              */
         crc == 0xa97606ab ||     /* Road Runner                    */
         crc == 0xb68d61e8 ||     /* Space Dungeon                  */
         crc == 0x73b5b6fb ||     /* Sport Goofy                    */
         crc == 0x7d819a9f ||     /* Star Raiders                   */
         crc == 0x69f23548 ||     /* Star Trek - Strategic Ops Sim  */
         crc == 0x75f566df ||     /* Star Wars - The Arcade Game    */
         crc == 0x1d1cee27 ||     /* Stargate                       */
         crc == 0xb8faaec3 ||     /* Xari Arena                     */
         /* Hacks derived from two chip originals (inherit two chip) */
         crc == 0x44ecaee7 ||     /* Gyruss (Auto-Fire) [h]         */
         crc == 0x8a80114d ||     /* Pac-Man (Bug Fixes) [h]        */
         crc == 0x07e02683 ||     /* Pac Pollux [h]                 */
         crc == 0x063fbbef ||     /* Pac-Man Plus [h]               */
         crc == 0x79d2684a ||     /* Pac Pollux Plus [h]            */
         crc == 0x932b21a1 ||     /* Pirate Pete (Jungle Hunt) [h]  */
         crc == 0xd1e9421e ||     /* Hangly Man [h]                 */
         crc == 0x90875ff3 ||     /* Hangly Pollux [h]              */
         crc == 0x82a956d0 ||     /* Super Pac-Man (2-chip image)   */
         /* Conversions that are two chip */
         crc == 0x9860fc33   ) {  /* Hyperblast (Conversion)        */
        return CART_16K_TWO_CHIP;
    }
    else
        return CART_16K_ONE_CHIP;

} /* cart_find_16k_mapping */

/******************************************************************************
**  Function   :  cart_get_mapping_string
**                            
**  Objective  :  This function looks up the string associated with the input
**                mapping.
**
**  Parameters :  type - input to look up
**                desc - string containing result
**                                                
**  return     :  pointer to input string
******************************************************************************/ 
char * cart_get_mapping_string ( e_cart_type type, char *desc ) {

    switch ( type ) {
        case CART_MIN: strcpy ( desc, "Auto" ); break;
        case CART_32K: strcpy ( desc, "32K Standard" ); break;
        case CART_16K_TWO_CHIP: strcpy ( desc, "16K Two Chip" ); break;
        case CART_16K_ONE_CHIP: strcpy ( desc, "16K One Chip" ); break;
        case CART_40K: strcpy ( desc, "40K Bounty Bob" ); break;
        case CART_64K: strcpy ( desc, "64K Bank Switch" ); break;
        case CART_8K: strcpy ( desc, "8K Standard" ); break;
        case CART_4K: strcpy ( desc, "4K Standard" ); break;
        case CART_16K_BE: strcpy ( desc, "16K Bank Switch BE" ); break;
        case CART_32K_BE: strcpy ( desc, "32K Bank Switch BE" ); break;
        case CART_PC_8K: strcpy ( desc, "8K Standard" ); break;
        case CART_PC_16K: strcpy ( desc, "16K Standard" ); break;
        case CART_PC_32K: strcpy ( desc, "32K DB" ); break;
        case CART_PC_40K: strcpy ( desc, "40K Bounty Bob" ); break;
        case CART_PC_RIGHT: strcpy ( desc, "8K Right Slot" ); break;
        case CART_XEGS: strcpy ( desc, "XEGS Bank Switch" ); break;
        case CART_SWXEGS: strcpy ( desc, "SWXEGS Bank Switch" ); break;
        case CART_MEGA: strcpy ( desc, "MegaCart" ); break;
        case CART_OSS: strcpy ( desc, "OSS SuperCart" ); break;
        case CART_OSS2: strcpy ( desc, "OSS2 SuperCart" ); break;
        case CART_ATMAX: strcpy ( desc, "ATMax Cart" ); break;
        case CART_ATRAX: strcpy ( desc, "ATRax Cart" ); break;
        case CART_WILL: strcpy ( desc, "WILL Cart" ); break;
        case CART_SDX: strcpy ( desc, "SDX Cart" ); break;
        case CART_EXP: strcpy ( desc, "EXP Cart" ); break;
        case CART_DIAMOND: strcpy ( desc, "Diamond Cart" ); break;
        case CART_PHOENIX: strcpy ( desc, "Phoenix Cart" ); break;
        case CART_BLIZZARD: strcpy ( desc, "Blizzard Cart" ); break;
        case CART_FLOPPY: strcpy ( desc, "Floppy" ); break;
        case CART_CASSETTE: strcpy ( desc, "Cassette" ); break;
        case CART_EXE: strcpy ( desc, "Executable" ); break;
        case CART_512K_HYBRID: strcpy ( desc, "512K Hybrid" ); break;
        default: strcpy ( desc, "Unknown" ); break;
    }

    return desc;

} /* cart_get_mapping_string */
