summaryrefslogtreecommitdiff
path: root/src/bin/fw
diff options
context:
space:
mode:
authorJari Vetoniemi <mailroxas@gmail.com>2017-04-20 16:49:35 +0300
committerJari Vetoniemi <mailroxas@gmail.com>2017-05-01 22:58:22 +0300
commit29086b1d12a2c28cffdbfbf0b3990a7bd75506b9 (patch)
tree8acd48bc30932812744c0adb102d7a7add494357 /src/bin/fw
parent76b8c9e03c97b16d9ff97f3b79c0ecbff0f5e7f2 (diff)
work in progress
Diffstat (limited to 'src/bin/fw')
-rw-r--r--src/bin/fw/uneaf.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/src/bin/fw/uneaf.c b/src/bin/fw/uneaf.c
new file mode 100644
index 0000000..f23c6d6
--- /dev/null
+++ b/src/bin/fw/uneaf.c
@@ -0,0 +1,208 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <err.h>
+#include <sys/stat.h>
+#include <zlib.h>
+
+static const char *stdin_name = "/dev/stdin";
+
+int ZEXPORT uncompress2 (dest, destLen, source, sourceLen)
+ Bytef *dest;
+ uLongf *destLen;
+ const Bytef *source;
+ uLong *sourceLen;
+{
+ z_stream stream;
+ int err;
+ const uInt max = (uInt)-1;
+ uLong len, left;
+ Byte buf[1]; /* for detection of incomplete stream when *destLen == 0 */
+
+ len = *sourceLen;
+ if (*destLen) {
+ left = *destLen;
+ *destLen = 0;
+ }
+ else {
+ left = 1;
+ dest = buf;
+ }
+
+ stream.next_in = (z_const Bytef *)source;
+ stream.avail_in = 0;
+ stream.zalloc = (alloc_func)0;
+ stream.zfree = (free_func)0;
+ stream.opaque = (voidpf)0;
+
+ err = inflateInit2(&stream, -15);
+ if (err != Z_OK) return err;
+
+ stream.next_out = dest;
+ stream.avail_out = 0;
+
+ do {
+ if (stream.avail_out == 0) {
+ stream.avail_out = left > (uLong)max ? max : (uInt)left;
+ left -= stream.avail_out;
+ }
+ if (stream.avail_in == 0) {
+ stream.avail_in = len > (uLong)max ? max : (uInt)len;
+ len -= stream.avail_in;
+ }
+ err = inflate(&stream, Z_NO_FLUSH);
+ } while (err == Z_OK);
+
+ *sourceLen -= len + stream.avail_in;
+ if (dest != buf)
+ *destLen = stream.total_out;
+ else if (stream.total_out && err == Z_BUF_ERROR)
+ left = 1;
+
+ inflateEnd(&stream);
+ return err == Z_STREAM_END ? Z_OK :
+ err == Z_NEED_DICT ? Z_DATA_ERROR :
+ err == Z_BUF_ERROR && left + stream.avail_out ? Z_DATA_ERROR :
+ err;
+}
+
+static void
+zdeflate(const uint8_t *buf, const size_t buf_sz, uint8_t **out_dec, size_t *inout_dec_sz)
+{
+ uLongf dsize = (*inout_dec_sz ? *inout_dec_sz : buf_sz * 2), bsize;
+ int ret = Z_OK;
+
+ do {
+ if (!(*out_dec = realloc(*out_dec, (bsize = dsize))))
+ err(EXIT_FAILURE, "realloc(%zu)", dsize);
+ dsize *= 2;
+ } while ((ret = uncompress(*out_dec, &bsize, buf, buf_sz)) == Z_BUF_ERROR && !*inout_dec_sz);
+
+ if (ret != Z_OK)
+ errx(EXIT_FAILURE, "uncompress(%zu, %zu) == %d", (size_t)(dsize / 2), buf_sz, ret);
+
+ *inout_dec_sz = bsize;
+}
+
+static FILE*
+fopen_or_die(const char *path, const char *mode)
+{
+ assert(path && mode);
+
+ FILE *f;
+ if (!(f = fopen(path, mode)))
+ err(EXIT_FAILURE, "fopen(%s, %s)", path, mode);
+
+ return f;
+}
+
+static void
+mkdirp(const char *path)
+{
+ assert(path);
+ for (const char *s = path; *s; ++s) {
+ if (*s != '/')
+ continue;
+
+ *(char*)s = 0;
+ mkdir(path, 0755);
+ *(char*)s = '/';
+ }
+}
+
+static void
+write_data_to(const uint8_t *data, const size_t size, const char *path)
+{
+ assert(data && path);
+ mkdirp(path);
+ FILE *f = fopen_or_die(path, "wb");
+
+ struct header {
+ uint8_t magic[4];
+ uint32_t unknown;
+ uint32_t size;
+ uint32_t offset;
+ } __attribute__((packed)) header;
+
+ memcpy(&header, data, sizeof(header));
+ warnx("%s", path);
+
+ if (!memcmp(header.magic, "#EMZ", sizeof(header.magic))) {
+ uint8_t *buf = NULL;
+ size_t dec_size = header.size;
+ zdeflate(data + header.offset, size - header.offset, &buf, &dec_size);
+ fwrite(buf, 1, dec_size, f);
+ free(buf);
+ } else {
+ fwrite(data, 1, size, f);
+ }
+
+ fclose(f);
+}
+
+static void
+unpack(const char *path, const char *outdir)
+{
+ assert(path);
+ const char *name = (!strcmp(path, "-") ? stdin_name : path);
+ FILE *f = (name == stdin_name ? stdin : fopen_or_die(name, "rb"));
+
+ struct header {
+ uint8_t magic[4];
+ uint16_t major, minor;
+ uint64_t size;
+ uint32_t count;
+ uint64_t unknown;
+ uint8_t padding[100];
+ } __attribute__((packed)) header;
+
+ if (fread(&header, 1, sizeof(header), f) != sizeof(header))
+ err(EXIT_FAILURE, "fread(%zu)", sizeof(header));
+
+ if (memcmp(header.magic, "#EAF", sizeof(header.magic)))
+ errx(EXIT_FAILURE, "'%s' is not a #EAF file", name);
+
+ for (size_t i = 0; i < header.count; ++i) {
+ struct file {
+ char path[256];
+ uint64_t offset, size;
+ uint8_t padding[16];
+ } __attribute__((packed)) file;
+
+ if (fread(&file, 1, sizeof(file), f) != sizeof(file))
+ err(EXIT_FAILURE, "fread(%zu)", sizeof(file));
+
+ fpos_t pos;
+ fgetpos(f, &pos);
+
+ uint8_t *data;
+ if (!(data = malloc(file.size)))
+ err(EXIT_FAILURE, "malloc(%zu)", file.size);
+
+ fseek(f, file.offset, SEEK_SET);
+ if (fread(data, 1, file.size, f) != file.size)
+ err(EXIT_FAILURE, "fread(%zu)", file.size);
+
+ char path[4096];
+ snprintf(path, sizeof(path), "%s/%s", outdir, file.path);
+ write_data_to(data, file.size, path);
+ free(data);
+ fsetpos(f, &pos);
+ }
+
+ fclose(f);
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc < 3)
+ errx(EXIT_FAILURE, "usage: %s outdir file ...", argv[0]);
+
+ for (int i = 2; i < argc; ++i)
+ unpack(argv[i], argv[1]);
+
+ return EXIT_SUCCESS;
+}