/* 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 #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STDIO_H #include #endif #ifdef HAVE_STDINT_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_INTTYPES_H #include #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 };