// file: $isip/class/io/Sof/sof_05.cc
// version: $Id: sof_05.cc 5643 2000-11-21 19:50:11Z duncan $
//

// isip include files
//
#include "Sof.h"
#include <Console.h>
#include <MemoryManager.h>

// method: readIndex
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method reads the index
// note: this needs to be able to compress the symbol table if necessary
//
boolean Sof::readIndex() {
  
  // read all the objects to the file in binary form
  //
  // the format:
  //  1 byte offset to the next entry
  //  4 byte position
  //  4 byte tag number
  //  4 byte size
  //  4 byte name index
  //
  //  note that the size of 4 bytes is hardcoded for integers. This is
  //  to facilitate the possibility of different word-length
  //  architectures that will grow into the environment.
  //
  //  for now we just read and write the information, upgrade this to
  //  a true read/write of 4 byte info in the future.
  //
  byte entry_buffer[BUFFER_SIZE];
  long buf_size = (long)-1;
  byte size = (byte)0;
  long ptr = (long)0;

  int32 object_tag = (int32)-1;
  int32 object_pos = (int32)-1;
  int32 object_size = (int32)-1;
  int32 object_name = (int32)-1;

  // use the lowest level read, since we decode things by hand
  //
  while((buf_size = fp_d.read(entry_buffer, sizeof(byte), BUFFER_SIZE)) > 0) {

    // increment position
    //
    cur_pos_d += buf_size * sizeof(byte);
    
    ptr = 0;
    
    // while we have the minimum number of bytes for an entry
    //
    while ((buf_size - ptr) >= INDEX_SIZE) {
      
      // the first byte of each entry is the size
      //
      size = entry_buffer[ptr + INDEX_OFFSET_NEXT];

      // sanity check on the size
      //
      if (size < INDEX_SIZE) {
	return Error::handle(name(), L"readIndex", ERR_STRUCT,
			     __FILE__, __LINE__, Error::WARNING);
      }

      // read the fields from the binary index
      //
      MemoryManager::memcpy(&object_pos, &entry_buffer[ptr+INDEX_OFFSET_POS],
			    sizeof(int32));
      MemoryManager::memcpy(&object_tag, &entry_buffer[ptr+INDEX_OFFSET_TAG],
			    sizeof(int32));
      MemoryManager::memcpy(&object_size,&entry_buffer[ptr+INDEX_OFFSET_SIZE],
			    sizeof(int32));
      MemoryManager::memcpy(&object_name,&entry_buffer[ptr+INDEX_OFFSET_NAME],
			    sizeof(int32));

      // determine the checksum of the entry
      //
      cksm_d.compute(&entry_buffer[ptr + INDEX_OFFSET_POS], 4 * sizeof(int32));

      // set the fields in the index array
      //
      index_d.add((long)fp_d.decode(object_name),
		  (long)fp_d.decode(object_tag),
		  (long)fp_d.decode(object_pos),
		  (long)fp_d.decode(object_size));

      // increment the pointer
      //
      ptr += size;
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: writeIndex
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method writes the index
//
boolean Sof::writeIndex() {

  // write all the objects to the file in binary form
  //
  // the format:
  //  1 byte offset to the next entry
  //  4 byte position
  //  4 byte tag number
  //  4 byte size
  //  4 byte name index
  //
  //  note that the size of 4 bytes is hardcoded for integers. This is
  //  to facilitate the possibility of different word-length
  //  architectures that will grow into the environment.
  //
  //  for now we just read and write the information, upgrade this to
  //  a true read/write of 4 byte info in the future.
  //
  byte entry_buffer[BUFFER_SIZE];
  MemoryManager::memset(entry_buffer, 0, BUFFER_SIZE);

  byte size = (byte)INDEX_SIZE;
  long ptr = (long)0;
  byte *last_size = &entry_buffer[INDEX_OFFSET_NEXT];

  // loop through all classes
  //
  for (long cname = getNameCount() - 1; cname >= 0; cname--) {

    // loop through all instances of this class
    //
    for (long tag = index_d.first(cname); tag != NO_TAG;
	 tag = index_d.next(cname, tag)) {

      // if we have a full buffer, write it
      //
      if ((ptr + INDEX_SIZE) > BUFFER_SIZE) {

	// increase last_size so that read will skip over empty pad
	//
	(*last_size) += (BUFFER_SIZE - ptr);

	// write the buffer (we don't want byte swapping, use lowest
	// level write).
	//
	if (fp_d.write(entry_buffer,sizeof(byte),BUFFER_SIZE) != BUFFER_SIZE) {
	  return Error::handle(name(), L"writeIndex", Error::WRITE,
			       __FILE__, __LINE__);
	}

	cur_pos_d += BUFFER_SIZE * sizeof(byte);
	
	// clear out the buffer and initialize pointer
	//
	MemoryManager::memset(entry_buffer, 0, BUFFER_SIZE);
	ptr = 0;
      }

      // encode values for this entry
      //
      int32 object_name = fp_d.encode((int32)cname);
      int32 object_tag = fp_d.encode((int32)tag);
      int32 object_pos = fp_d.encode((int32)index_d.getPosition());
      int32 object_size = fp_d.encode((int32)index_d.getSize());
      
      // build the binary index entry
      //
      MemoryManager::memcpy(&entry_buffer[ptr + INDEX_OFFSET_POS],
			    &object_pos, sizeof(int32));
      MemoryManager::memcpy(&entry_buffer[ptr + INDEX_OFFSET_TAG],
			    &object_tag, sizeof(int32));
      MemoryManager::memcpy(&entry_buffer[ptr + INDEX_OFFSET_SIZE],
			    &object_size, sizeof(int32));
      MemoryManager::memcpy(&entry_buffer[ptr + INDEX_OFFSET_NAME],
			    &object_name, sizeof(int32));
      entry_buffer[ptr + INDEX_OFFSET_NEXT] = (byte)size;

      // compute the checksum for this entry
      //
      cksm_d.compute(&entry_buffer[ptr+INDEX_OFFSET_POS], 4 * sizeof(int32));
      
      // set the last size pointer for the next time around
      //
      last_size = &entry_buffer[ptr + INDEX_OFFSET_NEXT];

      // increment pointer
      //
      ptr += INDEX_SIZE;
    }
  }

  // if there are entries left over, write them out
  //
  if (ptr > 0) {
    if (fp_d.write(entry_buffer, sizeof(byte), ptr) != ptr) {
      return Error::handle(name(), L"writeIndex", Error::WRITE,
			   __FILE__, __LINE__);
    }
    cur_pos_d += ptr * sizeof(byte);
  }
  
  // exit gracefully
  //
  return true;
}

// method: readTable
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method reads the symbol table. note how nasty such functions
// are, dealing with low level byte ordering and such. This is why all
// code people use is written ABOVE Sof.
//
boolean Sof::readTable() {

  // read all the objects to the file in binary form
  //
  // the format:
  //  1 byte offset to the next entry
  //  4 byte position
  //  4 byte tag number
  //  <246 byte null terminated string for the name
  //
  //  note that the size of 4 bytes is hardcoded for integers. This is
  //  to facilitate the possibility of different word-length
  //  architectures that will grow into the environment.
  //
  //  for now we just read and write the information, upgrade this to
  //  a true read/write of 4 byte info in the future.
  //
  byte entry_buffer[BUFFER_SIZE];
  byte size = (byte)0;
  long ptr = (long)sizeof(int32);
  int32 buf_size;
  int32 num_classes = (int32)-1;
  int32 num = (int32)0;
  int32 object_index;
  int32 object_refs;
  byte* object_name;
  SysString name_str;
  int16 encoding_int;	// this is unicode encoding, not byte order
  SysChar::ENCODE encoding;

  MemoryManager::memset(entry_buffer, 0, BUFFER_SIZE);

  // keep track of file position for error messages
  //
  long file_pos = ftell();
  
  // the number of classes is the first thing in the table's binary space
  //  cur_pos_d updated inside the do/while loop
  //
  buf_size = fp_d.read(entry_buffer, sizeof(byte), BUFFER_SIZE);
  MemoryManager::memcpy(&num_classes, entry_buffer, sizeof(int32));
  
  // read what encoding we should read
  //
  MemoryManager::memcpy(&encoding_int, &entry_buffer[ptr], sizeof(int16));
  
  ptr += sizeof(int16);
  
  // decode the values
  //
  num_classes = fp_d.decode(num_classes);
  encoding_int = fp_d.decode(encoding_int);
  encoding = (SysChar::ENCODE)encoding_int;
  
  if ((buf_size < ptr) || (num_classes < 0)) {
    return Error::handle(name(), L"readTable", ERR_STRUCT,
			 __FILE__, __LINE__, Error::WARNING);
  }
  
  if (debug_level_d > Integral::BRIEF) {
    SysString output;
    output.assign((long)num_classes);
    output.insert(L"<Sof::readTable> Reading ", 0);
    output.concat(L" table entries");
    Console::put(output);
  }

  // since we needed that first 4 bytes for the table size, we need to
  // use a do loop for the rest of the data.
  //
  do {

    // increment cur_pos
    //
    cur_pos_d += buf_size * sizeof(byte);
    
    while (((buf_size - ptr) >= SYMBOL_BASE) && (num < num_classes)) {

      // the first byte of each entry is the size
      //
      size = entry_buffer[ptr + SYMBOL_OFFSET_NEXT];
      
      // sanity check on the size
      //
      if (size < SYMBOL_BASE) {
	return Error::handle(name(), L"readTable", ERR_STRUCT,
			     __FILE__, __LINE__, Error::WARNING);
      }

      // read the fields from the binary index
      //
      MemoryManager::memcpy(&object_index,
			    &entry_buffer[ptr + SYMBOL_OFFSET_IND],
			    sizeof(int32));
      MemoryManager::memcpy(&object_refs,
			    &entry_buffer[ptr + SYMBOL_OFFSET_REFS],
			    sizeof(int32));
      object_name = &entry_buffer[ptr + SYMBOL_OFFSET_NAME];

      // assign the name string
      //
      name_str.assign(object_name, SysString::BIG_BUFFER_LENGTH, encoding);

      // note that this is NOT UNICODE SAFE
      //
      long slen = name_str.length();

      // compute the checksum
      //
      cksm_d.compute(&entry_buffer[ptr + SYMBOL_OFFSET_IND],
		     2 * sizeof(int32) + slen);

      // add the entry into the symbol table
      //
      table_d.add((long)fp_d.decode(object_index),
		       (long)fp_d.decode(object_refs), name_str);

      // increment pointers and counters
      //
      ptr += size;
      num++;
    }

    // once we have the last entry, reset pointer
    //
    if (num < num_classes) {
      ptr = 0;
    }

    // keep reading data until we have all the classes read in
    //
  }
  while ((num < num_classes) &&
	 ((buf_size =
	   fp_d.read(entry_buffer, sizeof(byte), BUFFER_SIZE)) > 0)
	 && (file_pos = ftell()));
  
  // seek back to the end of the table
  //
  if (!fseek(ptr - buf_size, File::POS_PLUS_CUR)) {
    return Error::handle(name(), L"readTable", Error::SEEK,
			 __FILE__, __LINE__, Error::WARNING);
  }

  // exit gracefully
  //
  return true;
}

// method: writeTable
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method writes the symbol table. note how nasty such functions
// are, dealing with low level byte ordering and such. This is why all
// code people use is written ABOVE Sof.
//
boolean Sof::writeTable() {

  // write the symbol table to the file
  //
  // the format:
  //  1 byte offset to the next entry
  //  4 byte index
  //  4 byte reference count
  //  <246 byte null terminated string for the name
  //
  //  note that the size of 4 bytes is hardcoded for integers. This is
  //  to facilitate the possibility of different word-length
  //  architectures that will grow into the environment.
  //
  //  for now we just read and write the information, upgrade this to
  //  a true read/write of 4 byte info in the future.
  //
  byte entry_buffer[BUFFER_SIZE];
  MemoryManager::memset(entry_buffer, 0, BUFFER_SIZE);

  byte size = (byte)0;
  long ptr = (long)sizeof(int32);

  // the first thing written is the size of the table
  //
  int32 num_classes = fp_d.encode((int32)table_d.getCount());
  MemoryManager::memcpy(entry_buffer, &num_classes, sizeof(int32));

  // write the encoding we will use (use ASCII until we support UTF8)
  //
  SysChar::ENCODE encoding = SysChar::ENCODE_ASCII;
  int16 encoding_int = (int16)encoding;
  int16 val = fp_d.encode(encoding_int);
  MemoryManager::memcpy(&entry_buffer[ptr], &val, sizeof(int16));
  ptr += sizeof(int16);
  
  // pointer to the last size entry
  //
  byte *last_size = &entry_buffer[ptr + SYMBOL_OFFSET_NEXT];
  
  // handle converting names from the symbol table to utf8 encoding
  //
  SysString name_str;
  long slen = 0;

  // we call this the max name length. it should allow ample room for
  // any utf8 characters to be expanded to full length
  //
  const long max_name_length = SofSymbolTable::MAX_SYM_LENGTH * 6;
  
  static byte name_buf[max_name_length];
  long count = 0;
  int32 refs = 0;
  int32 ind = 0;
  
  // loop through integers until all names are written
  //
  for (long i = 0; count < table_d.getCount(); i++) {

    // is this index a name?
    //
    if (table_d.getSymbol(name_str, i)) {

      // encode the values
      //
      refs = fp_d.encode((int32)table_d.getRefCount(name_str));
      ind = fp_d.encode((int32)i);
      count++;

      // encode the name_str
      //
      slen = name_str.getBuffer((byte*)name_buf, max_name_length, encoding);
      size = SYMBOL_BASE + slen + 1;

      // if we have a full buffer, write it
      //
      if (ptr + size > BUFFER_SIZE) {

	// increase last_size so that read will skip over empty pad
	//
	(*last_size) += (BUFFER_SIZE - ptr);

	// write the buffer (we don't want byte swapping, use lowest
	// level write).
	//
	if (fp_d.write(entry_buffer,sizeof(byte),BUFFER_SIZE) != BUFFER_SIZE) {
	  return Error::handle(name(), L"writeTable", Error::WRITE,
			       __FILE__, __LINE__);
	}
	cur_pos_d += BUFFER_SIZE * sizeof(byte);
	
	// clear out the buffer and initialize pointer
	//
	MemoryManager::memset(entry_buffer, 0, BUFFER_SIZE);
	ptr = 0;
      }

      // build the binary entry
      //
      entry_buffer[ptr + SYMBOL_OFFSET_NEXT] = (byte)size;
      MemoryManager::memcpy(&entry_buffer[ptr + SYMBOL_OFFSET_IND],
			    &ind, sizeof(int32));
      MemoryManager::memcpy(&entry_buffer[ptr + SYMBOL_OFFSET_REFS],
			    &refs, sizeof(int32));
      MemoryManager::memcpy(&entry_buffer[ptr + SYMBOL_OFFSET_NAME],
			    name_buf, slen);
      entry_buffer[ptr + SYMBOL_OFFSET_NAME + slen] = (byte)NULL;

      // compute the checksum
      //
      cksm_d.compute(&entry_buffer[ptr + SYMBOL_OFFSET_IND],
		     2 * sizeof(int32) + slen);

      // set the last size pointer for the next time around
      //
      last_size = &entry_buffer[ptr + INDEX_OFFSET_NEXT];

      // increment pointer
      //
      ptr += size;
    }
  }

  // if there are entries left over, write them out
  //
  if (ptr > 0) {
    if (fp_d.write(entry_buffer, sizeof(byte), ptr) != ptr) {
      return Error::handle(name(), L"writeTable", Error::WRITE,
			   __FILE__, __LINE__);
    }
    cur_pos_d += ptr * sizeof(byte);
  }

  // exit gracefully
  //
  return true;
}

// method: isSof
//
// arguments: none
//
// return: a boolean value indicating status
//
// check if the file is an Sof file. if it is, all values from the header are
// automatically read.
//
boolean Sof::isSof() {

  // initialize strings
  //
  cname_d.clear();
  version_d.clear();
  magic_d.clear();

  // make sure the file is open
  //
  if (!fp_d.isReadable()) {
    return false;
  }
  
  // read the first line from the file
  //
  SysString buf;
  fp_d.rewind();
  fp_d.get(buf);
  
  // fetch the header
  //
  SysChar delim_beg, delim_end;

  // try to get the first delimiter char
  //
  long index0 = buf.firstNotSpace();
  long index1 = -1;
  if (index0 < 0) {
    return false;
  }
  delim_beg.assign(buf(index0));

  // try to get the class name (Sof)
  //
  index0 = buf.firstNotSpace(index0 + 1);
  index1 = buf.firstSpace(index0);
  if ((index0 < 0) || (index1 < 0)) {
    return false;
  }
  buf.substr(cname_d, index0, index1 - index0);

  // try to get the version
  //
  index0 = buf.firstNotSpace(index1 + 1);
  index1 = buf.firstSpace(index0);
  if ((index0 < 0) || (index1 < 0)) {
    return false;
  }
  buf.substr(version_d, index0, index1 - index0);

  // try to get either the delimiter or the magic string
  //
  index0 = buf.firstNotSpace(index1 + 1);
  if (index0 < 0) {
    return false;
  }

  // if this index is the last character on the line, then we are in
  // a text file and no magic string was specified.
  //
  if (buf.firstNotSpace(index0 + 1) < 0) {
    file_type_d = File::TEXT;

    delim_end = buf(index0);
  }

  // else this better be a binary sof file
  //
  else {
    
    file_type_d = File::BINARY;
    
    index1 = buf.firstSpace(index0);
    if ((index1 < 0) || ((index1 - index0) != BSTR_LEN)) {
      return false;
    }
    buf.substr(magic_d, index0, BSTR_LEN);
    
    index0 = buf.firstNotSpace(index1 + 1);
    if (index0 < 0) {
      return false;
    }
    
    // the delimiter should be the last character on the line
    //
    if (buf.firstNotSpace(index0 + 1) < 0) {
      delim_end = buf(index0);
    }
    else {
      return false;
    }

    // check byte mode
    //
    int32 x = 0;
    float32 y = 0;
    byte magic_buf[sizeof(x) + 1];
    
    magic_d.getBuffer((byte*)magic_buf, sizeof(x) + 1);

    MemoryManager::memcpy(&x, magic_buf, sizeof(x));
    MemoryManager::memcpy(&y, magic_buf, sizeof(y));

    if (x == BCODE_NATIVE) {
      fp_d.setBMode(File::NATIVE);
    }
    else if (x == BCODE_SWAP) {
      fp_d.setBMode(File::SWAP);
    }
    else {
      return false;
    }

    // check floating point encoding
    //
    if ((y != IEEE_FLOAT_NATIVE) && (y != IEEE_FLOAT_SWAP)) {
      return false;
    }

    // read in the end of data pointer
    //
    int32 val;
    if (fp_d.read(&val, sizeof(int32), 1) != 1) {
      return false;
    }
    end_of_data_d = val;
    
    // sanity check on end_of_data_d
    //
    if (end_of_data_d < 1) {
      return false;
    }

    // read in the checksum
    //
    uint16 checksum;
    if (fp_d.read(&checksum, sizeof(uint16), 1) != 1) {
      return false;
    }
    file_checksum_d = checksum;

    if (debug_level_d >= Integral::DETAILED) {
      SysString output;
      output.assign(file_checksum_d);
      output.insert(L"read file_checksum_d = ", 0);
      Console::put(output);
    }
  }

  // save the position
  //
  end_of_header_d = fp_d.tell();
  cur_pos_d = end_of_header_d;
  
  // provide some debug information
  //
  if (debug_level_d > Integral::DETAILED) {
    debug(L"isSof");
  }
  
  // check the class name and version
  //
  if (!cname_d.eq(CLASS_NAME)) {
    return false;
  }
  if (!version_d.eq(VERSION)) {
    return false;
  }

  // make sure the delimiters are equal
  //
  if (delim_beg != delim_end) {
    return false;
  }
  else {
    delimiter_d = delim_beg;
  }

  // exit gracefully
  //
  return true;
}

// method: isSof
//
// arguments:
//  const SysString& fname: (input) filename
//
// return: a boolean value indicating status
//
// check if this file is an Sof file
//
boolean Sof::isSof(const SysString& fname_a) {
  
  // we need an Sof object
  //
  Sof sof;
  
  // open the file
  //
  if (!sof.fp_d.open(fname_a)) {
    return false;
  }

  // check if it is sof
  //
  if (!sof.isSof()) {

    // close the file pointers and return false
    //
    sof.fp_d.close();
    return false;
  }
  
  // close the file pointer
  //
  sof.fp_d.close();
  
  // exit gracefully
  //
  return true;
}

// method: update
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method updates the header and index
//
boolean Sof::update() {

  // local variables
  //
  SysString buf;
  buf.assign(L"  ");
  buf(0) = delimiter_d;
  buf.concat(cname_d);
  buf.concat(L" ");
  buf.concat(version_d);

  // start from the beginning of the file
  //
  fp_d.rewind();
  
  // text files are simpler
  //
  if (file_type_d == File::TEXT) {
    
    // concatenate the last delimiter and write string
    //
    buf.concat(L"  \n");
    buf(buf.length() - 2) = delimiter_d;
    fp_d.put(buf);
    fp_d.flush();
    end_of_header_d = ftell();
    return true;
  }

  // binary file, build magic string from bmode
  //
  int32 x[2];
  File::BMODE byte_mode = fp_d.getBMode();
  File::BMODE mach_byte_mode = fp_d.getMachineByteOrder();
  
  if (byte_mode == mach_byte_mode) {
    x[0] = BCODE_NATIVE;
  }
  else {
    x[0] = BCODE_SWAP;
  }

  x[1] = (long)0;
  magic_d.assign((byte*)&x);
  
  buf.concat(L" ");
  buf.concat(magic_d);

  // concatenate the last delimiter and write string
  //
  buf.concat(L"  \n");
  buf(buf.length() - 2) = delimiter_d;
  fp_d.put(buf);

  int32 val = (int32)end_of_data_d;
  
  if (fp_d.write(&val, sizeof(int32), 1) != 1) {
    return Error::handle(name(), L"update", Error::WRITE, __FILE__, __LINE__);
  } 

  // write out a null checksum (we'll update it after we write the
  // index and table)
  //
  uint16 valc = 0;
  if (fp_d.write(&valc, sizeof(uint16), 1) != 1) {
    return Error::handle(name(), L"update", Error::WRITE, __FILE__, __LINE__);
  } 
  
  // set end of header, possibly set end of data
  //
  end_of_header_d = fp_d.tell();
  cur_pos_d = end_of_header_d;
  if (end_of_data_d < end_of_header_d) {
    end_of_data_d = end_of_header_d;
  }

  // if end of data exists, seek to it
  //
  else {
    
    // seek to the end of the data
    //
    if (!fseek(end_of_data_d, File::POS)) {
      return Error::handle(name(), L"update", Error::SEEK, __FILE__, __LINE__);
    }
  }

  // clear the checksum object so it can accumulate a new checksum
  //
  cksm_d.clear(Integral::RETAIN);
  
  // write out the symbol table
  //
  if (debug_level_d > Integral::BRIEF) {
    SysString output;
    output.assign((long)end_of_data_d);
    output.insert(L"<Sof::update> writing table to position ", 0);
    Console::put(output);
  }
  if (!writeTable()) {
    return Error::handle(name(), L"update", SofSymbolTable::ERR,
			 __FILE__, __LINE__);
  }

  // update the index at the end of the file
  //
  if (debug_level_d > Integral::BRIEF) {
    SysString output;
    output.assign((long)ftell());
    output.insert(L"<Sof::update> writing index to position ", 0);
    Console::put(output);
  }
  if (!writeIndex()) {
    return Error::handle(name(), L"update", SofList::ERR, __FILE__, __LINE__);
  }
  
  // truncate the file if necessary
  //
  long end_of_file = ftell();
  
  if (!fseek(0, File::POS_PLUS_END)) {
    return Error::handle(name(), L"update", Error::SEEK, __FILE__, __LINE__);
  }
  
  if (ftell() > (end_of_file + 1)) {

    // give debug message
    //
    if (debug_level_d > Integral::BRIEF) {

      SysString output;
      SysString numeric;

      output.assign(L"<Sof::update> truncating file [");
      numeric.assign(ftell());
      output.concat(numeric);

      output.concat(L" - ");
      numeric.assign(end_of_file);
      output.concat(numeric);
      output.concat(L"]");
      
      Console::put(output);
    }

    // truncate file
    //
    if (!fp_d.truncate(end_of_file + 1)) {
      return Error::handle(name(), L"update", Error::IO, __FILE__, __LINE__);
    }
  }

  // write out the checksum
  //
  if (!fseek(end_of_header_d - sizeof(uint16), File::POS)) {
    return Error::handle(name(), L"update", Error::SEEK, __FILE__, __LINE__);
  }

  
  valc = cksm_d.get();
  if (fp_d.write(&valc, sizeof(uint16), 1) != 1) {
    return Error::handle(name(), L"update", Error::WRITE, __FILE__, __LINE__);
  } 

  if (debug_level_d >= Integral::DETAILED) {
    SysString output;
    output.assign(valc);
    output.insert(L"writing checksum = ", 0);
    Console::put(output);
  }

  
  // seek back to the beginning of the current object, if set
  //
  if (cur_data_d >= end_of_header_d) {
    rewind();
  }
  
  fp_d.flush();

  // exit gracefully
  //
  return true;
}
