summaryrefslogtreecommitdiff
path: root/xpand.c
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers@digital-scurf.org>2012-10-06 15:55:53 +0100
committerDaniel Silverstone <dsilvers@digital-scurf.org>2012-10-06 15:55:53 +0100
commite962168ecfe389000d07ed20203e8aa558827e9f (patch)
tree932e5907f3c13ad102143550fb4c04c8b5af7cb1 /xpand.c
downloadsqueeze-e962168ecfe389000d07ed20203e8aa558827e9f.tar.gz
squeeze-e962168ecfe389000d07ed20203e8aa558827e9f.tar.bz2
Initial dump of Castle sources
Diffstat (limited to 'xpand.c')
-rw-r--r--xpand.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/xpand.c b/xpand.c
new file mode 100644
index 0000000..2b29acb
--- /dev/null
+++ b/xpand.c
@@ -0,0 +1,348 @@
+/* This source code in this file is licensed to You by Castle Technology
+ * Limited ("Castle") and its licensors on contractual terms and conditions
+ * ("Licence") which entitle you freely to modify and/or to distribute this
+ * source code subject to Your compliance with the terms of the Licence.
+ *
+ * This source code has been made available to You without any warranties
+ * whatsoever. Consequently, Your use, modification and distribution of this
+ * source code is entirely at Your own risk and neither Castle, its licensors
+ * nor any other person who has contributed to this source code shall be
+ * liable to You for any loss or damage which You may suffer as a result of
+ * Your use, modification or distribution of this source code.
+ *
+ * Full details of Your rights and obligations are set out in the Licence.
+ * You should have received a copy of the Licence with this source code file.
+ * If You have not received a copy, the text of the Licence is available
+ * online at www.castle-technology.co.uk/riscosbaselicence.htm
+ */
+/*
+ * Title: xpand - decompression of squeezed AIF executable images
+ * Author: RCC
+ * Copyright: (C) 1988, Acorn Computers Ltd, Cambridge, England.
+ * Date: 24-Mar-88
+ * LastEdit: 24-Mar-88
+
+ * 22Feb99: SJM: Note started to convert this to be able to run on
+ * Solaris. However this isn't easily possible since currently it xpands
+ * by running the unsqueeze code embedded in the code.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "CLX/wholefls.h"
+
+#ifdef __riscos
+#include "CLib/kernel.h"
+#include "CLib/swis.h"
+#else
+typedef struct {
+ int load, exec; /* load, exec addresses */
+ int start, end; /* start address/length, end address/attributes */
+} _kernel_osfile_block;
+#endif
+
+#ifndef __riscos
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#include "squeeze.h"
+#include "VersionNum"
+
+#define DATE Module_Date
+/* If macro value is empty, expression rewrites to "0 * + 1" which is zero. */
+#if 0 * Module_MinorVersion_CMHG + 1 == 0
+# define VSN Module_MajorVersion
+#else
+# define VSN Module_MajorVersion " (" Module_MinorVersion ")"
+#endif
+#define SELF "xpand"
+
+typedef int func(int, int, int);
+
+#ifndef __riscos
+# include "asmcall1.h"
+#else
+extern int asmcall_call(func *, int, int, int);
+extern int *asmcall_exit;
+#endif
+
+static int debug;
+static int verbose;
+
+#undef AIFPRELUDE /* musn't rely on this constant - it's changed over time */
+
+/*
+ * Veneer on file-handling.
+ */
+
+#define SAVE 0
+#define WRITEINFO 1
+#define READINFO 5
+#define LOAD 0xff
+
+#define FILFOUND 1
+
+#ifdef __riscos
+static void arthurise(_kernel_osfile_block *info)
+{ if ((info->load == info->exec) && (info->load == 0x8000)) {
+ /* can we use Arthur 'FF8' filetype ? */
+ if (_kernel_hostos() == _kernel_ARTHUR) {
+ /* This is Arthur - get time of day */
+ int data[2];
+ data[0] = 3;
+ if (_kernel_osword(14, data) != _kernel_ERROR) {
+ info->exec = data[0];
+ info->load = 0xfffff800 + (data[1] & 0xff);
+ }
+ }
+ }
+}
+#endif
+
+static int fileinfo(_kernel_osfile_block *info, const char *name)
+{
+#ifdef __riscos
+ if (_kernel_osfile(READINFO, name, info) != FILFOUND)
+ return -1;
+#else
+ struct stat buf;
+ int len, ftype;
+
+ if (stat(name, &buf) != 0) return -1;
+
+ len = strlen(name);
+ if (len > 4 && name[len-4] == ',')
+ ftype = (int)strtoul(name + len - 4, NULL, 16);
+ else
+ ftype = 0xfff;
+
+ info->load = 0xfff00000 | (ftype << 8);
+ info->exec = buf.st_mtime * 100 / 256;
+ info->start = buf.st_size;
+ info->end = 0;
+#endif
+ return 0;
+}
+
+static void fatalerror(const char *format, const char *name) {
+ fputs(SELF ": ", stderr);
+ fprintf(stderr, format, name);
+#ifdef __riscos
+ { _kernel_oserror *e = _kernel_last_oserror();
+ if (e != 0) fprintf(stderr, " (host error %#x: %s)", e->errnum, e->errmess);
+ }
+#endif
+ fputc('\n', stderr);
+ exit(1);
+}
+
+#define ROR(x, n) (((x)<<(32-(n))) | (((x)>>(n)) & ((1<<(32-(n)))-1)))
+
+static int immfield(word inst) {
+ int shift = (inst & 0xf00) >> 7;
+ int val = inst & 0xff;
+ return ROR(val, shift);
+}
+
+static SqueezeHeader *find_squeeze_header(int isaif, int *decompress)
+{
+ /*
+ * The size of the decoded thing is stored 6 words before the
+ * start of the unsqueezing code (see squeeze.c).
+ */
+ if (!isaif) {
+ /*
+ * Non-AIF images skip the first few instructions of the unsqueezing
+ * code; this has changed over time, so search backwards for
+ * MOV R0, #<imm>, which is always the first instruction
+ */
+ while (((*decompress & 0xfffff000) != 0xe3a00000)) decompress--;
+ }
+ return (SqueezeHeader *) (decompress-SQUEEZEWORDS);
+}
+
+static int xpand(char *in, char *out)
+{ _kernel_osfile_block info;
+ bool isaif, isdata;
+ int squeezedby = 0;
+ int size;
+ int *ws;
+ aifhdr *hdr;
+ char *lastb;
+
+ if (verbose)
+ fprintf(stderr, "-- xpanding '%s' to '%s'\n", in, out);
+
+ if (fileinfo(&info, in) == -1)
+ fatalerror("no file '%s'", in);
+ size = info.start;
+ ws = (int *)malloc(size + sizeof(int) + (24*1024)); /* allow space for unsqueezing */
+ hdr = (aifhdr *)(ws + 1);
+ if (wf_load(in, hdr, size) == -1)
+ fatalerror("can't load '%s'", in);
+ if ((info.load & 0xfc000000) != 0) { /* Not a valid address */
+ if ((info.load & 0xffffff00) == 0xfffff800) { /* Arthur absolute */
+ info.load = 0x8000; info.exec = 0x8000;
+ } else if ((info.load & 0xffffff00) != SQUEEZED) {
+ info.exec = info.load;
+ }
+ }
+
+ if ((size & 15) == 15 &&
+ ((hdr->bl_decompress & ~B_OFFSET) == BL ||
+ (hdr->bl_decompress & ~B_OFFSET) == B)
+ ) {
+ int d1, d2, d3;
+ lastb = (char *)hdr + size;
+ while (*--lastb == ' ') /* nothing */;
+ if (isdigit(d3 = *--lastb) &&
+ isdigit(d2 = *--lastb) &&
+ *--lastb == '.' &&
+ isdigit(d1 = *--lastb) &&
+ *--lastb == ' ' &&
+ *--lastb == 'c' &&
+ *--lastb == 'c' &&
+ *--lastb == 'r')
+ squeezedby = (d1-'0')*100+(d2-'0')*10+(d3-'0');
+ }
+
+ if (squeezedby == 0)
+ fatalerror("'%s' is not squeezed", in);
+
+ if (hdr->swi_exit == 0xef000011) /* OK, it's AIF */
+ isdata = NO, isaif = YES;
+ else if (((datahdr *)hdr)->datamagic == DATAMAGIC) /* OK, it's squeezed data */
+ isdata = YES, isaif = NO;
+ else
+ isdata = NO, isaif = NO;
+
+ { int *decompress = &(((int *)hdr)[(hdr->bl_decompress & B_OFFSET) + PREFETCH]);
+ SqueezeHeader *h = find_squeeze_header(isaif, decompress);
+ int realsize = h->decodedsize;
+ word *lastw = (word *)lastb - 1;
+
+ if (debug)
+ fprintf(stderr, "decodedsize %x encodedsize %x encodedtabs %x\n"
+ "nshorts %x nlongs %x bytestomove %x\n"
+ "squeezer %d.%02d\n",
+ h->decodedsize, h->encodedsize, h->encodedtabs,
+ h->nshorts, h->nlongs, h->bytestomove,
+ squeezedby/100, squeezedby%100);
+ /* Arrange to get back control after decompression. We can't plant a
+ branch, since the decompression code gets shifted up store before
+ executing (and we'd rather not know the details). We rely on the
+ fact that at the end of decompression, r8 points to the base of the
+ decompressed image and this is followed by
+ SUB r8, r8, #&7c
+ MOV pc, r8
+ (for aif images) or
+
+ ADD r8, r8, #exec_address - load_address
+ MOV pc, r8
+ (otherwise)
+ */
+
+ if (((int)lastb & 3) != 0 ||
+ *lastw != MOV_PC_R8)
+ fatalerror("format error in '%s'", in);
+ if (isaif) {
+ int backwards;
+ for (backwards = 1; backwards < 8; ++backwards) {
+ if (*(lastw-backwards) == (SUB_R8_R8_0 | 0x7c)) break;
+ }
+ if (backwards == 8) {
+ fatalerror("aif format error in '%s' (end of expansion code not found)", in);
+ }
+ *(lastw-backwards) = (SUB_R8_R8_0 | 0x80);
+ } else {
+ int execoffset = 0;
+ for (;; lastw--) {
+ word inst = *(lastw-1);
+ if ((inst & ~0xfff) == ADD_R8_R8_0)
+ execoffset += immfield(inst);
+ else if ((inst & ~0xfff) == SUB_R8_R8_0)
+ execoffset -= immfield(inst);
+ else
+ break;
+ }
+ info.exec += execoffset;
+ }
+ *lastw = LDR_PC_R8_MINUS4;
+ *ws = (int)(&asmcall_exit);
+ asmcall_call((func *)hdr, 0, 0, 0);
+ /*
+ * Just have to save it again.
+ */
+ if (isdata) {
+ datahdr *d = (datahdr *)hdr;
+ info.load = (int)d->load;
+ info.exec = d->exec;
+ info.start = (int)(d+1);
+ info.end = info.start + d->size;
+ } else {
+#ifdef __riscos
+ arthurise(&info);
+#endif
+ info.start = (int)hdr;
+ if (isaif)
+ info.end = info.start + sizeof(*hdr) + realsize;
+ else
+ info.end = info.start + realsize;
+ }
+ if (wf_save(out, (void *)info.start, info.end - info.start) == -1)
+ fatalerror("failed to write '%s'", out);
+#ifdef __riscos
+ _swix(OS_File, _INR(0,2), 18, out, (info.load << 12) >> 20);
+#endif
+
+ return(0);
+ }
+}
+
+static void help(void)
+{
+ fprintf(stderr, "\n%s vsn %s [%s] - \n", SELF, VSN, DATE);
+ fprintf(stderr, "takes an executable AIF image or data file compressed by\n");
+ fprintf(stderr, "'squeeze' and decompresses it to reproduce the original image\n");
+ fprintf(stderr, "(possibly with an extra 4 bytes of zeros on the end).\n\n");
+ fprintf(stderr, "syntax: xpand <squeezed-file> [<unsqueezed-file>]\n");
+}
+
+int main(int argc, char *argv[])
+{ int j;
+ char *arg;
+ char *a = NULL;
+ char *b = NULL;
+ char c;
+
+ debug = verbose = 0;
+ for (j = 1; j < argc; ++j) {
+ arg = argv[j];
+ if (arg[0] == '-') {
+ c = arg[1];
+ if (('A' <= c) && (c <= 'Z')) c += ('a' - 'A');
+ switch (c) {
+ case 'h': help(); exit(0);
+ case 'q': ++debug; break;
+ case 'v': ++verbose; break;
+ default:
+ fprintf(stderr, SELF ": flag '%c' not recognised", c);
+ help();
+ exit(1);
+ }
+ } else { /* a filename */
+ if (a == NULL) a = arg;
+ else if (b == NULL) b = arg;
+ else {
+ fatalerror("too many args '%s'", arg);
+ }
+ }
+ }
+ if (a == NULL) fatalerror("need <file-to-xpand>", 0);
+ if (b == NULL) b = a; /* xpand it to itself */
+ return(xpand(a, b));
+}