summaryrefslogtreecommitdiffstats
path: root/utils/rk27utils/rkwtool
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2014-08-07 19:09:35 +0200
committerMarcin Bukat <marcin.bukat@gmail.com>2014-08-07 19:09:35 +0200
commit53d9f2e6a7564e487bdac87f6e28c662e8407458 (patch)
tree7c2f3c40414d568b3623a6817a65d0188bbec9d5 /utils/rk27utils/rkwtool
parent81ffd9bfeee6aca65f507a46c8123b47ca6e2803 (diff)
downloadrockbox-53d9f2e6a7564e487bdac87f6e28c662e8407458.tar.gz
rockbox-53d9f2e6a7564e487bdac87f6e28c662e8407458.tar.bz2
rockbox-53d9f2e6a7564e487bdac87f6e28c662e8407458.zip
rkwtool: The tool to inspect and extract update RKW files
Change-Id: Ie32d0a597b93d23a7d5946a3d9409572b41b45bc
Diffstat (limited to 'utils/rk27utils/rkwtool')
-rw-r--r--utils/rk27utils/rkwtool/Makefile7
-rw-r--r--utils/rk27utils/rkwtool/main.c129
-rw-r--r--utils/rk27utils/rkwtool/rkw.c538
-rw-r--r--utils/rk27utils/rkwtool/rkw.h123
4 files changed, 797 insertions, 0 deletions
diff --git a/utils/rk27utils/rkwtool/Makefile b/utils/rk27utils/rkwtool/Makefile
new file mode 100644
index 0000000000..239733cb87
--- /dev/null
+++ b/utils/rk27utils/rkwtool/Makefile
@@ -0,0 +1,7 @@
+all: rkwtool
+
+rkwtool: rkw.c main.c
+ gcc -g -std=c99 -o $@ -W -Wall $^
+
+clean:
+ rm -fr rkwtool
diff --git a/utils/rk27utils/rkwtool/main.c b/utils/rk27utils/rkwtool/main.c
new file mode 100644
index 0000000000..dacfd9ffab
--- /dev/null
+++ b/utils/rk27utils/rkwtool/main.c
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Marcin Bukat
+ *
+ * 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
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <getopt.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include "rkw.h"
+
+#define VERSION "v0.1"
+
+static void banner(void)
+{
+ printf("RKWtool " VERSION " (C) Marcin Bukat 2014\n");
+ printf("This is free software; see the source for copying conditions. There is NO\n");
+ printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n");
+}
+
+static void usage(char *name)
+{
+ banner();
+
+ printf("Usage: %s [-i] [-b] [-e] [-a] [-o prefix] file.rkw\n", name);
+ printf("-i\t\tprint info about RKW file\n");
+ printf("-b\t\textract nand bootloader images (s1.bin and s2.bin)\n");
+ printf("-e\t\textract firmware files stored in RKST section\n");
+ printf("-o prefix\twhen extracting firmware files put it there\n");
+ printf("-a\t\textract additional file(s) (usually Rock27Boot.bin)\n");
+ printf("-A\t\textract all data\n");
+ printf("file.rkw\tRKW file to be processed\n");
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ struct rkw_info_t *rkw_info = NULL;
+ char *prefix = NULL;
+ bool info = false;
+ bool extract = false;
+ bool bootloader = false;
+ bool addfile = false;
+
+ while ((opt = getopt(argc, argv, "iebo:aA")) != -1)
+ {
+ switch (opt)
+ {
+ case 'i':
+ info = true;
+ break;
+
+ case 'e':
+ extract = true;
+ break;
+
+ case 'b':
+ bootloader = true;
+ break;
+
+ case 'o':
+ prefix = optarg;
+ break;
+
+ case 'a':
+ addfile = true;
+ break;
+
+ case 'A':
+ extract = true;
+ bootloader = true;
+ addfile = true;
+ break;
+
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if ((argc - optind) != 1 ||
+ (!info && !extract && ! bootloader && !addfile))
+ {
+ usage(argv[0]);
+ return -1;
+ }
+
+ banner();
+
+ rkw_info = rkw_slurp(argv[optind]);
+
+ if (rkw_info)
+ {
+ if (info)
+ {
+ rkrs_list_named_items(rkw_info);
+ rkst_list_named_items(rkw_info);
+ }
+
+ if (extract)
+ unpack_rkst(rkw_info, prefix);
+
+ if (bootloader)
+ unpack_bootloader(rkw_info, prefix);
+
+ if (addfile)
+ unpack_addfile(rkw_info, prefix);
+
+ rkw_free(rkw_info);
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/utils/rk27utils/rkwtool/rkw.c b/utils/rk27utils/rkwtool/rkw.c
new file mode 100644
index 0000000000..eacef87b8e
--- /dev/null
+++ b/utils/rk27utils/rkwtool/rkw.c
@@ -0,0 +1,538 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Marcin Bukat
+ *
+ * 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
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "rkw.h"
+
+const char *section_name[] = {
+ "RKLD",
+ "RKRS",
+ "RKST"
+};
+
+const uint32_t section_magic[] = {
+ RKLD_MAGIC,
+ RKRS_MAGIC,
+ RKST_MAGIC
+};
+
+const char *rkrs_action_name[] = {
+ [act_null] = "null",
+ [act_mkdir] = "mkdir",
+ [act_fcopy] = "fcopy",
+ [act_fsoper] = "fsoper",
+ [act_format] = "format",
+ [act_loader] = "loader",
+ [act_dispbmp] = "dispbmp",
+ [act_dispstr] = "dispstr",
+ [act_setfont] = "setfont",
+ [act_delay] = "delay",
+ [act_system] = "system",
+ [act_readme] = "readme",
+ [act_copyright] = "copyright",
+ [act_select] = "select",
+ [act_restart] = "restart",
+ [act_regkey] = "regkey",
+ [act_version] = "version",
+ [act_freplace] = "freplace",
+ [act_fpreplace] = "fpreplace",
+ [act_fsdel] = "fsdel",
+ [act_space] = "space",
+ [act_addfile] = "addfile",
+ [act_setmem] = "setmem",
+ [act_getmem] = "getmem"
+};
+
+/* scrambling/descrambling reverse engineered by AleMaxx */
+static void encode_page(uint8_t *inpg, uint8_t *outpg, const int size)
+{
+
+uint8_t key[] = {
+ 0x7C, 0x4E, 0x03, 0x04,
+ 0x55, 0x05, 0x09, 0x07,
+ 0x2D, 0x2C, 0x7B, 0x38,
+ 0x17, 0x0D, 0x17, 0x11
+};
+ int i, i3, x, val, idx;
+
+ uint8_t key1[0x100];
+ uint8_t key2[0x100];
+
+ for (i=0; i<0x100; i++) {
+ key1[i] = i;
+ key2[i] = key[i&0xf];
+ }
+
+ i3 = 0;
+ for (i=0; i<0x100; i++) {
+ x = key1[i];
+ i3 = key1[i] + i3;
+ i3 += key2[i];
+ i3 &= 0xff;
+ key1[i] = key1[i3];
+ key1[i3] = x;
+ }
+
+ idx = 0;
+ for (i=0; i<size; i++) {
+ x = key1[(i+1) & 0xff];
+ val = x;
+ idx = (x + idx) & 0xff;
+ key1[(i+1) & 0xff] = key1[idx];
+ key1[idx] = (x & 0xff);
+ val = (key1[(i+1)&0xff] + x) & 0xff;
+ val = key1[val];
+ outpg[i] = val ^ inpg[i];
+ }
+}
+
+/* take path as stored in RKST and convert it to unix path
+ * with optional prefix
+ */
+static char *unixpath(char *path, char *prefix)
+{
+ char *parsed, *ptr;
+ size_t size = 0;
+
+ if (NULL == path)
+ return NULL;
+
+ size = strlen(path) + 1;
+
+ /* full windows path i.e C:\something */
+ if (strlen(path) > 2 && ':' == path[1])
+ path += 3;
+
+ if (prefix)
+ {
+ /* account for '/' after prefix */
+ size += strlen(prefix) + 1;
+ }
+
+ /* allocate buffer */
+ parsed = malloc(size);
+ ptr = parsed;
+
+ /* malloc failed */
+ if (NULL == ptr)
+ return NULL;
+
+ /* copy prefix */
+ if (prefix)
+ {
+ strcpy(ptr, prefix);
+ ptr += strlen(prefix);
+ *ptr++ = '/';
+ }
+
+ do
+ {
+ if (*path == '\\')
+ *ptr = '/';
+ else
+ *ptr = *path;
+
+ ptr++;
+ } while ('\0' != *(path++));
+
+ return parsed;
+}
+
+/* returns pointer to the rkrs header in rkw file */
+static char *find_section(struct rkw_info_t *rkw_info, enum section_type_t type)
+{
+ char *ptr;
+ struct section_header_t *h;
+
+ switch(type)
+ {
+ case ST_RKRS:
+ case ST_RKST:
+ for (ptr=(char *)rkw_info->rkw; ptr<(char *)rkw_info->rkw+rkw_info->size; ptr++)
+ {
+ h = (struct section_header_t *)ptr;
+ if (h->magic == section_magic[type] &&
+ h->size == sizeof(struct section_header_t) &&
+ h->number_of_named_entries != 0)
+ {
+ fprintf(stderr, "[info]: %s found at 0x%0x\n",
+ section_name[type], (int)(ptr - rkw_info->rkw));
+
+ return ptr;
+ }
+ }
+ break;
+
+ default:
+ fprintf(stderr, "[error]: Not supported section type %d\n", type);
+ return NULL;
+ }
+ return NULL;
+}
+
+/* load rkw file into memory and setup pointers to various sections */
+struct rkw_info_t *rkw_slurp(char *filename)
+{
+ FILE *fp;
+ struct rkw_info_t *rkw_info;
+
+ rkw_info = (struct rkw_info_t *)malloc(sizeof(struct rkw_info_t));
+
+ if (NULL == rkw_info)
+ {
+ fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
+ sizeof(struct rkw_info_t));
+
+ return NULL;
+ }
+
+ fp = fopen(filename, "rb");
+
+ if (NULL == fp)
+ {
+ fprintf(stderr, "[error]: Can't open %s\n", filename);
+ free(rkw_info);
+ return NULL;
+ }
+
+ fseek(fp, 0, SEEK_END);
+ rkw_info->size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ rkw_info->rkw = (char *)malloc(rkw_info->size);
+
+ if (NULL == rkw_info->rkw)
+ {
+ fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
+ rkw_info->size);
+
+ free(rkw_info);
+ fclose(fp);
+ return NULL;
+ }
+
+ if (fread(rkw_info->rkw, rkw_info->size, 1, fp) != 1)
+ {
+ fprintf(stderr, "[error]: Can't read %s\n", filename);
+ free(rkw_info);
+ fclose(fp);
+ return NULL;
+ }
+
+ rkw_info->rkrs_info.header = find_section(rkw_info, ST_RKRS);
+ rkw_info->rkrs_info.items = rkw_info->rkrs_info.header +
+ ((struct section_header_t *)
+ (rkw_info->rkrs_info.header))->offset_of_named_entries;
+
+ rkw_info->rkst_info.header = find_section(rkw_info, ST_RKST);
+ rkw_info->rkst_info.items = rkw_info->rkst_info.header +
+ ((struct section_header_t *)
+ (rkw_info->rkst_info.header))->offset_of_named_entries;
+
+ fclose(fp);
+ return rkw_info;
+}
+
+void rkw_free(struct rkw_info_t *rkw_info)
+{
+ free(rkw_info->rkw);
+ free(rkw_info);
+}
+
+static void rkrs_named_item_info(struct rkrs_named_t *item)
+{
+ fprintf(stderr, "[info]: size=0x%0x (%d)\n", item->size, item->size);
+ fprintf(stderr, "[info]: type=0x%0x (%d) %s\n", item->type, item->type, rkrs_action_name[item->type]);
+ fprintf(stderr, "[info]: data_offset=0x%0x (%d)\n", item->data_offset, item->data_offset);
+ fprintf(stderr, "[info]: data_size=0x%0x (%d)\n", item->data_size, item->data_size);
+ fprintf(stderr, "[info]: param[0]=0x%0x (%d)\n", item->param[0], item->param[0]);
+ fprintf(stderr, "[info]: param[1]=0x%0x (%d)\n", item->param[1], item->param[1]);
+ fprintf(stderr, "[info]: param[2]=0x%0x (%d)\n", item->param[2], item->param[2]);
+ fprintf(stderr, "[info]: param[3]=0x%0x (%d)\n", item->param[3], item->param[3]);
+}
+
+static void rkst_named_item_info(struct rkst_named_t *item)
+{
+ fprintf(stderr, "[info]: size=0x%0x (%d)\n", item->size, item->size);
+ fprintf(stderr, "[info]: action=0x%0x (%d) %s\n", item->action, item->action, rkrs_action_name[item->action]);
+ fprintf(stderr, "[info]: data_offset=0x%0x (%d)\n", item->data_offset, item->data_offset);
+ fprintf(stderr, "[info]: data_size=0x%0x (%d)\n", item->data_size, item->data_size);
+ fprintf(stderr, "[info]: name=\"%s\"\n", &item->name);
+}
+
+static struct rkrs_named_t *find_item(struct rkw_info_t *rkw_info, enum rkst_action_t type, bool search_start)
+{
+ static struct rkrs_named_t *item;
+
+ if (search_start)
+ {
+ item = (struct rkrs_named_t *)rkw_info->rkrs_info.items;
+ }
+ else
+ {
+ if (item)
+ item++;
+ else
+ return NULL;
+ }
+
+ while (item->size > 0)
+ {
+ if (item->type == type)
+ {
+ fprintf(stderr, "[info]: Item type=%d found at 0x%x\n", type,
+ (int)((char *)item - rkw_info->rkw));
+ return item;
+ }
+ item++;
+ }
+
+ return NULL;
+}
+
+void rkrs_list_named_items(struct rkw_info_t *rkw_info)
+{
+ struct rkrs_named_t *item = (struct rkrs_named_t *)rkw_info->rkrs_info.items;
+ struct section_header_t *rkrs_header = (struct section_header_t *)(rkw_info->rkrs_info.header);
+ int i;
+
+ for (i=0; i<rkrs_header->number_of_named_entries; i++)
+ {
+ fprintf(stderr, "[info]: rkrs named entry %d\n", i);
+ rkrs_named_item_info(item++);
+ fprintf(stderr, "\n");
+ }
+}
+
+void rkst_list_named_items(struct rkw_info_t *rkw_info)
+{
+ struct rkst_named_t *item = (struct rkst_named_t *)rkw_info->rkst_info.items;
+ struct section_header_t *rkst_header = (struct section_header_t *)(rkw_info->rkst_info.header);
+ int i;
+
+ for (i=0; i<rkst_header->number_of_named_entries; i++)
+ {
+ fprintf(stderr, "[info]: rkst named entry %d\n", i);
+ rkst_named_item_info(item);
+ item = (struct rkst_named_t *)((char *)item + item->size);
+ fprintf(stderr, "\n");
+ }
+}
+
+void unpack_bootloader(struct rkw_info_t *rkw_info, char *prefix)
+{
+ FILE *fp;
+ char *ptr;
+ size_t size;
+ int len;
+ char *buf;
+ struct rkrs_named_t *item = find_item(rkw_info, act_loader, true);
+
+ if (NULL == item)
+ {
+ fprintf(stderr, "[error]: Can't find nand bootloader\n");
+ return;
+ }
+
+ ptr = (char *)(rkw_info->rkrs_info.header) + item->data_offset;
+ size = item->param[0];
+ buf = malloc(size);
+
+ if (NULL == buf)
+ {
+ fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
+ size);
+ return;
+ }
+
+ /* make a copy for decryption */
+ memcpy(buf, ptr, size);
+ encode_page((uint8_t *)buf, (uint8_t *)buf, size);
+ fp = fopen(unixpath("s1.bin", prefix), "w");
+
+ if (NULL == fp)
+ {
+ fprintf(stderr, "[error]: Can't open s1.bin for writing\n");
+ free(buf);
+ return;
+ }
+
+ if (fwrite(buf, size, 1, fp) != 1)
+ {
+ fprintf(stderr, "[error]: Can't write s1.bin file\n");
+ free(buf);
+ fclose(fp);
+ return;
+ }
+
+ fclose(fp);
+
+ ptr = (char *)(rkw_info->rkrs_info.header) + item->param[1];
+ size = item->param[2];
+ len = size;
+ buf = realloc(buf, size);
+
+ if (NULL == buf)
+ {
+ fprintf(stderr, "[error]: Can't allocate %ld bytes of memory\n",
+ size);
+
+ free(buf);
+ return;
+ }
+
+ memcpy(buf, ptr, size);
+ ptr = buf;
+
+ while (len >= 0x200)
+ {
+ encode_page((uint8_t *)ptr, (uint8_t *)ptr, 0x200);
+ ptr += 0x200;
+ len -= 0x200;
+ }
+ encode_page((uint8_t *)ptr, (uint8_t *)ptr, len);
+
+ fp = fopen(unixpath("s2.bin", prefix), "w");
+
+ if (NULL == fp)
+ {
+ fprintf(stderr, "[error]: Can't open s2.bin for writing\n");
+ free(buf);
+ return;
+ }
+
+ if (fwrite(buf, size, 1, fp) != 1)
+ {
+ fprintf(stderr, "[error]: Can't write s2.bin file\n");
+ free(buf);
+ fclose(fp);
+ return;
+ }
+
+ fclose(fp);
+ free(buf);
+ fprintf(stderr, "[info]: Extracted bootloader version: %x.%x\n",
+ (item->param[3] >> 8) & 0xff, item->param[3] & 0xff);
+}
+
+void unpack_addfile(struct rkw_info_t *rkw_info, char *prefix)
+{
+ FILE *fp;
+ char *name;
+ int name_len;
+
+ struct rkrs_named_t *item = find_item(rkw_info, act_addfile, true);
+
+ do
+ {
+ name = unixpath(rkw_info->rkrs_info.header + item->data_offset, prefix);
+ name_len = item->param[0];
+
+ fprintf(stderr, "[info]: unpacking addfile %s\n", name);
+
+ fp = fopen(name, "w");
+
+ if (NULL == fp)
+ {
+ fprintf(stderr, "[error]: Can't open %s for writing\n", name);
+ return;
+ }
+
+ if (fwrite(rkw_info->rkrs_info.header + item->data_offset + name_len,
+ item->data_size - name_len, 1, fp) != 1)
+ {
+ fprintf(stderr, "[error]: Can't write %s file\n", name);
+ fclose(fp);
+ return;
+ }
+
+ fclose(fp);
+ } while (NULL != (item = find_item(rkw_info, act_addfile, false)));
+}
+
+/* unpack content of RKST section
+ * this mimics what is done when processing 'fsoper' field of RKRS
+ */
+void unpack_rkst(struct rkw_info_t *rkw_info, char *prefix)
+{
+ FILE *fp;
+ struct rkst_named_t *item = (struct rkst_named_t *)rkw_info->rkst_info.items;
+ struct section_header_t *rkst_header = (struct section_header_t *)(rkw_info->rkst_info.header);
+ char *name;
+ int i;
+
+ if (prefix)
+ {
+ if (0 != mkdir(prefix, 0755))
+ {
+ fprintf(stderr, "[error]: Can't create %s directory (%s)\n",
+ prefix, strerror(errno));
+ return;
+ }
+ }
+
+ fprintf(stderr, "[info]: Unpacking content of RKST section\n");
+
+ for (i=0; i<rkst_header->number_of_named_entries; i++)
+ {
+ name = unixpath((char *)&(item->name), prefix);
+
+ switch (item->action)
+ {
+ case act_mkdir:
+ if (0 != mkdir(name, 0755))
+ {
+ fprintf(stderr, "[error]: Can't create %s directory (%s)\n",
+ name, strerror(errno));
+ return;
+ }
+ fprintf(stderr, "[info]: mkdir %s\n", name);
+ break;
+
+ case act_fcopy:
+ fp = fopen(name, "w");
+ if (NULL == fp)
+ {
+ fprintf(stderr, "[error]: Can't open %s for writing (%s)\n",
+ name, strerror(errno));
+ return;
+ }
+
+ fwrite((char *)rkst_header + item->data_offset, item->data_size, 1, fp);
+ fprintf(stderr, "[info]: unpack %s\n", name);
+ fclose(fp);
+ break;
+
+ default:
+ break;
+ }
+
+ if (name) free(name);
+ item = (struct rkst_named_t *)((char *)item + item->size);
+ }
+}
diff --git a/utils/rk27utils/rkwtool/rkw.h b/utils/rk27utils/rkwtool/rkw.h
new file mode 100644
index 0000000000..eba673c974
--- /dev/null
+++ b/utils/rk27utils/rkwtool/rkw.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ *
+ * Copyright (C) 2014 by Marcin Bukat
+ *
+ * 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
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include <stdint.h>
+
+#define RKLD_MAGIC 0x4C44524B
+#define RKRS_MAGIC 0x53524B52
+#define RKST_MAGIC 0X53544B52
+
+enum section_type_t {
+ ST_RKLD,
+ ST_RKRS,
+ ST_RKST
+};
+
+enum rkst_action_t {
+ act_null = 0,
+ act_mkdir = 1,
+ act_fcopy = 2,
+ act_fsoper = 3,
+ act_format = 4,
+ act_loader = 5,
+
+ act_dispbmp = 10,
+ act_dispstr = 11,
+ act_setfont = 12,
+
+ act_delay = 20,
+
+ act_system = 100,
+ act_uilogo = 100,
+ act_readme = 101,
+ act_copyright = 102,
+ act_select = 103,
+ act_restart = 104,
+
+ act_regkey = 120,
+ act_version = 121,
+
+ act_freplace = 130,
+ act_fpreplace = 131,
+ act_fsdel = 132,
+
+ act_space = 200,
+
+ act_addfile = 300,
+
+ act_setmem = 1000,
+ act_getmem = 1001,
+};
+
+struct section_header_t {
+ uint32_t size;
+ uint32_t magic;
+ uint32_t property;
+ uint32_t timestamp;
+ uint32_t allign;
+ uint32_t file_size;
+ uint16_t size_of_name_dir;
+ uint16_t size_of_id_dir;
+ uint16_t number_of_named_entries;
+ uint16_t number_of_id_entries;
+ uint32_t offset_of_named_entries;
+ uint32_t offset_of_id_entries;
+} __attribute__((__packed__));
+
+struct rkrs_named_t {
+ uint32_t size;
+ uint32_t type;
+ uint32_t data_offset;
+ uint32_t data_size;
+ uint32_t param[4];
+} __attribute__((__packed__));
+
+struct rkst_named_t {
+ uint32_t size;
+ uint32_t action;
+ uint32_t data_offset;
+ uint32_t data_size;
+ uint8_t name;
+};
+
+struct section_info_t {
+ char *header;
+ char *items;
+};
+
+struct rkw_info_t {
+ char *rkw;
+ long size;
+ struct section_info_t rkrs_info;
+ struct section_info_t rkst_info;
+};
+
+/* general functions */
+struct rkw_info_t *rkw_slurp(char *filename);
+void rkw_free(struct rkw_info_t *rkw_info);
+
+/* info functions */
+void rkrs_list_named_items(struct rkw_info_t *rkw_info);
+void rkst_list_named_items(struct rkw_info_t *rkw_info);
+
+/* extract functions */
+void unpack_bootloader(struct rkw_info_t *rkw_info, char *prefix);
+void unpack_rkst(struct rkw_info_t *rkw_info, char *prefix);
+void unpack_addfile(struct rkw_info_t *rkw_info, char *prefix);