/*
 * dissect an aof file into areas
 */

/*----- Licensing note ----------------------------------------------------*
 *
 * This file is part of Straylight's Dynamic Linking System (SDLS)
 *
 * SDLS 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, or (at your option)
 * any later version.
 *
 * SDLS 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 SDLS.  If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "aof/chunk.h"

#define aof_RELOC (0xC5E2D080ul)	/* Relocatable object type	*/

typedef struct
{
  unsigned long type;			/* Type of object file		*/
  int version;				/* Version number of the format	*/
  int areas;				/* Number of AREAs defined	*/
  int symbols;				/* Number of symbols in table	*/
  int entryArea;			/* Index of AREA with ENTRY att	*/
  int entryOff;				/* Offset in AREA for ENTRY	*/
}
aof_fixedHeader;

typedef struct
{
  int name;				/* AREA name (OBJ_STRT offset)	*/

  int alignment			:8;	/* AREA alignment (must be 2)	*/

  int 				:1;	/* Reserved bit			*/
  int code			:1;	/* AREA contains code		*/
  int common			:1;	/* Common AREA definition	*/
  int commonRef			:1;	/* Reference to common AREA	*/
  int zinit			:1;	/* AREA is zero-initialised	*/
  int readonly			:1;	/* AREA is (sort-of) readonly	*/
  int				:1;	/* Reserved bit			*/
  int debug			:1;	/* AREA contains debug tables	*/
  int				:16;	/* Reserved shortword		*/

  int size;				/* Size of this AREA		*/
  int relocs;				/* Number of relocations	*/
  int				:32;	/* Reserved word		*/
}
aof_areaEntry;

typedef struct
{
  aof_fixedHeader hdr;			/* The fixed header info	*/
  aof_areaEntry table[1];		/* AREA table (unsized array)	*/
}
aof_header;

typedef struct
{
  int name;				/* Name string table entry	*/

  int defined			:1;	/* Symbol defined in file	*/
  int export			:1;	/* Symbol is exported globally	*/
  int absolute			:1;	/* Symbol not relative to AREA	*/
  int ignoreCase		:1;	/* Symbol is not case sensitive	*/
  int weak			:1;	/* Symbol is weak external ref	*/
  int strong			:1;	/* Symbol is strong global	*/
  int common			:1;	/* Symbol is in a common AREA	*/
  int				:32-7;	/* Pad out to integer boundary	*/

  int value;				/* Value of the symbol		*/
  int area;				/* Offset of AREA name		*/
}
aof_symbol;

typedef struct
{
  int offset;				/* Offset of word to relocate	*/
  union
  {
    struct
    {
      int symbol		:16;	/* Symbol to relocate by/to	*/
      int field			:2;	/* Field size to alter		*/
      int type			:1;	/* Relocation type		*/
      int symreloc		:1;	/* Relocation is symbol-relative*/
      int			:12;	/* Reserved bits		*/
    }
    type_1;				/* Type 1 relocation directive	*/

    struct
    {
      int symbol		:24;	/* Symbol to relocate by/to	*/
      int field			:2;	/* Field size to alter		*/
      int type			:1;	/* Relocation type		*/
      int symreloc		:1;	/* Relocation is symbol-relative*/
      int			:3;	/* Reserved bits		*/
      int set_me		:1;	/* Set this bit for type 2	*/
    }
    type_2;				/* Type 2 relocation directive	*/
  }
  t;
}
aof_relocstr;

enum
{
  aof_BYTE,
  aof_HALFWORD,
  aof_FULLWORD
};

enum
{
  aof_ADDITIVE,
  aof_PCRELATIVE
};

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>

static char *nicely(char *name)
{
  char *p=name;
  while (*name)
  {
    if (!isalnum(*name))
      *name='_';
    name++;
  }
  if (strlen(p)>10)
    p[10]=0;
  return (p);
}

static int oscli(char *cmd,...)
{
  char buffer[256];
  va_list ap;
  va_start(ap,cmd);
  vsprintf(buffer,cmd,ap);
  va_end(ap);
  return (system(buffer));
}

static int findChunk(chunk_header *c,char *name)
{
  int i;
  for (i=0;i<c->hdr.maxChunks;i++)
  {
    if (c->table[i].offset && !memcmp(c->table[i].chunkName,name,8))
      return (i);
  }
  return (-1);
}

static void get(void *p,FILE *fp,size_t size,size_t offset)
{
  fpos_t off;
  off.__lo=offset;
  fsetpos(fp,&off);
  fread(p,size,1,fp);
}

static int dissect(char *aof,char *dir)
{
  FILE *fp;
  FILE *out;
  chunk_fixedHeader ch={0,0,0};
  chunk_header *chf;
  aof_header *header;
  char *area;
  char *string;
  int h;
  int a;
  int s;
  int bit=0;
  char name[256];
  int i;

  oscli("cdir %s",dir);
  fp=fopen(aof,"rb");
  if (!fp)
  {
    fprintf(stderr,"couldn't open '%s'\n",aof);
    return (1);
  }
  fread(&ch,sizeof(ch),1,fp);
  if (ch.id!=chunk_MAGIC)
  {
    fprintf(stderr,"bad aof '%s'\n",aof);
    return (1);
  }
  chf=malloc(sizeof(chunk_fixedHeader)+
             ch.maxChunks*sizeof(chunk_tableEntry));
  if (!chf)
  {
    fprintf(stderr,"no memory\n");
    exit(1);
  }
  chf->hdr=ch;
  fread(chf->table,sizeof(chunk_tableEntry),ch.maxChunks,fp);

  h=findChunk(chf,"OBJ_HEAD");
  a=findChunk(chf,"OBJ_AREA");
  s=findChunk(chf,"OBJ_STRT");
  if (h==-1 || a==-1 || s==-1)
  {
    fprintf(stderr,"bad aof '%s'\n",aof);
    return (1);
  }

  header=malloc(chf->table[h].size);
  area=malloc(chf->table[a].size);
  string=malloc(chf->table[s].size);
  if (!header || !area || !string)
  {
    fprintf(stderr,"no memory\n");
    exit(1);
  }

  get(header,fp,chf->table[h].size,chf->table[h].offset);
  get(area,fp,chf->table[a].size,chf->table[a].offset);
  get(string,fp,chf->table[s].size,chf->table[s].offset);

  for (i=0;i<header->hdr.areas;i++)
  {
    if (header->table[i].zinit)
      continue;
    sprintf(name,"%s.%s",dir,nicely(string+header->table[i].name));
    out=fopen(name,"wb");
    if (!out)
    {
      fprintf(stderr,"couldn't write '%s'\n",name);
      continue;
    }
    fwrite(area+bit,1,header->table[i].size,out);
    bit+=header->table[i].size+header->table[i].relocs*8;
    fclose(out);
  }
  return (0);
}

int main(int argc,char *argv[])
{
  if (argc!=3)
  {
    fprintf(stderr,"dissect <aof> <dir>\n");
    exit(1);
  }
  return (dissect(argv[1],argv[2]));
}
