From aac62a84f63207f1af5349445a12d31a9971b855 Mon Sep 17 00:00:00 2001 From: chaoskagami Date: Thu, 30 Jun 2016 02:53:36 -0400 Subject: [PATCH 1/1] Commit this code I've had lying around for a while --- COPYING | 20 + DOCUMENT | 25 ++ INSTALL | 10 + Makefile.in | 103 ++++++ README | 120 ++++++ autogen.sh | 3 + bflag.c | 158 ++++++++ bgrep.c | 82 +++++ bips.c | 1000 ++++++++++++++++++++++++++++++++++++++++++++++++++ blib.c | 300 +++++++++++++++ blib.h | 100 +++++ bsed.c | 100 +++++ bwrit.c | 58 +++ bxxd.c | 79 ++++ config.h.in | 70 ++++ configure.ac | 25 ++ install-sh | 501 +++++++++++++++++++++++++ ips_fmt.h | 64 ++++ 18 files changed, 2818 insertions(+) create mode 100644 COPYING create mode 100644 DOCUMENT create mode 100644 INSTALL create mode 100644 Makefile.in create mode 100644 README create mode 100755 autogen.sh create mode 100644 bflag.c create mode 100644 bgrep.c create mode 100644 bips.c create mode 100644 blib.c create mode 100644 blib.h create mode 100644 bsed.c create mode 100644 bwrit.c create mode 100644 bxxd.c create mode 100644 config.h.in create mode 100644 configure.ac create mode 100755 install-sh create mode 100644 ips_fmt.h diff --git a/COPYING b/COPYING new file mode 100644 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 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 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 index 0000000..a33cb46 --- /dev/null +++ b/Makefile.in @@ -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 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 index 0000000..f378f20 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +autoreconf -f -i diff --git a/bflag.c b/bflag.c new file mode 100644 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 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 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 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 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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 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 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(" :'\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\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 index 0000000..1bbc075 --- /dev/null +++ b/config.h.in @@ -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 header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the 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 header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the 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 , + , or 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 index 0000000..dbb1d7b --- /dev/null +++ b/configure.ac @@ -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 index 0000000..0b0fdcb --- /dev/null +++ b/install-sh @@ -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 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) -- 2.39.5