static void tzx_write_archive_info( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { size_t i, count, total_length; count = libspectrum_tape_block_count( block ); /* 1 count byte, 2 bytes (ID and length) for every string */ total_length = 1 + 2 * count; /* And then the length of all the strings */ for( i = 0; i < count; i++ ) total_length += strlen( (char*)libspectrum_tape_block_texts( block, i ) ); /* Make room for all that, along with ID byte and two bytes storing the total length */ libspectrum_make_room( buffer, total_length + 3, ptr, length ); /* Write out the metadata */ *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO; libspectrum_write_word( ptr, total_length ); *(*ptr)++ = count; /* And the strings */ for( i = 0; i < count; i++ ) { *(*ptr)++ = libspectrum_tape_block_ids( block, i ); tzx_write_string( ptr, libspectrum_tape_block_texts( block, i ) ); } }
static void tzx_write_select( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { size_t count, total_length, i; /* The id byte, the total length (2 bytes), the count byte, and ( 2 offset bytes and 1 length byte ) per selection */ count = libspectrum_tape_block_count( block ); total_length = 4 + 3 * count; for( i = 0; i < count; i++ ) total_length += strlen( (char*)libspectrum_tape_block_texts( block, i ) ); libspectrum_make_room( buffer, total_length, ptr, length ); *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_SELECT; libspectrum_write_word( ptr, total_length ); *(*ptr)++ = count; for( i = 0; i < count; i++ ) { libspectrum_write_word( ptr, libspectrum_tape_block_offsets( block, i ) ); tzx_write_string( ptr, libspectrum_tape_block_texts( block, i ) ); } }
static void tzx_write_loop_start( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { libspectrum_make_room( buffer, 3, ptr, length ); *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_LOOP_START; libspectrum_write_word( ptr, libspectrum_tape_block_count( block ) ); }
static void tzx_write_pure_tone( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { /* Make room for the ID byte and the data */ libspectrum_make_room( buffer, 5, ptr, length ); *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_PURE_TONE; libspectrum_write_word( ptr, libspectrum_tape_block_pulse_length( block ) ); libspectrum_write_word( ptr, libspectrum_tape_block_count( block ) ); }
static void tzx_write_pulses( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { size_t i, count, block_length; /* ID byte, count and 2 bytes for length of each pulse */ count = libspectrum_tape_block_count( block ); block_length = 2 + 2 * count; /* Make room for the ID byte, the count and the data */ libspectrum_make_room( buffer, block_length, ptr, length ); *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_PULSES; *(*ptr)++ = count; for( i = 0; i < count; i++ ) libspectrum_write_word( ptr, libspectrum_tape_block_pulse_lengths( block, i ) ); }
static void tzx_write_hardware( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { size_t i, count; /* We need one ID byte, one count byte and then three bytes for every entry */ count = libspectrum_tape_block_count( block ); libspectrum_make_room( buffer, 2 + 3 * count, ptr, length ); /* Write out the metadata */ *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_HARDWARE; *(*ptr)++ = count; /* And the info */ for( i = 0; i < count; i++ ) { *(*ptr)++ = libspectrum_tape_block_types( block, i ); *(*ptr)++ = libspectrum_tape_block_ids ( block, i ); *(*ptr)++ = libspectrum_tape_block_values( block, i ); } }
// Return a string describing a give tape block const char *Tape::GetBlockDetails (libspectrum_tape_block *block) { static char sz[128]; sz[0] = '\0'; char szExtra[64] = ""; char szName[11] = ""; const char *psz = NULL; libspectrum_byte *data = libspectrum_tape_block_data(block); long length = static_cast<long>(libspectrum_tape_block_data_length(block)); // Is there enough data to include a possible filename? if (length >= 12) { for (int i = 0 ; i < 10 ; i++) { char ch = data[i+2]; szName[i] = (ch >= ' ' && ch <= 0x7f) ? ch : '?'; } szName[10] = '\0'; } // Spectrum header length and type byte? if (length == 17+2 && data[0] == 0x00) { // Examine Spectrum file type switch (data[1]) { case 0: { psz = "ZX BASIC"; UINT uLine = (data[15] << 8) | data[14]; if (uLine != 0xffff) sprintf(szExtra, " LINE %u", uLine); break; } case 1: psz = "ZX DATA()"; break; case 2: psz = "ZX DATA$()"; break; case 3: { psz = "ZX CODE"; UINT uAddr = (data[15] << 8) | data[14]; UINT uLen = (data[13] << 8) | data[12]; sprintf(szExtra, " %u,%u", uAddr, uLen); break; } } } // SAM header length and type byte? // Real length is 82, but TZX spec suggests there could be up to 7-8 trailing bits, so accept 83 else if ((length == 80+2 || length == 80+1+2) && data[0] == 0x01) { // Examine SAM file type switch (data[1]) { case 16: { psz = "BASIC"; UINT uLine = (data[40] << 8) | data[39]; if (data[38] == 0) sprintf(szExtra, " LINE %u", uLine); break; } case 17: psz = "DATA()"; break; case 18: psz = "DATA$"; break; case 19: { psz = "CODE"; UINT uAddr = TPeek(data+32) + 16384; UINT uLen = TPeek(data+35); sprintf(szExtra, " %u,%u", uAddr, uLen); if (data[38] == 0) sprintf(szExtra+strlen(szExtra), ",%u", TPeek(data+38)); break; } case 20: { psz = "SCREEN$"; UINT uMode = data[17]+1; sprintf(szExtra, " MODE %u", uMode); break; } } } // Do we have a type string? if (psz) { // Start with type and append filename strcpy(sz, psz); strcat(sz, ": '"); strcat(sz, szName); strcat(sz, "'"); // Append any additional type-specific details if (szExtra[0]) { strcat(sz, " "); strcat(sz, szExtra); } } // No details yet? if (!sz[0]) { libspectrum_tape_type type = libspectrum_tape_block_type(block); switch (type) { case LIBSPECTRUM_TAPE_BLOCK_ROM: case LIBSPECTRUM_TAPE_BLOCK_TURBO: { // Raw tape block data length size_t length = libspectrum_tape_block_data_length(block); // If possible, exclude the type, sync, and checksum bytes from the length if (length >= 3) length -= 3; snprintf(sz, sizeof(sz), "%u bytes", length); break; } case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: snprintf(sz, sizeof(sz), "%u bytes", libspectrum_tape_block_data_length(block)); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: snprintf(sz, sizeof(sz), "%u tstates", libspectrum_tape_block_pulse_length(block)); break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: snprintf(sz, sizeof(sz), "%u pulses", libspectrum_tape_block_count(block)); break; case LIBSPECTRUM_TAPE_BLOCK_PAUSE: snprintf(sz, sizeof(sz), "%ums", libspectrum_tape_block_pause(block)); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: case LIBSPECTRUM_TAPE_BLOCK_COMMENT: case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: snprintf(sz, sizeof(sz), "%s", libspectrum_tape_block_text(block)); break; case LIBSPECTRUM_TAPE_BLOCK_JUMP: { int offset = libspectrum_tape_block_offset(block); if (offset >= 0) snprintf(sz, sizeof(sz), "Forward %d blocks", offset); else snprintf(sz, sizeof(sz), "Backward %d blocks", -offset); break; } case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: snprintf(sz, sizeof(sz), "%u iterations", libspectrum_tape_block_count(block)); break; case LIBSPECTRUM_TAPE_BLOCK_SELECT: snprintf(sz, sizeof(sz), "%u options", libspectrum_tape_block_count(block)); break; case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: snprintf(sz, sizeof(sz), "%u data symbols", libspectrum_tape_generalised_data_symbol_table_symbols_in_block(libspectrum_tape_block_data_table(block))); break; case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: { size_t count = libspectrum_tape_block_count(block); for (size_t i = 0 ; i < count ; i++) { int id = libspectrum_tape_block_ids(block, i); const char *value = libspectrum_tape_block_texts(block, i); // Full title TZX id? if (id == 0x00) strncpy(sz, value, sizeof(sz)-1); } break; } case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: { size_t count = libspectrum_tape_block_count(block); for (size_t i = 0 ; i < count ; i++) { int type = libspectrum_tape_block_types(block, i); int id = libspectrum_tape_block_ids(block, i); // Skip anything but the TZX "Computers" type if (type != 0) continue; // Check for relevant computer ids if (id == 9) strcpy(sz, "SAM Coupe"); else if ((id >= 0x00 && id <= 0x05) || id == 0x0e) strcpy(sz, "ZX Spectrum"); else if (id == 0x08) strcpy(sz, "Pentagon"); else if (id == 0x06 || id == 0x07) strcpy(sz, "Timex Sinclair"); else snprintf(sz, sizeof(sz), "Unknown (%02X)", id); } break; } default: break; } } return sz; }
int tape_block_details( char *buffer, size_t length, libspectrum_tape_block *block ) { libspectrum_byte *data; const char *type; unsigned char name[11]; int offset; size_t i; unsigned long total_pulses; buffer[0] = '\0'; switch( libspectrum_tape_block_type( block ) ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: case LIBSPECTRUM_TAPE_BLOCK_DATA_BLOCK: /* See if this looks like a standard Spectrum header and if so display some extra data */ if( libspectrum_tape_block_data_length( block ) != 19 ) goto normal; data = libspectrum_tape_block_data( block ); /* Flag byte is 0x00 for headers */ if( data[0] != 0x00 ) goto normal; switch( data[1] ) { case 0x00: type = "Program"; break; case 0x01: type = "Number array"; break; case 0x02: type = "Character array"; break; case 0x03: type = "Bytes"; break; default: goto normal; } make_name( name, &data[2] ); snprintf( buffer, length, "%s: \"%s\"", type, name ); break; normal: snprintf( buffer, length, "%lu bytes", (unsigned long)libspectrum_tape_block_data_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_TURBO: case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: snprintf( buffer, length, "%lu bytes", (unsigned long)libspectrum_tape_block_data_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: snprintf( buffer, length, "%lu tstates", (unsigned long)libspectrum_tape_block_pulse_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: snprintf( buffer, length, "%lu pulses", (unsigned long)libspectrum_tape_block_count( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_PULSE_SEQUENCE: total_pulses = 0; for( i=0; i < libspectrum_tape_block_count( block ); i++ ) total_pulses += libspectrum_tape_block_pulse_repeats( block, i ); snprintf( buffer, length, "%lu pulses", total_pulses ); break; case LIBSPECTRUM_TAPE_BLOCK_PAUSE: snprintf( buffer, length, "%lu ms", (unsigned long)libspectrum_tape_block_pause( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: case LIBSPECTRUM_TAPE_BLOCK_COMMENT: case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: snprintf( buffer, length, "%s", libspectrum_tape_block_text( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_JUMP: offset = libspectrum_tape_block_offset( block ); if( offset > 0 ) { snprintf( buffer, length, "Forward %d blocks", offset ); } else { snprintf( buffer, length, "Backward %d blocks", -offset ); } break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: snprintf( buffer, length, "%lu iterations", (unsigned long)libspectrum_tape_block_count( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_SELECT: snprintf( buffer, length, "%lu options", (unsigned long)libspectrum_tape_block_count( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: snprintf( buffer, length, "%lu data symbols", (unsigned long)libspectrum_tape_generalised_data_symbol_table_symbols_in_block( libspectrum_tape_block_data_table( block ) ) ); break; case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE: /* Could do something better with this one */ break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_END: case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: case LIBSPECTRUM_TAPE_BLOCK_STOP48: case LIBSPECTRUM_TAPE_BLOCK_SET_SIGNAL_LEVEL: case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: case LIBSPECTRUM_TAPE_BLOCK_CONCAT: break; } return 0; }
int tape_block_details( char *buffer, size_t length, libspectrum_tape_block *block ) { libspectrum_byte *data; const char *type; unsigned char name[11]; int offset; buffer[0] = '\0'; switch( libspectrum_tape_block_type( block ) ) { case LIBSPECTRUM_TAPE_BLOCK_ROM: /* See if this looks like a standard Spectrum header and if so display some extra data */ if( libspectrum_tape_block_data_length( block ) != 19 ) goto normal; data = libspectrum_tape_block_data( block ); /* Flag byte is 0x00 for headers */ if( data[0] != 0x00 ) goto normal; switch( data[1] ) { case 0x00: type = "Program"; break; case 0x01: type = "Number array"; break; case 0x02: type = "Character array"; break; case 0x03: type = "Bytes"; break; default: goto normal; } make_name( name, &data[2] ); snprintf( buffer, length, "%s: \"%s\"", type, name ); break; normal: snprintf( buffer, length, "Data (%lu Bytes)", (unsigned long)libspectrum_tape_block_data_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_TURBO: snprintf( buffer, length, "Turbo Data (%lu Bytes)", (unsigned long)libspectrum_tape_block_data_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_DATA: snprintf( buffer, length, "Pure Data (%lu Bytes)", (unsigned long)libspectrum_tape_block_data_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_RAW_DATA: snprintf( buffer, length, "Raw Data (%lu Bytes)", (unsigned long)libspectrum_tape_block_data_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_PURE_TONE: snprintf( buffer, length, "Pure Tone (%lu tstates)", (unsigned long)libspectrum_tape_block_pulse_length( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_PULSES: snprintf( buffer, length, "Sequence Of %lu Pulses", (unsigned long)libspectrum_tape_block_count( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_PAUSE: if((unsigned long)libspectrum_tape_block_pause( block )==0) snprintf( buffer, length, "[Stop The Tape]"); else snprintf( buffer, length, "[Pause - %lu ms]",(unsigned long)libspectrum_tape_block_pause( block )); break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_START: case LIBSPECTRUM_TAPE_BLOCK_COMMENT: case LIBSPECTRUM_TAPE_BLOCK_MESSAGE: case LIBSPECTRUM_TAPE_BLOCK_CUSTOM: snprintf( buffer, length, "[%s]", libspectrum_tape_block_text( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_JUMP: offset = libspectrum_tape_block_offset( block ); if( offset > 0 ) { snprintf( buffer, length, "[Forward %d Blocks]", offset ); } else { snprintf( buffer, length, "[Backward %d Blocks]", -offset ); } break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_START: snprintf( buffer, length, "[Loop %lu Times]", (unsigned long)libspectrum_tape_block_count( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_SELECT: snprintf( buffer, length, "[Select %lu Options]", (unsigned long)libspectrum_tape_block_count( block ) ); break; case LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA: snprintf( buffer, length, "[Generalised Data - %lu Data Symbols]", (unsigned long)libspectrum_tape_generalised_data_symbol_table_symbols_in_block( (void*)libspectrum_tape_block_data_table( block ) ) ); break; case LIBSPECTRUM_TAPE_BLOCK_RLE_PULSE: /* Could do something better with this one */ break; case LIBSPECTRUM_TAPE_BLOCK_GROUP_END: snprintf( buffer, length, "[Group End]"); break; case LIBSPECTRUM_TAPE_BLOCK_LOOP_END: snprintf( buffer, length, "[Loop End]"); break; case LIBSPECTRUM_TAPE_BLOCK_STOP48: snprintf( buffer, length, "[Stop The Tape If 48]"); break; case LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO: snprintf( buffer, length, "[Archive Info]"); break; case LIBSPECTRUM_TAPE_BLOCK_HARDWARE: snprintf( buffer, length, "[Hardware]"); break; case LIBSPECTRUM_TAPE_BLOCK_CONCAT: snprintf( buffer, length, "[Concat]"); break; } return 0; }