Пример #1
0
        public void Write(GameBoxWriter w, ClassIDRemap remap)
        {
            using (MemoryStream ms = new MemoryStream())
                using (GameBoxWriter bodyW = new GameBoxWriter(ms))
                {
                    (Body as ILookbackable).IdWritten = false;
                    (Body as ILookbackable).IdStrings.Clear();
                    Body.AuxilaryNodes.Clear();

                    Log.Write("Writing the body...");

                    Body.Write(bodyW, remap); // Body is written first so that the aux node count is determined properly

                    Log.Write("Writing the header...");

                    (Header as ILookbackable).IdWritten = false;
                    (Header as ILookbackable).IdStrings.Clear();
                    Header.Write(w, Body.AuxilaryNodes.Count + 1, remap);

                    Log.Write("Writing the reference table...");

                    if (RefTable == null)
                    {
                        w.Write(0);
                    }
                    else
                    {
                        RefTable.Write(w);
                    }

                    w.Write(ms.ToArray(), 0, (int)ms.Length);
                }
        }
Пример #2
0
        public void Write(GameBoxWriter w)
        {
            w.Write(ExternalNodes.Length);
            w.Write(RootFolder.Folders.Count);

            // ...
        }
Пример #3
0
        public void Write(GameBoxWriter w, ClassIDRemap remap)
        {
            if (GBX.BodyCompression == 'C')
            {
                using (var msBody = new MemoryStream())
                    using (var gbxwBody = new GameBoxWriter(msBody, this))
                    {
                        GBX.MainNode.Write(gbxwBody, remap);
                        MiniLZO.Compress(msBody.ToArray(), out byte[] output);

                        w.Write((int)msBody.Length);       // Uncompressed
                        w.Write(output.Length);            // Compressed
                        w.Write(output, 0, output.Length); // Compressed body data
                    }
            }
            else
            {
                GBX.MainNode.Write(w);
            }

            // ...
        }
Пример #4
0
 public void Write(GameBoxWriter w)
 {
     w.Write(Stream.ToArray(), 0, (int)Stream.Length);
 }
Пример #5
0
 public override void Write(T n, GameBoxWriter w, GameBoxReader unknownR)
 {
     w.Write(Stream.ToArray(), 0, (int)Stream.Length);
 }
Пример #6
0
        public void Write(GameBoxWriter w, ClassIDRemap remap)
        {
            var stopwatch = Stopwatch.StartNew();

            int counter = 0;

            foreach (Chunk chunk in Chunks)
            {
                counter++;

                var logChunk = $"[{ClassName}] 0x{chunk.ID:X8} ({(float)counter / Chunks.Count:0.00%})";
                if (Body?.GBX.ClassID.HasValue == true && Remap(Body.GBX.ClassID.Value) == ID)
                {
                    Log.Write(logChunk);
                }
                else
                {
                    Log.Write($"~ {logChunk}");
                }

                ((IChunk)chunk).Node   = this;
                chunk.Unknown.Position = 0;

                ILookbackable lb = chunk.Lookbackable;

                if (chunk is ILookbackable l)
                {
                    l.IdWritten = false;
                    l.IdStrings.Clear();

                    lb = l;
                }

                if (lb == null)
                {
                    if (ParentChunk is ILookbackable l2)
                    {
                        lb = l2;
                    }
                    else
                    {
                        lb = w.Lookbackable;
                    }
                }

                using (var ms = new MemoryStream())
                    using (var msW = new GameBoxWriter(ms, lb))
                    {
                        var rw = new GameBoxReaderWriter(msW);

                        msW.Chunk = chunk;

                        try
                        {
                            if (chunk is ISkippableChunk s && !s.Discovered)
                            {
                                s.Write(msW);
                            }
                            else if (chunk.GetType().GetCustomAttribute <AutoReadWriteChunkAttribute>() == null)
                            {
                                ((IChunk)chunk).ReadWrite(this, rw);
                            }
                            else
                            {
                                msW.Write(chunk.Unknown.ToArray(), 0, (int)chunk.Unknown.Length);
                            }

                            w.Write(Chunk.Remap(chunk.ID, remap));

                            if (chunk is ISkippableChunk)
                            {
                                w.Write(0x534B4950);
                                w.Write((int)ms.Length);
                            }

                            w.Write(ms.ToArray(), 0, (int)ms.Length);
                        }
Пример #7
0
        public static T Parse <T>(GameBoxReader r, uint?classID = null, GameBox <T> gbx = null, IProgress <GameBoxReadProgress> progress = null) where T : Node
        {
            var stopwatch = Stopwatch.StartNew();

            if (!classID.HasValue)
            {
                classID = r.ReadUInt32();
            }

            if (classID == uint.MaxValue)
            {
                return(null);
            }

            classID = Remap(classID.Value);

            if (!AvailableClasses.TryGetValue(classID.Value, out Type type))
            {
                throw new NotImplementedException($"Node ID 0x{classID.Value:X8} is not implemented. ({Names.Where(x => x.Key == Chunk.Remap(classID.Value)).Select(x => x.Value).FirstOrDefault() ?? "unknown class"})");
            }

            T node = null;

            if (gbx == null)
            {
                node = (T)Activator.CreateInstance(type);
            }
            else
            {
                node = gbx.MainNode;
            }

            IGameBoxBody body;

            if (r.Lookbackable is Chunk ch)
            {
                body = (IGameBoxBody)ch.Part;
            }
            else
            {
                body = (IGameBoxBody)r.Lookbackable;
            }

            node.Body = body;

            var chunks = new ChunkSet {
                Node = node
            };

            node.Chunks = chunks;

            uint?previousChunk = null;

            while (r.BaseStream.Position < r.BaseStream.Length)
            {
                if (r.BaseStream.Position + 4 > r.BaseStream.Length)
                {
                    Debug.WriteLine($"Unexpected end of the stream: {r.BaseStream.Position}/{r.BaseStream.Length}");
                    var bytes = r.ReadBytes((int)(r.BaseStream.Length - r.BaseStream.Position));
                    break;
                }

                var chunkID = r.ReadUInt32();

                if (chunkID == 0xFACADE01) // no more chunks
                {
                    break;
                }
                else if (chunkID == 0)
                {
                    // weird case after ending node reference
                }
                else
                {
                    var logChunk = $"[{node.ClassName}] 0x{chunkID:X8} ({(float)r.BaseStream.Position / r.BaseStream.Length:0.00%})";
                    if (node.Body?.GBX.ClassID.HasValue == true && Remap(node.Body.GBX.ClassID.Value) == node.ID)
                    {
                        Log.Write(logChunk);
                    }
                    else
                    {
                        Log.Write($"~ {logChunk}");
                    }
                }

                Type  chunkClass = null;
                Chunk chunk      = null;

                var chunkRemapped = Chunk.Remap(chunkID);

                var reflected = ((chunkRemapped & 0xFFFFF000) == node.ID || AvailableInheritanceClasses[type].Contains(chunkRemapped & 0xFFFFF000)) &&
                                (AvailableChunkClasses[type].TryGetValue(chunkRemapped, out chunkClass) || AvailableChunkClasses[type].TryGetValue(chunkID & 0xFFF, out chunkClass));

                var skippable = reflected && chunkClass.BaseType.GetGenericTypeDefinition() == typeof(SkippableChunk <>);

                if (!reflected || skippable)
                {
                    var skip = r.ReadUInt32();

                    if (skip != 0x534B4950)
                    {
                        if (chunkID != 0 && !reflected)
                        {
                            node.FaultyChunk = chunkID;

                            var logChunkError = $"[{node.ClassName}] 0x{chunkID:X8} ERROR (wrong chunk format or unknown unskippable chunk)";
                            if (node.Body?.GBX.ClassID.HasValue == true && Remap(node.Body.GBX.ClassID.Value) == node.ID)
                            {
                                Log.Write(logChunkError, ConsoleColor.Red);
                            }
                            else
                            {
                                Log.Write($"~ {logChunkError}", ConsoleColor.Red);
                            }

                            throw new Exception($"Wrong chunk format or unskippable chunk: 0x{chunkID:X8} (" +
                                                $"{Names.Where(x => x.Key == Chunk.Remap(chunkID & 0xFFFFF000)).Select(x => x.Value).FirstOrDefault() ?? "unknown class"})" +
                                                $"\nPrevious chunk: 0x{previousChunk ?? 0:X8} (" +
                                                $"{(previousChunk.HasValue ? (Names.Where(x => x.Key == Chunk.Remap(previousChunk.Value & 0xFFFFF000)).Select(x => x.Value).FirstOrDefault() ?? "unknown class") : "not a class")})");

                            /* Usually breaks in the current state and causes confusion
                             *
                             * var buffer = BitConverter.GetBytes(chunkID);
                             * using (var restMs = new MemoryStream(ushort.MaxValue))
                             * {
                             *  restMs.Write(buffer, 0, buffer.Length);
                             *
                             *  while (r.PeekUInt32() != 0xFACADE01)
                             *      restMs.WriteByte(r.ReadByte());
                             *
                             *  node.Rest = restMs.ToArray();
                             * }
                             * Debug.WriteLine("FACADE found.");*/
                        }
                        break;
                    }

                    var chunkDataSize = r.ReadInt32();
                    var chunkData     = new byte[chunkDataSize];
                    if (chunkDataSize > 0)
                    {
                        r.Read(chunkData, 0, chunkDataSize);
                    }

                    if (reflected && chunkClass.GetCustomAttribute <IgnoreChunkAttribute>() == null)
                    {
                        ISkippableChunk c;

                        var constructor       = chunkClass.GetConstructors().First();
                        var constructorParams = constructor.GetParameters();
                        if (constructorParams.Length == 0)
                        {
                            c        = (ISkippableChunk)constructor.Invoke(new object[0]);
                            c.Node   = node;
                            c.Part   = (GameBoxPart)body;
                            c.Stream = new MemoryStream(chunkData, 0, chunkData.Length, false);
                            if (chunkData == null || chunkData.Length == 0)
                            {
                                c.Discovered = true;
                            }
                            c.OnLoad();
                        }
                        else if (constructorParams.Length == 2)
                        {
                            c = (ISkippableChunk)constructor.Invoke(new object[] { node, chunkData });
                        }
                        else
                        {
                            throw new ArgumentException($"{type.FullName} has an invalid amount of parameters.");
                        }

                        chunks.Add((Chunk)c);

                        if (chunkClass.GetCustomAttribute <ChunkAttribute>().ProcessSync)
                        {
                            c.Discover();
                        }

                        chunk = (Chunk)c;
                    }
                    else
                    {
                        Debug.WriteLine("Unknown skippable chunk: " + chunkID.ToString("X"));
                        chunk = (Chunk)Activator.CreateInstance(typeof(SkippableChunk <>).MakeGenericType(type), node, chunkRemapped, chunkData);
                        chunks.Add(chunk);
                    }
                }

                if (reflected && !skippable)
                {
                    if (skippable) // Does it ever happen?
                    {
                        var skip          = r.ReadUInt32();
                        var chunkDataSize = r.ReadInt32();
                    }

                    IChunk c;

                    var constructor       = chunkClass.GetConstructors().First();
                    var constructorParams = constructor.GetParameters();
                    if (constructorParams.Length == 0)
                    {
                        c      = (IChunk)constructor.Invoke(new object[0]);
                        c.Node = node;
                    }
                    else if (constructorParams.Length == 1)
                    {
                        c = (IChunk)constructor.Invoke(new object[] { node });
                    }
                    else
                    {
                        throw new ArgumentException($"{type.FullName} has an invalid amount of parameters.");
                    }

                    c.Part = (GameBoxPart)body;
                    c.OnLoad();

                    chunks.Add((Chunk)c);

                    r.Chunk = (Chunk)c; // Set chunk temporarily for reading

                    var posBefore = r.BaseStream.Position;

                    GameBoxReaderWriter gbxrw = new GameBoxReaderWriter(r);

                    try
                    {
                        if (chunkClass.GetCustomAttribute <IgnoreChunkAttribute>() == null)
                        {
                            if (chunkClass.GetCustomAttribute <AutoReadWriteChunkAttribute>() == null)
                            {
                                c.ReadWrite(node, gbxrw);
                            }
                            else
                            {
                                var unknown     = new GameBoxWriter(((Chunk)c).Unknown, r.Lookbackable);
                                var unknownData = r.ReadTillFacade();
                                unknown.Write(unknownData, 0, unknownData.Length);
                            }
                        }
                        else
                        {
                            throw new Exception($"Chunk 0x{chunkID & 0xFFF:x3} from class {node.ClassName} is known but its content is unknown to read.");
                        }
                    }
                    catch (EndOfStreamException)
                    {
                        Debug.WriteLine($"Unexpected end of the stream while reading the chunk.");
                    }

                    c.Progress = (int)(r.BaseStream.Position - posBefore);

                    r.Chunk = null;

                    chunk = (Chunk)c;
                }

                progress?.Report(new GameBoxReadProgress(GameBoxReadProgressStage.Body, (float)r.BaseStream.Position / r.BaseStream.Length, gbx, chunk));

                previousChunk = chunkID;
            }

            stopwatch.Stop();

            var logNodeCompletion = $"[{node.ClassName}] DONE! ({stopwatch.Elapsed.TotalMilliseconds}ms)";

            if (node.Body?.GBX.ClassID.HasValue == true && Remap(node.Body.GBX.ClassID.Value) == node.ID)
            {
                Log.Write(logNodeCompletion, ConsoleColor.Green);
            }
            else
            {
                Log.Write($"~ {logNodeCompletion}", ConsoleColor.Green);
            }

            return(node);
        }
Пример #8
0
        public void Write(GameBoxWriter w, int numNodes, ClassIDRemap remap)
        {
            w.Write(GameBox.Magic, StringLengthPrefix.None);
            w.Write(GBX.Version);

            if (GBX.Version >= 3)
            {
                w.Write((byte)GBX.ByteFormat.GetValueOrDefault());
                w.Write((byte)GBX.RefTableCompression.GetValueOrDefault());
                w.Write((byte)GBX.BodyCompression.GetValueOrDefault());
                if (GBX.Version >= 4)
                {
                    w.Write((byte)GBX.UnknownByte.GetValueOrDefault());
                }
                w.Write(GBX.ClassID.GetValueOrDefault());

                if (GBX.Version >= 6)
                {
                    if (Chunks == null)
                    {
                        w.Write(0);
                    }
                    else
                    {
                        using (var userData = new MemoryStream())
                            using (var gbxw = new GameBoxWriter(userData, this))
                            {
                                var gbxrw = new GameBoxReaderWriter(gbxw);

                                Dictionary <uint, int> lengths = new Dictionary <uint, int>();

                                foreach (var chunk in Chunks)
                                {
                                    chunk.Unknown.Position = 0;

                                    var pos = userData.Position;
                                    if (((ISkippableChunk)chunk).Discovered)
                                    {
                                        ((IChunk)chunk).ReadWrite(((GameBox <T>)GBX).MainNode, gbxrw);
                                    }
                                    else
                                    {
                                        ((ISkippableChunk)chunk).Write(gbxw);
                                    }

                                    lengths[chunk.ID] = (int)(userData.Position - pos);
                                }

                                // Actual data size plus the class id (4 bytes) and each length (4 bytes) plus the number of chunks integer
                                w.Write((int)userData.Length + Chunks.Count * 8 + 4);

                                // Write number of header chunks integer
                                w.Write(Chunks.Count);

                                foreach (Chunk chunk in Chunks)
                                {
                                    w.Write(Chunk.Remap(chunk.ID, remap));
                                    var length = lengths[chunk.ID];
                                    if (((IHeaderChunk)chunk).IsHeavy)
                                    {
                                        length |= 1 << 31;
                                    }
                                    w.Write(length);
                                }

                                w.Write(userData.ToArray(), 0, (int)userData.Length);
                            }
                    }
                }

                w.Write(numNodes);
            }
        }