Author
|
Topic: New patching format, UPS, debuts today (Read 4 times)
|
byuu
Guest
|
|
« on: March 31, 2008, 01:55:56 am » |
|
Okay, it's been talked about forever. Now it's finally ready for prime time. Background: The goal of UPS is different from NINJA / xdelta / etc. It is designed to be a direct replacement for IPS. The difficulty of implementing it is equal to IPS (it has trickier pointers, but no RLE magic.) The spec for UPS supports any file size, though the binary is currently limited to 4GB files due to libc limitations. But yes, thanks to a condensed pointer encoding method, the file size can be infinite. UPS is completely future proof in that regard. Now, UPS is designed for patching files, not performing black magic voodoo. It won't perform advanced clipping methods to move data around and restructure files like xdelta will. However, it's possible for anyone to implement UPS, whereas there has only ever been one xdelta implementation. Guess why. If you need this kind of magic, then you probably want to use xdelta or bsdiff instead. UPS also doesn't try and support every last console on the planet. The general rule is to make patches without headers when they aren't needed (eg SNES copier), and with headers when required (eg iNES.) The platform-specific detection will be part of NINJA. UPS doesn't compete with NINJA, they will complement each other. NINJA3 will use UPS internally as the raw patch data, and will handle detection and support for each individual system, as needed. NINJA3 is to OGG as UPS is to Vorbis, for instance. UPS doesn't waste time with compression tricks such as RLE coding. The reason is because ZIP, 7-Zip, etc etc have always and will always do it better. It's better to just have a bigger patch that gets packed down more by compression. Most emulators support patches inside ZIP files anyway. This has the added benefit of making the file format easier for others to implement. Finally, UPS is a finalized spec. With roughly a year to review it, I've still not felt the need to change it in any way. So even though the UPS patcher and library have first release version numbers (0.xx), the patches created will work with all future versions. The spec is unchanging. Don't even ask. I've posted here for a good year asking for feedback, this is set in stone now. Now, if you decide to make your own patcher, make sure that the output from your patcher is 1:1 identical with the reference library. If it has even one bit different, it's not a compliant patcher, and this is a license violation. Otherwise, my reference code is public domain. I need to do this because I don't want the format splintering like all the IPS "extensions". Let's not start that mess. UPS doesn't need any extensions, it does everything it needs to do right from the start. Still, as an absolute failsafe, and to make the sig dword-aligned, the signature is "UPS1". If you want to modify the format, don't use "UPS2", or anything with "UPS" at the start, please. Change the name to something else. --- Advantages: - simple file format, easy for anyone to implement. - automatic bi-directional patching. The same patch can both patch and unpatch a game. - CRC32 checksums on the original, modified and patch files guarantees patches will not apply to the incorrect games. We use CRC32 as this is designed to test integrity, not to prevent malicious checksum conflicts. Odds of a false positive are 1:4 billion with CRC32, and CRC32 is 100x easier to implement than eg MD5SUM, etc. Very important for others implementing this algorithm. - infinite file sizes. No more 16MB limitation as with IPS. - Windows / Linux GUI patchers, core library written in ISO C++9x. - all of this is public domain --- byuu.cinnamonpirate.com/files/ups_v03.zip (Windows and Linux binaries) byuu.cinnamonpirate.com/files/ups_v03.tar.bz2 (source) The executables themselves have simplistic UIs, I was aiming for something as easy as LunarIPS. Linux GUI: Windows GUI (yes, it supports XP / Vista themes, too): Both Windows and Linux binaries work on the command line, too. Use: ups --create original.bin modified.bin patch.ups ups --apply input.bin output.bin patch.ups Run directly to get the UI. --- The spec itself: /***** * file format: ***** * [4] signature ("UPS1") * [V] X file size * [V] Y file size * [?] { * [V] relative difference offset * [?] X ^ Y * [1] 0x00 terminator * } * [4] X file crc32 * [4] Y file crc32 * [4] Z file crc32 *****/ If you want the more detailed spec, see the source code, libups/libups.hpp file. It contains all specific rules. And libups/libups.cpp has the reference implementation with the pointer length encoding / decoding algorithm. It's simple enough. --- Lastly, I've taken the input of mostly everyone here into mind when making this, except obviously when conflicting. Most importantly, I'm of the same mind as Nightcrawler for how to replace IPS: simple format, simple tools, simple implementation. Simple all around. Just address IPS' limitations. Now, if we're ever going to get rid of IPS, we need to rally behind a format. If you guys don't want UPS, fine. But we need to pick something. I hope it'll be UPS, of course. What we need is for as many people as possible to push a replacement format and get it out there. Otherwise we'll be stuck with IPS forever :/
|
|
« Last Edit: March 31, 2008, 05:47:40 am by byuu »
|
|
|
|
BRPXQZME
Guest
|
|
« Reply #1 on: March 31, 2008, 02:15:10 am » |
|
Superb! One little thing: I don’t mean to throw wrenches or anything, but the binary name upx is already used by the UPX utility. This will create unpleasant name collisions for some poor sap somewhere.
|
|
|
|
Lenophis
Guest
|
|
« Reply #2 on: March 31, 2008, 02:31:31 am » |
|
Well, if it doesn't mess with a source file, or like to point out things that end user should have say over in the first place, then I think this is a step in the right direction. One little thing: I don’t mean to throw wrenches or anything, but the binary name upx is already used by the UPX utility. This will create unpleasant name collisions for some poor sap somewhere. Your reading isn't as good. byuu is calling it "UPS," as in "What can Brown do you for?" UPS != UPX --Edit-- Testing it out real quick, (XP user here, forgive my ineptness), the only thing I would point out (UI only, didn't test command line) is that when you create a patch, "Save as" doesn't add an extension if one is not supplied. Not a big deal by any means, just wanting to point it out.
|
|
« Last Edit: March 31, 2008, 02:43:15 am by Lenophis »
|
|
|
|
BRPXQZME
Guest
|
|
« Reply #3 on: March 31, 2008, 02:54:31 am » |
|
Your reading isn't as good. byuu is calling it "UPS," as in "What can Brown do you for?" UPS != UPX One little thing: I don’t mean to throw wrenches or anything, but the binary name upx is already used by the UPX utility. This will create unpleasant name collisions for some poor sap somewhere.
|
|
|
|
byuu
Guest
|
|
« Reply #4 on: March 31, 2008, 03:19:09 am » |
|
Typo in my post, sorry. The binaries are called "ups". Also, I should clarify that this is basically Nach's NPS format with DMV27's pointer encoding trick (from http://www.codecodex.com/wiki/index.php?title=Variable-Length_Integers). The only thing I really did here was come up with the same idea after hearing Nach's (but without seeing his actual format), and implement it in C++. But really, all credit should go to Nach and whoever came up with the pointer encode trick. Also, Nach is going to write a patcher as well. It'll probably be much higher quality than mine.
|
|
« Last Edit: March 31, 2008, 03:31:32 am by byuu »
|
|
|
|
BRPXQZME
Guest
|
|
« Reply #5 on: March 31, 2008, 03:41:27 am » |
|
Typo in my post, sorry. The binaries are called "ups".
Way better. (Didn’t verify; mac user. Perhaps I should take one from Reagan’s book more often, eh?)
|
|
|
|
byuu
Guest
|
|
« Reply #6 on: March 31, 2008, 05:50:28 am » |
|
Okay, really sorry about this ... if you downloaded v0.01 or v0.02, please delete it and download v0.03.
When Nach was making his patcher, he noticed a flaw in mine, as well as some optimizations. I've updated the binaries and source to fix this. This should be absolutely final now. But if you want to play it safe, Nach should have his patcher out by tomorrow. Our patchers should generate 100% identical files.
I also made it default to ".ups" for the patch select button now.
|
|
|
|
I.S.T.
Guest
|
|
« Reply #7 on: March 31, 2008, 09:30:43 am » |
|
Fuck yes! We(Being the hacking/translation scene)'ve been needing this for years!
|
|
|
|
Ryusui
Guest
|
|
« Reply #8 on: March 31, 2008, 10:41:50 am » |
|
You know what I'm going to do? When I release my BoF2 patch, it's gonna be in UPS format. :3 And now that the max file size has been upped, I can release a patch for Death Note: The Kira Game with no problems. ^_^
|
|
|
|
byuu
Guest
|
|
« Reply #9 on: March 31, 2008, 11:40:10 am » |
|
Yay! Glad to see you guys like it. Thank you so much for the support!! It's absolutely critical to make this work (and replace IPS once and for all), of course. I'll go over the format here in more technical detail, for those interested. Okay, so we already know the format is: [4] signature ("UPS1") [V] X file size [V] Y file size [?] { [V] relative difference offset [?] X ^ Y [1] 0x00 terminator } [4] X file crc32 [4] Y file crc32 [4] Z file crc32 So let's go over why things are the way they are ... The signature, I wanted a dword-alignment, so I added "1" at the end. This isn't so that the format can change in the future to "UPS2", although perhaps in the far future, "UPS2" will be a different type of patch, such as an xdelta-like patching algorithm. They won't compete against each other. They would be combined in a container such as NINJA3 or whatever. But suffice to say for now, it's just a signature. Next, the file sizes. The reason they are at the top and not the bottom of the file, is because it's easy to get a file size without reading the whole thing, and because if it were at the bottom, you couldn't automatically tell where the footer began, as it would not be constant. And the file sizes are variable-length encoded so as to support up to infinitely sized files. So why put the CRC32's at the bottom? Simple, they can be computed during patch creation. As you make the patch, compute the CRC32, and you only need a single pass over each file to make the patch. Likewise, the patch CRC32 will be known at this point if you keep track of each write. The patch CRC32 comes last because it contains even the file CRC32s (eg if you modify the file CRC32's, the patch CRC32 needs to be modified as well. This is a good thing.) And because of this exact ordering, it means the two input files can be read, start to finish, one time. The patch can be written, start to finish, one time. There's no need for special back-seeking, or read+write file permissions as would be required with any other ordering. --- Okay, now what's this [V] stuff about? This is basically: http://www.codecodex.com/wiki/index.php?title=Variable-Length_Integers... but with a twist. We toggle the meaning of bit 7, 0 = continue, 1 = end. This cuts out two math operations during patching, which makes the algorithm faster with no speed penalty. Here is a C implementation of the encoding / decoding of pointers. It's the same for file sizes and for patch relative offsets. void encode_pointer(uint64_t offset) { for(;"";) { uint64_t x = offset & 0x7f; offset >>= 7; if(offset == 0) { fputc(0x80 | x, patch); break; } fputc(x, patch); offset--; } }
uint64_t decode_pointer() { uint64_t offset = 0, shift = 1; for(;"";) { uint8_t x = fgetc(patch); offset += (x & 0x7f) * shift; if(x & 0x80) break; shift <<= 7; offset += shift; } return offset; } A bit of magic, but 100% optimal. There are no two pointers that decode to the same value. You'll see why this trick is needed soon. Lastly, we have the actual patch blocks. All blocks contain XOR data of the two input files, followed by block 0x00 terminators when differences end. XOR has an interesting property: X^Y=Z. And now, both X^Z=Y and Y^X=Z. This property means that the same exact patch file is bi-directional. That means you can make a patch against your original Japanese file and the English translation. When someone applies the patch once, it turns into the English game. Apply it again, and it turns back into the Japanese game! You don't have to keep both files around anymore! The upside is that decompressed, it takes the same exact number of bytes as an IPS patch. The downside is you lose a bit of compression power, as XORed data contains two streams of data, rather than one. It's less likely a compressor will find patterns. We think it's worth it. Now, the actual offset pointers are relative, not absolute. This combined with the pointer encoding trick means that ~95% of pointers are only one byte long, compared to a fixed 3 for IPS. And better yet, it supports infinitely sized files. Because the pointers are always relative, we avoid the semi-tricky case with IPS patches that store out-of-order patch offsets. This is impossible with relative pointers, so we need not even worry about this case. By relative, I mean the decoded pointer says "seek N bytes forward to find the next difference", rather than 'seek to offset N to find the next difference." This has an economy of scale that works to our advantage, too. When data has lots of changes close together, you have to keep making new change blocks. This is the densest, worst case scenario for both UPS and IPS. IPS' worst case is a file that changes every other byte. In that case, an IPS patch will be 8+filesize/2*(3+2+1) bytes. Eg 1MB becomes a ~3MB patch. For UPS, this will be: 20+filesize/2+(3) bytes, or ~1.5MB. Still bad, of course, but not awful. I will note that uCON64 has some black magic that writes out unchanged bytes as an optimization, we give that up to avoid encoding the lengths. That is ... each block ends with 0x00. This is actually another special property. If X==Y, then X^Y==0x00. Convenient. As soon as two bytes are identical, that marks the end of a block. So long as we write out the first identical byte (as 0x00), this counts as our terminator. Think of it like C-style strings. Null-terminated, rather than Pasal-style strings, with the length at the beginning. Another advantage is that we don't ever have to stop a diff block because the length exceeded our length size (eg a match >= 65536 bytes in IPS.) It simplifies patching a bit more. Next up, we omit RLE. Why? I've done tests against IPS with and without RLE. My findings were that IPS+RLE was smaller than IPS, but IPS+RLE+ZIP was larger than IPS+ZIP. So you add extra complexity to shrink down a patch, and for what? Your bandwidth bill ends up larger, and the part that really doesn't matter (the user's ~200GB hard drive) shaves off a bit of size. Lame. I've decided it's better to leave compression to compression algorithms. They continue to improve with time. Store the UPS file in a ZIP, or 7-Zip, or whatever archive type is popular in the future. Patch appliers can even do this automatically, stick the ups file inside a ZIP and call it .upz. Most emus support IPS patches inside ZIPs already ... this is a logical extension of that. By omitting RLE, we also make the patching algorithm a lot simpler. Both for creating and for applying patches. When reading past the end of the file, you have to have a value to XOR against. Unlike libc, I return 0x00 here, rather than 0xff. Why? 0x00 is more common, and it also results in clear-visible data at the end of the file. Let's be real here for a minute, using 0xff to invert the data doesn't do us any good at all. In fact it may hinder some compression algorithms (eg ones with explicit ASCII text detection,) and the data there, if copyrighted, is no less illegal when XORed against 0xff as it is against 0x00. The downside here is that if you expand a ROM from 2MB to 4MB, and you expand with 0xff, expect the UPS file to end up at ~2MB or so, because it will see that 0x00 != 0xff. Again, the compression eliminates this. It will compact that entire expanded block to ~3 bytes or whatever. Lastly, all blocks, even the last one, write out the 0x00 terminator, even though the last one isn't technically needed. That simplifies the patch application process a bit, and makes the patch format completely consistent. And that's it! That's the whole spec. One of the neatest things is that any compatible patcher can only make a patch exactly one way. There's zero room for variation, there's zero "holes" where some odd behavior can occur between different patchers (eg IPS with an offset of "EOF", or length of 0, etc.) It's easy to tell if your patcher is correct -- just compare a patch made against the reference library. If it's bit perfect, you got it right. And again, the format is 110% finalized and sealed off. This is so that we don't end up with all the extension shit added onto IPS. Eg Lunar IPS patches add a clip command that SNESTool won't parse. They also added RLE after release, and such. If anyone does this with UPS, it will be a license violation. It is absolutely not permitted. This means that the current UPS patcher will support every UPS patch ever made, which is very important. UPS doesn't need a clip command, because unlike IPS, it stores file sizes for each file, which makes clipping / growing trivial. Probably the most controversial feature is the lack of RLE. Again, I really feel that simplicity is more important. The patch applier can pull the UPS right out of a ZIP if need be. I can see some people stressing when the UPS patch is much larger than UPS before compression. And that's all. That covers every possible input case and edge case. Feel free to ask if you need more clarification on any specifics of the format.
|
|
« Last Edit: March 31, 2008, 12:00:34 pm by byuu »
|
|
|
|
I.S.T.
Guest
|
|
« Reply #10 on: March 31, 2008, 11:49:39 am » |
|
Byuu, I'd go make a news post about this...
|
|
|
|
byuu
Guest
|
|
« Reply #11 on: March 31, 2008, 12:11:03 pm » |
|
I'm lazy, anyone want to help out? Here's something to make it more interesting: http://projects.elsallia.com/http://projects.elsallia.com/der-langrisser/downloadsDer Langrisser v1.02 was released today in UPS format. It's a six-year anniversary, April Fool's release. Don't worry, it's a real release :) Also, Nach's patcher should be out soon enough. So you won't be limited to just mine for long.
|
|
|
|
Ryusui
Guest
|
|
« Reply #12 on: March 31, 2008, 01:06:18 pm » |
|
I wouldn't have released SF1 in IPS format at all if 1. D hadn't alienated himself from the community and 2. a prominent member of my fanbase didn't happen to be a Mac user (the NINJA patcher can be run on a Mac, but it requires a buttload of work). Hopefully this time I'm putting my proverbial money behind a winning pony.
|
|
|
|
byuu
Guest
|
|
« Reply #13 on: March 31, 2008, 01:26:37 pm » |
|
Well, my patcher requires Windows or Linux. I'm not going to pay Apple $600 for $200 worth of hardware just to run their crippled OS with two configuration options in the entire OS (color of buttons = blue or teal, minimize effect = genie or scale.) If they don't want me to run their OS on affordable hardware, then their users suffer by my work not being available there.
Mine at least doesn't require anything else to run. No .NET, no MFC DLLs ... none of that.
Anyway, if you've ever used Nach's NSRT or NSFE programs written in Qt ... that's what his patcher should be like. His stuff, being Qt, runs on Windows, Linux and OS X. And he usually provides binaries for them all, along with auto-translation stuff for other languages and such. In other words, it should be a lot nicer than mine.
As for D and the community ... I don't see why that matters, really. D wasn't involved in UPS, it's basically Nach's NPS format ideas with me setting up the file layout and adding that pointer encoding thing and making a package out of it. Nach agreed that the encoded pointers work good, so we're merging that to UPS.
D will be creating the NINJA3 spec. Both specs are basically public domain, but like Firefox, if you want to change it, you need to rename it. Thus, it shouldn't matter who dislikes who.
But if you guys want to stick to pure UPS to spite D, that's fine by me. Or use NINJA3 and get all the benefits the container format adds. Just please, let's kill IPS once and for all. Please :)
NINJA3 will basically store UPS files inside of it for each individual patch, and will encode things such as authorship, system information, header information, etc on top of it. It will also handle multi-patch setups for eg PC games. But as the core uses UPS anyway, you can use one or the other. It's up to you. We aren't pushing either one over the other.
If either get accepted, the UPS spec will succeed. But NINJA3 will only succeed if people take to that over the simplicity of raw UPS files. It's really up to you guys to decide on that one. The main advantage of NINJA3 (header detection and such) can be abstracted out of the patch format to the patcher for most systems (SNES, Genesis, etc), thanks to storing file sizes and CRCs.
Hopefully myself and Nach have good standing in the community, at least :)
|
|
« Last Edit: March 31, 2008, 01:35:00 pm by byuu »
|
|
|
|
Lenophis
Guest
|
|
« Reply #14 on: March 31, 2008, 01:44:54 pm » |
|
Mine at least doesn't require anything else to run. No .NET, no MFC DLLs ... none of that. I always like things that run out of the box. Hopefully myself and Nach have good standing in the community, at least So far, I like what I see. :thumbsup: One question (lack of readme does this to me), does it fail to patch if the CRC32 check fails?
|
|
|
|
|