--- /dev/null
+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.
+
--- /dev/null
+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.
--- /dev/null
+ 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.
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+#!/bin/bash
+
+autoreconf -f -i
--- /dev/null
+/* 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;
+ }
+
+}
--- /dev/null
+/* 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();
+ }
+ }
+}
--- /dev/null
+/* 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;
+ }
+ }
+}
--- /dev/null
+#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]);
+ }
+}
--- /dev/null
+/* 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
--- /dev/null
+/* 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();
+ }
+ }
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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
--- /dev/null
+# -*- 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
--- /dev/null
+#!/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:
--- /dev/null
+/* 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)