static libspectrum_error tzx_write_generalised_data( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { size_t data_length, bits_per_symbol; libspectrum_error error; libspectrum_tape_generalised_data_symbol_table *pilot_table, *data_table; libspectrum_dword pilot_symbol_count, data_symbol_count, i; data_length = generalised_data_length( block ); libspectrum_make_room( buffer, 5 + data_length, ptr, length ); *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_GENERALISED_DATA; libspectrum_write_dword( ptr, data_length ); libspectrum_write_word( ptr, libspectrum_tape_block_pause( block ) ); pilot_table = libspectrum_tape_block_pilot_table( block ); data_table = libspectrum_tape_block_data_table( block ); error = serialise_generalised_data_table( ptr, pilot_table ); if( error != LIBSPECTRUM_ERROR_NONE ) return error; error = serialise_generalised_data_table( ptr, data_table ); if( error != LIBSPECTRUM_ERROR_NONE ) return error; serialise_generalised_data_symbols( ptr, pilot_table ); pilot_symbol_count = libspectrum_tape_generalised_data_symbol_table_symbols_in_block( pilot_table ); for( i = 0; i < pilot_symbol_count; i++ ) { *(*ptr)++ = libspectrum_tape_block_pilot_symbols( block, i ); libspectrum_write_word( ptr, libspectrum_tape_block_pilot_repeats( block, i ) ); } serialise_generalised_data_symbols( ptr, data_table ); bits_per_symbol = libspectrum_tape_block_bits_per_data_symbol( block ); data_symbol_count = libspectrum_tape_generalised_data_symbol_table_symbols_in_block( data_table ); data_length = ( ( bits_per_symbol * data_symbol_count ) + 7 ) / 8; memcpy( *ptr, libspectrum_tape_block_data( block ), data_length ); (*ptr) += data_length; return LIBSPECTRUM_ERROR_NONE; }
static void tzx_write_rom( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { /* Make room for the ID byte, the pause, the length and the actual data */ libspectrum_make_room( buffer, 5 + libspectrum_tape_block_data_length( block ), ptr, length); /* Write the ID byte and the pause */ *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_ROM; libspectrum_write_word( ptr, libspectrum_tape_block_pause( block ) ); /* Copy the data across */ tzx_write_bytes( ptr, libspectrum_tape_block_data_length( block ), 2, libspectrum_tape_block_data( block ) ); }
static void tzx_write_custom( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { size_t data_length; data_length = libspectrum_tape_block_data_length( block ); /* An ID byte, 16 description bytes, 4 length bytes and the data itself */ libspectrum_make_room( buffer, 1 + 16 + 4 + data_length, ptr, length ); *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_CUSTOM; memcpy( *ptr, libspectrum_tape_block_text( block ), 16 ); *ptr += 16; tzx_write_bytes( ptr, data_length, 4, libspectrum_tape_block_data( block ) ); }
static void tzx_write_raw_data( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { size_t data_length; /* Make room for the ID byte, the metadata and the actual data */ data_length = libspectrum_tape_block_data_length( block ); libspectrum_make_room( buffer, 8 + data_length, ptr, length ); /* Write the ID byte and the metadata */ *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_RAW_DATA; libspectrum_write_word( ptr, libspectrum_tape_block_bit_length( block ) ); libspectrum_write_word( ptr, libspectrum_tape_block_pause( block ) ); *(*ptr)++ = libspectrum_tape_block_bits_in_last_byte( block ); tzx_write_bytes( ptr, data_length, 3, libspectrum_tape_block_data( block ) ); }
static void tzx_write_turbo( libspectrum_tape_block *block, libspectrum_byte **buffer, libspectrum_byte **ptr, size_t *length ) { /* Make room for the ID byte, the metadata and the actual data */ libspectrum_make_room( buffer, 19 + libspectrum_tape_block_data_length( block ), ptr, length); /* Write the ID byte and the metadata */ *(*ptr)++ = LIBSPECTRUM_TAPE_BLOCK_TURBO; libspectrum_write_word( ptr, libspectrum_tape_block_pilot_length( block ) ); libspectrum_write_word( ptr, libspectrum_tape_block_sync1_length( block ) ); libspectrum_write_word( ptr, libspectrum_tape_block_sync2_length( block ) ); libspectrum_write_word( ptr, libspectrum_tape_block_bit0_length ( block ) ); libspectrum_write_word( ptr, libspectrum_tape_block_bit1_length ( block ) ); libspectrum_write_word( ptr, libspectrum_tape_block_pilot_pulses( block ) ); *(*ptr)++ = libspectrum_tape_block_bits_in_last_byte( block ); libspectrum_write_word( ptr, libspectrum_tape_block_pause ( block ) ); tzx_write_bytes( ptr, libspectrum_tape_block_data_length( block ), 3, libspectrum_tape_block_data( block ) ); }
static int trap_load_block( libspectrum_tape_block *block, Z80Regs * regs ) { regs->BC.W=0xCB01;//?? regs->HL.W=0;//?? int where,flags,howmany,bytes,f; byte *src; src = libspectrum_tape_block_data( block ); bytes = libspectrum_tape_block_data_length( block ); where = regs->IX.W; flags= (*src++); /* read flag type and ignore it */ howmany = regs->DE.W; if (bytes - 2 < howmany) { howmany = bytes - 2; (regs->AF.B.l &= ~(C_FLAG)); regs->IX.W += bytes - 2; } else regs->IX.W += regs->DE.W; for (f = 0; f < howmany; f++) { Z80WriteMem_notiming(where + f, *src++); } src++; /* read checksum (and ignore it :-) */ /* if load was successful, reset C_FLAG */ if (howmany == regs->DE.W) regs->AF.B.l |= (C_FLAG); regs->DE.W = 0; return 0; }
// 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; }
bool Tape::LoadTrap () { if (!IsInserted()) return false; // If traps are disabled, try normal loading if (!GetOption(tapetraps)) { Play(); return false; } // Skip over any metadata blocks libspectrum_tape_block *block = libspectrum_tape_current_block(pTape); while (block && libspectrum_tape_block_metadata(block)) block = libspectrum_tape_select_next_block(pTape); // Nothing else to process? if (!block) return false; libspectrum_tape_type type = libspectrum_tape_block_type(block); libspectrum_tape_state_type state = libspectrum_tape_state(pTape); // Consider both ROM blocks (normal speed) and turbo blocks, as used by custom SAM tape speeds (DEVICE tX) if ((type != LIBSPECTRUM_TAPE_BLOCK_ROM && type != LIBSPECTRUM_TAPE_BLOCK_TURBO) || state != LIBSPECTRUM_TAPE_STATE_PILOT) { // Fall back on non-trap loading for anything else Play(); return false; } libspectrum_byte *pbData = libspectrum_tape_block_data(block); size_t nData = libspectrum_tape_block_data_length(block); // Base load address and load request size WORD wDest = HL; int nWanted = (read_byte(0x5ac8) << 16) | DE; // Fetch block type H = *pbData++; nData--; // Spectrum header? if (H == 0) { // Override request length nWanted = (nWanted & ~0xff) | 17; } // Otherwise the type byte must match the request else if (H != A_) { // Advance to next block libspectrum_tape_select_next_block(pTape); // Failed, exit via: RET NZ F &= ~(FLAG_C|FLAG_Z); PC = 0xe6f6; return true; } // Parity byte initialised to type byte L = H; // More still to load? while (nWanted >= 0) { // Are we out of source data? (ToDo: support continuation blocks?) if (!nData) { // Advance to next block libspectrum_tape_select_next_block(pTape); // Failed, exit via: RET NZ F &= ~(FLAG_C|FLAG_Z); PC = 0xe6f6; return true; } // Read next byte and update parity L ^= (H = *pbData++); nData--; // Request complete? if (!nWanted) break; // Write new byte write_byte(wDest, H); wDest++; nWanted--; // Destination now in the top 16K? if (wDest >= 0xc000) { // Slide paging up and move pointer back IO::OutHmpr(hmpr+1); wDest -= 0x4000; } } // Advance to next block libspectrum_tape_select_next_block(pTape); // Exit via: LD A,L ; CP 1 ; RET PC = 0xe739; return true; }
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; }
static int trap_load_block( libspectrum_tape_block *block ) { libspectrum_byte parity, *data; int i = 0, length, read, verify; /* On exit: * A = calculated parity byte if parity checked, else 0 (CHECKME) * F : if parity checked, all flags are modified * else carry only is modified (FIXME) * B = 0xB0 (success) or 0x00 (failure) * C = 0x01 (confirmed), 0x21, 0xFE or 0xDE (CHECKME) * DE : decremented by number of bytes loaded or verified * H = calculated parity byte or undefined * L = last byte read, or 1 if none * IX : incremented by number of bytes loaded or verified * A' = unchanged on error + no flag byte, else 0x01 * F' = 0x01 on error + no flag byte, else 0x45 * R = no point in altering it :-) * Other registers unchanged. */ data = libspectrum_tape_block_data( block ); length = libspectrum_tape_block_data_length( block ); /* Number of bytes to load or verify */ read = length - 1; if( read > DE ) read = DE; /* If there's no data in the block (!), set L then error exit. * We don't need to alter H, IX or DE here */ if( !length ) { L = F_ = 1; F &= ~FLAG_C; return 0; } verify = !(F_ & FLAG_C); i = A_; /* i = A' (flag byte) */ AF_ = 0x0145; A = 0; /* Initialise the parity check and L to the block ID byte */ L = parity = *data++; /* If the block ID byte != the flag byte, clear carry and return */ if( parity != i ) goto error_ret; /* Now set L to the *last* byte in the block */ L = data[read - 1]; /* Loading or verifying determined by the carry flag of F' */ if( verify ) { /* verifying */ for( i = 0; i < read; i++ ) { parity ^= data[i]; if( data[i] != readbyte_internal(IX+i) ) { /* Verification failure */ L = data[i]; goto error_ret; } } } else { for( i = 0; i < read; i++ ) { parity ^= data[i]; writebyte_internal( IX+i, data[i] ); } } /* At this point, i == number of bytes actually read or verified */ /* If |DE| bytes have been read and there's more data, do the parity check */ if( DE == i && read + 1 < length ) { parity ^= data[read]; A = parity; CP( 1 ); /* parity check is successful if A==0 */ B = 0xB0; } else { /* Failure to read first bit of the next byte (ref. 48K ROM, 0x5EC) */ B = 255; L = 1; INC( B ); error_ret: F &= ~FLAG_C; } /* At this point, AF, AF', B and L are already modified */ C = 1; H = parity; DE -= i; IX += i; 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; }
int main(int argc, char *argv[]) { int opt, i, quiet = 0; char *output = "output.tzx", *p; int turbo_params[6] = { 2168, 3323, 667, 735, 855, 1710 }; FILE *fd; size_t data_len; libspectrum_tape *tape; libspectrum_byte *data; libspectrum_tape_block *block; libspectrum_dword pause, pause_tstates; while ((opt = getopt(argc, argv, "vhqt:o:")) != -1) { switch(opt) { case 'q': quiet = 1; break; case 't': if (sscanf(optarg, "%i:%i:%i:%i:%i:%i", &turbo_params[0], &turbo_params[1], &turbo_params[2], &turbo_params[3], &turbo_params[4], &turbo_params[5]) != 6) fatal(argv[0], "Failed to parse turbo params"); break; case 'o': output = strdup(optarg); break; case 'h': help(argv[0]); exit(0); case 'v': fprintf(stderr, VERSION "\n"); exit(0); default: fprintf(stderr, "\n"); help(argv[0]); exit(1); } } if (optind >= argc) fatal(argv[0], "Expected input filename to process"); if (libspectrum_init()) fatal(argv[0], "Failed to init libspectrum"); fd = fopen(argv[optind], "rb"); if (!fd) fatal(argv[0], "Failed to open input file"); fseek(fd, 0, SEEK_END); data_len = ftell(fd); fseek(fd, 0, SEEK_SET); data = (libspectrum_byte *) malloc(data_len); if (!data) { fclose(fd); fatal(argv[0], "Failed to allocate memory"); } if (fread(data, sizeof(libspectrum_byte), data_len, fd) != data_len) { fclose(fd); free(data); fatal(argv[0], "Failed to read input file"); } fclose(fd); tape = libspectrum_tape_alloc(); if (!tape) { fclose(fd); free(data); fatal(argv[0], "Failed to allocate tape memory"); } if (libspectrum_tape_read(tape, data, data_len, LIBSPECTRUM_ID_UNKNOWN, argv[optind])) { fclose(fd); free(data); free(tape); fatal(argv[0], "Failed to read tape"); } free(data); for (i = optind + 1; i < argc; i++) { for (p = argv[i]; *p && isdigit(*p); p++); if (*p) { fprintf(stderr, "WARNING: Parameter is not a valid block number, skipping\n"); continue; } if (libspectrum_tape_nth_block(tape, atoi(argv[i]))) continue; block = libspectrum_tape_current_block(tape); if (!block) continue; if (!quiet) printf("%s: Setting block #%i (type 0x%02x): ", argv[0], atoi(argv[i]), libspectrum_tape_block_type(block)); data = libspectrum_tape_block_data(block); data_len = libspectrum_tape_block_data_length(block); pause = libspectrum_tape_block_pause(block); pause_tstates = libspectrum_tape_block_pause_tstates(block); if (libspectrum_tape_block_set_type(block, LIBSPECTRUM_TAPE_BLOCK_TURBO) || libspectrum_tape_block_set_pilot_pulses(block, turbo_params[1]) || libspectrum_tape_block_set_pilot_length(block, turbo_params[0]) || libspectrum_tape_block_set_sync1_length(block, turbo_params[2]) || libspectrum_tape_block_set_sync2_length(block, turbo_params[3]) || libspectrum_tape_block_set_bit0_length(block, turbo_params[4]) || libspectrum_tape_block_set_bit1_length(block, turbo_params[5]) || libspectrum_tape_block_set_data(block, data) || libspectrum_tape_block_set_data_length(block, data_len) || libspectrum_tape_block_set_pause(block, pause) || libspectrum_tape_block_set_pause_tstates(block, pause_tstates) || libspectrum_tape_block_set_bits_in_last_byte(block, 8) ) { if (!quiet) printf("failed!\n"); fprintf(stderr, "WARNING: Failed to set block %i\n", atoi(argv[i])); continue; } if (!quiet) printf("OK\n"); } data = NULL; data_len = 0; if (libspectrum_tape_write(&data, &data_len, tape, LIBSPECTRUM_ID_TAPE_TZX)) { free(tape); fatal(argv[0], "Failed to write the output file"); } fd = fopen(output, "wb"); if (!fd) { free(data); free(tape); fatal(argv[0], "Failed to open output file"); } if (fwrite(data, sizeof(libspectrum_byte), data_len, fd) != data_len) { fclose(fd); free(data); free(tape); fatal(argv[0], "Failed to write output file"); } fclose(fd); free(data); free(tape); exit(0); }