]> Chaos Git - misc/libbinmod.git/commitdiff
Commit this code I've had lying around for a while master
authorchaoskagami <chaos.kagami@gmail.com>
Thu, 30 Jun 2016 06:53:36 +0000 (02:53 -0400)
committerchaoskagami <chaos.kagami@gmail.com>
Thu, 30 Jun 2016 06:53:36 +0000 (02:53 -0400)
18 files changed:
COPYING [new file with mode: 0644]
DOCUMENT [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
bflag.c [new file with mode: 0644]
bgrep.c [new file with mode: 0644]
bips.c [new file with mode: 0644]
blib.c [new file with mode: 0644]
blib.h [new file with mode: 0644]
bsed.c [new file with mode: 0644]
bwrit.c [new file with mode: 0644]
bxxd.c [new file with mode: 0644]
config.h.in [new file with mode: 0644]
configure.ac [new file with mode: 0644]
install-sh [new file with mode: 0755]
ips_fmt.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..4e72d6e
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Jon Feldman (@chaoskagami)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/DOCUMENT b/DOCUMENT
new file mode 100644 (file)
index 0000000..721876d
--- /dev/null
+++ b/DOCUMENT
@@ -0,0 +1,25 @@
+IPS Format (general):
+
+       IPS files are patches consisting of chunks with 24-bit offsets and
+       description of bytes to write and length. They are incredibly
+       simple.
+
+       Here is what a patch would look like if you transcribed it to be
+       human readable:
+
+       PATCH
+
+       offset: 000f40
+       size: 10
+       DE AD BE EF DE AD BE EF 00 00
+
+       offset: 001267
+       size: rle
+       rle-size: 200
+       rle-byte: 50
+
+       EEOF
+
+IPS32 extension:
+
+       A rather simple extension. Offsets are 32-bit versus 24.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..5e55bf0
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,10 @@
+ To install this:
+
+  ./configure && make && make install
+
+ In other words, generic autoconf. If you're too lazy to run
+ these commands (seriously?), it's your lucky day. This file can be
+ executed as such: sh INSTALL
+
+ Yeah, seriously. I've deliberately started all lines with non-commands.
+ There's some syntax errors, but hey.
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..a33cb46
--- /dev/null
@@ -0,0 +1,103 @@
+SHELL = /bin/sh
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+.SUFFIXES:
+.SUFFIXES: .c .o
+
+OPT=-g -O
+
+AR = ar
+AR_FLAGS = rc
+RANLIB = @RANLIB@
+
+CC = @CC@
+CFLAGS = -I. @CFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+INSTALL = @INSTALL@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = $(exec_prefix)/bin
+libdir = $(prefix)/lib
+infodir = $(prefix)/info
+
+SOURCES=blib.c bgrep.c bips.c bsed.c bxxd.c bflag.c
+# DOCS=libbinmod.texi binmod.info
+MISC=configure mkinstalldirs install-sh aclocal.m4
+LIB_OBJS=blib.o
+BGREP_OBJS=bgrep.o
+BSED_OBJS=bsed.o
+BXXD_OBJS=bxxd.o
+BIPS_OBJS=bips.o
+BFLAG_OBJS=bflag.o
+BWRIT_OBJS=bwrit.o
+
+# ??? replace with your targets
+all: libbinmod.a bgrep bsed bips bxxd bwrit
+
+# ??? here I make the bindir, libdir and infodir directories; you
+# might not need all of these.  also, I assumed the names PROG and
+# libMYPROG.a for the program and library.
+install: all
+       $(top_srcdir)/mkinstalldirs $(bindir)
+       $(top_srcdir)/mkinstalldirs $(libdir)
+       $(top_srcdir)/mkinstalldirs $(infodir)
+       $(INSTALL) bgrep $(bindir)/bgrep
+       $(INSTALL) bsed  $(bindir)/bsed
+       $(INSTALL) bips  $(bindir)/bips
+       $(INSTALL) bxxd  $(bindir)/bxxd
+       $(INSTALL) bxxd  $(bindir)/bwrit
+       $(INSTALL) bxxd  $(bindir)/bflag
+       $(INSTALL) libbinmod.a $(libdir)/libbinmod.a
+#      $(INSTALL) PROG.info $(infodir)/PROG.info
+
+uninstall:
+       /bin/rm -f $(bindir)/bsed
+       /bin/rm -f $(bindir)/bgrep
+       /bin/rm -f $(bindir)/bips
+       /bin/rm -f $(bindir)/bxxd
+       /bin/rm -f $(bindir)/bflag
+       /bin/rm -f $(bindir)/bwrit
+       /bin/rm -f $(libdir)/libbinmod.a
+       /bin/rm -f $(infodir)/libbinmod.info
+
+libbinmod.a: $(LIB_OBJS)
+       /bin/rm -f libbinmod.a
+       $(AR) $(AR_FLAGS) libbinmod.a $(LIB_OBJS)
+       $(RANLIB) libbinmod.a
+
+bsed: $(BSED_OBJS) libbinmod.a
+       $(CC) $(CFLAGS) -o bsed $(BSED_OBJS) $(LIB_OBJS)
+
+bgrep: $(BGREP_OBJS) libbinmod.a
+       $(CC) $(CFLAGS) -o bgrep $(BGREP_OBJS) $(LIB_OBJS)
+
+bips: $(BIPS_OBJS) libbinmod.a
+       $(CC) $(CFLAGS) -o bips $(BIPS_OBJS) $(LIB_OBJS)
+
+bxxd: $(BXXD_OBJS) libbinmod.a
+       $(CC) $(CFLAGS) -o bxxd $(BXXD_OBJS) $(LIB_OBJS)
+
+bflag: $(BFLAG_OBJS) libbinmod.a
+       $(CC) $(CFLAGS) -o bflag $(BFLAG_OBJS) $(LIB_OBJS)
+
+bwrit: $(BWRIT_OBJS) libbinmod.a
+       $(CC) $(CFLAGS) -o bwrit $(BWRIT_OBJS) $(LIB_OBJS)
+
+clean:
+       /bin/rm -f core *.o bxxd bips bgrep bsed bflag bwrit libbinmod.a
+
+distclean: clean
+       /bin/rm -f Makefile config.h config.status config.cache config.log
+
+nuke: distclean
+       /bin/rm -fr autom4te.cache configure
+
+mostlyclean: clean
+
+maintainer-clean: clean
+
+#PROG.info: PROG.texi
+#      makeinfo PROG.texi
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..d33cc2c
--- /dev/null
+++ b/README
@@ -0,0 +1,120 @@
+libbinmod - A generic binary inspection and patching library/tools
+========================================================================
+
+0) Short installation
+1) About
+2) Goals/Motivation
+3) Structure
+4) Tool-specific info
+  4.1) bgrep
+  4.2) bsed
+  4.3) bips
+  4.4) bxxd
+
+------------------------------------------------------------------------
+0) Short Installation Instructions
+------------------------------------------------------------------------
+
+If you downloaded this source from git:
+  autoreconf -fi && ./configure && make && make install
+
+If you downloaded a release tarball:
+  ./configure && make && make install
+
+In other words, standard autoconf.
+
+------------------------------------------------------------------------
+1) About libbinmod
+------------------------------------------------------------------------
+
+libbinmod is a set of utilities for inspecting and altering binary
+files. By 'binary files', I am referring not to executables, but any
+form of data blob, executable, or otherwise. This is also pretty
+shitty and has been sitting on my HD for a while, so...yeah.
+
+These (mostly) functional tools are provided.
+
+  bgrep - Binary grep
+  bsed  - Binary stream editor (incomplete)
+  bips  - IPS patcher
+  bxxd  - Hex dumper
+  bflag - LAA flag flipper
+  bwrit - Binary insertion
+
+------------------------------------------------------------------------
+2) Goals and Motivation
+------------------------------------------------------------------------
+
+libbinmod was created out of frustration with current binary editing
+tools. Essentially, your choices now boil down to one of the following:
+
+  * A graphical hex editor
+  * xdelta3
+  * One of numerous bgrep implementations
+
+This is inadequate. For many reasons, not limited to the following:
+
+  * dd is not a good stream editor, and sed has limited usability on
+    binaries, at least safely so.
+  * Un-hexdumping is complicated by the multiple tools available.
+    There's xxd, hexdump, etc. All with different formats.
+  * xdelta is not a plaintext or readable format. It is special purpose,
+    and while it fares well, for smaller jobs a tool that operates on
+    plaintext would be nice.
+  * All of the many bgrep tools are not feature complete.
+    They might not be able to print context, display, etc.
+  * There's a lack of IPS patchers on linux.
+
+------------------------------------------------------------------------
+4) Tool-specific Information
+------------------------------------------------------------------------
+
+----- 4.1) bgrep
+
+    Binary grep isn't a terribly new concept. It is however, not handled
+    by GNU grep, and it is also done rather terribly in the numerous
+    implementations. grep's greatest strength is in the flexibility of
+    output that makes usage of the output elsewhere possible.
+
+    None of the implementations of bgrep I have tried provide this.
+
+    Not that my implementation does either, but hey. I'm trying at least.
+
+----- 4.2) bsed
+
+    Just a dump search and replace tool. Move along now. Maybe someday.
+
+----- 4.3) bips
+
+    bips is an IPS/IPS32 patcher. IPS and IPS32 are patch formats
+    commonly used in ROM hacks and game modifications. They offer no
+    contextual information, however, so once a patch is applied it
+    cannot be reverted without generating a patch backwards.
+
+    IPS is much more common than IPS32. However, it has a hard limit on
+    file size of 2^24-1 bytes, or 16M. This makes it useless on larger
+    files, which is why BPS/beat was used for Mother 3's fan translation
+    for example.
+
+    IPS32 is a variant of IPS which rather than get fancy like BPS or
+    xdelta, just fixes what broke and uses 32-bit offsets. Some of the
+    touhoumon rom hacks were distributed in this format. 2^32-1 is 4GB,
+    which is not likely to be hit since IPS has been largely obsoleted
+    already.
+
+    Additionally, there is an extension to the above used by Lunar IPS -
+    a truncation/expansion value immediately after the end. I'm unsure
+    if this can occur in IPS32; regardless, I check for a four-byte
+    value anyways.
+
+----- 4.4) bxxd
+
+    Another hexdump implementation.
+
+----- 4.5) bflag
+
+    Flips the large address flag in windows executables.
+
+----- 4.6) bwrit
+
+    Writes a value to a file. Useful as a substitute for quick hex edits.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..f378f20
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+autoreconf -f -i
diff --git a/bflag.c b/bflag.c
new file mode 100644 (file)
index 0000000..a7703b0
--- /dev/null
+++ b/bflag.c
@@ -0,0 +1,158 @@
+/* bgrep - A binary grep tool. */
+
+#include "blib.h"
+
+#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x20
+
+int flip_pe_la_flag(char* name) {
+       map_file(name, WRITE_FILE);
+
+       if( CH_BUF(0) != 'M' || CH_BUF(1) != 'Z' )
+               goto invalid_ret; // MZ header missing.
+
+       uint32_t pe_loc = U32_BUF(0x3C);
+
+       if ( U32_BUF(pe_loc) != 0x4550 )
+               goto invalid_ret; // No PE header here.
+
+       pe_loc += 0x12;
+       U16_BUF(pe_loc) ^= IMAGE_FILE_LARGE_ADDRESS_AWARE; // Toggle it.
+
+       // Succeeded
+       unmap_file();
+       return 0;
+
+invalid_ret:
+       unmap_file();
+       return 1;
+}
+
+int check_pe_la_flag(char* name) {
+       map_file(name, READ_FILE);
+
+       if( CH_BUF(0) != 'M' || CH_BUF(1) != 'Z' )
+               goto invalid_ret; // MZ header missing.
+
+       uint32_t pe_loc = U32_BUF(0x3C);
+
+       if ( U32_BUF(pe_loc) != 0x4550 )
+               goto invalid_ret; // No PE header here.
+
+       pe_loc += 0x12;
+       if( !(U16_BUF(pe_loc) & IMAGE_FILE_LARGE_ADDRESS_AWARE) )
+               goto no_ret; // Not LA aware, is PE
+
+       // LA aware PE image.
+       unmap_file();
+       return 0;
+
+no_ret:
+       unmap_file();
+       return 1;
+
+invalid_ret:
+       unmap_file();
+       return 2;
+}
+
+int check_pe(char* name) {
+       map_file(name, READ_FILE);
+
+       if( CH_BUF(0) != 'M' || CH_BUF(1) != 'Z' )
+               goto invalid_ret; // MZ header missing.
+
+       uint32_t pe_loc = U32_BUF(0x3C);
+
+       if ( U32_BUF(pe_loc) != 0x4550 )
+               goto invalid_ret; // No PE header here.
+
+       unmap_file();
+       return 0; // Is PE
+
+invalid_ret:
+       unmap_file();
+       return 1; // Not PE
+}
+
+
+void help(char* name) {
+       printf("%s bflag\n", PACKAGE_STRING);
+       printf("(C) 2015 Jon Feldman (@chaoskagami) <%s>\n", PACKAGE_BUGREPORT);
+       printf("Usage:\n");
+       printf("   %s [args] file ...\n", name);
+       printf("Options (PE Images):\n");
+       printf("   -v        Show information on file\n");
+       printf("   -L        Flip the large address aware bit\n");
+       printf("Report bugs to <%s>\n", PACKAGE_URL);
+       printf("This software is licensed under the MIT license.\n");
+}
+
+int main(int argc, char** argv) {
+       int opt;
+
+       int do_op = 0;
+
+       while ( (opt = getopt(argc, argv, "hvL")) != -1) {
+               switch(opt) {
+                       case 'h':
+                               help(argv[0]);
+                               return 0;
+                               break;
+                       case 'v':
+                               do_op = 1; // View
+                               break;
+                       case 'L':
+                               do_op = 2; // Flip LA flag
+                               break;
+                       case '?':
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+                       default:
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+               }
+       }
+
+       if (optind == argc) {
+               fprintf(stderr, "error: requires a file argument. Run with -h for more info\n");
+               return 1;
+       }
+
+       int ret = 0;
+
+       switch(do_op) {
+               case 0:
+                       fprintf(stderr, "error: no operation specified\n");
+                       return 1;
+               case 1: // View flags
+                       printf("Is PE Image: ");
+                       ret = check_pe(argv[optind]);
+
+                       if (ret == 0) {
+                               printf("Yes\n");
+
+                               printf("Large Address Aware: ");
+
+                               ret = check_pe_la_flag(argv[optind]);
+
+                               if (ret == 0) {
+                                       printf("Yes\n");
+                               } else {
+                                       printf("No\n");
+                               }
+                       } else {
+                               printf("No\n");
+                       }
+
+                       break;
+               case 2:
+                       ret = flip_pe_la_flag(argv[optind]);
+                       if (ret == 0)
+                               printf("Flipped the LA bit.\n");
+                       else
+                               printf("Failed to flip LA bit.\n");
+
+                       break;
+       }
+
+}
diff --git a/bgrep.c b/bgrep.c
new file mode 100644 (file)
index 0000000..0582373
--- /dev/null
+++ b/bgrep.c
@@ -0,0 +1,82 @@
+/* bgrep - A binary grep tool. */
+
+#include "blib.h"
+
+void help(char* name) {
+       printf("%s bgrep\n", PACKAGE_STRING);
+       printf("(C) 2015 Jon Feldman (@chaoskagami) <%s>\n", PACKAGE_BUGREPORT);
+       printf("Usage:\n");
+       printf("   %s [args] search file ...\n", name);
+       printf("Options:\n");
+       printf("   -x        Interpret search as hexadecimal pairs\n");
+       printf("   -C BYTES  Context bytes around match (default: 8)\n");
+       printf("   -H        Print filename before match\n");
+       printf("   -E        Interpret search string as regex\n");
+       printf("   -q        Quiet. No stdout. Returns 0 on match, 1 on no match.\n");
+       printf("             All files specified must match for a success return.\n");
+       printf("Report bugs to <%s>\n", PACKAGE_URL);
+       printf("This software is licensed under the MIT license.\n");
+}
+
+int main(int argc, char** argv) {
+       int      ret;
+       uint64_t offset = 0;
+
+       int opt;
+
+       int hex_mode = 0;
+
+       while ( (opt = getopt(argc, argv, "hx")) != -1) {
+               switch(opt) {
+                       case 'h':
+                               help(argv[0]);
+                               return 0;
+                       case 'x':
+                               hex_mode = 1;
+                               break;
+                       case '?':
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+                       default:
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+               }
+       }
+
+       if (optind == argc) {
+               fprintf(stderr, "error: requires a search pattern and file argument. Run with -h for more info\n");
+               return 1;
+       }
+
+       uint8_t* search_pattern = NULL;
+       int search_pattern_len = 0;
+
+       for (int index = optind; index < argc; index++) {
+               if (search_pattern == NULL) {
+                       if (hex_mode == 1) {
+                               search_pattern_len = strlen(argv[index]) / 2;
+                               search_pattern = malloc(search_pattern_len);
+                               unhexdump_buffer(argv[index], strlen(argv[index]), search_pattern);
+                       } else {
+                               search_pattern = argv[index];
+                               search_pattern_len = strlen(argv[index]);
+                       }
+/*                     printf("Finding '");
+                       for(int i=0; i < search_pattern_len; i++)
+                               printf("%hhx ", search_pattern[i]);
+                       printf("'\n");*/
+               } else {
+                       // File argument. Hexdump it.
+                       map_file(argv[index], READ_FILE);
+                       ret = 1;
+                       while (ret) {
+                               ret = search_file_raw(search_pattern, search_pattern_len, &offset);
+                               if (ret) {
+                                       printf("Match at 0x%08X\n", offset);
+                                       offset++;
+                               }
+                       }
+                       ret = unmap_file();
+               }
+       }
+}
diff --git a/bips.c b/bips.c
new file mode 100644 (file)
index 0000000..9ee8fad
--- /dev/null
+++ b/bips.c
@@ -0,0 +1,1000 @@
+/* bips - A IPS patcher tool */
+
+#include "blib.h"
+#include "ips_fmt.h"
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+uint8_t* ips_buffer = NULL;
+
+int simulate  = 0; // Don't actually apply; only show the results.
+int plaintext = 0; // Decompile IPS to a human-readable listing.
+int splitmini = 0; // Split IPS to one chunk mini-ips patches in a folder.
+
+// On success, returns the number of IPS records read. On failure,
+// returns -1.
+int load_ips(__READ char* ips_filename, __WRITE ips_record_t** ips_structs) {
+       // We load the entire thing to memory.
+
+       FILE* f = fopen(ips_filename, "r");
+       fseek(f, 0, SEEK_END);
+       uint64_t pos = ftell(f);
+       rewind(f);
+
+       ips_buffer = (uint8_t*)malloc(pos);
+
+       fread(ips_buffer, 1, pos, f);
+
+       fclose(f);
+
+       printf("Loaded file to memory successfully. Size: %lld\n", pos);
+
+       // Loaded. Begin by checking shit.
+       if( strncmp(ips_buffer, IPS_MAGIC, IPS_MAGIC_LENGTH) ) {
+               // Invalid signature. Not an IPS.
+               free(ips_buffer);
+               ips_buffer = NULL;
+               return -1;
+       }
+
+       // Seems legit. Begin record calculation.
+       int ips_count = 0;
+       uint64_t offset_in = 5;
+       ips_record_t* ips_data = NULL;
+       while( offset_in < pos && strncmp((char*)&ips_buffer[offset_in], IPS_TAIL, IPS_TAIL_LENGTH) ) {
+               // Increment.
+               ips_count++;
+
+               // Reallocate.
+               ips_data = (ips_record_t*)realloc(ips_data, sizeof(ips_record_t) * ips_count);
+
+               ips_data[ips_count-1].info = (ips_record_com_t*)&ips_buffer[offset_in];
+
+               offset_in += sizeof(ips_record_com_t);
+
+               ips_data[ips_count-1].data = (void*)&ips_buffer[offset_in];
+
+               if(ips_data[ips_count-1].info->size[0] == 0x00 && ips_data[ips_count-1].info->size[1] == 0x00) { // Zero is zero regardless of byte order, no casting needed
+                       // RLE. Add the size of an RLE struct.
+                       offset_in += sizeof(ips_record_rle_t);
+
+/*                     printf("[RLE]\t%06x <- len:%u\t[%02x]\n",
+                               BYTE3_TO_UINT32(ips_data[ips_count-1].info->offset),
+                               BYTE2_TO_UINT16( ((ips_record_rle_t*)ips_data[ips_count-1].data)->rle_size ),
+                               ((ips_record_rle_t*)ips_data[ips_count-1].data)->byte); */
+               } else {
+                       offset_in += BYTE2_TO_UINT16(ips_data[ips_count-1].info->size);
+
+/*                     printf("[NORM]\t%06x <- len:%u\t[%02x ... %02x]\n",
+                               BYTE3_TO_UINT32(ips_data[ips_count-1].info->offset),
+                               BYTE2_TO_UINT16(ips_data[ips_count-1].info->size  ),
+                               ((uint8_t*)(ips_data[ips_count-1].data))[0],
+                               ((uint8_t*)(ips_data[ips_count-1].data))[ BYTE2_TO_UINT16(ips_data[ips_count-1].info->size) - 1] ); */
+               }
+
+               // Aaand onto the next.
+       }
+
+       printf("Read in IPS data. Record count: %d\n", ips_count);
+
+       ips_structs[0] = ips_data;
+
+       return ips_count;
+}
+
+int split_ips(__READ const char* filename, __READ ips_record_t* records, __READ int record_count) {
+       char* name = strdup("00000000.ips");
+
+       for (int i=0; i < record_count; i++) {
+
+               uint32_t offset = BYTE3_TO_UINT32(records[i].info->offset);
+
+               sprintf(name, "%08X.ips", offset);
+               FILE* patch = fopen(name, "w");
+
+               fwrite(IPS_MAGIC, 1, IPS_MAGIC_LENGTH, patch); // Write header
+               fwrite(records[i].info, 1, sizeof(ips_record_com_t), patch); // Write patch record.
+
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+
+               if (size == 0) // RLE
+                       fwrite(records[i].data, 1, sizeof(ips_record_rle_t), patch); // Write RLE struct.
+               else // Normal Data
+                       fwrite(records[i].data, 1, size, patch);
+
+               fwrite((void*)IPS_TAIL, 1, IPS_TAIL_LENGTH, patch); // Write footer
+
+               fclose(patch);
+       }
+
+       free(name);
+}
+
+
+int dump_ips_pt(__READ const char* filename, __READ ips_record_t* records, __READ int record_count) {
+       FILE* plain = fopen(filename, "w");
+
+       uint32_t       max_size = 0;
+       for (int i=0; i < record_count; i++) {
+               uint32_t write = BYTE4_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+               if (size == 0) {
+                       size = BYTE2_TO_UINT16( ((ips_record_rle_t*)records[i].data)->rle_size );
+               }
+
+               write += size;
+
+               if (write > max_size)
+                       max_size = write;
+       }
+
+       fprintf(plain, "Patch file: format 'PATCH'\n");
+       fprintf(plain, "Maximum write offset: %u bytes\n", max_size);
+       fprintf(plain, "-----\n");
+
+
+       for (int i=0; i < record_count; i++) {
+               uint32_t offset = BYTE3_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+
+               if (size == 0) { // RLE
+                       ips_record_rle_t* rle = (ips_record_rle_t*)records[i].data;
+
+                       size = BYTE2_TO_UINT16(rle->rle_size);
+
+                       fprintf(plain, "%08X: RLE\nValue %02x, write %u times\n", offset, rle->byte, size);
+               } else { // Normal
+                       uint8_t* bytes = (uint8_t*)records[i].data;
+
+                       fprintf(plain, "%08X: Normal, length is %u\n", offset, size);
+
+                       hexdump_manual(offset, bytes, size, USE_SPACES | BYTE_A | WITH_ASCII | LINE_BREAKS | CENTER_SPLIT | NONPRINT_PERIOD, plain);
+               }
+
+               fprintf(plain, "-----\n");
+       }
+
+       fclose(plain);
+
+       return 0;
+}
+
+int apply_ips(__READ const char* filename, __READ ips_record_t* records, __READ int record_count) {
+       // First; a bit of business. IPS patches can write past a file's
+       // bounds, so we need to calculate the largest possible write.
+       // This involves scanning through the IPS structs.
+
+       uint32_t       max_size = 0;
+       for (int i=0; i < record_count; i++) {
+               uint32_t write = BYTE3_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+               if (size == 0) {
+                       size = BYTE2_TO_UINT16( ((ips32_record_rle_t*)records[i].data)->rle_size );
+               }
+
+               write += size;
+
+               if (write > max_size)
+                       max_size = write;
+       }
+
+       // Simulations shouldn't modify file size.
+       printf("End offset of IPS is %u\n", max_size);
+
+       if (simulate) return 0; // None of the below will actually do anything while simulated.
+
+       // Load file in R/W mmap, with expansion.
+       map_file_expand(filename, max_size);
+
+       for (int i=0; i < record_count; i++) {
+               uint32_t offset = BYTE3_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+
+               if (size == 0) { // RLE
+                       ips_record_rle_t* rle = (ips_record_rle_t*)records[i].data;
+
+                       size = BYTE2_TO_UINT16(rle->rle_size);
+
+                       if (!simulate)
+                               for(int i=0; i < size; i++)
+                                       blib_buffer[offset+i] = rle->byte;
+               } else { // Normal
+                       uint8_t* bytes = (uint8_t*)records[i].data;
+
+                       if (!simulate)
+                               for(int i=0; i < size; i++)
+                                       blib_buffer[offset+i] = bytes[i];
+               }
+       }
+
+       unmap_file();
+
+       return 0;
+}
+
+
+// On success, returns the number of IPS records read. On failure,
+// returns -1.
+int load_ips32(__READ char* ips_filename, __WRITE ips32_record_t** ips_structs) {
+       // We load the entire thing to memory.
+
+       FILE* f = fopen(ips_filename, "r");
+       fseek(f, 0, SEEK_END);
+       uint64_t pos = ftell(f);
+       rewind(f);
+
+       ips_buffer = (uint8_t*)malloc(pos);
+
+       fread(ips_buffer, 1, pos, f);
+
+       fclose(f);
+
+       printf("Loaded file to memory successfully. Size: %lld\n", pos);
+
+       // Loaded. Begin by checking shit.
+       if( strncmp(ips_buffer, IPS32_MAGIC, IPS32_MAGIC_LENGTH) ) {
+               // Invalid signature. Not an IPS.
+               free(ips_buffer);
+               ips_buffer = NULL;
+               return -1;
+       }
+
+       // Seems legit. Begin record calculation.
+       int ips_count = 0;
+       uint64_t offset_in = 5;
+       ips32_record_t* ips_data = NULL;
+       while( offset_in < pos && strncmp((char*)&ips_buffer[offset_in], IPS32_TAIL, IPS32_TAIL_LENGTH) ) {
+               // Increment.
+               ips_count++;
+
+               // Reallocate.
+               ips_data = (ips32_record_t*)realloc(ips_data, sizeof(ips32_record_t) * ips_count);
+
+               ips_data[ips_count-1].info = (ips32_record_com_t*)&ips_buffer[offset_in];
+
+               offset_in += sizeof(ips32_record_com_t);
+
+               ips_data[ips_count-1].data = (void*)&ips_buffer[offset_in];
+
+               if(ips_data[ips_count-1].info->size[0] == 0x00 && ips_data[ips_count-1].info->size[1] == 0x00) { // Zero is zero regardless of byte order, no casting needed
+                       // RLE. Add the size of an RLE struct.
+                       offset_in += sizeof(ips32_record_rle_t);
+
+/*                     printf("[RLE]\t%u <- len:%u\t[%02x]\n",
+                               BYTE4_TO_UINT32(ips_data[ips_count-1].info->offset),
+                               BYTE2_TO_UINT16( ((ips32_record_rle_t*)ips_data[ips_count-1].data)->rle_size ),
+                               ((ips32_record_rle_t*)ips_data[ips_count-1].data)->byte); */
+               } else {
+                       offset_in += BYTE2_TO_UINT16(ips_data[ips_count-1].info->size);
+
+/*                     printf("[NORM]\t%u <- len:%u\t[%02x ... %02x]\n",
+                               BYTE4_TO_UINT32(ips_data[ips_count-1].info->offset),
+                               BYTE2_TO_UINT16(ips_data[ips_count-1].info->size  ),
+                               ((uint8_t*)(ips_data[ips_count-1].data))[0],
+                               ((uint8_t*)(ips_data[ips_count-1].data))[ BYTE2_TO_UINT16(ips_data[ips_count-1].info->size) - 1] ); */
+               }
+
+               // Aaand onto the next.
+       }
+
+       printf("Read in IPS data. Record count: %d\n", ips_count);
+
+       ips_structs[0] = ips_data;
+
+       return ips_count;
+}
+
+int split_ips32(__READ const char* filename, __READ ips32_record_t* records, __READ int record_count) {
+       char* name = strdup("00000000.ips");
+
+       for (int i=0; i < record_count; i++) {
+
+               uint32_t offset = BYTE4_TO_UINT32(records[i].info->offset);
+
+               sprintf(name, "%08X.ips", offset);
+               FILE* patch = fopen(name, "w");
+
+               fwrite((void*)IPS32_MAGIC, 1, IPS32_MAGIC_LENGTH, patch); // Write header
+               fwrite(& records[i].info, 1, sizeof(ips32_record_com_t), patch); // Write patch record.
+
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+
+               if (size == 0) // RLE
+                       fwrite(records[i].data, 1, sizeof(ips32_record_rle_t), patch); // Write RLE struct.
+               else // Normal Data
+                       fwrite(records[i].data, 1, size, patch);
+
+               fwrite((void*)IPS32_TAIL, 1, IPS32_TAIL_LENGTH, patch); // Write footer
+
+               fclose(patch);
+       }
+
+       free(name);
+}
+
+
+int dump_ips32_pt(__READ const char* filename, __READ ips32_record_t* records, __READ int record_count) {
+       FILE* plain = fopen(filename, "w");
+
+       uint32_t       max_size = 0;
+       for (int i=0; i < record_count; i++) {
+               uint32_t write = BYTE4_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+               if (size == 0) {
+                       size = BYTE2_TO_UINT16( ((ips32_record_rle_t*)records[i].data)->rle_size );
+               }
+
+               write += size;
+
+               if (write > max_size)
+                       max_size = write;
+       }
+
+       fprintf(plain, "# Patch file: format 'IPS32'\n");
+       fprintf(plain, "# Maximum write offset: %u bytes\n", max_size);
+
+       for (int i=0; i < record_count; i++) {
+               uint32_t offset = BYTE4_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+
+               if (size == 0) { // RLE
+                       ips32_record_rle_t* rle = (ips32_record_rle_t*)records[i].data;
+
+                       size = BYTE2_TO_UINT16(rle->rle_size);
+
+                       fprintf(plain, "%08X: RLE\nValue %02x, write %u times\n", offset, rle->byte, size);
+               } else { // Normal
+                       uint8_t* bytes = (uint8_t*)records[i].data;
+
+                       fprintf(plain, "%08X: Normal, length is %u\n", offset, size);
+
+                       for(int i=0; i < size; i++)
+                               fprintf(plain, "%02x ", bytes[i]);
+
+                       fprintf(plain, "\n");
+               }
+       }
+
+       fclose(plain);
+
+       return 0;
+}
+
+int apply_ips32(__READ const char* filename, __READ ips32_record_t* records, __READ int record_count) {
+       // First; a bit of business. IPS patches can write past a file's
+       // bounds, so we need to calculate the largest possible write.
+       // This involves scanning through the IPS structs.
+
+       uint32_t       max_size = 0;
+       for (int i=0; i < record_count; i++) {
+               uint32_t write = BYTE4_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+               if (size == 0) {
+                       size = BYTE2_TO_UINT16( ((ips32_record_rle_t*)records[i].data)->rle_size );
+               }
+
+               write += size;
+
+               if (write > max_size)
+                       max_size = write;
+       }
+
+       // Simulations shouldn't modify file size.
+       printf("End offset of IPS is %u\n", max_size);
+
+       if (simulate) return 0; // None of the below will actually do anything while simulated.
+
+       // Load file in R/W mmap, with expansion.
+       map_file_expand(filename, max_size);
+
+       for (int i=0; i < record_count; i++) {
+               uint32_t offset = BYTE4_TO_UINT32(records[i].info->offset);
+               uint16_t size = BYTE2_TO_UINT16(records[i].info->size);
+
+               if (size == 0) { // RLE
+                       ips32_record_rle_t* rle = (ips32_record_rle_t*)records[i].data;
+
+                       size = BYTE2_TO_UINT16(rle->rle_size);
+
+                       if (!simulate)
+                               for(int i=0; i < size; i++)
+                                       blib_buffer[offset+i] = rle->byte;
+               } else { // Normal
+                       uint8_t* bytes = (uint8_t*)records[i].data;
+
+                       if (!simulate)
+                               for(int i=0; i < size; i++)
+                                       blib_buffer[offset+i] = bytes[i];
+               }
+       }
+
+       unmap_file();
+
+       return 0;
+}
+
+void generate_ips_opt(uint8_t* from, size_t from_len, uint8_t* to, size_t to_len, ips_record_t** ips_dat_out, size_t* ips_record_out) {
+       // Pass one - 1byte = 1chunk
+       ips_record_t* ips_dat = NULL;
+       size_t record_num = 0;
+
+       uint32_t i=0;
+       size_t end_same = MIN(from_len, to_len); // We want whatever is smaller.
+
+       // We first build an incredibly unoptimized patch.
+       for(int i=0; i < end_same; i++) {
+               if (from[i] != to[i]) {
+                       // Create a patch record.
+                       ++record_num;
+                       ips_dat = realloc(ips_dat, record_num*sizeof(ips_record_t));
+                       ips_dat[record_num-1].info = calloc(1, sizeof(ips_record_com_t));
+
+                       ips_dat[record_num-1].info->offset[0] = (i >> 16) & 0xFF;
+                       ips_dat[record_num-1].info->offset[1] = (i >> 8) & 0xFF;
+                       ips_dat[record_num-1].info->offset[2] = i & 0xFF;
+
+                       ips_dat[record_num-1].data = &to[i];
+
+                       ips_dat[record_num-1].info->size[0] = 0;
+                       ips_dat[record_num-1].info->size[1] = 1;
+               }
+       }
+
+       printf("Number of changed bytes: %lu\n", record_num);
+
+       ips_record_t* current = ips_dat;
+       ips_dat = NULL;
+
+       size_t rled_num = 0;
+
+       // Next, we go through and convert potential sequences of RLEs.
+       // RLE is a increase in size unless conditions are met:
+
+       // If isolated (e.g. surrounded by same)
+       //   Must be >3 bytes
+       // If Next to changes:
+       //   RLE must be >8 bytes
+
+       while(i < record_num) {
+               // Combine adjacents if relevant.
+               int streak = 0;
+               for( ; i+streak < record_num-1 && streak < 0xffff; streak++) {
+                       uint32_t at   = BYTE3_TO_UINT32(current[i+streak].info->offset);
+                       uint32_t next = BYTE3_TO_UINT32(current[i+streak+1].info->offset);
+                       uint8_t  at_byte   = ((uint8_t*)current[i+streak].data)[0];
+                       uint8_t  next_byte = ((uint8_t*)current[i+streak+1].data)[0];
+                       if (at+1 != next || at_byte != next_byte)
+                               break;
+               }
+               streak++;
+
+               rled_num++;
+
+               ips_dat = realloc(ips_dat, rled_num*sizeof(ips_record_t));
+               ips_dat[rled_num-1].info = calloc(1, sizeof(ips_record_com_t));
+
+               ips_dat[rled_num-1].info->offset[0] = current[i].info->offset[0];
+               ips_dat[rled_num-1].info->offset[1] = current[i].info->offset[1];
+               ips_dat[rled_num-1].info->offset[2] = current[i].info->offset[2];
+
+               if (streak < 4) {
+                       // No RLE. Copy and move on.
+                       ips_dat[rled_num-1].info->size[0] = 0;
+                       ips_dat[rled_num-1].info->size[1] = 1;
+                       ips_dat[rled_num-1].data = current[i].data;
+
+                       i += 1;
+               } else {
+                       // Potentially RLE.
+
+                       int require_extra = 0;
+                       // Is the left side an adjacent patch?
+                       if (i > 0) {
+                               // If i is zero, we're at the beginning so this isn't needed
+                               uint32_t prev = BYTE3_TO_UINT32(current[i-1].info->offset);
+                               uint32_t cur  = BYTE3_TO_UINT32(current[i].info->offset);
+
+                               if (prev+1 == cur)
+                                       require_extra += 1; // We're next to another chunk.
+                       }
+
+                       // Is the right side an adjacent patch?
+                       if (i+streak+1 < record_num) {
+                               uint32_t next = BYTE3_TO_UINT32(current[i+streak+1].info->offset);
+                               uint32_t cur  = BYTE3_TO_UINT32(current[i+streak].info->offset);
+
+                               if (cur+1 == next)
+                                       require_extra += 1; // Next to another chunk.
+                       }
+
+                       int must_be_n = 0;
+                       switch (require_extra) {
+                               case 0:
+                                       must_be_n = 4;
+                                       break;
+                               case 1:
+                                       must_be_n = 9;
+                                       break;
+                               case 2:
+                                       must_be_n = 14;
+                                       break;
+                       }
+
+                       if (streak >= must_be_n) {
+                               // Use RLE encoding here.
+                               ips_dat[rled_num-1].info->size[0] = 0; // Marks RLE.
+                               ips_dat[rled_num-1].info->size[1] = 0; // Marks RLE.
+
+                               ips_dat[rled_num-1].data = calloc(1, sizeof(ips_record_rle_t));
+                               ((ips_record_rle_t*)(ips_dat[rled_num-1].data))->rle_size[0] = (streak >> 8) & 0xFF;
+                               ((ips_record_rle_t*)(ips_dat[rled_num-1].data))->rle_size[1] = streak & 0xFF;
+                               ((ips_record_rle_t*)(ips_dat[rled_num-1].data))->byte = ((uint8_t*)current[i].data)[0];
+
+                               i += streak; // Skip things in the RLE.
+                       } else {
+                               // Standard copy.
+                               ips_dat[rled_num-1].info->size[0] = 0;
+                               ips_dat[rled_num-1].info->size[1] = 1;
+                               ips_dat[rled_num-1].data = current[i].data;
+
+                               i += 1;
+                       }
+               }
+       }
+
+       printf("Number of records after RLE pass: %lu\n", rled_num);
+
+       // Free everything from current.
+       for(int i=0; i < record_num; i++) {
+               free(current[i].info);
+       }
+       free(current);
+
+       current = ips_dat;
+
+       ips_dat = NULL;
+
+       size_t final_num = 0;
+
+       // RLE optimizations have been applied. Loop thru now and combine
+       // all adjacent 1byte records
+       i = 0;
+       while(i < rled_num) {
+               if (BYTE2_TO_UINT16(current[i].info->size) == 0) {
+                       // RLE chunks get copied as-is.
+                       final_num++;
+
+                       ips_dat = realloc(ips_dat, final_num*sizeof(ips_record_t));
+                       ips_dat[final_num-1].info = calloc(1, sizeof(ips_record_com_t));
+
+                       ips_dat[final_num-1].info->offset[0] = current[i].info->offset[0];
+                       ips_dat[final_num-1].info->offset[1] = current[i].info->offset[1];
+                       ips_dat[final_num-1].info->offset[2] = current[i].info->offset[2];
+
+                       ips_dat[final_num-1].info->size[0] = current[i].info->size[0];
+                       ips_dat[final_num-1].info->size[1] = current[i].info->size[1];
+
+                       ips_dat[final_num-1].data = calloc(1, sizeof(ips_record_rle_t));
+
+                       ((ips_record_rle_t*)(ips_dat[final_num-1].data))->rle_size[0] = ((ips_record_rle_t*)(current[i].data))->rle_size[0];
+                       ((ips_record_rle_t*)(ips_dat[final_num-1].data))->rle_size[1] = ((ips_record_rle_t*)(current[i].data))->rle_size[1];
+                       ((ips_record_rle_t*)(ips_dat[final_num-1].data))->byte    = ((ips_record_rle_t*)(current[i].data))->byte;
+
+                       i++;
+               } else {
+                       // Combine adjacents if relevant.
+                       int streak = 0;
+                       for( ; i+streak < rled_num-1 && streak < 0xffff; streak++) {
+                               uint32_t at   = BYTE3_TO_UINT32(current[i+streak].info->offset);
+                               uint32_t next = BYTE3_TO_UINT32(current[i+streak+1].info->offset);
+                               uint32_t next_rle = BYTE3_TO_UINT32(current[i+streak+1].info->size);
+                               if (at+1 != next || next_rle == 0)
+                                       break;
+                       }
+                       streak++;
+
+                       // Normals actually still point into the original memory, so
+                       // data actually stays and we just change the size.
+
+                       final_num++;
+
+                       ips_dat = realloc(ips_dat, final_num*sizeof(ips_record_t));
+                       ips_dat[final_num-1].info = calloc(1, sizeof(ips_record_com_t));
+
+                       ips_dat[final_num-1].info->offset[0] = current[i].info->offset[0];
+                       ips_dat[final_num-1].info->offset[1] = current[i].info->offset[1];
+                       ips_dat[final_num-1].info->offset[2] = current[i].info->offset[2];
+
+                       ips_dat[final_num-1].info->size[0] = (streak >> 8) & 0xFF;
+                       ips_dat[final_num-1].info->size[1] = streak & 0xFF;
+
+                       ips_dat[final_num-1].data = current[i].data;
+
+                       i += streak;
+               }
+       }
+
+       printf("Number of records after combine pass: %lu\n", final_num);
+
+       // Free everything from current.
+       for(int i=0; i < rled_num; i++) {
+               free(current[i].info);
+       }
+       free(current);
+
+       ips_record_out[0] = final_num;
+       ips_dat_out[0] = ips_dat;
+}
+
+void generate_ips(uint8_t* from, size_t from_len, uint8_t* to, size_t to_len, ips_record_t** ips_dat_out, size_t* ips_record_out) {
+       ips_record_t* ips_dat = NULL;
+       size_t record_num = 0;
+
+       uint32_t i=0;
+       size_t end_same = MIN(from_len, to_len); // We want whatever is smaller.
+
+       while(1) {
+               ++record_num;
+
+               // Allocate a new struct.
+               ips_dat = realloc(ips_dat, record_num*sizeof(ips_record_t));
+               ips_dat[record_num-1].info = calloc(1, sizeof(ips_record_com_t));
+
+               for(; i < end_same; i++) {
+                       // Seek until the first *different* bit.
+                       if (from[i] != to[i]) break;
+               }
+
+               // End of file?
+               if (i >= end_same - 1) {
+                       free(ips_dat[record_num-1].info);
+                       --record_num;
+                       ips_dat = realloc(ips_dat, record_num*sizeof(ips_record_t));
+                       break;
+               }
+
+               ips_dat[record_num-1].info->offset[0] = (i >> 16) & 0xFF;
+               ips_dat[record_num-1].info->offset[1] = (i >> 8) & 0xFF;
+               ips_dat[record_num-1].info->offset[2] = i & 0xFF;
+
+               // Different bit.
+
+               ips_dat[record_num-1].data = &to[i];
+               uint16_t write_len = 0;
+
+               for(; i < end_same; i++) {
+                       if (from[i] == to[i]) break; // Same bit.
+                       ++write_len;
+               }
+
+               ips_dat[record_num-1].info->size[0] = (write_len >> 8) & 0xFF;
+               ips_dat[record_num-1].info->size[1] = write_len & 0xFF;
+
+               if (i >= end_same - 1) break;
+       }
+
+       ips_record_out[0] = record_num;
+       ips_dat_out[0] = ips_dat;
+}
+
+void generate_ips32(uint8_t* from, size_t from_len, uint8_t* to, size_t to_len, ips32_record_t** ips_dat_out, size_t* ips_record_out) {
+       ips32_record_t* ips_dat = NULL;
+       size_t record_num = 0;
+
+       uint32_t i=0;
+       size_t end_same = MIN(from_len, to_len); // We want whatever is smaller.
+
+       while(1) {
+               ++record_num;
+
+               // Allocate a new struct.
+               ips_dat = realloc(ips_dat, record_num*sizeof(ips32_record_t));
+               ips_dat[record_num-1].info = calloc(1, sizeof(ips32_record_com_t));
+
+               for(; i < end_same; i++) {
+                       // Seek until the first *different* bit.
+                       if (from[i] != to[i]) break;
+               }
+
+               // End of file?
+               if (i >= end_same - 1) {
+                       free(ips_dat[record_num-1].info);
+                       --record_num;
+                       ips_dat = realloc(ips_dat, record_num*sizeof(ips32_record_t));
+                       break;
+               }
+               ips_dat[record_num-1].info->offset[0] = (i >> 24) & 0xFF;
+               ips_dat[record_num-1].info->offset[1] = (i >> 16) & 0xFF;
+               ips_dat[record_num-1].info->offset[2] = (i >> 8) & 0xFF;
+               ips_dat[record_num-1].info->offset[3] = i & 0xFF;
+
+               // Different bit.
+
+               ips_dat[record_num-1].data = &to[i];
+               uint16_t write_len = 0;
+
+               for(; i < end_same; i++) {
+                       if (from[i] == to[i]) break; // Same bit.
+                       ++write_len;
+               }
+
+               ips_dat[record_num-1].info->size[0] = (write_len >> 8) & 0xFF;
+               ips_dat[record_num-1].info->size[1] = write_len & 0xFF;
+
+               if (i >= end_same - 1) break;
+       }
+
+       ips_record_out[0] = record_num;
+       ips_dat_out[0] = ips_dat;
+}
+
+int identify_patch(__READ const char* filename) {
+       char test[8];
+       FILE* f = fopen(filename, "r");
+       fseek(f, 0, SEEK_END);
+       if (ftell(f) < 8) {
+               // Wrong. No patch is smaller than this. Die.
+               return TYPE_INVALID;
+       }
+       rewind(f);
+       fread(test, 1, 8, f);
+
+       fclose(f);
+
+       if ( !strncmp(test, IPS_MAGIC, IPS_MAGIC_LENGTH) )
+               return TYPE_IPS;
+       if ( !strncmp(test, IPS32_MAGIC, IPS32_MAGIC_LENGTH) )
+               return TYPE_IPS32;
+
+       return TYPE_INVALID;
+}
+
+void help(char* name) {
+       printf("%s bips\n", PACKAGE_STRING);
+       printf("(C) 2015 Jon Feldman (@chaoskagami) <%s>\n", PACKAGE_BUGREPORT);
+       printf("Usage:\n");
+       printf("Apply a patch:\n");
+       printf("   %s a [args] patch file [output_file]\n", name);
+       printf("Generate a patch:\n");
+       printf("   %s c [args] file_a file_b output_file\n", name);
+       printf("Options:\n");
+       printf("   -s        Simulate; Only print what would be done, don't actually make any changes.\n");
+       printf("   -x        Split each chunk out to separate IPS patches\n");
+       printf("   -d        Dump a patch to a format that's human readable.\n");
+       printf("   -r        Create a 'raw' patch, with no header nor tail.\n");
+       printf("   -f        Force generating patches which would be larger than input files\n");
+       printf("Report bugs to <%s>\n", PACKAGE_URL);
+       printf("This software is licensed under the MIT license.\n");
+
+}
+
+int main(int argc, char** argv) {
+       ips_record_t*   ips   = NULL;
+       ips32_record_t* ips32 = NULL;
+       int record_count = 0;
+       int opt;
+       int force = 0;
+       int raw = 0;
+
+       while ( (opt = getopt(argc, argv, "hdxsrf")) != -1) {
+               switch(opt) {
+                       case 'h':
+                               help(argv[0]);
+                               return 1;
+                       case 'd':
+                               plaintext = 1;
+                               break;
+                       case 'x':
+                               splitmini = 1;
+                               break;
+                       case 's':
+                               simulate = 1;
+                               break;
+                       case 'r':
+                               raw = 1;
+                               break;
+                       case 'f':
+                               force = 1;
+                               break;
+                       case '?':
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+                       default:
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+               }
+       }
+
+       if (argc - optind < 3) {
+               fprintf(stderr, "error: requires more arguments. Run with -h for more info\n");
+               return 1;
+       }
+
+       int operation = 0;
+       switch(argv[optind][0]) {
+               case 'a':
+                       operation = 0;
+                       break;
+               case 'c':
+                       operation = 1;
+                       break;
+               default:
+                       break;
+       }
+
+       if (operation == 0) {
+               char* patch  = argv[optind+1];
+               int type = identify_patch(patch);
+               char* input  = argv[optind+2];
+
+               if (splitmini == 1) {
+                       switch(type) {
+                               case TYPE_IPS:
+                                       printf("Patch format: IPS (24-bit offsets)\n");
+                                       record_count = load_ips(patch, &ips);
+                                       split_ips(input, ips, record_count);
+                                       break;
+                               case TYPE_IPS32:
+                                       printf("Patch format: IPS32 (IPS derivative w/ 32-bit offsets)\n");
+                                       record_count = load_ips32(argv[optind+1], &ips32);
+                                       split_ips32(input, ips32, record_count);
+                                       break;
+                               default:
+                                       printf("Patch format not understood or invalid.\n");
+                                       break;
+                       }
+               } else if (plaintext == 1) {
+                       switch(type) {
+                               case TYPE_IPS:
+                                       printf("Patch format: IPS (24-bit offsets)\n");
+                                       record_count = load_ips(patch, &ips);
+                                       dump_ips_pt(input, ips, record_count);
+                                       break;
+                               case TYPE_IPS32:
+                                       printf("Patch format: IPS32 (IPS derivative w/ 32-bit offsets)\n");
+                                       record_count = load_ips32(patch, &ips32);
+                                       dump_ips32_pt(input, ips32, record_count);
+                                       break;
+                               default:
+                                       printf("Patch format not understood or invalid.\n");
+                                       break;
+                       }
+               } else {
+                       char* out_file = input;
+                       if (argc - optind == 4) {
+                               // Output file is specified. Copy file.
+                               int ret = copy_file(argv[optind+3], out_file);
+
+                               if (ret) {
+                                       printf("Error copying file '%s' to '%s'\n", out_file, argv[optind+3]);
+                                       return -1; // Error.
+                               }
+
+                               printf("Copied file '%s' to '%s'\n", out_file, argv[optind+3]);
+
+                               out_file = argv[optind+3];
+                       }
+
+                       if (simulate)
+                               printf("Patching is simulated; e.g. won't be written to disk.\n");
+
+                       switch(type) {
+                               case TYPE_IPS:
+                                       printf("Patch format: IPS (24-bit offsets)\n");
+                                       record_count = load_ips(patch, &ips);
+                                       apply_ips(out_file, ips, record_count);
+                                       break;
+                               case TYPE_IPS32:
+                                       printf("Patch format: IPS32 (IPS derivative w/ 32-bit offsets)\n");
+                                       record_count = load_ips32(patch, &ips32);
+                                       apply_ips32(out_file, ips32, record_count);
+                                       break;
+                               default:
+                                       printf("Patch format not understood or invalid.\n");
+                                       break;
+                       }
+               }
+
+               free(ips);
+               free(ips32);
+               free(ips_buffer);
+       } else if (operation == 1) {
+               FILE* a = fopen(argv[optind+1], "rb");
+               FILE* b = fopen(argv[optind+2], "rb");
+               fseek(a, 0, SEEK_END);
+               fseek(b, 0, SEEK_END);
+               size_t a_len = ftell(a);
+               size_t b_len = ftell(b);
+               rewind(a);
+               rewind(b);
+               if (b_len > 0xFFFFFFFF) {
+                       // ...Too large.
+                       fclose(a);
+                       fclose(b);
+                       fprintf(stderr, "Files are too large for IPS format.");
+                       return 1;
+               } else if(b_len > 0xFFFFFF) {
+                       uint8_t* a_d = malloc(a_len);
+                       fread(a_d, 1, a_len, a);
+                       fclose(a);
+                       uint8_t* b_d = malloc(b_len);
+                       fread(b_d, 1, b_len, b);
+                       fclose(b);
+
+                       // IPS32. Can't fit in IPS.
+                       size_t records = 0;
+                       generate_ips32(a_d, a_len, b_d, b_len, &ips32, &records);
+
+                       FILE* out = fopen(argv[optind+3], "w");
+                       if (raw == 0) fwrite(IPS32_MAGIC, 1, IPS32_MAGIC_LENGTH, out);
+
+                       for(size_t i=0; i < records; i++) {
+                               fwrite(&(ips32[i].info), 1, sizeof(ips32_record_com_t), out);
+                               if (BYTE2_TO_UINT16(ips32[i].info->size) == 0) { // RLE
+                                       fwrite(ips32[i].data, 1, sizeof(ips32_record_rle_t), out);
+                               } else {
+                                       fwrite(ips32[i].data, 1, BYTE2_TO_UINT16(ips32[i].info->size), out);
+                               }
+                       }
+
+                       if (raw == 0) fwrite(IPS32_TAIL, 1, IPS32_TAIL_LENGTH, out);
+
+                       fclose(out);
+
+                       return 0;
+               } else {
+                       uint8_t* a_d = malloc(a_len);
+                       fread(a_d, 1, a_len, a);
+                       fclose(a);
+                       uint8_t* b_d = malloc(b_len);
+                       fread(b_d, 1, b_len, b);
+                       fclose(b);
+
+                       // Plain ol' IPS.
+                       size_t records = 0;
+                       generate_ips_opt(a_d, a_len, b_d, b_len, &ips, &records);
+
+                       printf("Generated patch consists of %ld records\n", records);
+                       size_t size = 0;
+                       for (int i=0; i < records; i++) {
+                               size += sizeof(ips_record_com_t);
+                               int t = BYTE2_TO_UINT16(ips[i].info->size);
+                               if (!t)
+                                       size += sizeof(ips_record_rle_t);
+                               size += t;
+                       }
+                       size += IPS_MAGIC_LENGTH + IPS_TAIL_LENGTH;
+                       printf("Generated patch output size will be: %ld bytes\n", size);
+
+                       if (size > b_len) {
+                               if (force) {
+                                       printf("WARNING - output patch is larger than input (-f was specified)\n");
+                               } else {
+                                       printf("ERROR - output patch is larger than input\n");
+                                       // Clean up.
+                                       exit(1);
+                               }
+                       }
+
+                       FILE* out = fopen(argv[optind+3], "w");
+                       if (raw == 0) fwrite(IPS_MAGIC, 1, IPS_MAGIC_LENGTH, out);
+
+                       for(size_t i=0; i < records; i++) {
+                               fwrite(ips[i].info, 1, sizeof(ips_record_com_t), out);
+                               if (BYTE2_TO_UINT16(ips[i].info->size) == 0) { // RLE
+                                       fwrite(ips[i].data, 1, sizeof(ips_record_rle_t), out);
+                               } else {
+                                       fwrite(ips[i].data, 1, BYTE2_TO_UINT16(ips[i].info->size), out);
+                               }
+                       }
+
+                       if (raw == 0) fwrite(IPS_TAIL, 1, IPS_TAIL_LENGTH, out);
+
+                       fclose(out);
+
+                       printf("Done\n", records);
+
+                       return 0;
+               }
+       }
+}
diff --git a/blib.c b/blib.c
new file mode 100644 (file)
index 0000000..09ddf5b
--- /dev/null
+++ b/blib.c
@@ -0,0 +1,300 @@
+#define BLIB_NO_EXTERNS
+
+#include "blib.h"
+
+int            blib_filefd;
+unsigned char* blib_buffer;
+struct stat    blib_stat;
+int            open_flags;
+int            mmap_flags;
+
+int copy_file(__READ const char* dest, __READ const char* src) {
+       // We use the generic POSIX way.
+
+       int in_fd = open(src, O_RDONLY);
+       if(in_fd <= 0)
+               goto error_copy_file;
+
+       int out_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+       if (out_fd <= 0)
+               goto error_copy_file;
+
+       char buf[8192];
+
+       while (1) {
+               ssize_t result = read(in_fd, buf, sizeof(buf));
+
+               if (!result) // Done?
+                       break;
+
+               if(result < 0) {
+                       // ERROR!
+                       fprintf(stderr, "Negative bytes read?\n");
+                       goto error_copy_file;
+               }
+
+               if (write(out_fd, buf, result) != result) {
+                       // Short write? Out of disk, maybe. Either way, abort.
+                       fprintf(stderr, "Short write?\n");
+                       goto error_copy_file;
+               }
+       }
+
+       close(in_fd);
+       close(out_fd);
+
+       return 0;
+
+error_copy_file:
+       if (in_fd) close(in_fd);
+       if (out_fd) close(out_fd);
+
+       return 1;
+}
+
+// Loads up a file. 0 on success, 1 on error.
+int map_file(__READ const char* name,
+             __READ const int mode)
+{
+       open_flags = 0;
+       mmap_flags = 0;
+
+       if (mode == READ_FILE) {
+               open_flags = O_RDONLY;
+               mmap_flags = PROT_READ;
+       }
+       else if (mode == WRITE_FILE) {
+               open_flags = O_RDWR;
+               mmap_flags = PROT_READ | PROT_WRITE;
+       }
+
+    blib_filefd = open(name, open_flags);
+    if (blib_filefd == -1) {
+               perror("Error opening file for reading");
+               exit(EXIT_FAILURE);
+    }
+
+       int status = fstat(blib_filefd, &blib_stat);
+
+    blib_buffer = mmap(0, blib_stat.st_size, mmap_flags, MAP_SHARED, blib_filefd, 0);
+    if (blib_buffer == MAP_FAILED) {
+               close(blib_filefd);
+               perror("Error mmapping the file");
+               exit(EXIT_FAILURE);
+    }
+
+    return 0;
+}
+
+// Loads up a file. 0 on success, 1 on error.
+// Note that it must be opened write.
+int map_file_expand(__READ const char* name,
+                    __READ const uint32_t expand)
+{
+       open_flags = O_RDWR;
+       mmap_flags = PROT_READ | PROT_WRITE;
+
+    blib_filefd = open(name, open_flags);
+    if (blib_filefd == -1) {
+               perror("Error opening file for reading");
+               exit(EXIT_FAILURE);
+    }
+
+       int status = fstat(blib_filefd, &blib_stat);
+
+       // Check size and expand.
+       if (blib_stat.st_size < expand) {
+               // Smaller than write. Expand.
+               ftruncate(blib_filefd, expand);
+               // Re-read stat.
+               int status = fstat(blib_filefd, &blib_stat);
+       }
+
+    blib_buffer = mmap(0, blib_stat.st_size, mmap_flags, MAP_SHARED, blib_filefd, 0);
+    if (blib_buffer == MAP_FAILED) {
+               close(blib_filefd);
+               perror("Error mmapping the file");
+               exit(EXIT_FAILURE);
+    }
+
+    return 0;
+}
+
+// Unloads the file.
+int unmap_file() {
+       if (mmap_flags == PROT_READ | PROT_WRITE) {
+               // Sync it to disk.
+               msync(blib_buffer, blib_stat.st_size, MS_SYNC);
+       }
+
+       if (munmap(blib_buffer, blib_stat.st_size) == -1) {
+               perror("Error un-mmapping the file");
+       }
+       close(blib_filefd);
+}
+
+// Searches for a pattern. Returns 1 on match with offset stored to
+// 'offset'. Returns 0 on EOF/no more matches.
+int search_file_raw(__READ const unsigned char* pattern,
+                                       __READ const int pattern_len,
+                    __WRITE uint64_t* offset)
+{
+       for(uint64_t i = *offset; i < blib_stat.st_size; i++) { // mmap offset
+               int ret = memcmp(blib_buffer+i, pattern, pattern_len);
+               if (ret == 0) {
+                       *offset = i;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int hexdump_file(__READ uint64_t offset, __READ uint64_t len, __READ int format) {
+
+       if (offset > blib_stat.st_size) {
+               // Invalid. Exit.
+               return 0;
+       } else if (offset + len > blib_stat.st_size) {
+               // Length is too long. Return the amount to correct it.
+               return blib_stat.st_size - (offset+len);
+       }
+
+       return hexdump_manual(offset, blib_buffer, len, format, stdout);
+}
+
+int hexdump_manual(__READ uint64_t offset, __READ uint8_t* buffer, __READ uint64_t len, __READ int format, FILE* output) {
+       // Okay, hexdump.
+
+       for (int i = 0; i < len;) {
+               int length = 16;
+               if (len - i < 16) // Incomplete line.
+                       length = len - i;
+
+               // First, offsets.
+               if (format & PRINT_OFFSET) {
+
+                       fprintf(output, "%08x", i);
+
+                       if (format & USE_COLON)
+                               fprintf(output, ":");
+                       else if (format & PIPE_OFFSET)
+                               fprintf(output, " | ");
+                       else
+                               fprintf(output, " ");
+                       fprintf(output, " ");
+               }
+
+               // Next, bytes.
+               if (format & BYTE_A) { // One byte
+                       int copylen = length;
+                       for(int j=0; j < 16; j++) {
+                               if (copylen) {
+                                       fprintf(output, "%02x ", buffer[i+j]);
+                                       copylen--;
+                               } else {
+                                       fprintf(output, "   ");
+                               }
+                               if (j == 7 && (format & CENTER_SPLIT) )
+                                       fprintf(output, " ");
+                       }
+               } else if (format & BYTE_B) { // Two byte
+                       int copylen = length;
+                       for(int j=0; j < 16; j++) {
+                               if (copylen) {
+                                       fprintf(output, "%02x", buffer[i+j]);
+                                       if (j % 2 == 1)
+                                               fprintf(output, " ");
+                                       copylen--;
+                               } else {
+                                       fprintf(output, "  ");
+                                       if (j % 2 == 1)
+                                               fprintf(output, " ");
+                               }
+                               if (j == 7 && (format & CENTER_SPLIT) )
+                                       fprintf(output, " ");
+                       }
+               } else if (format & BYTE_C) { // Three byte
+                       int copylen = length;
+                       for(int j=0; j < 16; j++) {
+                               if (copylen) {
+                                       fprintf(output, "%02x", buffer[i+j]);
+                                       if (j % 4 == 3)
+                                               fprintf(output, " ");
+                                       copylen--;
+                               } else {
+                                       fprintf(output, "  ");
+                                       if (j % 4 == 3)
+                                               fprintf(output, " ");
+                               }
+                               if (j == 7 && (format & CENTER_SPLIT) )
+                                       fprintf(output, " ");
+                       }
+               }
+
+               if (format & WITH_ASCII) { // Print ascii
+                       fprintf(output, " ");
+
+                       if (format & PIPE_SEPARATE) {
+                               fprintf(output, "|");
+                       }
+
+                       for(int j=0; j < length; j++) {
+                               // We only print printables.
+                               int c = buffer[i+j];
+                               if (c > 0x1f && c < 0x7f) // Printable?
+                                       fprintf(output, "%c", c);
+                               else {
+                                       if (format & NONPRINT_PERIOD) {
+                                               fprintf(output, ".");
+                                       } else if (format & NONPRINT_UNDERS) {
+                                               fprintf(output, "_");
+                                       } else {
+                                               fprintf(output, " ");
+                                       }
+                               }
+                       }
+
+                       if (format & PIPE_SEPARATE) {
+                               fprintf(output, "|");
+                       }
+               }
+
+               i += 16;
+               fprintf(output, "\n");
+       }
+}
+
+uint8_t hexb_to_u8(char a, char b) {
+       if (a >= 'a' && a <= 'f') {
+               a -= 'a';
+               a += 10;
+       } else if (a >= 'A' && a <= 'F') {
+               a -= 'A';
+               a += 10;
+       } else if (a >= '0' && a <= '9') {
+               a -= '0';
+       } else {
+               return 0;
+       }
+
+       if (b >= 'a' && b <= 'f') {
+               b -= 'a';
+               b += 10;
+       } else if (b >= 'A' && b <= 'F') {
+               b -= 'A';
+               b += 10;
+       } else if (b >= '0' && b <= '9') {
+               b -= '0';
+       } else {
+               return 0;
+       }
+
+       return ((a<<4)|b);
+}
+
+// Unhexdump
+int unhexdump_buffer(__READ uint8_t* buffer, __READ uint64_t len, __WRITE uint8_t* output) {
+       for(int i=0; i < (len/2); i++) {
+               output[i] = hexb_to_u8(buffer[i*2], buffer[i*2+1]);
+       }
+}
diff --git a/blib.h b/blib.h
new file mode 100644 (file)
index 0000000..1315c8c
--- /dev/null
+++ b/blib.h
@@ -0,0 +1,100 @@
+/* Common functions for binary modifying programs. */
+
+#ifndef __BINLIB_H
+#define __BINLIB_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#include "config.h"
+
+#ifdef MAX
+#undef MAX
+#endif
+#define MAX(a,b) \
+  ({ __typeof__ (a) _a = (a); \
+     __typeof__ (b) _b = (b); \
+     _a > _b ? _a : _b; })
+
+#ifndef BLIB_NO_EXTERNS
+  // No externs in blib.c.
+  extern unsigned char* blib_buffer;
+  extern struct stat    blib_stat;
+#endif
+
+#define U32_BUF(x) (((uint32_t*)(&blib_buffer[x]))[0])
+#define U16_BUF(x) (((uint16_t*)(&blib_buffer[x]))[0])
+#define CH_BUF(x)  blib_buffer[x]
+
+// map_file flags
+#define READ_FILE  0
+#define WRITE_FILE 1 // Implies read
+
+// Hexdump feature bits
+#define USE_SPACES      1
+#define PRINT_OFFSET    2
+#define USE_COLON       4
+#define LINE_BREAKS     8
+#define PIPE_SEPARATE   16
+#define PIPE_OFFSET     32
+#define WITH_ASCII      64
+#define BYTE_A          128  // 1byte
+#define BYTE_B          256  // 2byte
+#define BYTE_C          512  // 4byte
+#define CENTER_SPLIT    1024
+#define NONPRINT_PERIOD 2048
+#define NONPRINT_UNDERS 4096
+#define COLORIZED       8192
+
+// Hexdump presets.
+#define SPACED_BYTES       USE_SPACES | BYTE_A
+#define PRESET_XXD         USE_SPACES | BYTE_B | PRINT_OFFSET | USE_COLON | WITH_ASCII | LINE_BREAKS | NONPRINT_PERIOD
+#define PRESET_HEXDUMP_C   USE_SPACES | BYTE_A | PRINT_OFFSET | PIPE_SEPARATE | WITH_ASCII | LINE_BREAKS | CENTER_SPLIT | NONPRINT_PERIOD
+#define PRESET_FANCY       USE_SPACES | BYTE_A | PRINT_OFFSET | PIPE_SEPARATE | WITH_ASCII | LINE_BREAKS | CENTER_SPLIT | PIPE_OFFSET
+
+// None of these have any meaning, but serve as documentation.
+#define __WRITE
+#define __READ
+#define __WRITEREAD
+
+// Buffer size.
+#define BUFFER_SIZE 1024
+
+// Loads up a file. 0 on success, 1 on error.
+int map_file(__READ const char* name,
+             __READ const int mode);
+
+// Loads up a file, expanding first if needed.
+int map_file_expand(__READ const char* name,
+                    __READ const uint32_t expand);
+
+// Unmap and sync file.
+int unmap_file();
+
+// Copy file.
+int copy_file(__READ const char* dest, __READ const char* src);
+
+// Searches for a pattern. Returns 1 on match with offset stored to
+// 'offset'. Returns 0 on EOF/no more matches.
+
+int search_file_raw(__READ const unsigned char* pattern,
+                                       __READ const int pattern_len,
+                    __WRITE uint64_t* offset);
+
+// Hexdump
+int hexdump_file(__READ uint64_t offset, __READ uint64_t len, __READ int format);
+int hexdump_manual(__READ uint64_t offset, __READ uint8_t* buffer, __READ uint64_t len, __READ int format, FILE* output);
+
+// Unhexdump
+int unhexdump_buffer(__READ uint8_t* buffer, __READ uint64_t len, __WRITE uint8_t* output);
+
+#endif
diff --git a/bsed.c b/bsed.c
new file mode 100644 (file)
index 0000000..7903f9c
--- /dev/null
+++ b/bsed.c
@@ -0,0 +1,100 @@
+/* brepl - A binary replacement tool. */
+
+// There's two modes to operate in; merged and standalone.
+// Merged is like busybox, standalone gives each program it's own main.
+
+#include "blib.h"
+
+void help(char* name) {
+       printf("%s brepl\n", PACKAGE_STRING);
+       printf("(C) 2015 Jon Feldman (@chaoskagami) <%s>\n", PACKAGE_BUGREPORT);
+       printf("Usage:\n");
+       printf("   %s [args] src_pattern repl_pattern file ...\n", name);
+       printf("Options:\n");
+       printf("   -x        Interpret patterns as hexadecimal strings.\n");
+       printf("   -W        Interpret wildcards in input pattern.\n");
+       printf("   -q        Quiet. No stdout. Returns 0 on match, 1 on no match.\n");
+       printf("\n");
+       printf("Note that the replacement will never change the filesize, e.g.\n");
+       printf("replacing 'name' with 'crane' on '@name@' will result in '@crane'.\n");
+       printf("\n");
+       printf("Program will return zero on successful replacement on all files.\n");
+       printf("\n");
+       printf("The number of the failed file (starting from 1)\n");
+       printf("is returned when a replacement fails for any reason.\n");
+       printf("\n");
+       printf("Report bugs to <%s>\n", PACKAGE_URL);
+       printf("This software is licensed under the MIT license.\n");
+}
+
+int main(int argc, char** argv) {
+       int      ret;
+       uint64_t offset = 0;
+
+       int opt;
+       int hex_mode = 0;
+
+       while ( (opt = getopt(argc, argv, "h")) != -1) {
+               switch(opt) {
+                       case 'h':
+                               help(argv[0]);
+                               return 0;
+                       case 'x':
+                               hex_mode = 1;
+                               break;
+                       case '?':
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+                       default:
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+               }
+       }
+
+if (optind == argc) {
+               fprintf(stderr, "error: requires a search pattern and file argument. Run with -h for more info\n");
+               return 1;
+       }
+
+       char* search_pattern = NULL;
+       char* repl_pattern = NULL;
+       int search_pattern_len = 0;
+       int repl_pattern_len = 0;
+
+       for (int index = optind; index < argc; index++) {
+               if (search_pattern == NULL) {
+                       if (hex_mode == 1) {
+                               search_pattern_len = strlen(argv[index]) / 2;
+                               search_pattern = malloc(search_pattern_len);
+                               unhexdump_buffer(argv[index], strlen(argv[index]), search_pattern);
+                       } else {
+                               search_pattern = argv[index];
+                               search_pattern_len = strlen(argv[index]);
+                       }
+               } else if (repl_pattern == NULL) {
+                       if (hex_mode == 1) {
+                               repl_pattern_len = strlen(argv[index]) / 2;
+                               repl_pattern = malloc(repl_pattern_len);
+                               unhexdump_buffer(argv[index], strlen(argv[index]), repl_pattern);
+                       } else {
+                               repl_pattern = argv[index];
+                               repl_pattern_len = strlen(argv[index]);
+                       }
+               } else {
+                       // File argument. Hexdump it.
+                       map_file(argv[index], WRITE_FILE);
+                       ret = 1;
+                       while (ret) {
+                               ret = search_file_raw(search_pattern, search_pattern_len, &offset);
+                               if (ret) {
+                                       uint64_t max = MAX( (offset+repl_pattern_len), (blib_stat.st_size) );
+                                       for(int i=offset; i < max; i++) {
+                                               CH_BUF((offset+i)) = repl_pattern[i];
+                                       }
+                                       offset++;
+                               }
+                       }
+                       ret = unmap_file();
+               }
+       }
+}
diff --git a/bwrit.c b/bwrit.c
new file mode 100644 (file)
index 0000000..ad99e75
--- /dev/null
+++ b/bwrit.c
@@ -0,0 +1,58 @@
+/* bwrit - Writes values into files. */
+
+#include "blib.h"
+
+void help(char* name) {
+       printf("%s brepl\n", PACKAGE_STRING);
+       printf("(C) 2015 Jon Feldman (@chaoskagami) <%s>\n", PACKAGE_BUGREPORT);
+       printf("Usage:\n");
+       printf("   %s [args] file writes ...\n", name);
+       printf("Options:\n");
+       printf("   -h        Print this message.\n");
+       printf("\n");
+       printf("Writes should be specified in hexadecimal and of the format:\n");
+       printf("   <offset>:<bytes>'\n");
+       printf("\n");
+       printf("Report bugs to <%s>\n", PACKAGE_URL);
+       printf("This software is licensed under the MIT license.\n");
+}
+
+// Example: ac543:ff means write one byte, ff to offset ac543
+
+int main(int c, char** v) {
+       if (c >= 2 && !strcmp(v[1], "-h")) {
+               help(v[0]);
+               return 0;
+       } else if (c < 3) {
+               printf("error: not enough arguments. Run `%s -h` for more info.\n", v[0]);
+               return 1;
+       }
+
+       map_file(v[1], WRITE_FILE);
+
+       for(int i=2; i<c; i++) {
+               char* offset_str = strdup(v[i]);
+               char* inject_str = offset_str;
+               while(*inject_str != ':' && inject_str != 0 && inject_str++); // Find ':'
+               if (*inject_str == 0 || inject_str == offset_str) {
+                       printf("Improperly separated pattern.\n");
+                       unmap_file();
+                       return 1;
+               }
+
+               *(inject_str) = 0;
+               inject_str++;
+
+               printf("off:%s\ninj:%s\n", offset_str, inject_str);
+
+               uint32_t loc = 0;
+
+               sscanf(offset_str, "%x", &loc);
+
+               unhexdump_buffer(inject_str, strlen(inject_str), & CH_BUF(loc)); // We just unhexdump straight to the destination. It's easier.
+       }
+
+       unmap_file();
+
+       return 0;
+}
diff --git a/bxxd.c b/bxxd.c
new file mode 100644 (file)
index 0000000..6b1bee8
--- /dev/null
+++ b/bxxd.c
@@ -0,0 +1,79 @@
+/* bxxd - A binary hexdump tool. */
+
+#include "blib.h"
+
+// Parameters:
+//   -C canonical / coreutils hexdump
+//   -X xxd / vim xxd format
+//   -F Fancy mode, colorized
+//   -f Fancy mode, no color.
+
+void help(char* name) {
+       printf("%s bxxd\n", PACKAGE_STRING);
+       printf("(C) 2015 Jon Feldman (@chaoskagami) <%s>\n", PACKAGE_BUGREPORT);
+       printf("Usage:\n");
+       printf("   %s [args] file ...\n", name);
+       printf("Options:\n");
+       printf("   -C        Canonical / 'hexdump -C' emulation (color)\n");
+       printf("   -c        Canonical / 'hexdump -C' emulation\n");
+       printf("   -X        vim 'xxd' emulation (color)\n");
+       printf("   -x        vim 'xxd' emulation\n");
+       printf("   -F        Fancy (color)\n");
+       printf("   -f        Fancy\n");
+       printf("Report bugs to <%s>\n", PACKAGE_URL);
+       printf("This software is licensed under the MIT license.\n");
+}
+
+int main(int argc, char* argv[]) {
+       int      ret;
+       uint64_t offset = 0;
+       int mode = SPACED_BYTES;
+       int opt;
+
+       while ( (opt = getopt(argc, argv, "hCcXxFf")) != -1) {
+               switch(opt) {
+                       case 'h':
+                               help(argv[0]);
+                               return 0;
+                               break;
+                       case 'C':
+                               mode = PRESET_HEXDUMP_C | COLORIZED;
+                               break;
+                       case 'c':
+                               mode = PRESET_HEXDUMP_C;
+                               break;
+                       case 'X':
+                               mode = PRESET_XXD | COLORIZED;
+                               break;
+                       case 'x':
+                               mode = PRESET_XXD;
+                               break;
+                       case 'F':
+                               mode = PRESET_FANCY | COLORIZED;
+                               break;
+                       case 'f':
+                               mode = PRESET_FANCY;
+                               break;
+                       case '?':
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+                       default:
+                               fprintf(stderr, "error: unknown option. Run with -h for more info\n");
+                               return 1;
+               }
+       }
+
+       if (optind == argc) {
+               fprintf(stderr, "error: requires a file argument. Run with -h for more info\n");
+               return 1;
+       }
+
+       for (int index = optind; index < argc; index++) {
+               // File argument. Hexdump it.
+               ret = map_file(argv[index], READ_FILE);
+               ret = hexdump_file(0, blib_stat.st_size, mode);
+               ret = unmap_file();
+       }
+
+       return 0;
+}
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..1bbc075
--- /dev/null
@@ -0,0 +1,70 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define to 1 if you have the `munmap' function. */
+#undef HAVE_MUNMAP
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+   <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+   #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+   such a type exists and the standard includes do not define it. */
+#undef uint64_t
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..dbb1d7b
--- /dev/null
@@ -0,0 +1,25 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ([2.69])
+AC_INIT([libbinmod], [0.1], [chaos.kagami@gmail.com], [libbinmod], [http://github.com/chaoskagami/libbinmod])
+AC_CONFIG_SRCDIR([blib.h])
+AC_CONFIG_HEADERS([config.h])
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+
+# Checks for header files.
+# AC_CHECK_HEADERS([sys/types.h], [sys/stat.h], [fcntl.h], [sys/mman.h], [stdio.h], [stdlib.h], [stdint.h], [string.h], [unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_TYPE_UINT64_T
+
+# Checks for library functions.
+AC_FUNC_MMAP
+AC_CHECK_FUNCS([munmap])
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..0b0fdcb
--- /dev/null
@@ -0,0 +1,501 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2013-12-25.23; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab='  '
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+     --help     display this help and exit.
+     --version  display version info and exit.
+
+  -c            (ignored)
+  -C            install only if different (preserve the last data modification time)
+  -d            create directories instead of installing files.
+  -g GROUP      $chgrpprog installed files to GROUP.
+  -m MODE       $chmodprog installed files to MODE.
+  -o USER       $chownprog installed files to USER.
+  -s            $stripprog installed files.
+  -t DIRECTORY  install into DIRECTORY.
+  -T            report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+  RMPROG STRIPPROG
+"
+
+while test $# -ne 0; do
+  case $1 in
+    -c) ;;
+
+    -C) copy_on_change=true;;
+
+    -d) dir_arg=true;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift;;
+
+    --help) echo "$usage"; exit $?;;
+
+    -m) mode=$2
+        case $mode in
+          *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+            echo "$0: invalid mode: $mode" >&2
+            exit 1;;
+        esac
+        shift;;
+
+    -o) chowncmd="$chownprog $2"
+        shift;;
+
+    -s) stripcmd=$stripprog;;
+
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
+        # Protect names problematic for 'test' and other utilities.
+        case $dst_arg in
+          -* | [=\(\)!]) dst_arg=./$dst_arg;;
+        esac
+        shift;;
+
+    -T) is_target_a_directory=never;;
+
+    --version) echo "$0 $scriptversion"; exit $?;;
+
+    --) shift
+        break;;
+
+    -*) echo "$0: invalid option: $1" >&2
+        exit 1;;
+
+    *)  break;;
+  esac
+  shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+  # When -d is used, all remaining arguments are directories to create.
+  # When -t is used, the destination is already specified.
+  # Otherwise, the last argument is the destination.  Remove it from $@.
+  for arg
+  do
+    if test -n "$dst_arg"; then
+      # $@ is not empty: it contains at least $arg.
+      set fnord "$@" "$dst_arg"
+      shift # fnord
+    fi
+    shift # arg
+    dst_arg=$arg
+    # Protect names problematic for 'test' and other utilities.
+    case $dst_arg in
+      -* | [=\(\)!]) dst_arg=./$dst_arg;;
+    esac
+  done
+fi
+
+if test $# -eq 0; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call 'install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
+if test -z "$dir_arg"; then
+  do_exit='(exit $ret); exit $ret'
+  trap "ret=129; $do_exit" 1
+  trap "ret=130; $do_exit" 2
+  trap "ret=141; $do_exit" 13
+  trap "ret=143; $do_exit" 15
+
+  # Set umask so as not to create temps with too-generous modes.
+  # However, 'strip' requires both read and write access to temps.
+  case $mode in
+    # Optimize common cases.
+    *644) cp_umask=133;;
+    *755) cp_umask=22;;
+
+    *[0-7])
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw='% 200'
+      fi
+      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+    *)
+      if test -z "$stripcmd"; then
+        u_plus_rw=
+      else
+        u_plus_rw=,u+rw
+      fi
+      cp_umask=$mode$u_plus_rw;;
+  esac
+fi
+
+for src
+do
+  # Protect names problematic for 'test' and other utilities.
+  case $src in
+    -* | [=\(\)!]) src=./$src;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    dstdir=$dst
+    test -d "$dstdir"
+    dstdir_status=$?
+  else
+
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dst_arg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+    dst=$dst_arg
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test "$is_target_a_directory" = never; then
+        echo "$0: $dst_arg: Is a directory" >&2
+        exit 1
+      fi
+      dstdir=$dst
+      dst=$dstdir/`basename "$src"`
+      dstdir_status=0
+    else
+      dstdir=`dirname "$dst"`
+      test -d "$dstdir"
+      dstdir_status=$?
+    fi
+  fi
+
+  obsolete_mkdir_used=false
+
+  if test $dstdir_status != 0; then
+    case $posix_mkdir in
+      '')
+        # Create intermediate dirs using mode 755 as modified by the umask.
+        # This is like FreeBSD 'install' as of 1997-10-28.
+        umask=`umask`
+        case $stripcmd.$umask in
+          # Optimize common cases.
+          *[2367][2367]) mkdir_umask=$umask;;
+          .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
+
+          *[0-7])
+            mkdir_umask=`expr $umask + 22 \
+              - $umask % 100 % 40 + $umask % 20 \
+              - $umask % 10 % 4 + $umask % 2
+            `;;
+          *) mkdir_umask=$umask,go-w;;
+        esac
+
+        # With -d, create the new directory with the user-specified mode.
+        # Otherwise, rely on $mkdir_umask.
+        if test -n "$dir_arg"; then
+          mkdir_mode=-m$mode
+        else
+          mkdir_mode=
+        fi
+
+        posix_mkdir=false
+        case $umask in
+          *[123567][0-7][0-7])
+            # POSIX mkdir -p sets u+wx bits regardless of umask, which
+            # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
+            ;;
+          *)
+            tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+            trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
+
+            if (umask $mkdir_umask &&
+                exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
+            then
+              if test -z "$dir_arg" || {
+                   # Check for POSIX incompatibilities with -m.
+                   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+                   # other-writable bit of parent directory when it shouldn't.
+                   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+                   ls_ld_tmpdir=`ls -ld "$tmpdir"`
+                   case $ls_ld_tmpdir in
+                     d????-?r-*) different_mode=700;;
+                     d????-?--*) different_mode=755;;
+                     *) false;;
+                   esac &&
+                   $mkdirprog -m$different_mode -p -- "$tmpdir" && {
+                     ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
+                     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+                   }
+                 }
+              then posix_mkdir=:
+              fi
+              rmdir "$tmpdir/d" "$tmpdir"
+            else
+              # Remove any dirs left behind by ancient mkdir implementations.
+              rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
+            fi
+            trap '' 0;;
+        esac;;
+    esac
+
+    if
+      $posix_mkdir && (
+        umask $mkdir_umask &&
+        $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+      )
+    then :
+    else
+
+      # The umask is ridiculous, or mkdir does not conform to POSIX,
+      # or it failed possibly due to a race condition.  Create the
+      # directory the slow way, step by step, checking for races as we go.
+
+      case $dstdir in
+        /*) prefix='/';;
+        [-=\(\)!]*) prefix='./';;
+        *)  prefix='';;
+      esac
+
+      oIFS=$IFS
+      IFS=/
+      set -f
+      set fnord $dstdir
+      shift
+      set +f
+      IFS=$oIFS
+
+      prefixes=
+
+      for d
+      do
+        test X"$d" = X && continue
+
+        prefix=$prefix$d
+        if test -d "$prefix"; then
+          prefixes=
+        else
+          if $posix_mkdir; then
+            (umask=$mkdir_umask &&
+             $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+            # Don't fail if two instances are running concurrently.
+            test -d "$prefix" || exit 1
+          else
+            case $prefix in
+              *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+              *) qprefix=$prefix;;
+            esac
+            prefixes="$prefixes '$qprefix'"
+          fi
+        fi
+        prefix=$prefix/
+      done
+
+      if test -n "$prefixes"; then
+        # Don't fail if two instances are running concurrently.
+        (umask $mkdir_umask &&
+         eval "\$doit_exec \$mkdirprog $prefixes") ||
+          test -d "$dstdir" || exit 1
+        obsolete_mkdir_used=true
+      fi
+    fi
+  fi
+
+  if test -n "$dir_arg"; then
+    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+  else
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+    # Copy the file name to the temp name.
+    (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+    { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+    { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+    # If -C, don't bother to copy if it wouldn't change the file.
+    if $copy_on_change &&
+       old=`LC_ALL=C ls -dlL "$dst"     2>/dev/null` &&
+       new=`LC_ALL=C ls -dlL "$dsttmp"  2>/dev/null` &&
+       set -f &&
+       set X $old && old=:$2:$4:$5:$6 &&
+       set X $new && new=:$2:$4:$5:$6 &&
+       set +f &&
+       test "$old" = "$new" &&
+       $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+    then
+      rm -f "$dsttmp"
+    else
+      # Rename the file to the real destination.
+      $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+      # The rename failed, perhaps because mv can't rename something else
+      # to itself, or perhaps because mv is so ancient that it does not
+      # support -f.
+      {
+        # Now remove or move aside any old file at destination location.
+        # We try this two ways since rm can't unlink itself on some
+        # systems and the destination file might be busy for other
+        # reasons.  In this case, the final cleanup might fail but the new
+        # file should still install successfully.
+        {
+          test ! -f "$dst" ||
+          $doit $rmcmd -f "$dst" 2>/dev/null ||
+          { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+            { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
+          } ||
+          { echo "$0: cannot unlink or rename $dst" >&2
+            (exit 1); exit 1
+          }
+        } &&
+
+        # Now rename the file to the real destination.
+        $doit $mvcmd "$dsttmp" "$dst"
+      }
+    fi || exit 1
+
+    trap '' 0
+  fi
+done
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/ips_fmt.h b/ips_fmt.h
new file mode 100644 (file)
index 0000000..cfd736a
--- /dev/null
+++ b/ips_fmt.h
@@ -0,0 +1,64 @@
+/* IPS. Pretty standard stuff. */
+
+#define IPS_MAGIC       "PATCH" // Original Spec
+#define IPS_MAGIC_LENGTH 5
+
+#define IPS_TAIL         "EOF"
+#define IPS_TAIL_LENGTH  3
+
+typedef struct ips_record_com_s {
+       uint8_t offset[3];
+       uint8_t size[2];
+} ips_record_com_t;
+
+typedef struct ips_record_rle_s {
+       uint8_t  rle_size[2];
+       uint8_t  byte;
+} ips_record_rle_t;
+
+typedef struct ips_record_s {
+       ips_record_com_t* info;
+       void* data; // Can be ips_record_rle_t or uint8_t.
+} ips_record_t;
+
+/* IPS32 is IPS with 32-bit offsets. */
+
+#define IPS32_MAGIC        "IPS32" // 32-bit extension
+#define IPS32_MAGIC_LENGTH 5
+
+#define IPS32_TAIL         "EEOF"
+#define IPS32_TAIL_LENGTH  4
+
+typedef struct ips32_record_com_s {
+       uint8_t offset[4];
+       uint8_t size[2];
+} ips32_record_com_t;
+
+typedef struct ips32_record_rle_s {
+       uint8_t  rle_size[2];
+       uint8_t  byte;
+} ips32_record_rle_t;
+
+typedef struct ips32_record_s {
+       ips32_record_com_t* info;
+       void* data; // Can be ips_record_rle_t or uint8_t.
+} ips32_record_t;
+
+#define TYPE_INVALID 0
+#define TYPE_IPS     1
+#define TYPE_IPS32   2
+
+#define BYTE4_TO_UINT32(bp) \
+     (((uint32_t)(bp [0]) << 24) & 0xFF000000) | \
+     (((uint32_t)(bp [1]) << 16) & 0x00FF0000) | \
+     (((uint32_t)(bp [2]) << 8 ) & 0x0000FF00) | \
+     ( (uint32_t)(bp [3])        & 0x000000FF)
+
+#define BYTE3_TO_UINT32(bp) \
+     (((uint32_t)(bp [0]) << 16) & 0x00FF0000) | \
+     (((uint32_t)(bp [1]) << 8 ) & 0x0000FF00) | \
+     ( (uint32_t)(bp [2])        & 0x000000FF)
+
+#define BYTE2_TO_UINT16(bp) \
+     (((uint16_t)(bp [0]) << 8 ) & 0xFF00) | \
+     ( (uint16_t)(bp [1])        & 0x00FF)