Exemple #1
0
int main(int argc, char **argv)
{
  program_name = argv[0];
  static char stderr_buf[BUFSIZ];
  setbuf(stderr, stderr_buf);
  
  const char *base_name = 0;
  typedef int (*parser_t)(const char *);
  parser_t parser = do_file;
  const char *directory = 0;
  const char *foption = 0;
  int opt;
  static const struct option long_options[] = {
    { "help", no_argument, 0, CHAR_MAX + 1 },
    { "version", no_argument, 0, 'v' },
    { NULL, 0, 0, 0 }
  };
  while ((opt = getopt_long(argc, argv, "c:o:h:i:k:l:t:n:c:d:f:vw",
			    long_options, NULL))
	 != EOF)
    switch (opt) {
    case 'c':
      common_words_file = optarg;
      break;
    case 'd':
      directory = optarg;
      break;
    case 'f':
      foption = optarg;
      break;
    case 'h':
      check_integer_arg('h', optarg, 1, &hash_table_size);
      if (!is_prime(hash_table_size)) {
	while (!is_prime(++hash_table_size))
	  ;
	warning("%1 not prime: using %2 instead", optarg, hash_table_size);
      }
      break;
    case 'i':
      ignore_fields = optarg;
      break;
    case 'k':
      check_integer_arg('k', optarg, 1, &max_keys_per_item);
      break;
    case 'l':
      check_integer_arg('l', optarg, 0, &shortest_len);
      break;
    case 'n':
      check_integer_arg('n', optarg, 0, &n_ignore_words);
      break;
    case 'o':
      base_name = optarg;
      break;
    case 't':
      check_integer_arg('t', optarg, 1, &truncate_len);
      break;
    case 'w':
      parser = do_whole_file;
      break;
    case 'v':
      printf("GNU indxbib (groff) version %s\n", Version_string);
      exit(0);
      break;
    case CHAR_MAX + 1: // --help
      usage(stdout);
      exit(0);
      break;
    case '?':
      usage(stderr);
      exit(1);
      break;
    default:
      assert(0);
      break;
    }
  if (optind >= argc && foption == 0)
    fatal("no files and no -f option");
  if (!directory) {
    char *path = get_cwd();
    store_filename(path);
    a_delete path;
  }
  else
    store_filename(directory);
  init_hash_table();
  store_filename(common_words_file);
  store_filename(ignore_fields);
  key_buffer = new char[truncate_len];
  read_common_words_file();
  if (!base_name)
    base_name = optind < argc ? argv[optind] : DEFAULT_INDEX_NAME;
  const char *p = strrchr(base_name, DIR_SEPS[0]), *p1;
  const char *sep = &DIR_SEPS[1];
  while (*sep) {
    p1 = strrchr(base_name, *sep);
    if (p1 && (!p || p1 > p))
      p = p1;
    sep++;
  }
  size_t name_max;
  if (p) {
    char *dir = strsave(base_name);
    dir[p - base_name] = '\0';
    name_max = file_name_max(dir);
    a_delete dir;
  }
  else
    name_max = file_name_max(".");
  const char *filename = p ? p + 1 : base_name;
  if (strlen(filename) + sizeof(INDEX_SUFFIX) - 1 > name_max)
    fatal("`%1.%2' is too long for a filename", filename, INDEX_SUFFIX);
  if (p) {
    p++;
    temp_index_file = new char[p - base_name + sizeof(TEMP_INDEX_TEMPLATE)];
    memcpy(temp_index_file, base_name, p - base_name);
    strcpy(temp_index_file + (p - base_name), TEMP_INDEX_TEMPLATE);
  }
  else {
    temp_index_file = strsave(TEMP_INDEX_TEMPLATE);
  }
  catch_fatal_signals();
  int fd = mkstemp(temp_index_file);
  if (fd < 0)
    fatal("can't create temporary index file: %1", strerror(errno));
  indxfp = fdopen(fd, FOPEN_WB);
  if (indxfp == 0)
    fatal("fdopen failed");
  if (fseek(indxfp, sizeof(index_header), 0) < 0)
    fatal("can't seek past index header: %1", strerror(errno));
  int failed = 0;
  if (foption) {
    FILE *fp = stdin;
    if (strcmp(foption, "-") != 0) {
      errno = 0;
      fp = fopen(foption, "r");
      if (!fp)
	fatal("can't open `%1': %2", foption, strerror(errno));
    }
    string path;
    int lineno = 1;
    for (;;) {
      int c;
      for (c = getc(fp); c != '\n' && c != EOF; c = getc(fp)) {
	if (c == '\0')
	  error_with_file_and_line(foption, lineno,
				   "nul character in pathname ignored");
	else
	  path += c;
      }
      if (path.length() > 0) {
	path += '\0';
	if (!(*parser)(path.contents()))
	  failed = 1;
	path.clear();
      }
      if (c == EOF)
	break;
      lineno++;
    }
    if (fp != stdin)
      fclose(fp);
  }
  for (int i = optind; i < argc; i++)
    if (!(*parser)(argv[i]))
      failed = 1;
  write_hash_table();
  if (fclose(indxfp) < 0)
    fatal("error closing temporary index file: %1", strerror(errno));
  char *index_file = new char[strlen(base_name) + sizeof(INDEX_SUFFIX)];    
  strcpy(index_file, base_name);
  strcat(index_file, INDEX_SUFFIX);
#ifdef HAVE_RENAME
#ifdef __EMX__
  if (access(index_file, R_OK) == 0)
    unlink(index_file);
#endif /* __EMX__ */
  if (rename(temp_index_file, index_file) < 0) {
#ifdef __MSDOS__
    // RENAME could fail on plain MSDOS filesystems because
    // INDEX_FILE is an invalid filename, e.g. it has multiple dots.
    char *fname = p ? index_file + (p - base_name) : 0;
    char *dot = 0;

    // Replace the dot with an underscore and try again.
    if (fname
        && (dot = strchr(fname, '.')) != 0
        && strcmp(dot, INDEX_SUFFIX) != 0)
      *dot = '_';
    if (rename(temp_index_file, index_file) < 0)
#endif
    fatal("can't rename temporary index file: %1", strerror(errno));
  }
#else /* not HAVE_RENAME */
  ignore_fatal_signals();
  if (unlink(index_file) < 0) {
    if (errno != ENOENT)
      fatal("can't unlink `%1': %2", index_file, strerror(errno));
  }
  if (link(temp_index_file, index_file) < 0)
    fatal("can't link temporary index file: %1", strerror(errno));
  if (unlink(temp_index_file) < 0)
    fatal("can't unlink temporary index file: %1", strerror(errno));
#endif /* not HAVE_RENAME */
  temp_index_file = 0;
  return failed;
}
Exemple #2
0
// packing: use RCO_DATA_COMPRESSION_* constants
uint8_t
write_rco (rRCOFile * rco, char *fn, writerco_options opts)
{
  uint32_t i;
  rRCOFile_writehelper rcoH;

  // delete file if exists
  if (file_exists (fn)) {
    if (remove (fn)) {
      error ("Unable to write to file %s", fn);
      return FALSE;
    }
  }

  rcoH.rco = rco;
  rcoH.fp = fopen (fn, "wb");
  if (!rcoH.fp) {
    error ("Unable to open file %s", fn);
    return FALSE;
  }

  PRFHeader header;

  header.signature = RCO_SIGNATURE;
  header.version =
      (opts.packHeader == RCO_DATA_COMPRESSION_RLZ ? 0x95 : opts.packHeader ==
      RCO_DATA_COMPRESSION_ZLIB ? 0x90 : 0x71);
  if (rco->verId) {		// we won't actually use specified value,
    // rather, we'll require using the minimum
    // version from above
    if (rco->verId > header.version)
      header.version = rco->verId;
  }
  header.null = 0;
  header.compression = (opts.packHeader << 4) | (rco->umdFlag & 0xF);

  header.pMainTable = 0xA4;	// pretty much always the case
  // set other sections to nothing for now
  header.pVSMXTable = header.pTextTable = header.pSoundTable =
      header.pModelTable = header.pImgTable = header.pObjTable =
      header.pAnimTable = RCO_NULL_PTR;
  header.pUnknown = header.pFontTable = RCO_NULL_PTR;

  // don't know positions of text/label/event data too, but we do know the
  // lengths for label/events
  // header.pTextData = header.pLabelData = header.pEventData = 0;
  header.lLabelData = rco->labelsLen;
  header.lEventData = rco->eventsLen;
  header.lTextData = 0;

  // set pointer sections to blank too
  header.pTextPtrs = header.pImgPtrs = header.pModelPtrs = header.pSoundPtrs =
      header.pObjPtrs = header.pAnimPtrs = RCO_NULL_PTR;

  header.lTextPtrs = header.lImgPtrs = header.lModelPtrs = header.lSoundPtrs =
      header.lObjPtrs = header.lAnimPtrs = 0;

  // also blank...
  header.pImgData = header.pSoundData = header.pModelData = RCO_NULL_PTR;
  header.lImgData = header.lSoundData = header.lModelData = 0;

  header.unknown[0] = header.unknown[1] = header.unknown[2] = 0xFFFFFFFF;

  // write resources to a separate file to get around the issue of unknown
  // packed size when writing the header (and you can't change it backed after
  // the header is packed...)
  FILE *fTmp = NULL;

  if ((rco->tblImage && rco->tblImage->numSubentries)
      || (rco->tblSound && rco->tblSound->numSubentries)
      || (rco->tblModel && rco->tblModel->numSubentries)) {
    uint32_t totalPackedLen = 0;
    rRCOEntry *rcoNode;

    fTmp = tmpfile ();

    if (rco->tblImage && rco->tblImage->numSubentries) {
      for (rcoNode = rco->tblImage->firstChild; rcoNode;
	  rcoNode = rcoNode->next) {
	// our compression decision thing
	uint32_t c = ((rRCOImgModelEntry *) (rcoNode->extra))->compression;

	if (((rRCOImgModelEntry *) (rcoNode->extra))->format < RCO_IMG_BMP) {
	  if (opts.packImgCompr != -1)
	    c = opts.packImgCompr;
	} else {
	  if (opts.packImg != -1)
	    c = opts.packImg;
	}

	if (rcoNode->srcLenUnpacked) {
	  rcoNode->srcLen = rco_write_resource (fTmp, rcoNode, c, &opts, rco);
	  if (!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR)
	    warning ("[resource] Can't write image resource '%s'!",
		rco->labels + rcoNode->labelOffset);
	}
	rcoNode->srcCompression = c;
	rcoNode->srcAddr = totalPackedLen;

	totalPackedLen +=
	    (rcoNode->srcLen % 4 ? (rcoNode->srcLen / 4) * 4 +
	    4 : rcoNode->srcLen);
      }
      header.lImgData = totalPackedLen;
    }

    totalPackedLen = 0;
    if (rco->tblSound && rco->tblSound->numSubentries) {
      for (rcoNode = rco->tblSound->firstChild; rcoNode;
	  rcoNode = rcoNode->next) {
	if (rcoNode->srcLenUnpacked) {
	  uint32_t packedLen = rco_write_resource (fTmp, rcoNode,
	      RCO_DATA_COMPRESSION_NONE,
	      &opts,
	      rco);

	  if (!packedLen && rcoNode->labelOffset != RCO_NULL_PTR)
	    warning ("[resource] Can't write sound resource '%s'!",
		rco->labels + rcoNode->labelOffset);
	  totalPackedLen += ALIGN_TO_4 (packedLen);
	  // if(totalPackedLen %4) totalPackedLen += 4-(totalPackedLen%4);
	}
      }
      header.lSoundData = totalPackedLen;
    }

    totalPackedLen = 0;
    if (rco->tblModel && rco->tblModel->numSubentries) {
      for (rcoNode = rco->tblModel->firstChild; rcoNode;
	  rcoNode = rcoNode->next) {
	uint32_t c = ((rRCOImgModelEntry *) (rcoNode->extra))->compression;

	if (opts.packModel != -1)
	  c = opts.packModel;

	if (rcoNode->srcLenUnpacked) {
	  rcoNode->srcLen = rco_write_resource (fTmp, rcoNode, c, &opts, rco);
	  if (!rcoNode->srcLen && rcoNode->labelOffset != RCO_NULL_PTR)
	    warning ("[resource] Can't write model resource '%s'!",
		rco->labels + rcoNode->labelOffset);
	}
	rcoNode->srcCompression = c;
	rcoNode->srcAddr = totalPackedLen;

	totalPackedLen +=
	    (rcoNode->srcLen % 4 ? (rcoNode->srcLen / 4) * 4 +
	    4 : rcoNode->srcLen);
      }
      header.lModelData = totalPackedLen;
    }

    rewind (fTmp);
  }

  filewrite (rcoH.fp, &header, sizeof (header));

  rcoH.tables = 0;

  // if compressing, write to memory
  if (opts.packHeader) {
    rcoH.tables = malloc (RCO_WRITE_MEM_BUFFER);
    rcoH.memPos = rcoH.tablesSize = 0;
    rcoH.tablesBuffered = RCO_WRITE_MEM_BUFFER;
    rcoH.memOffset = ftell (rcoH.fp);
  }

  rcoH.sizeImg = rcoH.sizeModel = rcoH.sizeSound = rcoH.sizeText = 0;
  rcoH.longestLangData = 0;

  write_entry (&rcoH, &(rco->tblMain), 0xA4	/* typically where the main
						 * table is written to */ , 0,
      TRUE);

  // fix up object/anim extra data
  {
    if (rco->tblObj)
      rco_write_fix_refs (rco->tblObj, &rcoH, rco, RCO_OBJ_EXTRA_LEN,
	  RCO_OBJ_EXTRA_LEN_NUM, TRUE);
    if (rco->tblAnim)
      rco_write_fix_refs (rco->tblAnim, &rcoH, rco, RCO_ANIM_EXTRA_LEN,
	  RCO_ANIM_EXTRA_LEN_NUM, FALSE);
  }

  {				// write hashtable data

    /* { // special case for text hashes if(rco->numPtrText) { header.pTextPtrs
     * = rcowrite_ftell(&rcoH); for(i=0; i<rco->numPtrText; i++) { uint32_t
     * writePtr = 0; if(rco->ptrText[i].textEntry && rco->ptrText[i].index)
     * writePtr = rco->ptrText[i].textEntry->offset + sizeof(RCOEntry) +
     * sizeof(RCOTextEntry) + (rco->ptrText[i].index -
     * ((rRCOTextEntry*)(rco->ptrText[i].textEntry->extra))->indexes)*sizeof(RCOTextIndex);
     * rco_fwrite(&rcoH, &writePtr, sizeof(uint32_t)); } } } */
    if (rco->tblText) {
      header.pTextPtrs = rcowrite_ftell (&rcoH);
      header.lTextPtrs = 0;

      // generate sorted list of text entries, sorted by languageID
      rRCOEntry **sList = make_sorted_list_of_subentries (rco->tblText,
	  text_hash_table_qsort);

      for (i = 0; i < rco->tblText->numSubentries; i++)
	header.lTextPtrs += write_text_hash_table (&rcoH, sList[i], rco);
      free (sList);

      header.lTextPtrs *= sizeof (uint32_t);
    }

    if (rco->tblImage) {
      header.pImgPtrs = rcowrite_ftell (&rcoH);
      header.lImgPtrs =
	  write_hash_table (&rcoH, rco->tblImage, rco) * sizeof (uint32_t);
    }
    if (rco->tblModel) {
      header.pModelPtrs = rcowrite_ftell (&rcoH);
      header.lModelPtrs =
	  write_hash_table (&rcoH, rco->tblModel, rco) * sizeof (uint32_t);
    }
    if (rco->tblSound) {
      header.pSoundPtrs = rcowrite_ftell (&rcoH);
      header.lSoundPtrs =
	  write_hash_table (&rcoH, rco->tblSound, rco) * sizeof (uint32_t);
    }
    if (rco->tblObj) {
      header.pObjPtrs = rcowrite_ftell (&rcoH);
      header.lObjPtrs =
	  write_hash_table (&rcoH, rco->tblObj, rco) * sizeof (uint32_t);
    }
    if (rco->tblAnim) {
      header.pAnimPtrs = rcowrite_ftell (&rcoH);
      header.lAnimPtrs =
	  write_hash_table (&rcoH, rco->tblAnim, rco) * sizeof (uint32_t);
    }
    /*
     * #define RCO_WRITERCO_WRITE_PTR_SECT(pd, pl, hp) { \ if(pl) { \ hp =
     * rcowrite_ftell(&rcoH); \ for(i=0; i<pl; i++) { \ if(pd[i]) \
     * rco_fwrite(&rcoH, &(((rRCOEntry*)(pd[i]))->offset), sizeof(uint32_t)); \
     * else { \ uint32_t zero = 0; \ rco_fwrite(&rcoH, &zero, sizeof(uint32_t)); \
     * } \ } \ } \ } //RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrText,
     * rco->numPtrText, header.pTextPtrs);
     * RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrImg, rco->numPtrImg,
     * header.pImgPtrs); RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrModel,
     * rco->numPtrModel, header.pModelPtrs);
     * RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrSound, rco->numPtrSound,
     * header.pSoundPtrs); RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrObj,
     * rco->numPtrObj, header.pObjPtrs);
     * RCO_WRITERCO_WRITE_PTR_SECT(rco->ptrAnim, rco->numPtrAnim,
     * header.pAnimPtrs); */
  }

  {				// write label/event data (and text if
    // applicable)

    // write text (note, old behaviour - newer RCOs have text written in a
    // different location)
    if (!opts.packText && rco->tblText && rco->tblText->numSubentries) {
      rRCOEntry *rcoNode;

      header.pTextData = rcowrite_ftell (&rcoH);
      header.lTextData = rcoH.sizeText;
      for (rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) {
	rco_write_text_resource (&rcoH, rcoNode, RCO_DATA_COMPRESSION_NONE,
	    &opts, ((rRCOTextEntry *) (rcoNode->extra))->lang,
	    (rco->tblText->lastChild == rcoNode));
      }
    }
    // write label+event data
    header.pLabelData = rcowrite_ftell (&rcoH);
    if (rco->labelsLen)
      rco_fwrite (&rcoH, rco->labels, rco->labelsLen);
    header.pEventData = rcowrite_ftell (&rcoH);
    if (rco->eventsLen)
      rco_fwrite (&rcoH, rco->events, rco->eventsLen);
    else if (rco->tblObj || rco->tblAnim) {	// weird case: if there's
      // object entries, there will
      // be 4 bytes for events; I'll
      // assume this covers anim as
      // well (although there isn't
      // an RCO with anim that
      // doesn't have objects)
      uint32_t zero = 0;

      rco_fwrite (&rcoH, &zero, sizeof (zero));
      header.lEventData = sizeof (zero);
    }
    // the text pointer is weird in that if there's no text, it's set equal to
    // the label pointer; even weirder, some RCOs have a null pointer (for
    // FW5.00 all except lftv_* RCOs have null pointers for pTextData if
    // there's no text)
    // my theory: if compressing, it will be RCO_NULL_PTR, otherwise it'll =
    // header.pLabelData
    // if(!header.lTextData) header.pTextData = RCO_NULL_PTR;
    // if(!header.lTextData) header.pTextData = header.pLabelData;
    if (!header.lTextData)
      header.pTextData = (opts.packHeader ? RCO_NULL_PTR : header.pLabelData);
  }

  // flush compression stuff here
  HeaderComprInfo ci;

  if (opts.packHeader) {
    uint8_t *bufferOut = NULL;

    ci.lenLongestText = rcoH.longestLangData;
    ci.lenUnpacked = rcoH.tablesSize;
    ci.lenPacked = 0;

    if (opts.packHeader == RCO_DATA_COMPRESSION_ZLIB) {
      uint32_t bound = compressBound (rcoH.tablesSize);

      bufferOut = (uint8_t *) malloc (bound);
      ci.lenPacked =
	  zlib_compress (rcoH.tables, rcoH.tablesSize, bufferOut, bound,
	  opts.zlibLevel, opts.zlibMethod);
    } else if (opts.packHeader == RCO_DATA_COMPRESSION_RLZ) {
      bufferOut = (uint8_t *) malloc (rcoH.tablesSize);
      ci.lenPacked =
	  rlz_compress (rcoH.tables, rcoH.tablesSize, bufferOut,
	  rcoH.tablesSize, opts.rlzMode);
    } else {
      error ("lulwut?");
      exit (1);
    }
    int comprMisalign = ci.lenPacked % 4;
    uint32_t packedLen = ci.lenPacked;

    if (rco->eSwap)
      es_headerComprInfo (&ci);
    filewrite (rcoH.fp, &ci, sizeof (ci));
    filewrite (rcoH.fp, bufferOut, packedLen);
    free (bufferOut);

    if (comprMisalign) {	// 4 byte align
      uint32_t zero = 0;

      filewrite (rcoH.fp, &zero, 4 - comprMisalign);
    }
  }
  // write text if packing header
  if (opts.packText && rco->tblText && rco->tblText->numSubentries) {
    rRCOEntry *rcoNode;

    // header.pTextData = rcowrite_ftell(&rcoH);
    header.pTextData = ftell (rcoH.fp);
    header.lTextData = 0;	// rcoH.sizeText;
    for (rcoNode = rco->tblText->firstChild; rcoNode; rcoNode = rcoNode->next) {
      header.lTextData +=
	  rco_write_text_resource (&rcoH, rcoNode, opts.packHeader, &opts,
	  ((rRCOTextEntry *) (rcoNode->extra))->lang,
	  (rco->tblText->lastChild == rcoNode));
    }
  }
  // write resources
  /* { uint32_t totalPackedLen = 0; if(rco->tblImage) { header.pImgData =
   * rcowrite_ftell(&rcoH); header.lImgData = rcoH.sizeImg; // TOxDO: this
   * model actually won't work - we have to update the offsets of ALL the
   * entries after packing... for(i=0; i<rco->tblImage->numSubentries; i++) {
   * uint32_t packedSize = rco_write_resource(&rcoH,
   * &(rco->tblImage->subentries[i]), RCO_DATA_COMPRESSION_NONE); // TOxDO:
   * change this // TOxDO: update packed size value uint32_t curFpos =
   * rcowrite_ftell(rcoH.fp); totalPackedLen += (packedSize % 4 ?
   * (packedSize/4)*4+4 : packedSize); } header.lImgData = totalPackedLen; }
   * totalPackedLen = 0; if(rco->tblSound) { header.pSoundData =
   * rcowrite_ftell(&rcoH); header.lSoundData = rcoH.sizeSound; for(i=0;
   * i<rco->tblSound->numSubentries; i++) { totalPackedLen +=
   * rco_write_resource(&rcoH, &(rco->tblSound->subentries[i]),
   * RCO_DATA_COMPRESSION_NONE); if(totalPackedLen %4) totalPackedLen +=
   * 4-(totalPackedLen%4); } header.lSoundData = totalPackedLen; } // TOxDO:
   * write model resources } */

  if ((rco->tblImage && rco->tblImage->numSubentries)
      || (rco->tblSound && rco->tblSound->numSubentries)
      || (rco->tblModel && rco->tblModel->numSubentries)) {
    // update data pointers
    uint32_t pos = ftell (rcoH.fp);

    if (rco->tblImage && rco->tblImage->numSubentries) {
      header.pImgData = pos;
      pos += header.lImgData;
    }
    if (rco->tblSound && rco->tblSound->numSubentries) {
      header.pSoundData = pos;
      pos += header.lSoundData;
    }
    if (rco->tblModel && rco->tblModel->numSubentries) {
      header.pModelData = pos;
      pos += header.lModelData;
    }
    // copy contents of fTmp across (uses a simple buffered copy)
    uint32_t len = header.lImgData + header.lSoundData + header.lModelData;
    uint8_t buffer[65536];

    while (len) {
      uint32_t readAmt = (len > 65536 ? 65536 : len);

      fileread (fTmp, buffer, readAmt);
      filewrite (rcoH.fp, buffer, readAmt);
      len -= readAmt;
    }

    fclose (fTmp);		// this deletes our temp file
  }
  // fix header
  if (rco->tblVSMX)
    header.pVSMXTable = rco->tblVSMX->offset;
  if (rco->tblText)
    header.pTextTable = rco->tblText->offset;
  if (rco->tblSound)
    header.pSoundTable = rco->tblSound->offset;
  if (rco->tblModel)
    header.pModelTable = rco->tblModel->offset;
  if (rco->tblImage)
    header.pImgTable = rco->tblImage->offset;
  if (rco->tblFont)
    header.pFontTable = rco->tblFont->offset;
  if (rco->tblObj)
    header.pObjTable = rco->tblObj->offset;
  if (rco->tblAnim)
    header.pAnimTable = rco->tblAnim->offset;

  rewind (rcoH.fp);
  if (rco->eSwap)
    es_rcoHeader (&header);
  filewrite (rcoH.fp, &header, sizeof (header));

  // TODO: fix resource pointers?
  // TODO: tie things up etc??

  fclose (rcoH.fp);

  return TRUE;
}