/***************************************************************************
 *   Copyright (C) 2006 Nicolas Hadacek <hadacek@kde.org>                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/
#include "coff_archive.h"

//----------------------------------------------------------------------------
Coff::Member::Member(const TQByteArray &data, uint &offset, Log::Base &log)
{
  // parse header
  TQString s;
  if ( !getString(data, offset, 256, log, s) ) return;
  int i = s.find('/');
  if ( i==-1 ) {
    log.log(Log::LineType::Error, i18n("Member name not terminated by '/' (\"%1\").").arg(s));
    return;
  }
  _name = s.mid(0, i);
  if ( !getString(data, offset, 12, log, s) ) return; // mtime
  if ( !getString(data, offset, 10, log, s) ) return;
  i = s.find('l');
  if ( i==-1 ) {
    log.log(Log::LineType::Error, i18n("File size not terminated by 'l' (\"%1\").").arg(s));
    return;
  }
  bool ok;
  _nbBytes = s.mid(0, i).toUInt(&ok);
  if ( !ok ) {
    log.log(Log::LineType::Error, i18n("Wrong format for file size \"%1\".").arg(s));
    return;
  }
  TQ_UINT32 v;
  if ( !getULong(data, offset, 2, log, v) ) return;
  log.log(Log::DebugLevel::Extra, i18n("Magic number: %1").arg(toHexLabel(v, 4)));
//  if ( v!=0x600A ) {
//    log.log(Log::LineType::Error, i18n("Wrong magic for Microchip archive (\"%1\").").arg(toHexLabel(v, 4)));
//    return;
//  }
  offset += _nbBytes;
}

//----------------------------------------------------------------------------
Coff::Archive::Archive(const PURL::Url &url)
  : Base(url)
{}

bool Coff::Archive::parse(Log::Base &log)
{
  TQByteArray data;
  uint offset = 0, symbolEnd = 0;
  Member *symbol = 0;
  if ( !initParse(CoffType::Archive, data, offset, log) ) return false;
  for (;;) {
    if ( offset==uint(data.count()) ) break; // end of archive
    uint start = offset;
    Member *member = new Member(data, offset, log);
    if ( log.hasError() ) return false;
    if ( member->name().isEmpty() ) {
      symbolEnd = offset;
      symbol = member;
    } else {
      _members[member->name()] = member;
      _offsets[start] = member;
    }
  }
  if (symbol) {
    if ( !readSymbols(data, symbolEnd - symbol->nbBytes(), log) ) return false;
    delete symbol;
  }
  return true;
}

Coff::Archive::~Archive()
{
  TQMap<TQString, Member *>::const_iterator it;
  for (it=_members.begin(); it!=_members.end(); ++it) delete it.data();
}

bool Coff::Archive::readSymbols(const TQByteArray &data, uint offset, Log::Base &log)
{
  TQ_UINT32 nb;
  if ( !getULong(data, offset, 4, log, nb) ) return false;
  TQValueVector<Member *> members(nb);
  for (uint i=0; i<nb; i++) {
    TQ_UINT32 start;
    if ( !getULong(data, offset, 4, log, start) ) return false;
    if ( !_offsets.contains(start) ) {
      log.log(Log::LineType::Error, i18n("Unknown file member offset: %1").arg(toHexLabel(start, 8)));
      return false;
    }
    members[i] = _offsets[start];
  }
  for (uint i=0; i<nb; i++) {
    TQString name(data.data() + offset);
    offset += name.length() + 1;
    _symbols[name] = members[i];
  }
  return true;
}

Log::KeyList Coff::Archive::information() const
{
  Log::KeyList keys(i18n("Information:"));
  keys.append(i18n("No. of file members:"), TQString::number(members().count()));
  keys.append(i18n("No. of symbols:"), TQString::number(symbols().count()));
  return keys;
}

Log::KeyList Coff::Archive::membersInformation() const
{
  Log::KeyList keys(i18n("File Members:"));
  TQMap<TQString, Member *>::const_iterator it;
  for (it=members().begin(); it!=members().end(); ++it)
    keys.append(it.key(), i18n("size: %1 bytes").arg(it.data()->nbBytes()));
  return keys;
}

Log::KeyList Coff::Archive::symbolsInformation() const
{
  Log::KeyList keys(i18n("Symbols:"));
  TQMap<TQString, Member *>::const_iterator it;
  for (it=symbols().begin(); it!=symbols().end(); ++it)
    keys.append(it.key(), it.data()->name());
  return keys;
}
