private static void UpdateOffsets(IExportEntry export) { //update offsets for pcc-stored audio in wwisestreams if (export.ClassName == "WwiseStream" && export.GetProperty <NameProperty>("Filename") == null) { byte[] binData = export.getBinaryData(); binData.OverwriteRange(12, BitConverter.GetBytes(export.DataOffset + export.propsEnd() + 16)); export.setBinaryData(binData); } //update offsets for pcc-stored movies in texturemovies if (export.ClassName == "TextureMovie" && export.GetProperty <NameProperty>("TextureFileCacheName") == null) { byte[] binData = export.getBinaryData(); binData.OverwriteRange(12, BitConverter.GetBytes(export.DataOffset + export.propsEnd() + 16)); export.setBinaryData(binData); } //update offsets for pcc-stored mips in Textures else if (export.ClassName == "Texture2D" || export.ClassName == "LightMapTexture2D" || export.ClassName == "TextureFlipBook") { int baseOffset = export.DataOffset + export.propsEnd(); MemoryStream binData = new MemoryStream(export.getBinaryData()); for (int i = binData.ReadValueS32(); i > 0 && binData.Position < binData.Length; i--) { if (binData.ReadValueS32() == 0) //pcc-stored { int uncompressedSize = binData.ReadValueS32(); binData.Seek(4, SeekOrigin.Current); //skip compressed size binData.WriteValueS32(baseOffset + (int)binData.Position + 4); //update offset binData.Seek(uncompressedSize + 8, SeekOrigin.Current); //skip texture and width + height values } else { binData.Seek(20, SeekOrigin.Current);//skip whole rest of mip definition } } export.setBinaryData(binData.ToArray()); } }
/// <summary> /// Attempts to relink unreal binary data to object pointers if they are part of the clone tree. /// It's gonna be an ugly mess. /// </summary> /// <param name="importpcc">PCC being imported from</param> private List <string> relinkBinaryObjects(IMEPackage importpcc) { List <string> relinkFailedReport = new List <string>(); foreach (KeyValuePair <int, int> entry in crossPCCObjectMap) { if (entry.Key > 0) { IExportEntry exp = pcc.getExport(entry.Value); byte[] binarydata = exp.getBinaryData(); if (binarydata.Length > 0) { //has binary data //This is temporary until I find a more permanent style for relinking binary try { switch (exp.ClassName) { case "WwiseEvent": { int count = BitConverter.ToInt32(binarydata, 0); for (int i = 0; i < count; i++) { int originalValue = BitConverter.ToInt32(binarydata, 4 + (i * 4)); int currentObjectRef = unrealIndexToME3ExpIndexing(originalValue); //count + i * intsize int mapped; bool isMapped = crossPCCObjectMap.TryGetValue(currentObjectRef, out mapped); if (isMapped) { mapped = me3ExpIndexingToUnreal(mapped, originalValue < 0); //if the value is 0, it would have an error anyways. Debug.WriteLine("Binary relink hit for WwiseEvent Export " + exp.UIndex + " 0x" + (4 + (i * 4)).ToString("X6") + " " + originalValue + " -> " + (mapped + 1)); WriteMem(4 + (i * 4), binarydata, BitConverter.GetBytes(mapped)); int newValue = BitConverter.ToInt32(binarydata, 4 + (i * 4)); Debug.WriteLine(originalValue + " -> " + newValue); } else { Debug.WriteLine("Binary relink missed WwiseEvent Export " + exp.UIndex + " 0x" + (4 + (i * 4)).ToString("X6") + " " + originalValue); relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relink error: WwiseEvent referenced WwiseStream " + originalValue + " is not in the mapping tree and could not be relinked"); } } exp.setBinaryData(binarydata); } break; case "Class": { if (exp.FileRef.Game != importpcc.Game) { //Cannot relink against a different game. continue; } IExportEntry importingExp = importpcc.getExport(entry.Key); if (importingExp.ClassName != "Class") { continue; //the class was not actually set, so this is not really class. } //This is going to be pretty ugly try { byte[] newdata = importpcc.Exports[entry.Key].Data; //may need to rewrite first unreal header byte[] data = importpcc.Exports[entry.Key].Data; int offset = 0; int unrealExportIndex = BitConverter.ToInt32(data, offset); offset += 4; int superclassIndex = BitConverter.ToInt32(data, offset); if (superclassIndex < 0) { //its an import ImportEntry superclassImportEntry = importpcc.Imports[Math.Abs(unrealIndexToME3ExpIndexing(superclassIndex))]; ImportEntry newSuperclassValue = getOrAddCrossImport(superclassImportEntry.GetFullPath, importpcc, exp.FileRef); WriteMem(offset, newdata, BitConverter.GetBytes(newSuperclassValue.UIndex)); } else { relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relink error: Superclass is an export in the source package and was not relinked."); } int unknown1 = BitConverter.ToInt32(data, offset); offset += 4; int classObjTree = BitConverter.ToInt32(data, offset); //Don't know if I need to do this. offset += 4; //I am not sure what these mean. However if Pt1&2 are 33/25, the following bytes that follow are extended. int headerUnknown1 = BitConverter.ToInt32(data, offset); Int64 ignoreMask = BitConverter.ToInt64(data, offset); offset += 8; Int16 labelOffset = BitConverter.ToInt16(data, offset); offset += 2; int skipAmount = 0x6; //Find end of script block. Seems to be 10 FF's. while (offset + skipAmount + 10 < data.Length) { //Debug.WriteLine("Cheecking at 0x"+(offset + skipAmount + 10).ToString("X4")); bool isEnd = true; for (int i = 0; i < 10; i++) { byte b = data[offset + skipAmount + i]; if (b != 0xFF) { isEnd = false; break; } } if (isEnd) { break; } else { skipAmount++; } } int offsetEnd = offset + skipAmount + 10; offset += skipAmount + 10; //heuristic to find end of script uint stateMask = BitConverter.ToUInt32(data, offset); offset += 4; int localFunctionsTableCount = BitConverter.ToInt32(data, offset); offset += 4; bool isMapped; for (int i = 0; i < localFunctionsTableCount; i++) { int nameTableIndex = BitConverter.ToInt32(data, offset); int nameIndex = BitConverter.ToInt32(data, offset + 4); NameReference importingName = importpcc.getNameEntry(nameTableIndex); int newFuncName = exp.FileRef.FindNameOrAdd(importingName); WriteMem(offset, newdata, BitConverter.GetBytes(newFuncName)); offset += 8; int functionObjectIndex = unrealIndexToME3ExpIndexing(BitConverter.ToInt32(data, offset)); int mapped; isMapped = crossPCCObjectMap.TryGetValue(functionObjectIndex, out mapped); if (isMapped) { mapped = me3ExpIndexingToUnreal(mapped, functionObjectIndex < 0); //if the value is 0, it would have an error anyways. WriteMem(offset, newdata, BitConverter.GetBytes(mapped)); } else { relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relink error: Local function[" + i + "] could not be remapped during porting: " + functionObjectIndex + " is not in the mapping tree and could not be relinked"); } offset += 4; } int classMask = BitConverter.ToInt32(data, offset); offset += 4; if (importpcc.Game != MEGame.ME3) { offset += 1; //seems to be a blank byte here } int coreReference = BitConverter.ToInt32(data, offset); if (coreReference < 0) { //its an import ImportEntry outerclassReferenceImport = importpcc.Imports[Math.Abs(unrealIndexToME3ExpIndexing(coreReference))]; ImportEntry outerclassNewImport = getOrAddCrossImport(outerclassReferenceImport.GetFullPath, importpcc, exp.FileRef); WriteMem(offset, newdata, BitConverter.GetBytes(outerclassNewImport.UIndex)); } else { relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relink error: Outerclass is an export in the original package, not relinked."); } offset += 4; if (importpcc.Game == MEGame.ME3) { offset = ClassParser_RelinkComponentsTable(importpcc, exp, relinkFailedReport, ref newdata, offset); offset = ClassParser_ReadImplementsTable(importpcc, exp, relinkFailedReport, ref newdata, offset); int postComponentsNoneNameIndex = BitConverter.ToInt32(data, offset); int postComponentNoneIndex = BitConverter.ToInt32(data, offset + 4); string postCompName = importpcc.getNameEntry(postComponentsNoneNameIndex); //This appears to be unused in ME3, it is always None it seems. int newFuncName = exp.FileRef.FindNameOrAdd(postCompName); WriteMem(offset, newdata, BitConverter.GetBytes(newFuncName)); offset += 8; int unknown4 = BitConverter.ToInt32(data, offset); offset += 4; } else { offset = ClassParser_ReadImplementsTable(importpcc, exp, relinkFailedReport, ref data, offset); offset = ClassParser_RelinkComponentsTable(importpcc, exp, relinkFailedReport, ref data, offset); int me12unknownend1 = BitConverter.ToInt32(data, offset); offset += 4; int me12unknownend2 = BitConverter.ToInt32(data, offset); offset += 4; } int defaultsClassLink = unrealIndexToME3ExpIndexing(BitConverter.ToInt32(data, offset)); int defClassLink; isMapped = crossPCCObjectMap.TryGetValue(defaultsClassLink, out defClassLink); if (isMapped) { defClassLink = me3ExpIndexingToUnreal(defClassLink, defaultsClassLink < 0); //if the value is 0, it would have an error anyways. WriteMem(offset, newdata, BitConverter.GetBytes(defClassLink)); } else { relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relink error: DefaultsClassLink cannot be currently automatically relinked by Binary Relinker. Please manually set this in Binary Editor"); } offset += 4; if (importpcc.Game == MEGame.ME3) { int functionsTableCount = BitConverter.ToInt32(data, offset); offset += 4; for (int i = 0; i < functionsTableCount; i++) { int functionsTableIndex = unrealIndexToME3ExpIndexing(BitConverter.ToInt32(data, offset)); int mapped; isMapped = crossPCCObjectMap.TryGetValue(functionsTableIndex, out mapped); if (isMapped) { mapped = me3ExpIndexingToUnreal(mapped, functionsTableIndex < 0); //if the value is 0, it would have an error anyways. WriteMem(offset, newdata, BitConverter.GetBytes(mapped)); } else { if (functionsTableIndex < 0) { ImportEntry functionObjIndex = importpcc.Imports[Math.Abs(functionsTableIndex)]; ImportEntry newFunctionObjIndex = getOrAddCrossImport(functionObjIndex.GetFullPath, importpcc, exp.FileRef); WriteMem(offset, newdata, BitConverter.GetBytes(newFunctionObjIndex.UIndex)); } else { relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relink error: Full Functions List function[" + i + "] could not be remapped during porting: " + functionsTableIndex + " is not in the mapping tree and could not be relinked"); } } offset += 4; } } exp.Data = newdata; } catch (Exception ex) { relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relink error: Exception relinking: " + ex.Message); } } break; default: continue; } } catch (Exception e) { relinkFailedReport.Add(exp.Index + " " + exp.GetFullPath + " binary relinking failed: " + e.Message); } //Run an interpreter pass over it - we will find objectleafnodes and attempt to update the same offset in the destination file. //BinaryInterpreter binaryrelinkInterpreter = new ME3Explorer.BinaryInterpreter(importpcc, importpcc.Exports[entry.Key], pcc, pcc.Exports[entry.Value], crossPCCObjectMapping); } } } return(relinkFailedReport); }