/*ProfiPacket - packet radio terminal program
  Copyright (C) 1999  Alexander Feigl

  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.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Author:

  Alexander Feigl
  Burachstraße 51

  D-88250 Weingarten

  Mail : Alexander.Feigl@gmx.de
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <strings.h>
#include <stdlib.h>
#include <stdio.h>

FILE *infile;
FILE *outfile;

unsigned long read_amilong(void)
 {
  unsigned char buffer[4];
  unsigned long machlong;
  if (fread(buffer,1,4,infile)!=4)
   {
    fprintf(stderr,"Fatal error!\n");
    exit(999);
   }
  
  machlong=buffer[3]+(buffer[2]<<8)+(buffer[1]<<16)+(buffer[0]<<24);
  return(machlong);
 }

void write_amilong(unsigned long machlong)
 {
  unsigned char buffer[4];
  
  buffer[3]=machlong&0xff;
  buffer[2]=(machlong>>8)&0xff;
  buffer[1]=(machlong>>16)&0xff;
  buffer[0]=(machlong>>24)&0xff;
  if (fwrite(buffer,1,4,outfile)!=4)
   {
    fprintf(stderr,"Fatal error!\n");
    exit(999);
   }
 }

void copy_bytes(unsigned long length)
 {
  unsigned long data;
  while (length>0)
   {
    data=read_amilong();
    write_amilong(data);
    --length;
   }
  
 }


void skip_hunk_data(void)
 {
  unsigned long length;
  length=read_amilong();
  write_amilong(length);
  copy_bytes(length);
 }


unsigned long hunk_type;

int main(int argc,char **argv)
 {
  if (argc!=3)
   {
    fprintf(stderr,"Bad arguments!\n");
    exit(999);
   }
  
  infile=fopen(argv[1],"rb");
  if (infile==NULL)
   {
    fprintf(stderr,"Cannot open input!\n");
    exit(999);
   }

  outfile=fopen(argv[2],"wb");
  if (outfile==NULL)
   {
    fprintf(stderr,"Cannot open output!\n");
    exit(999);
   }
  
  while (!feof(infile))
   {
    
    hunk_type=read_amilong();
    write_amilong(hunk_type);
    switch (hunk_type&0xffffff)
     {
      case 0x3e7: /*name*/
      case 0x3e8: /*name*/
      case 0x3e9: /*code*/
      case 0x3ea: /*data*/
       skip_hunk_data();
       break;
      case 0x3ec: /*reloc*/
       {
        unsigned long len,hunk;
        while (1)
         {
          len=read_amilong();
          write_amilong(len);
          if (len==0) break;
          hunk=read_amilong();
          write_amilong(hunk);
          copy_bytes(len);
         }
        break;
       }
      case 0x3ef: /*external references */
       {
        unsigned long lentype,type,len;
        while (1)
         {
          lentype=read_amilong();
          if (lentype==0)
           {
            write_amilong(lentype);
            break;
           }
           
          type=lentype>>24;
          len=lentype&0xffffff;
          switch (type)
           {
            case 1:   /* export references */
              write_amilong(lentype);
              copy_bytes(len);
              copy_bytes(1);
              break;
            case 2:   /* export BASEREL references */
              write_amilong(lentype);
              copy_bytes(len);
              copy_bytes(1);
              break;
            case 129: /* external references */
              write_amilong(lentype);
              copy_bytes(len);
              skip_hunk_data();
              break;
            case 131: /* base rel ? must be FIXED for binutils */
              lentype&=0xffffff;
              lentype|=0x86000000;
              write_amilong(lentype);
              copy_bytes(len);
              skip_hunk_data();
              break;
            case 134: /* base rel ? FIXED */
              write_amilong(lentype);
              copy_bytes(len);
              skip_hunk_data();
              break;
              
            default:
              printf("Unknown symbol ref type : %ld\n",type);
              exit(999);
           }
         }
        break;
       }
 
      case 0x3f0:
       {
        unsigned long lentype,type,len;
        while (1)
         {
          lentype=read_amilong();
          if (lentype==0)
           {
            write_amilong(lentype);
            break;
           }
           
          type=lentype>>24;
          len=lentype&0xffffff;
          switch (type)
           {
            case 0:
              write_amilong(lentype);
              copy_bytes(len);
              copy_bytes(1);
              break;
            default:
              printf("Unknown symbol type : %ld\n",type);
              exit(999);
           }
         }
        break;
       
       }
   
      case 0x3f2:
        break;  
      default:
        fprintf(stderr,"Unknown HUNKTYPE : %08lx\n",hunk_type);
        exit(999);
     }
    if (hunk_type==0x3f2) break;
   }
  fclose(infile);
  fclose(outfile);
  return(0);
 }

