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

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

Write to an image in CBM DOS format

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 2269 of file image.c.

References backupBAM(), blocksFree(), Image::buf, DEL, deleteDirEnt(), DirEntOnlyCreate, Image::direntOpts, Image::dirtrack, Errors, findNextFree(), getDirEnt(), getFiletype(), getGeometry(), ImOK, isGeosDirEnt(), Filename::name, PRG, Filename::recordLength, REL, restoreBAM(), rounddiv, SEQ, setupSideSectors(), Filename::type, Image::type, USR, Warnings, WrFail, WrFileExists, writeInode(), WrNoSpace, and WrOK.

{
  struct DirEnt* dirent;

  if (!name || !data || !image || !image->buf || !getGeometry (image->type))
    return WrFail;

  /* See if it is a GEOS file. */
  if (name->type >= DEL && name->type < REL && length > 2 * 254 &&
      !strncmp ((char*)&data[sizeof (struct DirEnt) + 1],
            " formatted GEOS file ", 21)) {
    size_t len;
    struct Filename geosname;
    const byte_t* info = &data[254];
    dirent = (struct DirEnt*) &data[-2];

    /* Read the name from the directory entry. */
    memcpy (geosname.name, dirent->name, sizeof geosname.name);
    geosname.type = getFiletype (image, dirent);
    geosname.recordLength = 0;

    if (!isGeosDirEnt (dirent) || memcmp (info, "\3\25\277", 3))
      goto notGEOS;

    if (dirent->isVLIR) {
      const byte_t* vlir = &data[2 * 254];
      unsigned vlirblock;
      len = 3 * 254;

      for (vlirblock = 0; vlirblock < 127; vlirblock++) {
      unsigned blocks = vlir[2 * vlirblock];
      unsigned lastblocklen = vlir[2 * vlirblock + 1];

      if (!blocks) {
        if (lastblocklen != 0 && lastblocklen != 0xFF)
          goto notGEOS;
      }
      else if (lastblocklen < 2)
        goto notGEOS;
      else
        len = 254 * (rounddiv(len, 254) + blocks - 1) + lastblocklen - 1;
      }

      if (len > length) {
      (*log) (Warnings, &geosname, "%d bytes too short file", len - length);
      goto notGEOS;
      }
    }
    else
      len = length;

    if ((info[0x42] ^ dirent->type) & 0x8F)
      (*log) (Warnings, &geosname, "file types differ: $%02x $%02x",
            info[0x42], dirent->type);

    if (info[0x43] != dirent->geos.type)
      (*log) (Warnings, &geosname, "GEOS file types differ: $%02x $%02x",
            info[0x43], dirent->geos.type);

    if (info[0x44] != dirent->isVLIR)
      (*log) (Warnings, &geosname, "VLIR flags differ: $%02x $%02x",
            info[0x44], dirent->isVLIR);

    if (len != length)
      (*log) (Warnings, &geosname, "File size mismatch: %d extraneous bytes",
            length - len);

    if (rounddiv(len, 254) - 1 !=
      dirent->blocksLow + (dirent->blocksHigh << 8)) {
      unsigned blks = rounddiv(len, 254) - 1;
      dirent->blocksLow = blks & 0xFF;
      dirent->blocksHigh = blks >> 8;
      (*log) (Warnings, &geosname, "invalid block count");
    }

    dirent = getDirEnt (image, &geosname);

    if (!dirent)
      return WrNoSpace;

    if (dirent->type) {
      if (image->direntOpts == DirEntOnlyCreate)
      return WrFileExists;

      /* delete the old file */
      if (ImOK != deleteDirEnt (image, dirent)) {
      (*log) (Errors, &geosname, "Could not delete existing file.");
      return WrFail;
      }
    }

    if (blocksFree (image) < rounddiv(len, 254) - 1)
      return WrNoSpace;

    /* set the directory entry parameters */
    memcpy ((byte_t*)dirent + 2, data, sizeof (struct DirEnt) - 2);
    dirent->type = 0;
    dirent->firstTrack = 0;
    dirent->firstSector = 0;
    dirent->infoTrack = image->dirtrack + 1;
    dirent->infoSector = 0;

    {
      byte_t* oldBAM = 0;
      enum WrStatus status;

      /* back up the old BAM */

      if (!backupBAM (image, &oldBAM)) {
      (*log) (Errors, name, "Backing up the BAM failed.");
      return WrFail;
      }

      if (!findNextFree (image, &dirent->infoTrack, &dirent->infoSector))
      return WrNoSpace;

      status = writeInode (image, dirent->infoTrack, dirent->infoSector,
                     &data[254], 254);

      if (status != WrOK) {
      restoreBAM (image, &oldBAM);
      (*log) (Errors, &geosname, "Writing the info sector failed.");
      return status;
      }

      if (dirent->isVLIR) {
      byte_t vlir[254];
      const byte_t* vlirsrc = &data[254 * 2];
      unsigned vlirblock;
      const byte_t* buf = &data[254 * 3];
      byte_t track = dirent->infoTrack;
      byte_t sector = dirent->infoSector;

      memcpy (vlir, vlirsrc, 254);

      for (vlirblock = 0; vlirblock < 127; vlirblock++) {
        unsigned blocks = vlirsrc[2 * vlirblock];
        unsigned lastblocklen = vlirsrc[2 * vlirblock + 1];

        if (blocks) {
          if (!findNextFree (image, &track, &sector)) {
            restoreBAM (image, &oldBAM);
            return WrNoSpace;
          }

          vlir[vlirblock * 2] = track;
          vlir[vlirblock * 2 + 1] = sector;

          len = 254 * (blocks - 1) + lastblocklen - 1;
          status = writeInode (image, track, sector, buf, len);

          if (status != WrOK) {
            restoreBAM (image, &oldBAM);
            (*log) (Errors, &geosname, "Writing a VLIR node failed.");
            return status;
          }

          buf += 254 * blocks;
        }
      }

      dirent->firstTrack = dirent->infoTrack;
      dirent->firstSector = dirent->infoSector;

      if (!findNextFree (image, &dirent->firstTrack, &dirent->firstSector)) {
        restoreBAM (image, &oldBAM);
        return WrNoSpace;
      }

      status = writeInode (image, dirent->firstTrack, dirent->firstSector,
                       vlir, 254);

      if (status != WrOK) {
        restoreBAM (image, &oldBAM);
        (*log) (Errors, &geosname, "Writing the VLIR block failed.");
        return status;
      }
      }
      else {
      dirent->firstTrack = dirent->infoTrack;
      dirent->firstSector = dirent->infoSector;

      if (!findNextFree (image, &dirent->firstTrack, &dirent->firstSector)) {
        restoreBAM (image, &oldBAM);
        return WrNoSpace;
      }

      status = writeInode (image, dirent->firstTrack, dirent->firstSector,
                       &data[254 * 2], length - 254 * 2);

      if (status != WrOK) {
        restoreBAM (image, &oldBAM);
        (*log) (Errors, &geosname, "Writing the data sectors failed.");
        return status;
      }
      }

      free (oldBAM);
    }

    dirent->type = *data;
    return WrOK;

  notGEOS:
    (*log) (Warnings, name, "not a valid GEOS (Convert) file");
  }

  dirent = getDirEnt (image, name);

  if (!dirent)
    return WrNoSpace;

  if (dirent->type) {
    if (image->direntOpts == DirEntOnlyCreate)
      return WrFileExists;

    /* delete the old file */
    if (ImOK != deleteDirEnt (image, dirent)) {
      (*log) (Errors, name, "Could not delete existing file.");
      return WrFail;
    }
  }

  /* Check that there is enough space for the file. */

  if (blocksFree (image) < rounddiv(length, 254) +
      (name->type == REL ? rounddiv(rounddiv(length, 254), 120) : 0))
    return WrNoSpace;

  /* set the file name */
  memcpy (dirent->name, name->name, 16);
  /* set the track and sector of the file */
  dirent->firstTrack = image->dirtrack + 1;
  dirent->firstSector = 0;

  if (!findNextFree (image, &dirent->firstTrack, &dirent->firstSector))
    return WrNoSpace;

  {
    unsigned blocks;
    byte_t* oldBAM = 0;
    enum WrStatus status;

    /* set the block count */
    blocks = rounddiv(length, 254);

    if (name->type == REL) {
      /* set the record length for relative files */
      dirent->recordLength = name->recordLength;

      /* adjust the block count */
      blocks += rounddiv(blocks, 120);
    }

    dirent->blocksLow = blocks & 0xFF;
    dirent->blocksHigh = blocks >> 8;

    /* back up the old BAM */

    if (!backupBAM (image, &oldBAM)) {
      (*log) (Errors, name, "Backing up the BAM failed.");
      return WrFail;
    }

    status = writeInode (image, dirent->firstTrack, dirent->firstSector,
                   data, length);

    if (status != WrOK) {
      restoreBAM (image, &oldBAM);
      (*log) (Errors, name, "Writing the data bytes failed.");
      return status;
    }

    switch (name->type) {
    case REL:
      /* set the initial track and sector for the side sectors */
      dirent->ssTrack = image->dirtrack + 1;
      dirent->ssSector = 0;

      status = setupSideSectors (image, dirent, rounddiv(length, 254), log);

      if (status != WrOK) {
      restoreBAM (image, &oldBAM);
      (*log) (Errors, name, "Could not set up the side sectors.");
      return status;
      }

      /* fall through */

    case DEL:
    case SEQ:
    case PRG:
    case USR:
      free (oldBAM);

      dirent->type = name->type | 0x80;
      return WrOK;

    default:
      restoreBAM (image, &oldBAM);

      (*log) (Errors, name, "Unsupported file type.");
      return WrFail;
    }
  }
}


Generated by  Doxygen 1.6.0   Back to index