Logo Search packages:      
Sourcecode: cbmconvert version File versions  Download package

enum WrStatus WriteCpmImage ( const struct Filename name,
const byte_t data,
size_t  length,
struct Image image,
log_t  log 
)

Write a file into a CP/M disk image.

Parameters:
name native (PETSCII) name of the file
data the contents of the file
length length of the file contents
image the disk image
log Call-back function for diagnostic output
Returns:
status of the operation

Definition at line 1852 of file image.c.

References CpmDirEnt::area, CpmDirEnt::basename, CpmDirEnt::block, CpmDirEnt::blocks, Image::buf, CPMBLOCK, CpmConvertName(), CpmTransTable(), DirEntDontCreate, DirEntOnlyCreate, Image::direntOpts, CpmDirEnt::extent, Filename::name, rounddiv, CpmDirEnt::suffix, Warnings, WrFail, WrFileExists, WrNoSpace, and WrOK.

{
  byte_t** trans;
  struct CpmDirEnt** allocated;
  struct CpmDirEnt* dirent;
  struct CpmDirEnt cpmname;
  unsigned au; /* allocation unit size */
  unsigned sectors; /* number of disk sectors */
  unsigned slot; /* next directory slot */
  unsigned blocksfree; /* number of free blocks */

  if (!name || !data || !image || !image->buf ||
      !(trans = CpmTransTable (image, &au, &sectors)))
    return WrFail;

  if (!(allocated = calloc (2 * sectors / au, sizeof(*allocated)))) {
    free (trans);
    return WrFail;
  }

  if (!(dirent = malloc (au * 8 * sizeof (*dirent)))) {
    free (allocated);
    free (trans);
    return WrFail;
  }

  blocksfree = 2 * (sectors / au - 1);

  /* Convert the file name */
  {
    int i;

    memset (&cpmname, 0, sizeof cpmname);
    memset (cpmname.basename, ' ',
          sizeof cpmname.basename + sizeof cpmname.suffix);

    /* Convert the file name base */

    for (i = 0; i < sizeof(name->name) &&
         i < sizeof(cpmname.basename) &&
         !memchr (".\240", name->name[i], 3);
       i++)
      if (i && name->name[i] == ' ') /* stop at the first space */
      break;
      else if (name->name[i] >= 0x41 && name->name[i] <= 0x5A)
      cpmname.basename[i] = name->name[i] + 'A' - 0x41; /* upper case only */
      else if (name->name[i] >= 0xC1 && name->name[i] <= 0xDA)
      cpmname.basename[i] = name->name[i] + 'A' - 0xC1;
      else if ((name->name[i] & 0x7F) < 32 || name->name[i] == ' ')
      cpmname.basename[i] = '-'; /* control chars and space */
      else if (name->name[i] < 127)
      cpmname.basename[i] = name->name[i];
      else
      cpmname.basename[i] = '+'; /* graphics characters */

    /* Convert the file name suffix */

    if (name->name[i] != ' ' && ++i < sizeof(name->name)) {
      unsigned j;

      for (j = 0; j < sizeof(cpmname.suffix) && i < sizeof(name->name);
         i++, j++)
      if ((name->name[i] & 0x7F) == ' ') /* stop at the first space */
        break;
      else if (name->name[i] >= 0x41 && name->name[i] <= 0x5A)
        cpmname.suffix[j] = name->name[i] + 'A' - 0x41; /* upper case only */
      else if (name->name[i] >= 0xC1 && name->name[i] <= 0xDA)
        cpmname.suffix[j] = name->name[i] + 'A' - 0xC1;
      else if ((name->name[i] & 0x7F) < 32)
        cpmname.suffix[j] = '-'; /* control chars */
      else if (name->name[i] < 127)
        cpmname.suffix[j] = name->name[i];
      else
        cpmname.suffix[j] = '+'; /* graphics characters */
    }
  }

  /* Traverse through the directory and determine the amount and
     location of free blocks */
  {
    unsigned d, i;
    bool found = false;

    /* Read the directory entries */
    for (d = au; d--; )
      memcpy (&dirent[d * 8], trans[d], 8 * sizeof (*dirent));

    for (d = slot = 0; d < au * 8; d++) {
      if (dirent[d].area == 0xE5 ||
        !memcmp (&dirent[d], "\0\0\0\0\0\0\0\0\0\0\0", 12))
      continue;

      if (!memcmp (dirent[d].basename, cpmname.basename,
               sizeof cpmname.basename + sizeof cpmname.suffix)) {
      if (image->direntOpts == DirEntOnlyCreate) {
        free (dirent);
        free (allocated);
        free (trans);
        return WrFileExists;
      }

      found = true;
      continue; /* overwrite the file */
      }

      if (d != slot)
      memcpy (&dirent[slot], &dirent[d], (au * 8 - d) * sizeof(*dirent));

      d = slot++;

      for (i = 0; i < rounddiv(dirent[d].blocks, au); i++)
      if (CPMBLOCK (dirent[d].block, i) < 2 ||
          CPMBLOCK (dirent[d].block, i) >= 2 * sectors / au) {
        struct Filename fn;
        CpmConvertName (&dirent[d], &fn);
        (*log) (Warnings, &fn,
              "Illegal block address in block %u of extent 0x%02x",
              i, dirent[d].extent);
      }
      else if (allocated[CPMBLOCK (dirent[d].block, i)]) {
        struct Filename fn;
        CpmConvertName (&dirent[d], &fn);
        (*log) (Warnings, &fn, "Sector 0x%02x allocated multiple times",
              CPMBLOCK (dirent[d].block, i));
      }
      else {
        allocated[CPMBLOCK (dirent[d].block, i)] = &dirent[d];
        blocksfree--;
      }
    }

    /* See if the file was found */
    if (!found && image->direntOpts == DirEntDontCreate) {
      free (dirent);
      free (allocated);
      free (trans);
      return WrFail;
    }

    /* Clear the empty directory entries */
    memset (&dirent[slot], 0xE5, (au * 8 - slot) * sizeof(*dirent));

    /* Ensure that enough free space is available */

    if (slot >= 8 * au ||
      length > (8 * au - slot) * au / 2 * 16 * 128 ||
      length > blocksfree * au * 128) {
      free (dirent);
      free (allocated);
      free (trans);
      return WrNoSpace;
    }
  }

  /* Write the file */

  {
    struct CpmDirEnt* de = 0;
    unsigned block, blocks;
    unsigned freeblock = 2;

    for (block = 0, blocks = rounddiv(length, 128); blocks;) {
      if (!(block % 128)) { /* advance to next directory slot */
      de = &dirent[slot++];
      memcpy (de, &cpmname, sizeof cpmname);
      de->extent = block / 128;
      }

      /* Copy the blocks */

      {
      unsigned j;

      de->blocks = blocks < 128 ? blocks : 128;
      blocks -= de->blocks;

      for (j = 0; j < de->blocks; j++, block++) {
        if (!(j % au)) {
          unsigned k;
          /* Get next free block */
          while (allocated[freeblock]) freeblock++;
          allocated[freeblock] = de;
          if (au == 8)
            de->block[j / au] = freeblock;
          else {
            de->block[(j / au) * 2] = freeblock & 0xFF;
            de->block[(j / au) * 2 + 1] = freeblock >> 8;
          }
          /* Pad it with ^Z */
          for (k = 0; k < au / 2; k++)
            memset (trans[(au / 2) * freeblock + k], 0x1A, 256);
        }

        /* Copy the block */
        memcpy (trans[(au / 2) * freeblock + ((j / 2) % (au / 2))] +
              128 * (j % 2), data + 128 * block,
              length >= 128 * (block + 1) ? 128 : length - 128 * block);
      }
      }
    }
  }

  /* Write the directory entries */
  {
    int d = au;
    while (d--)
      memcpy (trans[d], &dirent[d * 8], 8 * sizeof (*dirent));
  }

  free (dirent);
  free (allocated);
  free (trans);
  return WrOK;
}


Generated by  Doxygen 1.6.0   Back to index