Example #1
0
        public void Deserialize(IMEPackage pcc)
        {
            PropertyCollection properties = export.GetProperties();
            int off;

            switch (pcc.Game)
            {
            case MEGame.ME3:
                off = export.propsEnd() + 8;
                break;

            case MEGame.ME2:
                off = export.propsEnd() + 0x28;
                break;

            default:
                throw new Exception("Can oly read WwiseStreams for ME3 and ME2!");
            }
            ValueOffset = off;
            DataSize    = BitConverter.ToInt32(memory, off);
            DataOffset  = BitConverter.ToInt32(memory, off + 4);
            NameProperty nameProp = properties.GetProp <NameProperty>("Filename");

            FileName = nameProp?.Value;
            Id       = properties.GetProp <IntProperty>("Id");
        }
Example #2
0
        public static StorageTypes GetTopMipStorageType(ExportEntry exportEntry)
        {
            MemoryStream ms = new MemoryStream(exportEntry.Data);

            ms.Seek(exportEntry.propsEnd(), SeekOrigin.Begin);
            if (exportEntry.FileRef.Game != Mod.MEGame.ME3)
            {
                ms.Skip(4);                     //BulkDataFlags
                ms.Skip(4);                     //ElementCount
                int bulkDataSize = ms.ReadInt32();
                ms.Seek(4, SeekOrigin.Current); // position in the package
                ms.Skip(bulkDataSize);          //skips over thumbnail png, if it exists
            }

            var mips       = new List <Texture2DMipInfo>();
            int numMipMaps = ms.ReadInt32();

            for (int l = 0; l < numMipMaps; l++)
            {
                var storageType = (StorageTypes)ms.ReadInt32();
                if (storageType != StorageTypes.empty)
                {
                    return(storageType);
                }
                var uncompressedSize  = ms.ReadInt32();
                var compressedSize    = ms.ReadInt32();
                var externalOffset    = ms.ReadInt32();
                var localExportOffset = (int)ms.Position;
            }
            return(StorageTypes.empty);
        }
        public ME3FaceFXAnimSet(IMEPackage Pcc, ExportEntry Entry)
        {
            pcc    = Pcc;
            export = Entry;
            int start = export.propsEnd() + 4;
            SerializingContainer Container = new SerializingContainer(new MemoryStream(export.Data.Skip(start).ToArray()));

            Container.isLoading = true;
            Serialize(Container);
        }
Example #4
0
        public void Save()
        {
            MemoryStream         m         = new MemoryStream();
            SerializingContainer Container = new SerializingContainer(m);

            Container.isLoading = false;
            Serialize(Container);
            m = Container.Memory;
            MemoryStream res   = new MemoryStream();
            int          start = export.propsEnd();

            res.Write(export.Data, 0, start);
            res.WriteInt32((int)m.Length);
            res.WriteStream(m);
            res.WriteInt32(0);
            export.Data = res.ToArray();
        }
        public static ObjectBinary ConvertPostPropBinary(ExportEntry export, MEGame newGame, PropertyCollection newProps)
        {
            if (export.propsEnd() == export.DataSize)
            {
                return(Array.Empty <byte>());
            }

            if (export.IsTexture())
            {
                return(ConvertTexture2D(export, newGame));
            }

            if (From(export) is ObjectBinary objbin)
            {
                if (objbin is AnimSequence animSeq)
                {
                    animSeq.UpdateProps(newProps, newGame);
                }
                return(objbin);
            }

            switch (export.ClassName)
            {
            case "DirectionalLightComponent":
            case "PointLightComponent":
            case "SkyLightComponent":
            case "SphericalHarmonicLightComponent":
            case "SpotLightComponent":
            case "DominantSpotLightComponent":
            case "DominantPointLightComponent":
            case "DominantDirectionalLightComponent":
                if (newGame == MEGame.UDK)
                {
                    return(Array.Empty <byte>());
                }
                else if (export.Game == MEGame.UDK && newGame != MEGame.UDK)
                {
                    return(new byte[8]);
                }
                break;
            }

            //no conversion neccesary
            return(export.GetBinaryData());
        }
Example #6
0
        public static List <string> Relink(ExportEntry sourceExport, ExportEntry relinkingExport, OrderedMultiValueDictionary <IEntry, IEntry> crossPCCObjectMappingList, bool importExportDependencies = false)
        {
            var        relinkFailedReport = new List <string>();
            IMEPackage sourcePcc          = sourceExport.FileRef;

            //Relink stack
            if (relinkingExport.HasStack)
            {
                byte[] stack = relinkingExport.GetStack();

                int    uIndex       = BitConverter.ToInt32(stack, 0);
                string relinkResult = relinkUIndex(sourceExport.FileRef, relinkingExport, ref uIndex, "Stack: Node",
                                                   crossPCCObjectMappingList, "", importExportDependencies);
                if (relinkResult is null)
                {
                    stack.OverwriteRange(0, BitConverter.GetBytes(uIndex));
                }
                else
                {
                    relinkFailedReport.Add(relinkResult);
                }

                uIndex       = BitConverter.ToInt32(stack, 4);
                relinkResult = relinkUIndex(sourceExport.FileRef, relinkingExport, ref uIndex, "Stack: StateNode",
                                            crossPCCObjectMappingList, "", importExportDependencies);
                if (relinkResult is null)
                {
                    stack.OverwriteRange(4, BitConverter.GetBytes(uIndex));
                }
                else
                {
                    relinkFailedReport.Add(relinkResult);
                }

                relinkingExport.SetStack(stack);
            }

            //Relink Properties
            PropertyCollection transplantProps = sourceExport.GetProperties();

            relinkFailedReport.AddRange(relinkPropertiesRecursive(sourcePcc, relinkingExport, transplantProps, crossPCCObjectMappingList, "", importExportDependencies));
            relinkingExport.WriteProperties(transplantProps);

            //Relink Binary
            try
            {
                if (relinkingExport.Game != sourcePcc.Game && (relinkingExport.IsClass || relinkingExport.ClassName == "State" || relinkingExport.ClassName == "Function"))
                {
                    relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed. Cannot port {relinkingExport.ClassName} between games!");
                    return(relinkFailedReport);
                }

                if (ObjectBinary.From(relinkingExport) is ObjectBinary objBin)
                {
                    List <(UIndex, string)> indices = objBin.GetUIndexes(relinkingExport.FileRef.Game);

                    foreach ((UIndex uIndex, string propName) in indices)
                    {
                        string result = relinkUIndex(sourcePcc, relinkingExport, ref uIndex.value, $"(Binary Property: {propName})", crossPCCObjectMappingList, "",
                                                     importExportDependencies);
                        if (result != null)
                        {
                            relinkFailedReport.Add(result);
                        }
                    }

                    //UStruct is abstract baseclass for Class, State, and Function, and can have script in it
                    if (objBin is UStruct uStructBinary && uStructBinary.ScriptBytes.Length > 0)
                    {
                        if (relinkingExport.Game == MEGame.ME3)
                        {
                            (List <Token> tokens, _) = Bytecode.ParseBytecode(uStructBinary.ScriptBytes, sourceExport);
                            foreach (Token token in tokens)
                            {
                                relinkFailedReport.AddRange(RelinkToken(token, uStructBinary.ScriptBytes, sourceExport, relinkingExport, crossPCCObjectMappingList,
                                                                        importExportDependencies));
                            }
                        }
                        else
                        {
                            relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed. {relinkingExport.ClassName} contains script, " +
                                                   $"which cannot be relinked for {relinkingExport.Game}");
                        }
                    }

                    relinkingExport.setBinaryData(objBin.ToBytes(relinkingExport.FileRef, relinkingExport.DataOffset + relinkingExport.propsEnd()));
                    return(relinkFailedReport);
                }

                byte[] binarydata = relinkingExport.getBinaryData();

                if (binarydata.Length > 0)
                {
                    switch (relinkingExport.ClassName)
                    {
                    //todo: make a WwiseEvent ObjectBinary class
                    case "WwiseEvent":
                    {
                        void relinkAtPosition(int binaryPosition, string propertyName)
                        {
                            int    uIndex       = BitConverter.ToInt32(binarydata, binaryPosition);
                            string relinkResult = relinkUIndex(sourcePcc, relinkingExport, ref uIndex, propertyName,
                                                               crossPCCObjectMappingList, "", importExportDependencies);

                            if (relinkResult is null)
                            {
                                binarydata.OverwriteRange(binaryPosition, BitConverter.GetBytes(uIndex));
                            }
                            else
                            {
                                relinkFailedReport.Add(relinkResult);
                            }
                        }

                        if (relinkingExport.FileRef.Game == MEGame.ME3)
                        {
                            int count = BitConverter.ToInt32(binarydata, 0);
                            for (int j = 0; j < count; j++)
                            {
                                relinkAtPosition(4 + (j * 4), $"(Binary Property: WwiseStreams[{j}])");
                            }

                            relinkingExport.setBinaryData(binarydata);
                        }
                        else if (relinkingExport.FileRef.Game == MEGame.ME2)
                        {
                            int parsingPos = 4;
                            int linkCount  = BitConverter.ToInt32(binarydata, parsingPos);
                            parsingPos += 4;
                            for (int j = 0; j < linkCount; j++)
                            {
                                int bankcount = BitConverter.ToInt32(binarydata, parsingPos);
                                parsingPos += 4;
                                for (int k = 0; k < bankcount; k++)
                                {
                                    relinkAtPosition(parsingPos, $"(Binary Property: link[{j}].WwiseBanks[{k}])");

                                    parsingPos += 4;
                                }

                                int wwisestreamcount = BitConverter.ToInt32(binarydata, parsingPos);
                                parsingPos += 4;
                                for (int k = 0; k < wwisestreamcount; k++)
                                {
                                    relinkAtPosition(parsingPos, $"(Binary Property: link[{j}].WwiseStreams[{k}])");

                                    parsingPos += 4;
                                }
                            }

                            relinkingExport.setBinaryData(binarydata);
                        }
                    }
                    break;

                    case "DominantDirectionalLightComponent":
                    case "SphericalHarmonicLightComponent":
                    case "DominantPointLightComponent":
                    case "StaticLightCollectionActor":
                    case "DominantSpotLightComponent":
                    case "DirectionalLightComponent":
                    case "StaticMeshCollectionActor":
                    case "TerrainWeightMapTexture":
                    case "PhysicsAssetInstance":
                    case "PointLightComponent":
                    case "ShadowMapTexture2D":
                    case "SpotLightComponent":
                    case "LightMapTexture2D":
                    case "SkyLightComponent":
                    case "TextureFlipBook":
                    case "BrushComponent":
                    case "FaceFXAnimSet":
                    case "TextureMovie":
                    case "AnimSequence":
                    case "RB_BodySetup":
                    case "MorphTarget":
                    case "ShadowMap1D":
                    case "WwiseStream":
                    case "WwiseBank":
                    case "Texture2D":
                        //these classes have binary but do not need relinking
                        break;

                    default:
                        if (binarydata.Any(b => b != 0))
                        {
                            relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} has unparsed binary. " +
                                                   $"This binary may contain items that need to be relinked. Come to the Discord server " +
                                                   $"(click ME3Tweaks logo in main window for invite) and ask devs to parse this class.");
                        }

                        break;
                    }
                }
            }
            catch (Exception e) when(!App.IsDebug)
            {
                relinkFailedReport.Add($"{relinkingExport.UIndex} {relinkingExport.FullPath} binary relinking failed due to exception: {e.Message}");
            }

            return(relinkFailedReport);
        }
Example #7
0
        public static List <Texture2DMipInfo> GetTexture2DMipInfos(ExportEntry exportEntry, string cacheName)
        {
            MemoryStream ms = new MemoryStream(exportEntry.Data);

            ms.Seek(exportEntry.propsEnd(), SeekOrigin.Begin);
            if (exportEntry.FileRef.Game != Mod.MEGame.ME3)
            {
                ms.Skip(4);                     //BulkDataFlags
                ms.Skip(4);                     //ElementCount
                int bulkDataSize = ms.ReadInt32();
                ms.Seek(4, SeekOrigin.Current); // position in the package
                ms.Skip(bulkDataSize);          //skips over thumbnail png, if it exists
            }

            var mips       = new List <Texture2DMipInfo>();
            int numMipMaps = ms.ReadInt32();

            for (int l = 0; l < numMipMaps; l++)
            {
                Texture2DMipInfo mip = new Texture2DMipInfo
                {
                    Export            = exportEntry,
                    index             = l,
                    storageType       = (StorageTypes)ms.ReadInt32(),
                    uncompressedSize  = ms.ReadInt32(),
                    compressedSize    = ms.ReadInt32(),
                    externalOffset    = ms.ReadInt32(),
                    localExportOffset = (int)ms.Position,
                    TextureCacheName  = cacheName //If this is ME1, this will simply be ignored in the setter
                };
                switch (mip.storageType)
                {
                case StorageTypes.pccUnc:
                    ms.Seek(mip.uncompressedSize, SeekOrigin.Current);
                    break;

                case StorageTypes.pccLZO:
                case StorageTypes.pccZlib:
                    ms.Seek(mip.compressedSize, SeekOrigin.Current);
                    break;
                }

                mip.width  = ms.ReadInt32();
                mip.height = ms.ReadInt32();
                if (mip.width == 4 && mips.Exists(m => m.width == mip.width))
                {
                    mip.width = mips.Last().width / 2;
                }
                if (mip.height == 4 && mips.Exists(m => m.height == mip.height))
                {
                    mip.height = mips.Last().height / 2;
                }
                if (mip.width == 0)
                {
                    mip.width = 1;
                }
                if (mip.height == 0)
                {
                    mip.height = 1;
                }
                mips.Add(mip);
            }

            return(mips);
        }
        public static bool RandomizeExport(ExportEntry export, RandomizationOption option)
        {
            if (!CanRandomize(export))
            {
                return(false);
            }
            var game = export.FileRef.Game;

            byte[] data = export.Data;
            try
            {
                var TrackOffsets = export.GetProperty <ArrayProperty <IntProperty> >("CompressedTrackOffsets");
                var animsetData  = export.GetProperty <ObjectProperty>("m_pBioAnimSetData");
                if (animsetData.Value <= 0)
                {
                    //Debug.WriteLine("trackdata is an import skipping");
                    return(false);
                } // don't randomize;

                var boneList = export.FileRef.GetUExport(animsetData.Value).GetProperty <ArrayProperty <NameProperty> >("TrackBoneNames");
                Enum.TryParse(export.GetProperty <EnumProperty>("RotationCompressionFormat").Value.Name, out AnimationCompressionFormat rotCompression);
                int offset = export.propsEnd();
                //ME2 SPECIFIC
                offset += 16; //3 0's, 1 offset of data point
                int binLength = BitConverter.ToInt32(data, offset);
                //var LengthNode = new BinInterpNode
                //{
                //    Header = $"0x{offset:X4} AnimBinary length: {binLength}",
                //    Name = "_" + offset,
                //    Tag = NodeType.StructLeafInt
                //};
                //offset += 4;
                //subnodes.Add(LengthNode);
                var animBinStart = offset;

                int bone = 0;

                for (int i = 0; i < TrackOffsets.Count; i++)
                {
                    var bonePosOffset = TrackOffsets[i].Value;
                    i++;
                    var  bonePosCount = TrackOffsets[i].Value;
                    var  boneName     = boneList[bone].Value;
                    bool doSomething  = shouldRandomizeBone(boneName, option);
                    //POSKEYS
                    for (int j = 0; j < bonePosCount; j++)
                    {
                        offset = animBinStart + bonePosOffset + j * 12;
                        //Key #
                        //var PosKeys = new BinInterpNode
                        //{
                        //    Header = $"0x{offset:X5} PosKey {j}",
                        //    Name = "_" + offset,
                        //    Tag = NodeType.Unknown
                        //};
                        //BoneID.Items.Add(PosKeys);


                        var posX = BitConverter.ToSingle(data, offset);
                        if (doSomething)
                        {
                            data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(posX - (posX * .3f), posX + (posX * .3f))));
                        }

                        //PosKeys.Items.Add(new BinInterpNode
                        //{
                        //    Header = $"0x{offset:X5} X: {posX} ",
                        //    Name = "_" + offset,
                        //    Tag = NodeType.StructLeafFloat
                        //});
                        offset += 4;

                        var posY = BitConverter.ToSingle(data, offset);
                        if (doSomething)
                        {
                            data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(posY - (posY * .3f), posY + (posY * .3f))));
                        }
                        //PosKeys.Items.Add(new BinInterpNode
                        //{
                        //    Header = $"0x{offset:X5} Y: {posY} ",
                        //    Name = "_" + offset,
                        //    Tag = NodeType.StructLeafFloat
                        //});
                        offset += 4;

                        var posZ = BitConverter.ToSingle(data, offset);
                        if (doSomething)
                        {
                            data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(posZ - (posZ * .3f), posZ + (posZ * .3f))));
                        }

                        //PosKeys.Items.Add(new BinInterpNode
                        //{
                        //    Header = $"0x{offset:X5} Z: {posZ} ",
                        //    Name = "_" + offset,
                        //    Tag = NodeType.StructLeafFloat
                        //});
                        offset += 4;
                    }

                    var lookat = boneName.Name.Contains("lookat");

                    i++;
                    var boneRotOffset = TrackOffsets[i].Value;
                    i++;
                    var boneRotCount = TrackOffsets[i].Value;
                    int l            = 12; // 12 length of rotation by default
                    var offsetRotX   = boneRotOffset;
                    var offsetRotY   = boneRotOffset;
                    var offsetRotZ   = boneRotOffset;
                    var offsetRotW   = boneRotOffset;
                    for (int j = 0; j < boneRotCount; j++)
                    {
                        float rotX = 0;
                        float rotY = 0;
                        float rotZ = 0;
                        float rotW = 0;

                        switch (rotCompression)
                        {
                        case AnimationCompressionFormat.ACF_None:
                            l          = 16;
                            offset     = animBinStart + boneRotOffset + j * l;
                            offsetRotX = offset;
                            rotX       = BitConverter.ToSingle(data, offset);
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotX - (rotX * .1f), rotX + (rotX * .1f))));
                            }
                            offset    += 4;
                            offsetRotY = offset;
                            rotY       = BitConverter.ToSingle(data, offset);
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotY - (rotY * .1f), rotY + (rotY * .1f))));
                            }
                            offset    += 4;
                            offsetRotZ = offset;
                            rotZ       = BitConverter.ToSingle(data, offset);
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotZ - (rotZ * .1f), rotZ + (rotZ * .1f))));
                            }
                            offset    += 4;
                            offsetRotW = offset;
                            rotW       = BitConverter.ToSingle(data, offset);
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotW - (rotW * .1f), rotW + (rotW * .1f))));
                            }
                            offset += 4;
                            break;

                        case AnimationCompressionFormat.ACF_Float96NoW:
                            offset     = animBinStart + boneRotOffset + j * l;
                            offsetRotX = offset;
                            rotX       = BitConverter.ToSingle(data, offset);
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotX - (rotX * .1f), rotX + (rotX * .1f))));
                            }

                            offset    += 4;
                            offsetRotY = offset;
                            rotY       = BitConverter.ToSingle(data, offset);
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotY - (rotY * .1f), rotY + (rotY * .1f))));
                            }

                            offset    += 4;
                            offsetRotZ = offset;
                            rotZ       = BitConverter.ToSingle(data, offset);
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotZ - (rotZ * .1f), rotZ + (rotZ * .1f))));
                            }

                            offset += 4;
                            break;

                        case AnimationCompressionFormat.ACF_Fixed48NoW:     // normalized quaternion with 3 16-bit fixed point fields
                        //FQuat r;
                        //r.X = (X - 32767) / 32767.0f;
                        //r.Y = (Y - 32767) / 32767.0f;
                        //r.Z = (Z - 32767) / 32767.0f;
                        //RESTORE_QUAT_W(r);
                        //break;
                        case AnimationCompressionFormat.ACF_Fixed32NoW:    // normalized quaternion with 11/11/10-bit fixed point fields
                        //FQuat r;
                        //r.X = X / 1023.0f - 1.0f;
                        //r.Y = Y / 1023.0f - 1.0f;
                        //r.Z = Z / 511.0f - 1.0f;
                        //RESTORE_QUAT_W(r);
                        //break;
                        case AnimationCompressionFormat.ACF_IntervalFixed32NoW:
                        //FQuat r;
                        //r.X = (X / 1023.0f - 1.0f) * Ranges.X + Mins.X;
                        //r.Y = (Y / 1023.0f - 1.0f) * Ranges.Y + Mins.Y;
                        //r.Z = (Z / 511.0f - 1.0f) * Ranges.Z + Mins.Z;
                        //RESTORE_QUAT_W(r);
                        //break;
                        case AnimationCompressionFormat.ACF_Float32NoW:
                            //FQuat r;

                            //int _X = data >> 21;            // 11 bits
                            //int _Y = (data >> 10) & 0x7FF;  // 11 bits
                            //int _Z = data & 0x3FF;          // 10 bits

                            //*(unsigned*)&r.X = ((((_X >> 7) & 7) + 123) << 23) | ((_X & 0x7F | 32 * (_X & 0xFFFFFC00)) << 16);
                            //*(unsigned*)&r.Y = ((((_Y >> 7) & 7) + 123) << 23) | ((_Y & 0x7F | 32 * (_Y & 0xFFFFFC00)) << 16);
                            //*(unsigned*)&r.Z = ((((_Z >> 6) & 7) + 123) << 23) | ((_Z & 0x3F | 32 * (_Z & 0xFFFFFE00)) << 17);

                            //RESTORE_QUAT_W(r);


                            break;

                        case AnimationCompressionFormat.ACF_BioFixed48:
                            offset = animBinStart + boneRotOffset + j * l;
                            const float shift = 0.70710678118f;
                            const float scale = 1.41421356237f;
                            offsetRotX = offset;
                            rotX       = (data[0] & 0x7FFF) / 32767.0f * scale - shift;
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotX - (rotX * .1f), rotX + (rotX * .1f))));
                            }

                            offset    += 4;
                            offsetRotY = offset;
                            rotY       = (data[1] & 0x7FFF) / 32767.0f * scale - shift;
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotY - (rotY * .1f), rotY + (rotY * .1f))));
                            }

                            offset    += 4;
                            offsetRotZ = offset;
                            rotZ       = (data[2] & 0x7FFF) / 32767.0f * scale - shift;
                            if (lookat)
                            {
                                data.OverwriteRange(offset, BitConverter.GetBytes(ThreadSafeRandom.NextFloat(rotZ - (rotZ * .1f), rotZ + (rotZ * .1f))));
                            }



                            //float w = 1.0f - (rotX * rotX + rotY * rotY + rotZ * rotZ);
                            //w = w >= 0.0f ? (float)Math.Sqrt(w) : 0.0f;
                            //int s = ((data[0] >> 14) & 2) | ((data[1] >> 15) & 1);
                            break;
                        }

                        if (rotCompression == AnimationCompressionFormat.ACF_BioFixed48 || rotCompression == AnimationCompressionFormat.ACF_Float96NoW || rotCompression == AnimationCompressionFormat.ACF_None)
                        {
                            //randomize here?
                            //var RotKeys = new BinInterpNode
                            //{
                            //    Header = $"0x{offsetRotX:X5} RotKey {j}",
                            //    Name = "_" + offsetRotX,
                            //    Tag = NodeType.Unknown
                            //};
                            //BoneID.Items.Add(RotKeys);
                            //RotKeys.Items.Add(new BinInterpNode
                            //{
                            //    Header = $"0x{offsetRotX:X5} RotX: {rotX} ",
                            //    Name = "_" + offsetRotX,
                            //    Tag = NodeType.StructLeafFloat
                            //});
                            //RotKeys.Items.Add(new BinInterpNode
                            //{
                            //    Header = $"0x{offsetRotY:X5} RotY: {rotY} ",
                            //    Name = "_" + offsetRotY,
                            //    Tag = NodeType.StructLeafFloat
                            //});
                            //RotKeys.Items.Add(new BinInterpNode
                            //{
                            //    Header = $"0x{offsetRotZ:X5} RotZ: {rotZ} ",
                            //    Name = "_" + offsetRotZ,
                            //    Tag = NodeType.StructLeafFloat
                            //});
                            if (rotCompression == AnimationCompressionFormat.ACF_None)
                            {
                                //RotKeys.Items.Add(new BinInterpNode
                                //{
                                //    Header = $"0x{offsetRotW:X5} RotW: {rotW} ",
                                //    Name = "_" + offsetRotW,
                                //    Tag = NodeType.StructLeafFloat
                                //});
                            }
                        }
                    }
                    bone++;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Error reading animsequence: " + ex.Message + ". Skipping");
            }

            export.Data = data; //write back
            return(true);
        }
Example #9
0
        public static T From <T>(ExportEntry export) where T : ObjectBinary, new()
        {
            var t = new T {
                Export = export
            };

            t.Serialize(new SerializingContainer2(export.GetReadOnlyBinaryStream(), export.FileRef, true, export.DataOffset + export.propsEnd()));
            return(t);
        }
Example #10
0
        public Bio2DA(ExportEntry export)
        {
            //Console.WriteLine("Loading " + export.ObjectName);
            this.export = export;
            IMEPackage pcc = export.FileRef;

            byte[] data = export.Data;

            RowNames = new List <string>();
            if (export.ClassName == "Bio2DA")
            {
                const string rowLabelsVar = "m_sRowLabel";
                var          props        = export.GetProperty <ArrayProperty <NameProperty> >(rowLabelsVar);
                if (props != null)
                {
                    foreach (NameProperty n in props)
                    {
                        RowNames.Add(n.ToString());
                    }
                }
                else
                {
                    Console.WriteLine("Unable to find row names property!");
                    Debugger.Break();
                    return;
                }
            }
            else
            {
                var props = export.GetProperty <ArrayProperty <IntProperty> >("m_lstRowNumbers");//Bio2DANumberedRows
                if (props != null)
                {
                    foreach (IntProperty n in props)
                    {
                        RowNames.Add(n.Value.ToString());
                    }
                }
                else
                {
                    Debug.WriteLine("Unable to find row names property (m_lstRowNumbers)!");
                    //Debugger.Break();
                    return;
                }
            }

            //Get Columns
            ColumnNames = new List <string>();
            int colcount         = BitConverter.ToInt32(data, data.Length - 4);
            int currentcoloffset = 0;

            while (colcount >= 0)
            {
                currentcoloffset += 4;
                int colindex = BitConverter.ToInt32(data, data.Length - currentcoloffset);
                currentcoloffset += 8; //names in this case don't use nameindex values.
                int    nameindex = BitConverter.ToInt32(data, data.Length - currentcoloffset);
                string name      = pcc.GetNameEntry(nameindex);
                ColumnNames.Insert(0, name);
                colcount--;
            }
            Cells = new Bio2DACell[RowCount, ColumnCount];

            currentcoloffset += 4;  //column count.
            int infilecolcount = BitConverter.ToInt32(data, data.Length - currentcoloffset);

            //start of binary data
            int binstartoffset = export.propsEnd(); //arrayheader + nonenamesize + number of items in this list
            int curroffset     = binstartoffset;

            int cellcount = BitConverter.ToInt32(data, curroffset);

            if (cellcount > 0)
            {
                curroffset += 4;
                for (int rowindex = 0; rowindex < RowCount; rowindex++)
                {
                    for (int colindex = 0; colindex < ColumnCount && curroffset < data.Length - currentcoloffset; colindex++)
                    {
                        byte dataType = data[curroffset];
                        curroffset++;
                        int    dataSize = dataType == (byte)Bio2DACell.Bio2DADataType.TYPE_NAME ? 8 : 4;
                        byte[] celldata = new byte[dataSize];
                        Buffer.BlockCopy(data, curroffset, celldata, 0, dataSize);
                        Bio2DACell cell = new Bio2DACell(pcc, curroffset, dataType, celldata);
                        Cells[rowindex, colindex] = cell;
                        curroffset += dataSize;
                    }
                }
                PopulatedCellCount = RowCount * ColumnCount;  //Required for edits to write correct count if SaveToExport
            }
            else
            {
                IsIndexed   = true;
                curroffset += 4; //theres a 0 here for some reason
                cellcount   = BitConverter.ToInt32(data, curroffset);
                curroffset += 4;

                //curroffset += 4;
                while (PopulatedCellCount < cellcount)
                {
                    int index = BitConverter.ToInt32(data, curroffset);
                    int row   = index / ColumnCount;
                    int col   = index % ColumnCount;
                    curroffset += 4;
                    byte dataType = data[curroffset];
                    int  dataSize = dataType == (byte)Bio2DACell.Bio2DADataType.TYPE_NAME ? 8 : 4;
                    curroffset++;
                    var celldata = new byte[dataSize];
                    Buffer.BlockCopy(data, curroffset, celldata, 0, dataSize);
                    Bio2DACell cell = new Bio2DACell(pcc, curroffset, dataType, celldata);
                    this[row, col] = cell;
                    curroffset    += dataSize;
                }
            }
            //Console.WriteLine("Finished loading " + export.ObjectName);
        }
Example #11
0
        public void Write2DAToExport()
        {
            using (var stream = new MemoryStream())
            {
                //Cell count
                if (IsIndexed)
                {
                    //Indexed ones seem to have 0 at start
                    stream.WriteBytes(BitConverter.GetBytes(0));
                }
                stream.WriteBytes(BitConverter.GetBytes(PopulatedCellCount));

                //Write cell data
                for (int rowindex = 0; rowindex < RowCount; rowindex++)
                {
                    for (int colindex = 0; colindex < ColumnCount; colindex++)
                    {
                        Bio2DACell cell = Cells[rowindex, colindex];
                        if (cell != null)
                        {
                            if (IsIndexed)
                            {
                                //write index
                                int index = (rowindex * ColumnCount) + colindex; //+1 because they are not zero based indexes since they are numerals
                                stream.WriteBytes(BitConverter.GetBytes(index));
                            }
                            stream.WriteByte((byte)cell.Type);
                            stream.WriteBytes(cell.Data);
                        }
                        else
                        {
                            if (IsIndexed)
                            {
                                //this is a blank cell. It is not present in the table.
                                continue;
                            }
                            else
                            {
                                Debug.WriteLine("THIS SHOULDN'T OCCUR!");
                                Debugger.Break();
                                throw new Exception("A non-indexed Bio2DA cannot have null cells.");
                            }
                        }
                    }
                }

                //Write Columns
                if (!IsIndexed)
                {
                    stream.WriteBytes(BitConverter.GetBytes(0)); //seems to be a 0 before column definitions
                }
                //Console.WriteLine("Columns defs start at " + stream.Position.ToString("X6"));
                stream.WriteBytes(BitConverter.GetBytes(ColumnCount));
                for (int colindex = 0; colindex < ColumnCount; colindex++)
                {
                    //Console.WriteLine("Writing column definition " + columnNames[colindex]);
                    int nameIndexForCol = export.FileRef.FindNameOrAdd(ColumnNames[colindex]);
                    stream.WriteBytes(BitConverter.GetBytes(nameIndexForCol));
                    stream.WriteBytes(BitConverter.GetBytes(0)); //second half of name reference in 2da is always zero since they're always indexed at 0
                    stream.WriteBytes(BitConverter.GetBytes(colindex));
                }

                int    propsEnd   = export.propsEnd();
                byte[] binarydata = stream.ToArray();

                //Todo: Rewrite properties here
                PropertyCollection props = new PropertyCollection();
                if (export.ClassName == "Bio2DA")
                {
                    var indicies = new ArrayProperty <NameProperty>("m_sRowLabel");
                    foreach (var rowname in RowNames)
                    {
                        indicies.Add(new NameProperty(rowname));
                    }
                    props.Add(indicies);
                }
                else
                {
                    var indices = new ArrayProperty <IntProperty>("m_lstRowNumbers");
                    foreach (var rowname in RowNames)
                    {
                        indices.Add(new IntProperty(int.Parse(rowname)));
                    }
                    props.Add(indices);
                }

                MemoryStream propsStream = new MemoryStream();
                props.WriteTo(propsStream, export.FileRef);
                MemoryStream currentDataStream   = new MemoryStream(export.Data);
                byte[]       propertydata        = propsStream.ToArray();
                int          propertyStartOffset = export.GetPropertyStart();
                var          newExportData       = new byte[propertyStartOffset + propertydata.Length + binarydata.Length];
                Buffer.BlockCopy(export.Data, 0, newExportData, 0, propertyStartOffset);
                propertydata.CopyTo(newExportData, propertyStartOffset);
                binarydata.CopyTo(newExportData, propertyStartOffset + propertydata.Length);
                //Console.WriteLine("Old data size: " + export.Data.Length);
                //Console.WriteLine("NEw data size: " + newExportData.Length);

                //This assumes the input and output data sizes are the same. We should not assume this with new functionality
                //if (export.Data.Length != newExportData.Length)
                //{
                //    Debug.WriteLine("FILES ARE WRONG SIZE");
                //    Debugger.Break();
                //}
                export.Data = newExportData;
            }
        }
        public static void ScanStuff(PackageEditorWPF pewpf)
        {
            //var filePaths = MELoadedFiles.GetOfficialFiles(MEGame.ME3);//.Concat(MELoadedFiles.GetOfficialFiles(MEGame.ME2));//.Concat(MELoadedFiles.GetOfficialFiles(MEGame.ME1));
            //var filePaths = MELoadedFiles.GetAllFiles(game);
            /*"Core.pcc", "Engine.pcc", "GameFramework.pcc", "GFxUI.pcc", "WwiseAudio.pcc", "SFXOnlineFoundation.pcc", "SFXGame.pcc" */
            var filePaths = new[] { "Core.pcc", "Engine.pcc", "GameFramework.pcc", "GFxUI.pcc", "WwiseAudio.pcc", "SFXOnlineFoundation.pcc" }.Select(f => Path.Combine(ME3Directory.CookedPCPath, f));
            var interestingExports = new List <EntryStringPair>();
            var foundClasses       = new HashSet <string>(); //new HashSet<string>(BinaryInterpreterWPF.ParsableBinaryClasses);
            var foundProps         = new Dictionary <string, string>();


            var unkOpcodes = new List <int>();//Enumerable.Range(0x5B, 8).ToList();

            unkOpcodes.Add(0);
            unkOpcodes.Add(1);
            var unkOpcodesInfo = unkOpcodes.ToDictionary(i => i, i => new OpcodeInfo());
            var comparisonDict = new Dictionary <string, (byte[] original, byte[] newData)>();

            var extraInfo = new HashSet <string>();

            pewpf.IsBusy   = true;
            pewpf.BusyText = "Scanning";
            Task.Run(() =>
            {
                //preload base files for faster scanning
                using var baseFiles = MEPackageHandler.OpenMEPackages(EntryImporter.FilesSafeToImportFrom(MEGame.ME3)
                                                                      .Select(f => Path.Combine(ME3Directory.CookedPCPath, f)));
                baseFiles.Add(
                    MEPackageHandler.OpenMEPackage(Path.Combine(ME3Directory.CookedPCPath, "BIOP_MP_COMMON.pcc")));

                foreach (string filePath in filePaths)
                {
                    //ScanShaderCache(filePath);
                    //ScanMaterials(filePath);
                    //ScanStaticMeshComponents(filePath);
                    //ScanLightComponents(filePath);
                    //ScanLevel(filePath);
                    //if (findClass(filePath, "ShaderCache", true)) break;
                    //findClassesWithBinary(filePath);
                    ScanScripts2(filePath);
                    //if (interestingExports.Count > 0)
                    //{
                    //    break;
                    //}
                    //if (resolveImports(filePath)) break;
                }
            }).ContinueWithOnUIThread(prevTask =>
            {
                pewpf.IsBusy = false;
                interestingExports.Add(new EntryStringPair(null, string.Join("\n", extraInfo)));
                var listDlg = new ListDialog(interestingExports, "Interesting Exports", "", pewpf)
                {
                    DoubleClickEntryHandler = entryItem =>
                    {
                        if (entryItem?.Entry is IEntry entryToSelect)
                        {
                            PackageEditorWPF p = new PackageEditorWPF();
                            p.Show();
                            p.LoadFile(entryToSelect.FileRef.FilePath, entryToSelect.UIndex);
                            p.Activate();
                            if (comparisonDict.TryGetValue($"{entryToSelect.UIndex} {entryToSelect.FileRef.FilePath}", out (byte[] original, byte[] newData)val))
                            {
                                File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "original.bin"), val.original);
                                File.WriteAllBytes(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "new.bin"), val.newData);
                            }
                        }
                    }
                };
                listDlg.Show();
            });

            #region extra scanning functions

            bool findClass(string filePath, string className, bool withBinary = false)
            {
                Debug.WriteLine($" {filePath}");
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    //if (!pcc.IsCompressed) return false;

                    var exports = pcc.Exports.Where(exp => !exp.IsDefaultObject && exp.IsA(className));
                    foreach (ExportEntry exp in exports)
                    {
                        try
                        {
                            //Debug.WriteLine($"{exp.UIndex}: {filePath}");
                            var originalData = exp.Data;
                            exp.WriteBinary(ObjectBinary.From(exp));
                            var newData = exp.Data;
                            if (!originalData.SequenceEqual(newData))
                            {
                                interestingExports.Add(exp);
                                File.WriteAllBytes(
                                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                                                 "original.bin"), originalData);
                                File.WriteAllBytes(
                                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                                                 "new.bin"), newData);
                                return(true);
                            }
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                            interestingExports.Add(new EntryStringPair(exp, $"{exception}"));
                            return(true);
                        }
                    }
                }

                return(false);
            }

            void findClassesWithBinary(string filePath)
            {
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    foreach (ExportEntry exp in pcc.Exports.Where(exp => !exp.IsDefaultObject))
                    {
                        try
                        {
                            if (!foundClasses.Contains(exp.ClassName) && exp.propsEnd() < exp.DataSize)
                            {
                                if (ObjectBinary.From(exp) != null)
                                {
                                    foundClasses.Add(exp.ClassName);
                                }
                                else if (exp.GetBinaryData().Any(b => b != 0))
                                {
                                    foundClasses.Add(exp.ClassName);
                                    interestingExports.Add(exp);
                                }
                            }
                        }
                        catch (Exception exception)
                        {
                            Console.WriteLine(exception);
                            interestingExports.Add(new EntryStringPair(exp, $"{exp.UIndex}: {filePath}\n{exception}"));
                        }
                    }
                }
            }

            void ScanShaderCache(string filePath)
            {
                using (IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath))
                {
                    ExportEntry shaderCache = pcc.Exports.FirstOrDefault(exp => exp.ClassName == "ShaderCache");
                    if (shaderCache == null)
                    {
                        return;
                    }
                    int oldDataOffset = shaderCache.DataOffset;

                    try
                    {
                        MemoryStream binData = new MemoryStream(shaderCache.Data);
                        binData.JumpTo(shaderCache.propsEnd() + 1);

                        int nameList1Count = binData.ReadInt32();
                        binData.Skip(nameList1Count * 12);

                        int namelist2Count = binData.ReadInt32(); //namelist2
                        binData.Skip(namelist2Count * 12);

                        int shaderCount = binData.ReadInt32();
                        for (int i = 0; i < shaderCount; i++)
                        {
                            binData.Skip(24);
                            int nextShaderOffset = binData.ReadInt32() - oldDataOffset;
                            binData.Skip(14);
                            if (binData.ReadInt32() != 1111577667) //CTAB
                            {
                                interestingExports.Add(new EntryStringPair(null,
                                                                           $"{binData.Position - 4}: {filePath}"));
                                return;
                            }

                            binData.JumpTo(nextShaderOffset);
                        }

                        int vertexFactoryMapCount = binData.ReadInt32();
                        binData.Skip(vertexFactoryMapCount * 12);

                        int materialShaderMapCount = binData.ReadInt32();
                        for (int i = 0; i < materialShaderMapCount; i++)
                        {
                            binData.Skip(16);

                            int switchParamCount = binData.ReadInt32();
                            binData.Skip(switchParamCount * 32);

                            int componentMaskParamCount = binData.ReadInt32();
                            //if (componentMaskParamCount != 0)
                            //{
                            //    interestingExports.Add($"{i}: {filePath}");
                            //    return;
                            //}

                            binData.Skip(componentMaskParamCount * 44);

                            int normalParams = binData.ReadInt32();
                            if (normalParams != 0)
                            {
                                interestingExports.Add(new EntryStringPair(null, $"{i}: {filePath}"));
                                return;
                            }

                            binData.Skip(normalParams * 29);

                            int unrealVersion   = binData.ReadInt32();
                            int licenseeVersion = binData.ReadInt32();
                            if (unrealVersion != 684 || licenseeVersion != 194)
                            {
                                interestingExports.Add(new EntryStringPair(null,
                                                                           $"{binData.Position - 8}: {filePath}"));
                                return;
                            }

                            int nextMaterialShaderMapOffset = binData.ReadInt32() - oldDataOffset;
                            binData.JumpTo(nextMaterialShaderMapOffset);
                        }
                    }
                    catch (Exception exception)
                    {
                        Console.WriteLine(exception);
                        interestingExports.Add(new EntryStringPair(null, $"{filePath}\n{exception}"));
                    }
                }
            }

            void ScanScripts(string filePath)
            {
                using IMEPackage pcc = MEPackageHandler.OpenMEPackage(filePath);
                foreach (ExportEntry exp in pcc.Exports.Where(exp => !exp.IsDefaultObject))
                {
                    try
                    {
                        if ((exp.ClassName == "State" || exp.ClassName == "Function") &&
                            ObjectBinary.From(exp) is UStruct uStruct)
                        {
                            byte[] data = exp.Data;
                            (_, List <BytecodeSingularToken> tokens) = Bytecode.ParseBytecode(uStruct.ScriptBytes, exp);
                            foreach (var token in tokens)
                            {
                                if (token.CurrentStack.Contains("UNKNOWN") || token.OpCodeString.Contains("UNKNOWN"))
                                {
                                    interestingExports.Add(exp);
                                }

                                if (unkOpcodes.Contains(token.OpCode))
                                {
                                    int    refUIndex = EndianReader.ToInt32(data, token.StartPos + 1, pcc.Endian);
                                    IEntry entry     = pcc.GetEntry(refUIndex);
                                    if (entry != null && (entry.ClassName == "ByteProperty"))
                                    {
                                        var info = unkOpcodesInfo[token.OpCode];
                                        info.Usages.Add(pcc.FilePath, exp.UIndex, token.StartPos);
                                        info.PropTypes.Add(refUIndex switch
                                        {
                                            0 => "Null",
                                            _ when entry != null => entry.ClassName,
                                            _ => "Invalid"
                                        });
                                        if (entry != null)
                                        {
                                            if (entry.Parent == exp)
                                            {
                                                info.PropLocations.Add("Local");
                                            }
                                            else if (entry.Parent == (exp.Parent.ClassName == "State" ? exp.Parent.Parent : exp.Parent))
                                            {
                                                info.PropLocations.Add("ThisClass");
                                            }
                                            else if (entry.Parent.ClassName == "Function")
                                            {
                                                info.PropLocations.Add("OtherFunction");
                                            }
                                            else if (exp.Parent.IsA(entry.Parent.ObjectName))
                                            {
                                                info.PropLocations.Add("AncestorClass");
                                            }
                                            else
                                            {
                                                info.PropLocations.Add("OtherClass");
                                            }
                                        }
                                    }
                                }
                            }