//Module name: libips //Author: Alcaro //Date: July 11, 2013 //Licence: GPL v3.0 or higher #ifndef __cplusplus #include //bool; if this does not exist, remove it and uncomment the following three lines. //#define bool int //#define true 1 //#define false 0 #endif #include //malloc, realloc, free #include //memcpy, memset #include "libips.h" typedef unsigned char byte; #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) #define clamp(a,b,c) max(a,min(b,c)) struct ipsstudy { enum ipserror error; unsigned int outlen_min; unsigned int outlen_max; unsigned int outlen_min_mem; }; enum ipserror ips_study(struct mem patch, struct ipsstudy * study) { study->error=ips_invalid; if (patch.len<8) return ips_invalid; const unsigned char * patchat=patch.ptr; const unsigned char * patchend=patchat+patch.len; #define read8() ((patchatoutlen) outlen=thisout; if (patchat>=patchend) return ips_invalid; offset=read24(); } study->outlen_min_mem=outlen; study->outlen_max=0xFFFFFFFF; if (patchat+3==patchend) { unsigned int truncate=read24(); study->outlen_max=truncate; if (outlen>truncate) { outlen=truncate; w_scrambled=true; } } if (patchat!=patchend) return ips_invalid; study->outlen_min=outlen; #undef read8 #undef read16 #undef read24 study->error=ips_ok; if (w_scrambled) study->error=ips_scrambled; return study->error; } enum ipserror ips_apply_study(struct mem patch, struct ipsstudy * study, struct mem in, struct mem * out) { out->ptr=NULL; out->len=0; if (study->error==ips_invalid) return study->error; #define read8() (*patchat++)//guaranteed to not overflow at this point, we already checked the patch #define read16() (patchat+=2,((patchat[-2]<<8)|patchat[-1])) #define read24() (patchat+=3,((patchat[-3]<<16)|(patchat[-2]<<8)|patchat[-1])) unsigned int outlen=clamp(study->outlen_min, in.len, study->outlen_max); out->ptr=(byte*)malloc(max(outlen, study->outlen_min_mem)); out->len=outlen; bool anychanges=false; if (outlen!=in.len) anychanges=true; if (out->len>in.len) { memcpy(out->ptr, in.ptr, in.len); memset(out->ptr+in.len, 0, out->len-in.len); } else memcpy(out->ptr, in.ptr, outlen); const unsigned char * patchat=patch.ptr+5; unsigned int offset=read24(); while (offset!=0x454F46) { unsigned int size=read16(); if (size==0) { size=read16(); if (!size) {}//no clue (fix the change detector if changing this) unsigned char b=read8(); if (size && (out->ptr[offset]!=b || memcmp(out->ptr+offset, out->ptr+offset, size-1))) anychanges=true; memset(out->ptr+offset, b, size); } else { if (memcmp(out->ptr+offset, patchat, size)) anychanges=true; memcpy(out->ptr+offset, patchat, size); patchat+=size; } offset=read24(); } #undef read8 #undef read16 #undef read24 if (study->outlen_max!=0xFFFFFFFF && in.len<=study->outlen_max) study->error=ips_notthis;//truncate data without this being needed is a poor idea if (!anychanges) study->error=ips_thisout; return study->error; } enum ipserror ips_apply(struct mem patch, struct mem in, struct mem * out) { struct ipsstudy study; ips_study(patch, &study); return ips_apply_study(patch, &study, in, out); } //Known situations where this function does not generate an optimal patch: //In: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 //Out: FF FF FF FF FF FF FF FF 00 01 02 03 04 05 06 07 FF FF FF FF FF FF FF FF //IPS: [ RLE ] [ Copy ] [ RLE ] //Possible improvement: RLE across the entire file, copy on top of that. //Rationale: It would be a huge pain to create such a multi-pass tool if it should support writing a byte // more than twice, and I don't like half-assing stuff. It's also unlikely to apply to anything. //Known improvements over LIPS: //In: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F //Out: FF 01 02 03 04 05 FF FF FF FF FF FF FF FF FF FF //LIPS:[ Copy ] [ RLE ] //Mine:[] [ Unchanged ] [ RLE ] //Rationale: While LIPS can break early if it finds something RLEable in the middle of a block, it's not // smart enough to back off if there's something unchanged between the changed area and the RLEable spot. //In: FF FF FF FF FF FF FF //Out: 00 00 00 00 01 02 03 //LIPS:[ RLE ] [ Copy ] //Mine:[ Copy ] //Rationale: Mistuned heuristics in LIPS. //It is also known that I win in some other situations. I didn't bother checking which, though. //There are no known cases where LIPS wins over libips. enum ipserror ips_create(struct mem sourcemem, struct mem targetmem, struct mem * patchmem) { unsigned int sourcelen=sourcemem.len; unsigned int targetlen=targetmem.len; const unsigned char * source=sourcemem.ptr; const unsigned char * target=targetmem.ptr; patchmem->ptr=NULL; patchmem->len=0; if (targetlen>=16777216) return ips_16MB; unsigned int offset=0; unsigned int outbuflen=4096; unsigned char * out=(byte*)malloc(outbuflen); unsigned int outlen=0; #define write8(val) do { out[outlen++]=(val); if (outlen==outbuflen) { outbuflen*=2; out=(byte*)realloc(out, outbuflen); } } while(0) #define write16(val) do { write8((val)>>8); write8((val)); } while(0) #define write24(val) do { write8((val)>>16); write8((val)>>8); write8((val)); } while(0) write8('P'); write8('A'); write8('T'); write8('C'); write8('H'); int lastknownchange=0; //int forcewrite=(targetlen>sourcelen?1:0); while (offset=6 || thislen>=65536) break; } //avoid premature EOF if (offset==0x454F46) { offset--; thislen++; } lastknownchange=offset+thislen; if (thislen>65535) thislen=65535; if (offset+thislen>targetlen) thislen=targetlen-offset; if (offset==targetlen) continue; //check if RLE here is worthwhile int byteshere; for (byteshere=0;byteshere=targetlen || target[pos]!=thisbyte || byteshere+i>65535) break; if (pos>=sourcelen || (pos8-5 && byteshere==thislen) || byteshere>8) { write24(offset); write16(0); write16(byteshere); write8(target[offset]); offset+=byteshere; } else { //check if we'd gain anything from ending the block early and switching to RLE int byteshere=0; int stopat=0; while (stopat+byteshere8+5 || //rle-worthy despite two ips headers (byteshere>8 && stopat+byteshere==thislen) || //rle-worthy at end of data (byteshere>8 && !memcmp(&target[offset+stopat+byteshere], &target[offset+stopat+byteshere+1], 9-1)))//rle-worthy before another rle-worthy { if (stopat) thislen=stopat; break;//we don't scan the entire block if we know we'll want to RLE, that'd gain nothing. } } //don't write unchanged bytes at the end of a block if we want to RLE the next couple of bytes if (offset+thislen!=targetlen) { while (offset+thislen-13 && !memcmp(&target[offset], &target[offset+1], thislen-1))//still worth it? { write24(offset); write16(0); write16(thislen); write8(target[offset]); } else { write24(offset); write16(thislen); int i; for (i=0;itargetlen) write24(targetlen); #undef write patchmem->ptr=out; patchmem->len=outlen; if (outlen==8) return ips_identical; return ips_ok; } void ips_free(struct mem mem) { free(mem.ptr); } #if 0 #warning Disable this in release versions. #include //Congratulations, you found the undocumented feature! I don't think it's useful for anything except debugging libips, though. void ips_dump(struct mem patch) { if (patch.len<8) { puts("Invalid"); return; } const unsigned char * patchat=patch.ptr; const unsigned char * patchend=patchat+patch.len; #define read8() ((patchatoutlen) outlen=thisout; if (patchat>=patchend) { puts("Invalid"); return; } blockstart=patchat-patch.ptr; offset=read24(); } printf("Expand to 0x%X\n", outlen); if (patchat+3==patchend) { int truncate=read24(); printf("Truncate to 0x%X\n", truncate); } if (patchat!=patchend) puts("Invalid"); #undef read8 #undef read16 #undef read24 } #endif