From: chaoskagami Date: Thu, 30 Jun 2016 06:53:36 +0000 (-0400) Subject: Commit this code I've had lying around for a while X-Git-Url: https://chaos.moe/g/?a=commitdiff_plain;h=HEAD;p=misc%2Flibbinmod.git Commit this code I've had lying around for a while --- aac62a84f63207f1af5349445a12d31a9971b855 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)