--- /dev/null
+/* bips - A IPS patcher tool */
+
+#include "blib.h"
+#include "ips_fmt.h"
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+uint8_t* ips_buffer = NULL;
+
+int simulate = 0; // Don't actually apply; only show the results.
+int plaintext = 0; // Decompile IPS to a human-readable listing.
+int splitmini = 0; // Split IPS to one chunk mini-ips patches in a folder.
+
+// On success, returns the number of IPS records read. On failure,
+// returns -1.
+int load_ips(__READ char* ips_filename, __WRITE ips_record_t** ips_structs) {
+ // We load the entire thing to memory.
+
+ FILE* f = fopen(ips_filename, "r");
+ fseek(f, 0, SEEK_END);
+ uint64_t pos = ftell(f);
+ rewind(f);
+
+ ips_buffer = (uint8_t*)malloc(pos);
+
+ fread(ips_buffer, 1, pos, f);
+
+ fclose(f);
+
+ printf("Loaded file to memory successfully. Size: %lu\n", pos);
+
+ // Loaded. Begin by checking shit.
+ if( strncmp((const char*)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);
+ else
+ offset_in += BYTE2_TO_UINT16(ips_data[ips_count-1].info->size);
+
+ // Aaand onto the next.
+ }
+
+ printf("Read in IPS data. Record count: %d\n", ips_count);
+
+ ips_structs[0] = ips_data;
+
+ return ips_count;
+}
+
+int conv_ips(__READ char* filename, __READ ips_record_t* records, __READ int record_count) {
+ FILE* out = stdout;
+ if (filename != NULL) {
+ out = fopen(filename, "wb");
+ if (!out) {
+ fprintf(stderr, "Can't open. Writing to stdout.\n");
+ out = stdout;
+ }
+ }
+
+ srand(time(NULL));
+ uint16_t uuid = (0x6600 + (rand() % 0x4400));
+
+ fprintf(out, "# $name EDITME\n");
+ fprintf(out, "# $desc Converted IPS Patch\n");
+ fprintf(out, "# $title EDITME\n");
+ fprintf(out, "# $ver 01\n");
+ fprintf(out, "# $uuid %04hx\n", uuid);
+ fprintf(out, "# This file was automatically generated by ips2pco\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);
+
+ fprintf(out, "seek %08x\n", offset);
+
+ if (size == 0) { // RLE
+ ips_record_rle_t* rle = (ips_record_rle_t*)records[i].data;
+
+ size = BYTE2_TO_UINT16(rle->rle_size);
+
+ for (int i=0; i < size; i += 0x20) {
+ uint32_t max_l = (size - i > 0x20) ?
+ 0x20 :
+ size - i;
+ fprintf(out, "set ");
+
+ for(uint32_t j=0; j < max_l; j++)
+ fprintf(out, "%02hhx", rle->byte);
+ fprintf(out, "\n");
+ }
+ } else { // Normal
+ uint8_t* bytes = (uint8_t*)records[i].data;
+
+ for (int i=0; i < size; i += 0x20) {
+ uint32_t max_l = (size - i > 0x20) ?
+ 0x20 :
+ size - i;
+ fprintf(out, "set ");
+
+ for(uint32_t j=0; j < max_l; j++)
+ fprintf(out, "%02hhx", bytes[i+j]);
+ fprintf(out, "\n");
+ }
+ }
+ }
+
+ 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: %lu\n", pos);
+
+ // Loaded. Begin by checking shit.
+ if( strncmp((const char*)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);
+ } else {
+ offset_in += BYTE2_TO_UINT16(ips_data[ips_count-1].info->size);
+ }
+
+ // Aaand onto the next.
+ }
+
+ printf("Read in IPS data. Record count: %d\n", ips_count);
+
+ ips_structs[0] = ips_data;
+
+ return ips_count;
+}
+
+int conv_ips32(__READ char* filename, __READ ips32_record_t* records, __READ int record_count) {
+ FILE* out = stdout;
+ if (filename != NULL) {
+ out = fopen(filename, "wb");
+ if (!out) {
+ fprintf(stderr, "Can't open. Writing to stdout.\n");
+ out = stdout;
+ }
+ }
+
+ srand(time(NULL));
+ uint16_t uuid = (0x6600 + (rand() % 0x4400));
+
+ fprintf(out, "# $name EDITME\n");
+ fprintf(out, "# $desc Converted IPS Patch\n");
+ fprintf(out, "# $title EDITME\n");
+ fprintf(out, "# $ver 01\n");
+ fprintf(out, "# $uuid %04hx\n", uuid);
+ fprintf(out, "# This file was automatically generated by ips2pco\n");
+
+ 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);
+
+ fprintf(out, "seek %08x\n", offset);
+
+ if (size == 0) { // RLE
+ ips32_record_rle_t* rle = (ips32_record_rle_t*)records[i].data;
+
+ size = BYTE2_TO_UINT16(rle->rle_size);
+
+ for (int i=0; i < size; i += 0x20) {
+ uint32_t max_l = (size - i > 0x20) ?
+ 0x20 :
+ size - i;
+ fprintf(out, "set ");
+
+ for(uint32_t j=0; j < max_l; j++)
+ fprintf(out, "%02hhx", rle->byte);
+ fprintf(out, "\n");
+ }
+ } else { // Normal
+ uint8_t* bytes = (uint8_t*)records[i].data;
+
+ for (int i=0; i < size; i += 0x20) {
+ uint32_t max_l = (size - i > 0x20) ?
+ 0x20 :
+ size - i;
+ fprintf(out, "set ");
+
+ for(uint32_t j=0; j < max_l; j++)
+ fprintf(out, "%02hhx", bytes[i+j]);
+ fprintf(out, "\n");
+ }
+ }
+ }
+
+ if (out != stdout)
+ fclose(out);
+
+ return 0;
+}
+
+
+int identify_patch(__READ 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("corbenik ips2pco v0.1\n");
+ printf("(C) 2015 Jon Feldman (@chaoskagami) <kagami@chaos.moe>\n");
+ printf("Usage:\n");
+ printf(" %s [options] patch.ips [out.pco]\n", name);
+ printf("Options:\n");
+ printf(" -h Print this help text.\n");
+ printf("Patch is dumped to stdout if a output file is not specified.\n");
+ printf("Report bugs to <https://github.com/chaoskagami/corbenik>\n");
+ 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 prog_ret = 0;
+
+ while ( (opt = getopt(argc, argv, "hs")) != -1) {
+ switch(opt) {
+ case 'h':
+ help(argv[0]);
+ return 1;
+ case 's':
+ simulate = 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 < 1) {
+ fprintf(stderr, "error: requires more arguments. Run with -h for more info\n");
+ return 1;
+ }
+
+ char* patch = argv[optind];
+ int type = identify_patch(patch);
+ char *out_file = NULL;
+ if (argc - optind >= 2) {
+ out_file = argv[optind+1];
+ }
+
+ switch(type) {
+ case TYPE_IPS:
+ fprintf(stderr, "Patch format: IPS (24-bit offsets)\n");
+ record_count = load_ips(patch, &ips);
+ conv_ips(out_file, ips, record_count);
+ break;
+ case TYPE_IPS32:
+ fprintf(stderr, "Patch format: IPS32 (IPS derivative w/ 32-bit offsets)\n");
+ record_count = load_ips32(patch, &ips32);
+ conv_ips32(out_file, ips32, record_count);
+ break;
+ default:
+ fprintf(stderr, "Patch format not understood or invalid.\n");
+ prog_ret = -2;
+ break;
+ }
+
+ free(ips);
+ free(ips32);
+ free(ips_buffer);
+
+ return prog_ret;
+}
--- /dev/null
+#define BLIB_NO_EXTERNS
+
+#include "blib.h"
+
+int blib_filefd;
+unsigned char* blib_buffer;
+struct stat blib_stat;
+
+int copy_file(__READ char* dest, __READ char* src) {
+ // Can you believe there's no standard method to copy files? Neither can I.
+ // 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;
+}
+
+int hexdump_file(__READ uint8_t *buffer, __READ uint64_t len, __READ int format) {
+ return hexdump_manual(0, 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");
+ }
+
+ return 0;
+}
+
+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(uint64_t i=0; i < (len/2); i++) {
+ output[i] = hexb_to_u8(buffer[i*2], buffer[i*2+1]);
+ }
+
+ return 0;
+}
--- /dev/null
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "blib.h"
+
+size_t fsize(FILE* file) {
+ size_t at = ftell(file);
+
+ fseek(file, 0, SEEK_END);
+ size_t size = ftell(file);
+ fseek(file, at, SEEK_SET);
+
+ return size;
+}
+
+int read_alloc_file(const char* filename, void** ptr, size_t* size) {
+ FILE* handle = fopen(filename, "rb");
+ if (!handle)
+ return -READALLOC_NOFILE;
+
+ size_t alloc_size = *size;
+
+ *size = fsize(handle);
+ if (size <= 0) {
+ fclose(handle);
+ *size = 0;
+ return -READALLOC_BADSIZE;
+ }
+
+ if (alloc_size) {
+ *ptr = malloc(alloc_size);
+ if (alloc_size < *size) {
+ *size = alloc_size;
+ }
+ } else {
+ *ptr = malloc(*size);
+ }
+
+ if (*ptr == NULL) {
+ fclose(handle);
+ *ptr = NULL;
+ *size = 0;
+ return -READALLOC_OOM;
+ }
+
+ if (fread(*ptr, 1, *size, handle) != *size) {
+ free(*ptr);
+ fclose(handle);
+ *ptr = NULL;
+ *size = 0;
+ return -READALLOC_READERR;
+ }
+
+ if (alloc_size && alloc_size > *size) {
+ *size = alloc_size;
+ }
+
+ fclose(handle);
+
+ return 0;
+}
+
+#define ALPHABET_LEN 256
+#define NOT_FOUND patlen
+#define max(a, b) ((a < b) ? b : a)
+
+// Quick Search algorithm, adapted from
+// http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
+const void *
+memfind(const void *input, uint32_t size, const void *pattern, uint32_t patternSize)
+{
+ const uint8_t *startPos = (const uint8_t *)input;
+ const uint8_t *patternc = (const uint8_t *)pattern;
+
+ // Preprocessing
+ uint32_t table[ALPHABET_LEN];
+
+ for (uint32_t i = 0; i < ALPHABET_LEN; ++i)
+ table[i] = patternSize + 1;
+ for (uint32_t i = 0; i < patternSize; ++i)
+ table[patternc[i]] = patternSize - i;
+
+ // Searching
+ size_t j = 0;
+
+ while (j <= size - patternSize) {
+ if (memcmp(patternc, startPos + j, patternSize) == 0)
+ return startPos + j;
+ j += table[startPos[j + patternSize]];
+ }
+
+ return NULL;
+}
+