Admin Panel
/* simple-object-amigahunk.c -- routines to manipulate Amiga hunk object files.
Copyright 2010 Free Software Foundation, Inc.
Written by Stefan "Bebbo" Franke @ Bebbosoft GbR.
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, 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, 51 Franklin Street - Fifth Floor,
Boston, MA 02110-1301, USA. */
#include "config.h"
#include "libiberty.h"
#include "simple-object.h"
#include <errno.h>
#include <stddef.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#include "simple-object-common.h"
/**
* all info needed to find hunks aka sections.
*/
struct hunk
{
struct hunk * next;
char const * name;
off_t offset;
unsigned length;
};
/**
* Used during initial file read.
* Track the hunks plus the name.
*/
struct simple_object_amigahunk_read
{
struct hunk * root;
char const * name;
int deleted;
};
/**
* A copy of simple_object_amigahunk_read atm.
* /
struct simple_object_amigahunk_attributes
{
struct hunk * root;
char const * name;
};
*/
#define simple_object_amigahunk_attributes simple_object_amigahunk_read
/**
* Read 4 bytes as big endian int and move the file offset.
*/
static unsigned
read4 (int descriptor, off_t * offset)
{
static unsigned char b[4];
const char *errmsg;
int err;
if (!simple_object_internal_read (descriptor, *offset, b, 4, &errmsg, &err))
return 0xffffffff;
*offset += 4;
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
}
// not reentrant atm.
static unsigned nameLen; // size of current name
static unsigned char * name; // some name
/**
* Read a name into the shared buffer.
*/
static int
readName (unsigned l, int descriptor, off_t * offset)
{
const char *errmsg;
int err;
l <<= 2;
if (nameLen < l)
{
XDELETE(name);
name = XNEWVEC(unsigned char, l + 1);
nameLen = l;
}
if (!l)
{
if (!name)
name = XNEWVEC(unsigned char, 1);
*name = 0;
return 1;
}
int r = simple_object_internal_read (descriptor, *offset, name, l, &errmsg,
&err);
name[l] = 0;
if (r)
*offset += l;
return r;
}
/* See if we have an Amiga file.
* Also remember all hunks.
*/
static void *
simple_object_amigahunk_match (
unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN], int descriptor,
off_t offset, const char *segment_name ATTRIBUTE_UNUSED,
const char **errmsg, int *err)
{
if (header[0] != 0 || header[1] != 0 || header[2] != 3 || header[3] != 0xe7)
return NULL;
// fprintf (stderr, "simple_object_amigahunk_match\n");
struct simple_object_amigahunk_read * oar = XNEW(
struct simple_object_amigahunk_read);
oar->root = NULL;
oar->name = NULL;
oar->deleted = 0;
for (;;)
{
unsigned hid = read4 (descriptor, &offset);
if (hid == 0xffffffff)
break;
if (hid == 0x3f2)
continue;
unsigned sz = read4 (descriptor, &offset);
switch (hid & 0x3fffffff)
{
case 0x3f3: // HEADER
if (sz)
{
readName (sz, descriptor, &offset);
}
// first last + sizes
unsigned nsecs = read4 (descriptor, &offset);
unsigned first = read4 (descriptor, &offset);
unsigned last = read4 (descriptor, &offset);
unsigned i;
for (i = 0; i < nsecs; ++i)
{
sz = read4 (descriptor, &offset);
}
break;
case 0x3e8: // NAME
readName (sz, descriptor, &offset);
struct hunk * h = XNEW(struct hunk);
h->next = oar->root;
h->name = xstrdup ((char const*) name);
offset += 4;
h->length = read4 (descriptor, &offset) * 4;
h->offset = offset;
offset -= 8;
oar->root = h;
break;
case 0x3e7: // UNIT
{
readName (sz, descriptor, &offset);
oar->name = xstrdup ((char const *) name);
break;
}
case 0x3e9: // CODE
case 0x3ea: // DATA
case 0x3f1: // DEBUG
// skip sz long words.
offset += sz * 4;
break;
case 0x3eb: // BSS
// no more data
break;
case 0x3ec: // RELOC32
case 0x3ed: // RELOC16
case 0x3ee: // RELOC8
case 0x3f7: // DRELOC32
case 0x3f8: // DRELOC16
case 0x3f9: // DRELOC8
case 0x3f0: // SYMBOL
case 0x3fe: // ABSRELOC16
while (sz)
{
unsigned hn = read4 (descriptor, &offset);
offset += sz * 4;
sz = read4 (descriptor, &offset);
}
break;
case 0x3fc: // RELOC32SHORT
while (sz)
{
unsigned hn = read4 (descriptor, &offset);
offset += sz * 2;
sz = read4 (descriptor, &offset);
}
break;
case 0x3ef: // EXT
while (sz & 0xff000000)
{
unsigned l = sz & 0xffffff;
unsigned b = sz >> 24;
readName (l, descriptor, &offset);
switch (b)
{
case 0: // ext_symb
case 1: // ext_def
case 2: // ext_abs
case 3: // ext_res
{
offset += 4;
}
break;
case 130: // ext_common EXT_ABSCOMMON
case 137: // EXT_RELCOMMON
case 208: // EXT_DEXT32COMMON
case 209: // EXT_DEXT16COMMON
case 210: // EXT_DEXT8COMMON
{
offset += 4;
unsigned blocksize = read4 (descriptor, &offset);
offset += blocksize * 4;
}
break;
case 129: // ext_ref32
case 131: // ext_ref16
case 132: // ext_ref8
case 133: // ext_dref32
case 134: // ext_dref16
case 135: // ext_dref8
case 136: // EXT_RELREF32
case 138: // EXT_ABSREF16
case 139: // EXT_ABSREF8
{
unsigned n = read4 (descriptor, &offset);
offset += n * 4;
}
break;
default:
break;
}
sz = read4 (descriptor, &offset);
}
break;
}
}
return (void *) oar;
}
/* Find all sections in an Amiga file.
* invoke the callback pfn if section name matches.
*/
static const char *
simple_object_amigahunk_find_sections (simple_object_read *sobj, int
(*pfn) (void *, const char *, off_t offset, off_t length),
void *data, int *err)
{
struct simple_object_amigahunk_read *eor =
(struct simple_object_amigahunk_read *) sobj->data;
struct hunk * h = eor->root;
// fprintf (stderr, "simple_object_amigahunk_find_sections\n");
while (h)
{
if (!(*pfn) (data, h->name, h->offset, h->length))
{
// fprintf (stderr, "simple_object_amigahunk_find_sections: %s\n", h->name);
break;
}
h = h->next;
}
return NULL;
}
/* Fetch the attributes for an simple_object_read. */
static void *
simple_object_amigahunk_fetch_attributes (simple_object_read *sobj,
const char **errmsg ATTRIBUTE_UNUSED,
int *err ATTRIBUTE_UNUSED)
{
struct simple_object_amigahunk_read *eor =
(struct simple_object_amigahunk_read *) sobj->data;
struct simple_object_amigahunk_attributes *ret;
// fprintf (stderr, "simple_object_amigahunk_fetch_attributes\n");
ret = XNEW(struct simple_object_amigahunk_attributes);
*ret = *eor;
eor->deleted = 1;
return ret;
}
/* Release the privata data for an simple_object_read. */
static void
simple_object_amigahunk_release_read (void *data)
{
struct simple_object_amigahunk_read *eor =
(struct simple_object_amigahunk_read *) data;
if (!eor->deleted)
{
struct hunk * h = eor->root;
while (h)
{
struct hunk * n = h;
h = h->next;
XDELETE(n);
}
XDELETE(eor->name);
}
XDELETE(data);
}
/* Compare two attributes structures.
* Not yet implemented.
*/
static const char *
simple_object_amigahunk_attributes_merge (void *todata, void *fromdata,
int *err)
{
struct simple_object_amigahunk_attributes *to =
(struct simple_object_amigahunk_attributes *) todata;
struct simple_object_amigahunk_attributes *from =
(struct simple_object_amigahunk_attributes *) fromdata;
// fprintf (stderr, "simple_object_amigahunk_attributes_merge\n");
return NULL;
}
/* Release the private data for an attributes structure. */
static void
simple_object_amigahunk_release_attributes (void *data)
{
simple_object_amigahunk_release_read(data);
}
/* Prepare to write out a file. */
static void *
simple_object_amigahunk_start_write (void *attributes_data,
const char **errmsg ATTRIBUTE_UNUSED,
int *err ATTRIBUTE_UNUSED)
{
struct simple_object_amigahunk_attributes *attrs =
(struct simple_object_amigahunk_attributes *) attributes_data;
struct simple_object_amigahunk_attributes *ret;
/* We're just going to record the attributes, but we need to make a
copy because the user may delete them. */
ret = XNEW(struct simple_object_amigahunk_attributes);
// fprintf (stderr, "simple_object_amigahunk_start_write\n");
*ret = *attrs;
attrs->deleted = 1;
return ret;
}
/**
* Write a name in Amiga hunk style.
*/
static int
write_name (int descriptor, off_t * offset, char const * name,
const char **errmsg, int *err)
{
unsigned slen = strlen (name);
unsigned len = (slen + 3) >> 2;
unsigned char b[4];
b[3] = len;
b[2] = len >> 8;
b[1] = len >> 16;
b[0] = 0;
if (!simple_object_internal_write (descriptor, *offset, b, 4, errmsg, err))
return 0;
*offset += 4;
if (!simple_object_internal_write (descriptor, *offset,
(unsigned char const *) name, slen, errmsg,
err))
return 0;
*offset += slen;
slen = (len << 2) - slen;
if (slen)
{
b[1] = 0;
b[2] = 0;
if (!simple_object_internal_write (descriptor, *offset, b, slen, errmsg,
err))
return 0;
*offset += slen;
}
return 1;
}
/* Write out a complete Amiga file. */
static const char *
simple_object_amigahunk_write_to_file (simple_object_write *sobj,
int descriptor, int *err)
{
struct simple_object_amigahunk_attributes *attrs =
(struct simple_object_amigahunk_attributes *) sobj->data;
const char *errmsg;
simple_object_write_section *section;
unsigned int shnum;
off_t offset = 0;
static const unsigned char HUNK_UNIT[] =
{ 0, 0, 3, 0xe7 };
static const unsigned char HUNK_NAME[] =
{ 0, 0, 3, 0xe8 };
static const unsigned char HUNK_DEBUG[] =
{ 0, 0, 3, 0xf1 };
static const unsigned char HUNK_END[] =
{ 0, 0, 3, 0xf2 };
// fprintf (stderr, "simple_object_amigahunk_write_to_file\n");
// write header
if (!simple_object_internal_write (descriptor, offset, HUNK_UNIT, 4, &errmsg,
err))
return NULL;
offset += 4;
if (!write_name (descriptor, &offset, attrs->name, &errmsg, err))
return NULL;
shnum = 0;
for (section = sobj->sections; section != NULL; section = section->next)
{
// fprintf (stderr, "%d: %s\n", shnum, section->name);
if (!simple_object_internal_write (descriptor, offset, HUNK_NAME, 4,
&errmsg, err))
return NULL;
offset += 4;
if (!write_name (descriptor, &offset, section->name, &errmsg, err))
return NULL;
if (!simple_object_internal_write (descriptor, offset, HUNK_DEBUG, 4,
&errmsg, err))
return NULL;
offset += 4;
// collect data len
struct simple_object_write_section_buffer * sb;
size_t slen = 0;
for (sb = section->buffers; sb; sb = sb->next)
slen += sb->size;
unsigned len = (slen + 3) >> 2;
unsigned char b[4];
b[3] = len;
b[2] = len >> 8;
b[1] = len >> 16;
b[0] = 0;
if (!simple_object_internal_write (descriptor, offset, b, 4, &errmsg,
err))
return 0;
offset += 4;
// write data
for (sb = section->buffers; sb; sb = sb->next)
{
if (!simple_object_internal_write (descriptor, offset,
(unsigned char const *) sb->buffer,
sb->size, &errmsg, err))
return 0;
offset += sb->size;
}
// pad
slen = (len << 2) - slen;
if (slen)
{
b[1] = 0;
b[2] = 0;
if (!simple_object_internal_write (descriptor, offset, b, slen,
&errmsg, err))
return 0;
offset += slen;
}
if (!simple_object_internal_write (descriptor, offset, HUNK_END, 4, &errmsg,
err))
return NULL;
offset += 4;
++shnum;
}
if (shnum == 0)
return NULL;
return NULL;
}
/* Release the private data for an simple_object_write structure. */
static void
simple_object_amigahunk_release_write (void *data)
{
simple_object_amigahunk_release_read(data);
}
/* The Amiga functions. */
const struct simple_object_functions simple_object_amigahunk_functions =
{ simple_object_amigahunk_match, simple_object_amigahunk_find_sections,
simple_object_amigahunk_fetch_attributes,
simple_object_amigahunk_release_read,
simple_object_amigahunk_attributes_merge,
simple_object_amigahunk_release_attributes,
simple_object_amigahunk_start_write,
simple_object_amigahunk_write_to_file,
simple_object_amigahunk_release_write };