diff --git a/patch/bps.cpp b/patch/bps.cpp index c8c5947..497ce43 100644 --- a/patch/bps.cpp +++ b/patch/bps.cpp @@ -19,7 +19,6 @@ namespace patch { namespace bps { enum { SourceRead, TargetRead, SourceCopy, TargetCopy }; -#define error(which) do { error=which; goto exit; } while(0) result apply(arrayview patchmem, arrayview in, array& out, bool accept_wrong_input) { if (patchmem.size()<4+3+12) return e_broken; @@ -29,6 +28,7 @@ result apply(arrayview patchmem, arrayview in, array& out, boo if (true) { +#define error(which) do { error=which; goto exit; } while(0) #define decodeto(var) \ do { \ if (!patch.bpsnum(var)) error(e_too_big); \ @@ -150,13 +150,13 @@ result apply(arrayview patchmem, arrayview in, array& out, boo return error; #undef decodeto +#undef error } exit: out.resize(0); return error; } -#undef error diff --git a/patch/ips.cpp b/patch/ips.cpp index bbe2ecb..09f9be6 100644 --- a/patch/ips.cpp +++ b/patch/ips.cpp @@ -1,155 +1,68 @@ #include "patch.h" namespace patch { namespace ips { -//TODO: HEAVY cleanups needed here -#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 { - result error; - unsigned int outlen_min; - unsigned int outlen_max; - unsigned int outlen_min_mem; -}; - -static result ips_study(struct mem patch, struct ipsstudy * study) +result apply(arrayview patchmem, const file& in, array& out) { - study->error=e_broken; - if (patch.len<8) return e_broken; - const unsigned char * patchat=patch.ptr; - const unsigned char * patchend=patchat+patch.len; -#define read8() ((patchatoutlen) outlen=thisout; - if (patchat>=patchend) return e_broken; - 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 e_broken; - study->outlen_min=outlen; -#undef read8 -#undef read16 -#undef read24 - study->error=e_ok; - if (w_scrambled) study->error=e_damaged; - return study->error; -} - -static result ips_apply_study(struct mem patch, struct ipsstudy * study, struct mem in, struct mem * out) -{ - out->ptr=NULL; - out->len=0; - if (study->error==e_broken) 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=(uint8_t*)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 (patch.remaining() < size+3) error(e_broken); - if (size && (out->ptr[offset]!=b || memcmp(out->ptr+offset, out->ptr+offset, size-1))) anychanges=true; - - memset(out->ptr+offset, b, size); + out.reserve(offset+size); + arrayview newdat = patch.bytes(size); + if (!anychanges && newdat!=out.slice(offset, size)) anychanges = true; + memcpy(out.slice(offset, size).ptr(), newdat.ptr(), newdat.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 (patch.remaining()==3) + { + uint32_t newsize = patch.u24(); + if (newsize <= out.size() && !error) error = e_not_this; + out.resize(newsize); + } + if (patch.remaining()!=0) error = e_damaged; + if (!anychanges && in.size()==out.size() && error != e_damaged) error = e_to_output; + return error; - if (study->outlen_max!=0xFFFFFFFF && in.len<=study->outlen_max) study->error=e_not_this;//truncate data without this being needed is a poor idea - if (!anychanges) study->error=e_to_output; - return study->error; -} - -static result 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); -} - -result apply(const file& patch, const file& source, file& target) -{ - struct mem patchmem = patch.mmap(); - struct mem inmem = source.mmap(); - struct mem outmem; - result r = apply(patchmem, inmem, &outmem); - patch.unmap(patchmem.v()); - source.unmap(inmem.v()); - target.write(outmem.v()); - free(outmem.ptr); - return r; +exit: + out.resize(0); + return error; } //Known situations where this function does not generate an optimal patch: diff --git a/patch/patch.h b/patch/patch.h index 207a3b0..11ceb8a 100644 --- a/patch/patch.h +++ b/patch/patch.h @@ -27,8 +27,12 @@ enum result { }; namespace ips { -result apply(const file& patch, const file& source, file& target); -static inline result apply(const file& patch, const file& source, file&& target) { return apply(patch, source, (file&)target); } +result apply(arrayview patch, const file& in, array& out); +static inline result apply(arrayview patch, arrayview in, array& out) +{ + file inf = file::mem(in); + return apply(patch, inf, out); +} result create(const file& source, const file& target, file& patch); static inline result create(const file& source, const file& target, file&& patch) { return create(source, target, (file&)patch); } } @@ -107,6 +111,12 @@ class memstream { public: memstream(arrayview buf) : start(buf.ptr()), at(buf.ptr()), end(buf.ptr()+buf.size()) {} arrayview bytes(size_t n) { arrayview ret = arrayview(at, n); at+=n; return ret; } + bool signature(cstring sig) + { + bool ok = (memcmp(at, sig.bytes().ptr(), sig.length())==0); + at+=sig.length(); + return ok; + } uint8_t u8() { return *(at++); @@ -121,11 +131,21 @@ public: arrayview b = bytes(2); return b[0] | b[1]<<8; } + uint16_t u16be() + { + arrayview b = bytes(2); + return b[0]<<8 | b[1]; + } uint32_t u24() { arrayview b = bytes(3); return b[0] | b[1]<<8 | b[2]<<16; } + uint32_t u24be() + { + arrayview b = bytes(3); + return b[0]<<16 | b[1]<<8 | b[2]; + } uint32_t u32() { arrayview b = bytes(4); @@ -136,6 +156,7 @@ public: const byte* b = start+pos; return b[0] | b[1]<<8 | b[2]<<16 | b[3]<<24; } + size_t pos() { return at-start; } size_t size() { return end-start; } size_t remaining() { return end-at; } diff --git a/patch/test.cpp b/patch/test.cpp index d72defd..3592b4a 100644 --- a/patch/test.cpp +++ b/patch/test.cpp @@ -65,7 +65,7 @@ static void createtest(arrayview a, arrayview b, size_t ipssize, siz result r = ips::create(file::mem(a), file::mem(b), file::mem(patch)); if (r!=e_identical) assert_eq(r, e_ok); array b2; - r = ips::apply(file::mem(patch), file::mem(a), file::mem(b2)); + r = ips::apply(patch, a, b2); if (r!=e_to_output) assert_eq(r, e_ok); assert(b == b2); @@ -179,9 +179,9 @@ test("the big ones") if (!smw || !smw_bps || !dl || !dl_ups || !sm64 || !sm64_bps) test_skip("test files not present; see patch/test/readme.txt"); result r; - array smwhack; - r = bps::apply(smw_bps, smw, smwhack); - assert_eq(r, e_ok); + //array smwhack; + //r = bps::apply(smw_bps, smw, smwhack); + //assert_eq(r, e_ok); //testcall(createtest(smw, smwhack, 3302980, 2077386)); //array sm64hack; diff --git a/patch/ups.cpp b/patch/ups.cpp index 11d3b81..00f35a6 100644 --- a/patch/ups.cpp +++ b/patch/ups.cpp @@ -3,7 +3,6 @@ namespace patch { namespace ups { //TODO: HEAVY cleanups needed here -#define error(which) do { error=which; goto exit; } while(0) result apply(arrayview patchmem, const file& in, array& outmem) { if (patchmem.size()<4+2+12) return e_broken; @@ -13,6 +12,7 @@ result apply(arrayview patchmem, const file& in, array& outmem) if (true) { +#define error(which) do { error=which; goto exit; } while(0) #define decodeto(var) \ do { \ if (!patch.bpsnum(var)) error(e_too_big); \ @@ -94,6 +94,7 @@ result apply(arrayview patchmem, const file& in, array& outmem) return e_ok; #undef decodeto +#undef error } exit: