Using tape_load_block with plus3dos doesn't seem to work

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
nuc1e0n
Member
Posts: 49
Joined: Wed Jun 10, 2020 12:34 am

Using tape_load_block with plus3dos doesn't seem to work

Post by nuc1e0n »

I have this piece of code to attempt to load some files from tape and store them on disk:

unarchive.c:

Code: Select all

/* This program will be compiled to a .tap file and run on a residos enabled emulator.
  It will read .tap blocks and convert them into files on the +3dos filesystem.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <conio.h>
#include <spectrum.h>

#define freeAndZero(p) { free(p); p = 0; }

uint16_t readBytes2;
char * temp;

int main(int argc, char *argv[]) {
  char * temp2;
  char * temp3 = NULL;
  char * firstBlock;
  uint16_t * readBytes;
  int c;

  FILE * output = NULL;

  temp = 0xBFEF;

  clrscr();

  fputs("Choose desination drive (A-P)\n"
        "or press any other key to use current drive\n", stdout);

  c = getchar() & 0x5f;

  if(c < 'A' || c > 'P') {
    c = 'T';
  }

  fputs("\n", stdout);

  do {
    tape_load_block(temp, 16400, 0xff);
    fputs("Pause!\n", stdout);

    firstBlock = temp + strlen(temp) + 1;
    readBytes = firstBlock + 1;
    readBytes2 = *readBytes;
    temp2 = firstBlock + 3;

    switch(*firstBlock) {
      case 0:
        fprintf(stdout, "Appending %u bytes to %s\n", readBytes2, temp);
      break;

      case 1:
        if(output) {
          fclose(output);
          output = NULL;
        }

        if(c != 'T') {
          temp3 = malloc(strlen(temp)+3);
          sprintf(temp3, "%c:%s", c, 0xBFEF);
        }
        else {
          temp3 = temp;
        }

        fprintf(stdout, "Writing %u bytes to %s\n", readBytes2, temp3);

        if((output = fopen(temp3, "wb")) == NULL) {
          fprintf(stderr, "Couldn't write to file %s\n", temp3);
          return 1;
        }

        if(c != 'T') {
          freeAndZero(temp3);
        }
      break;

      default:
        fputs("Termination block found. exiting\n \n \n", stdout);

        if(output) {
          fclose(output);
        }
      return 0;
    }

    fwrite(temp2, 1, readBytes2, output);

    /* residos files may need a soft eof */
    if(readBytes2 % 128 != 0) {
      fputs("Adding soft-EOF\n", stdout);
      fputc(0x1a, output);
    }

    fputs("Unpause!\n", stdout);
  } while(1);

  return 0;
}
This works as I would expect when compiled for residos using:

Code: Select all

zcc +zx -DRESIDOS -lp3 -pragma-define:CRT_ON_EXIT=0x10002 -DAMALLOC2 -create-app unarchive.c
However, it crashes when I try to run the same code compiled for plus3dos using:

Code: Select all

zcc +zx -lp3 -pragma-define:CRT_ON_EXIT=0x10002 -DAMALLOC2 -create-app unarchive.c
Is this a bug or is there something I need to do differently?

FYI, the code I'm using to construct the .tap files is compiled to run on Linux with

Code: Select all

gcc -o archive archive.c
Here's the .tap file producing code:

archive.c:

Code: Select all

/* This program will write a named .tap file containing a bunch of named files cut up and converted into data blocks */
/* It enables me to quickly copy a bunch of files into an emulated residos filesystem */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define FALSE 0
#define TRUE 1

#define freeAndZero(p) { free(p); p = 0; }

int d_fgets(char** ws, FILE* stream) {
  char buf[80];
  char* newWs = NULL;
  char* potentialNewWs = NULL;
  size_t totalLength = (size_t)0;
  size_t potentialTotalLength = (size_t)0;
  size_t bufferLength;

  /* check sanity of inputs */
  if(ws == NULL) {
    return FALSE;
  }

  /* try reading some text from the file into the buffer */
  while(fgets((char *)&(buf[0]), 80, stream) != NULL) {

    /* get the length of the string in the buffer */
    bufferLength = strlen((char *)&(buf[0]));

    /*
      add it to the potential new length.
      this might not become the actual new length if the realloc fails
    */
    potentialTotalLength+=bufferLength;

    /*
      try reallocating the output string to be a bit longer
      if it fails then set any existing buffer to the return value and return true
    */
    if((potentialNewWs = (char*)realloc(newWs, ((potentialTotalLength+1)*sizeof(char)))) == NULL) {

      /* if we've already retrieved some text */
      if(newWs != NULL) {

        /* ensure null termination of the string */
        newWs[totalLength] = '\0';

        /* set the output string pointer and return true */
        if(*ws) {
          free(*ws);
        }
        *ws = newWs;

        return TRUE;
      }

      /*
        otherwise no successful allocation was made.
        return false without modifying the output string location
      */
      return FALSE;
    }

    /* copy the buffer data into potentialNewWs */
    memcpy(potentialNewWs+totalLength, &buf, bufferLength*sizeof(char));

    /* the potential new string becomes the actual one */
    totalLength = potentialTotalLength;
    newWs = potentialNewWs;

    /* if the last character is '\n' (ie we've reached the end of a line) then return the result */
    if(newWs[totalLength-1] == '\n') {

      /* ensure null termination of the string */
      newWs[totalLength-1] = '\0';

      /* set the output string pointer and return true */
      if(*ws) {
        free(*ws);
      }

      *ws = newWs;

      return TRUE;
    }
  }

  /* if we've already retrieved some text */
  if(newWs != NULL) {
    /* ensure null termination of the string */
    newWs[totalLength] = '\0';

    /* set the output string point and return true */
    if(*ws) {
      free(*ws);
    }
    *ws = newWs;

    return TRUE;
  }

  /*
    otherwise no successful allocation was made.
    return false without modifying the output string location
  */
  return FALSE;
}

int main(int argc, char *argv[]) {
  char * temp = NULL;
  char * temp2 = NULL;

  FILE * input = NULL;
  FILE * output = NULL;
  uint32_t fileSize;
  unsigned char checksum;
  int c;
  uint16_t pageSize;
  int writeSize;
  uint16_t nameLength;
  unsigned char firstPage;

  fputs("Output filename?\n", stdout);
  d_fgets(&temp, stdin);

  if((output = fopen(temp, "wb")) == NULL) {
    fprintf(stderr, "Couldn't open %s for writing\n", temp);
    return 1;
  }

  do {
    fputs("Append input filename? (press enter to exit)\n", stdout);

    d_fgets(&temp, stdin);

    if(strcmp(temp, "") == 0) {
      if((input = fopen("/dev/null", "rb")) == NULL) {
        fprintf(stdout, "File %s doesn't exist, continuing\n", temp);
        continue;
      }

      firstPage = 2;  /* special code that tells the reader to abort */
      fileSize = 0;

      fprintf(stdout, "Appending termination block\n");

      nameLength = strlen(temp)+1;
    }
    else {
      if((input = fopen(temp, "rb")) == NULL) {
        fprintf(stdout, "File %s doesn't exist, continuing\n", temp);
        continue;
      }

      firstPage = TRUE;

      fseek(input, 0, SEEK_END);
      fileSize = ftell(input);
      fseek(input, 0, SEEK_SET);

      fprintf(stdout, "Appending file %s: %lu bytes\n", temp, fileSize);

      nameLength = strlen(temp)+1;
    }

    do {
      if((nameLength + fileSize) > 16397) {
        pageSize = 16397 - nameLength;
        fileSize -= pageSize;
      }
      else {
        pageSize = fileSize;
        fileSize = 0;
      }

      checksum = 0;

      /* write the block length (always 16400 for simplicity) */
      fputc(18, output);
      fputc(64, output);

      /* write 0xff (data block) */
      fputc(255, output);
      checksum ^= 255;

      /* write the filename */
      temp2 = temp;
      while(*temp2 != 0) {
        fputc(*temp2, output);
        checksum ^= *temp2;
        temp2++;
      }

      /* write the null terminator for the file name */
      fputc(0, output);
      checksum ^= *temp2;

      /* write 1 for replace or 0 for append */
      fputc(firstPage, output);
      checksum ^= firstPage;

      /* write how many bytes to add to the named file */
      c = pageSize & 0x00FF;
      fputc(c, output);
      checksum ^= c;

      c = (pageSize & 0xFF00) >> 8;
      fputc(c, output);
      checksum ^= c;

      /* always write the same number of bytes, but zero pad */
      writeSize = (16397 - nameLength) - pageSize;

      /* write the file contents */
      while(pageSize--) {
        c = fgetc(input);
        fputc(c, output);
        checksum ^= c;
      }

      /* write the zero padding */
      while(writeSize--) {
        fputc(0, output);
        checksum ^= 0;
      }

      /* write the checksum byte */
      fwrite(&checksum, 1, 1, output);
      firstPage = FALSE;
    } while (fileSize != 0);

    fclose(input);
  } while (strcmp(temp, "") != 0);

  fclose(output);
  freeAndZero(temp);

  return 0;
}
User avatar
dom
Well known member
Posts: 2076
Joined: Sun Jul 15, 2007 10:01 pm

Post by dom »

Thanks for details - I'm glad it works with Residos - that makes it a much easier issue to diagnose!

The problem with the +3 was some silly code where it didn't page in the DOS rom correctly. It should be fixed in the 20200611 build.
nuc1e0n
Member
Posts: 49
Joined: Wed Jun 10, 2020 12:34 am

Post by nuc1e0n »

Amazing! Yes it works fine for me now when compiling for a plus3 as well as Residos. Thanks for responding so quickly. This is the first time in a long time anyone has done something for me that they didn't have to. :D
Post Reply