Tes4Mod:Mod Merging

Unlike Morrowind, the Elder Scrolls Construction Set for Oblivion (CS4) does not support the merging of mods. Moreover, the introduction and pervasive use of FormIds greatly complicates the mod merging procedure. Given here is a rough outline of a procedure for merging mods in this new environment.

The latest version of TESCS supports some merge capabilities. Also, the merging capabilities of  ,  , and Tes4Edit can solve a lot of the issues discussed here. See Mod Reconciliation for a brief overview.

Procedure
While scanning each mod to be merged, user may have more than one master file loaded. Hence there could be several legitimate modindex numbers, (00,01,02, etc.). The ordering of these will match the ordering of the masters in the TES4 record. 
These can/should be kept the same.

The user may have several illegitimate modindices. This would be the case if they had loaded esps in addition to esms, and then tried to modify or even use any of the data objects from that esp. Even if the user did not use any data from those mods, then the modindex assigned to the mod being scanned would be too high. (E.g., 02, instead of 01.)
If this happens, then the simplest thing to do is to report an error, and ask the user to reload the mod under CS4 w/o the additional esps and let CS4 sort out/fix the modindices.
I haven't really checked how CS4 handles this case. I think that it just compresses the extra modindices. E.g., if 02,03 and 04 are illegitimate, I think that it just changes them all to 01.

So, assuming all that's sorted out, then you start the actual merging. I'm assuming that you start with one mod and merge the second mod into it. Suppose that you're merging mods A and B.

Differing masters: The mods may have different masters. E.g., A may have mods C and D as masters, while B may have mods C and E as masters. You'll have to remap modindices in both mods:
Assume that the merged mod will have masters C, D and E in that order. (I'm hoping that CS4 does not require this ordering to be the same as load order.)
Then for mod A, remap modindex 02 (the mod itself) to modindex 03.
And for mod B, remap modindex 02 to 03, and 01 (master E) to 02.

Now, if mod B overrides any of the same master data objects that mod A does, then those data records in mod A should be replaced with the version in mod B. While doing this, you should check to see if there's a change in EditorId, and if so, issue a warning. (EditorIds are at least used in the plain text version of scripts and will need to be adjusted.) If you're writing a log, you'll want to note the override.

Now, look for editor id conflicts. (E.g., different formids, but the same editorid.) Simplest solution is to just warn the user and quit. They can go fix it in CS4. Note that you should probably list all editor id conflicts rather than just quit on the first one.

Now, you start reassigning objectindices in mod B. It looks to me like the numbers are assigned sequentially, so I would probably find the highest used objectindex in mod A and start right after that. (But that's just my best guess.)

So find all the conflicting objectindices in mod B and reassign them. Then physically merge the records and write to file, and you're done!

Notes
If the first file is a master and the second file depends on the first, then the modindex remapping is adjusted somewhat of course. Circular masters are not allowed. (I.e., mods A and B cannot both be masters which depend on each other.)

GMSTs seem to be treated differently than other data objects -- i.e., they're identified by their editorid, ~not~ their formid, so they should override based on EditorId, not formid. (Note that I'm only fairly sure about this. You should double check.)

Subrecord Remaps
The main (non-coding) problem of mod merging is knowing where all the formids are. I.e., which subrecords contain them, and how they're contained. What you need is a list of all the formid storage points. Something like this:
RECR.SUBR (whole subrecord is a single formid)
RECR.SUBR:* (whole subrecord is an array of formids. Scripts do this, I think. Note that any reference to a global or data object of any sort in the script will produce a formid)
RECR.SUBR:8,10,20 (subrecord has several different formids in it, at different byte offsets. I don't know if anything does this.)