emoose / MBINCompiler

Now maintained by monkeyman192: https://github.com/monkeyman192/MBINCompiler
Other
43 stars 69 forks source link

EXML patching, a new way to share mods? #68

Open emoose opened 7 years ago

emoose commented 7 years ago

Apologies for the length, I write too much sometimes...

spAnser suggested this to me which reminded me of some ideas I had before. Now that we're able to decompile everything to XML we could probably do proper mod patches.

What I'm thinking is that we could use RFC5261 "An Extensible Markup Language (XML) Patch Operations Framework Utilizing XML Path Language (XPath) Selectors", or maybe Microsofts own XML patching stuff built into .NET (which seems different to RFC5261), or some other XML patching frame work.

There's a lot of benefits I can see to releasing mods as patches rather than full MBIN files:

Of course this would require some work from the developer of the mod manager, it'd need to have MBINCompiler built into it as well as the ability to unpak the original MBIN, and then repack the modified MBIN as a PAK file.

On the flip side, mod authors would no longer need to work with PAK tools to pak their mods - instead, they'd need to use MBINCompiler to create a patch file for their modded files, which is a decent trade-off since mod authors would likely already be familiar with MBINC. They could then distribute their mods using normal ZIP files, though users would need a mod manager installed to apply them.

The way I could see a mod author using it:

And the way I could see a mod manager working with these files:

Only main issues I can see with this are:

Any thoughts?

emoose commented 7 years ago

The main post was getting a bit long, so I've split this up.

Some people might think "why not just make binary patches for the MBIN files?", that's one thing that could get around the copyright issue, but it wouldn't be able to allow mods to work together, or detect conflicts.

Mods might work together, but binary patches don't know the sizes of fields, they don't know the data they're patching, they only know that the data at offset x should be changed to this y bytes buffer.

As an example, what if an MBIN had a field at offset 4, an integer field that's 4 bytes, a mod then changes that integer, but in the file itself only one byte of the integer changed, say the byte at 6.

To the patcher, it'd only know that a byte at 6 changed - not that the integer at 4 changed - so when another mod changes the integer, which results in a change at byte 4 this time, the patcher wouldn't be able to detect this conflict.

It'd only know that the patches aren't changing the same bytes, not that they're changing the same fields, so the patcher would allow the two mods to work together, in the process creating it's own franken-mod monster where two mods have edited the same field, with the result that neither mods changes have been properly applied. Of course this is an uncommon scenario to happen, but it will happen eventually, given enough mods being installed.

With XML based patching this couldn't happen. The patcher is aware of each field being changed, there's no chance of only part of a field being changed, to the patcher the whole field has changed. If another mod changes that field, it'll be able to see this and alert the user to it.

hhrhhr commented 7 years ago

@emoose

No copyrighted data included at all

references to copyright in this case completely inappropriate. ...\No Man's Sky\EULA.txt: "You may not... in whole or in part reproduce, translate, reverse engineer, derive source code from, modify, adapt, merge, translate, disassemble, decompile, or create derivative works based on or of the Game...".

Mods can work together on the same file Reduced size

modders can use an ordinary diff+patch.

Only contains actual mod data

diff file much clearer and more useful than the xml-patch. for example, i want to change the price of a single resource. in the form of a xml-patch it will look like this:

<diff> 
  <replace sel="/Data/Property/Data[11]/Property[@name='BaseValue']/> 
    <Property name="BaseValue" value="600" />
  </replace> 
</diff> 

here is not visible old price and it is unclear at what exactly the resource (what kind of index 11?) i change the price. but if like this,

--- NMS_REALITY_GCSUBSTANCETABLE.old.exml   2016-08-25 17:36:37.519855100 +0300
+++ NMS_REALITY_GCSUBSTANCETABLE.new.exml       2016-08-31 09:31:31.253102900 +0300
@@ -107 +107 @@
      <Property name="Id" value="TECHRARE1" />
@@ -133 +133 @@
-      <Property name="BaseValue" value="60" />
+      <Property name="BaseValue" value="600" />

as well as immediately clear what was going on.

besides, i can use a lot of tools to produce "diff-code". but how and with what i can automatically compare two xml-file and get the RFC5261-like file differences?

emoose commented 7 years ago

I didn't really think of diff patching, but I could see some problems with it, eg. what if the locator that diff patching uses

<Property name="Id" value="TECHRARE1" />

is no longer in the file anymore, or has changed a lot since the patch was made? (thanks to updates, or other mods changing it), with XML patching which uses XPath as a locator this would still be an issue, but much less of one as the locator isn't as exact as the diff locator (eg just changing the value of that property would throw diff off, but with an XPath based on selecting by "name='Id'" the value of it wouldn't matter)

diff patching is more suitable for text data, like source code, but for structured data like XML there are better alternatives available to us. Readability of the patch data isn't really a concern, since the tool will be generating/reading the patch files for the user.

From the XML patch you showed above that seems pretty readable to me though.

here is not visible old price

The old price shouldn't be a concern, since that could have easily been changed by the time the patch is being applied (by updates or other mods), having that be a part of the patch file would make the patch more likely to fail if anything.

and it is unclear at what exactly the resource (what kind of index 11?) i change the price

Well the way I read it, it's changing the 12th Data element inside the Property element, and then searching for the Property inside that Data element by the name "BaseValue", once it's been found it replaces that Property element with the one inside the patch.

If we wanted these patches more readable (showing the original data being changed and what it's being changed to etc), it'd be trivial to implement a standard diff into the patching program, just for letting users see exactly what's changed, but while that's useful for users it's less useful for making a patch that can work well with other patches.

hhrhhr commented 7 years ago

what if the locator that diff patching uses ... is no longer in the file anymore, or has changed a lot since the patch was made?

if you add a new element to the structure or remove old one, then the index of the desired item can be changed. in that case xml-patch is also not acceptable: diff-patch did not work and can report the error, but xml-patch change is not what is needed.

emoose commented 7 years ago

Well I've decided that the best way to get the ball rolling is with a proof of concept. Over the next few days I'll be moving MBINC's main code into a class library, along with porting over spAnsers excellent PSARC packing/unpacking code to C#, which will allow us to handle PSARC/PAK files without needing to touch any copyrighted Sony tools. (MBINC won't be using this PAK code ofc, but the proof of concept will, along with any other C# programs written by others that wish to use the PSARC code)

This MBINC repo will still be used as the main repo, but MBINC will be relegated to a simple frontend for the code in the class library.

Meanwhile the proof of concept will show how patching + paking is the best way for mods, by allowing the features stated above (multiple mods editing the same file, game version independence, smaller file size..) in a simple to use app.

My hope is that through this proof of concept other mod managers might come around to using the same system for their mods.

(for anyone worried about the transition, it'll be done in a separate branch to the master of course, so any contributions people make won't go to waste. Keep making those pull requests :)

@hhrhhr

if you add a new element to the structure or remove old one, then the index of the desired item can be changed. in that case xml-patch is also not acceptable: diff-patch did not work and can report the error, but xml-patch change is not what is needed.

The XML patch would still have a better chance of success over the diff patch though, but error reporting is a good point.

Not sure how we could detect all the possible errors that might come up, maybe in the patches we can give the locator more specific information to locate the field that's being changed, and then if it isn't found we'll get our error, not sure how feasible that is to implement though.