/* dck.c: Routines for handling Warajevo DCK files Copyright (c) 2003 Darren Salt, Fredrick Meunier $Id: dck.c,v 1.6 2007/02/02 16:35:42 pak21 Exp $ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Author contact information: E-mail: philip-fuse@shadowmagic.org.uk Darren: linux@youmustbejoking.demon.co.uk */ #include #include #include #include "internals.h" static const int DCK_PAGE_SIZE = 0x2000; /* Initialise a libspectrum_dck_block structure */ static libspectrum_error libspectrum_dck_block_alloc( libspectrum_dck_block **dck ) { size_t i; libspectrum_dck_block *ld; ld = *dck = malloc( sizeof( libspectrum_dck_block ) ); if( !ld ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "libspectrum_dck_block_alloc: out of memory" ); return LIBSPECTRUM_ERROR_MEMORY; } ld->bank = LIBSPECTRUM_DCK_BANK_DOCK; for( i = 0; i < 8; i++ ) { ld->access[i] = LIBSPECTRUM_DCK_PAGE_NULL; ld->pages[i] = NULL; } return LIBSPECTRUM_ERROR_NONE; } /* Free all memory used by a libspectrum_dck_block structure */ static libspectrum_error libspectrum_dck_block_free( libspectrum_dck_block *dck, int keep_pages ) { size_t i; if( !keep_pages ) for( i = 0; i < 8; i++ ) if( dck->pages[i] ) free( dck->pages[i] ); free( dck ); return LIBSPECTRUM_ERROR_NONE; } /* Initialise a libspectrum_dck structure (constructor!) */ libspectrum_error libspectrum_dck_alloc( libspectrum_dck **dck ) { size_t i; libspectrum_dck *ld; ld = *dck = malloc( sizeof( libspectrum_dck ) ); if( !ld ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "libspectrum_dck_alloc: out of memory" ); return LIBSPECTRUM_ERROR_MEMORY; } for( i=0; i<256; i++ ) ld->dck[i] = NULL; return LIBSPECTRUM_ERROR_NONE; } /* Free all memory used by a libspectrum_dck structure (destructor...) */ libspectrum_error libspectrum_dck_free( libspectrum_dck *dck, int keep_pages ) { size_t i; for( i=0; i<256; i++ ) if( dck->dck[i] ) { libspectrum_dck_block_free( dck->dck[i], keep_pages ); dck->dck[i] = NULL; } return LIBSPECTRUM_ERROR_NONE; } /* Read in the DCK file */ libspectrum_error libspectrum_dck_read( libspectrum_dck *dck, const libspectrum_byte *buffer, const size_t length ) { return libspectrum_dck_read2( dck, buffer, length, NULL ); } libspectrum_error libspectrum_dck_read2( libspectrum_dck *dck, const libspectrum_byte *buffer, const size_t clength, const char *filename ) { int i; int num_dck_block = 0; libspectrum_error error; const libspectrum_byte *end; libspectrum_id_t raw_type; libspectrum_class_t class; libspectrum_byte *new_buffer; size_t length; /* Ugly but necessary to get around the fact that 'clength' is a const parameter, but we now modify it */ length = clength; /* For storing the uncompressed data in if the input file was compressed */ new_buffer = NULL; error = libspectrum_identify_file_raw( &raw_type, filename, buffer, length ); if( error ) return error; error = libspectrum_identify_class( &class, raw_type ); if( error ) return error; if( class == LIBSPECTRUM_CLASS_COMPRESSED ) { size_t new_length; error = libspectrum_uncompress_file( &new_buffer, &new_length, NULL, raw_type, buffer, length, NULL ); buffer = new_buffer; length = new_length; } end = buffer + length; for( i=0; i<256; i++ ) dck->dck[i]=NULL; while( buffer < end ) { int pages = 0; if( buffer + 9 > end ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_dck_read: not enough data in buffer" ); error = LIBSPECTRUM_ERROR_CORRUPT; goto end; } switch( buffer[0] ) { case LIBSPECTRUM_DCK_BANK_DOCK: case LIBSPECTRUM_DCK_BANK_EXROM: case LIBSPECTRUM_DCK_BANK_HOME: break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_dck_read: unknown bank ID %d", buffer[0] ); error = LIBSPECTRUM_ERROR_UNKNOWN; goto end; } for( i = 1; i < 9; i++ ) switch( buffer[i] ) { case LIBSPECTRUM_DCK_PAGE_NULL: case LIBSPECTRUM_DCK_PAGE_RAM_EMPTY: break; case LIBSPECTRUM_DCK_PAGE_ROM: case LIBSPECTRUM_DCK_PAGE_RAM: pages++; break; default: libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libspectrum_dck_read: unknown page type %d", buffer[i] ); error = LIBSPECTRUM_ERROR_UNKNOWN; goto end; } if( buffer + 9 + DCK_PAGE_SIZE * pages > end ) { libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "libspectrum_dck_read: not enough data in buffer" ); error = LIBSPECTRUM_ERROR_CORRUPT; goto end; } /* Allocate new dck_block */ error = libspectrum_dck_block_alloc( &dck->dck[num_dck_block] ); if( error != LIBSPECTRUM_ERROR_NONE ) goto end; /* Copy the bank ID */ dck->dck[num_dck_block]->bank = *buffer++; /* Copy the page types */ for( i = 0; i < 8; i++ ) dck->dck[num_dck_block]->access[i] = *buffer++; /* Allocate the pages */ for( i = 0; i < 8; i++ ) { switch( dck->dck[num_dck_block]->access[i] ) { case LIBSPECTRUM_DCK_PAGE_NULL: break; case LIBSPECTRUM_DCK_PAGE_RAM_EMPTY: dck->dck[num_dck_block]->pages[i] = calloc( DCK_PAGE_SIZE, sizeof( libspectrum_byte ) ); if( !dck->dck[num_dck_block]->pages[i] ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "libspectrum_dck_read: out of memory" ); error = LIBSPECTRUM_ERROR_MEMORY; goto end; } break; case LIBSPECTRUM_DCK_PAGE_ROM: case LIBSPECTRUM_DCK_PAGE_RAM: dck->dck[num_dck_block]->pages[i] = malloc( DCK_PAGE_SIZE * sizeof( libspectrum_byte ) ); if( !dck->dck[num_dck_block]->pages[i] ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "libspectrum_dck_read: out of memory" ); error = LIBSPECTRUM_ERROR_MEMORY; goto end; } memcpy( dck->dck[num_dck_block]->pages[i], buffer, DCK_PAGE_SIZE ); buffer += DCK_PAGE_SIZE; break; } } num_dck_block++; if( num_dck_block == 256 ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "libspectrum_dck_read: more than 256 banks" ); error = LIBSPECTRUM_ERROR_MEMORY; goto end; } } /* Finished successfully */ error = LIBSPECTRUM_ERROR_NONE; end: free( new_buffer ); return error; }