public object Serdes(object existing, AssetInfo config, AssetMapping mapping, ISerializer s)
        {
            if (s == null)
            {
                throw new ArgumentNullException(nameof(s));
            }
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }
            if (s.Mode != SerializerMode.Reading)
            {
                throw new NotImplementedException($"Writing of amorphous sprites is not currently supported");
            }
            ApiUtil.Assert(config.Transposed != true);

            var sizes = ParseSpriteSizes(config.Get <string>("SubSprites", null));

            int spriteWidth = 0;
            int currentY    = 0;
            var frameBytes  = new List <byte[]>();
            var frames      = new List <AlbionSpriteFrame>();

            foreach (var(width, height) in sizes)
            {
                if (s.BytesRemaining <= 0)
                {
                    break;
                }

                var bytes = s.ByteArray("PixelData", null, width * height);
                frames.Add(new AlbionSpriteFrame(0, currentY, width, height));
                frameBytes.Add(bytes);

                currentY += height;
                if (width > spriteWidth)
                {
                    spriteWidth = width;
                }
            }

            var spriteHeight = currentY;
            var pixelData    = new byte[frames.Count * spriteWidth * currentY];

            for (int n = 0; n < frames.Count; n++)
            {
                var frame = frames[n];

                for (int j = 0; j < frame.Height; j++)
                {
                    for (int i = 0; i < frame.Width; i++)
                    {
                        pixelData[(frame.Y + j) * spriteWidth + frame.X + i] = frameBytes[n][j * frame.Width + i];
                    }
                }
            }

            s.Check();
            return(new AlbionSprite(config.AssetId.ToString(), spriteWidth, spriteHeight, false, pixelData, frames));
        }
Example #2
0
        void ApplyFrame(FlicFrame frame)
        {
            ApiUtil.Assert(frame.Width == 0, "Frame width overrides are not currently handled");
            ApiUtil.Assert(frame.Height == 0, "Frame height overrides are not currently handled");

            foreach (var subChunk in frame.SubChunks)
            {
                switch (subChunk)
                {
                case Palette8Chunk paletteChunk:
                    paletteChunk.GetEffectivePalette(Palette).CopyTo(Palette, 0);
                    break;

                case CopyChunk copy:
                    copy.PixelData.CopyTo(PixelData, 0);
                    break;

                case DeltaFlcChunk delta:
                    delta.Apply(PixelData, _flic.Width);
                    break;

                case FullByteOrientedRleChunk rle:
                    rle.PixelData.CopyTo(PixelData, 0);
                    break;
                }
            }
        }
Example #3
0
        public static MapChangeCollection Serdes(int _, MapChangeCollection c, AssetMapping mapping, ISerializer s)
        {
            if (s == null)
            {
                throw new ArgumentNullException(nameof(s));
            }
            c ??= new MapChangeCollection();
            uint   size  = s.UInt32("Size", (uint)(c.Count * MapChange.SizeOnDisk + 2));
            ushort count = s.UInt16(nameof(Count), (ushort)c.Count);

            ApiUtil.Assert(count * MapChange.SizeOnDisk == size - 2);
            for (int i = 0; i < count; i++)
            {
                if (i < c.Count)
                {
                    c[i] = MapChange.Serdes(i, c[i], mapping, s);
                }
                else
                {
                    c.Add(MapChange.Serdes(i, null, mapping, s));
                }
            }

            return(c);
        }
Example #4
0
        public XldFile(string filename)
        {
            Filename = filename;
            _stream  = File.OpenRead(filename);

            using var br = new BinaryReader(_stream, Encoding.Default, true);
            var headerBytes  = Encoding.ASCII.GetBytes(MagicString);
            var actualHeader = br.ReadBytes(headerBytes.Length);

            if (!actualHeader.SequenceEqual(headerBytes))
            {
                throw new FormatException("XLD file magic string not found");
            }

            byte terminator = br.ReadByte();

            ApiUtil.Assert(terminator == 0);

            int objectCount = br.ReadUInt16();

            _objectOffsets = new int[objectCount + 1];
            int offset = (int)_stream.Position + 4 * objectCount;

            for (int i = 0; i < objectCount; i++)
            {
                _objectOffsets[i] = offset;
                int length = br.ReadInt32();
                offset += length;
            }

            ApiUtil.Assert(offset == _stream.Length);
            _objectOffsets[objectCount] = offset;
        }
Example #5
0
    static IReadOnlyTexture <byte> Read(AssetInfo info, ISerializer s)
    {
        int width     = s.UInt16("Width", 0);
        int height    = s.UInt16("Height", 0);
        int something = s.UInt8(null, 0);

        ApiUtil.Assert(something == 0);
        byte frameCount = s.UInt8("Frames", 1);

        // TODO: When writing, assert that uniform and the frame sizes match up
        var frames = new List <IReadOnlyTexture <byte> >();

        for (int i = 0; i < frameCount; i++)
        {
            if (i > 0)
            {
                width     = s.UInt16("FrameWidth", 0);
                height    = s.UInt16("FrameHeight", 0);
                something = s.UInt8(null, 0);
                ApiUtil.Assert(something == 0);
                byte spriteCount2 = s.UInt8(null, frameCount);
                ApiUtil.Assert(spriteCount2 == frameCount);
            }

            byte[] frameBytes = s.Bytes("Frame" + i, null, width * height);
            frames.Add(new SimpleTexture <byte>(null, null, width, height, frameBytes));
        }

        s.Check();

        return(BlitUtil.CombineFramesVertically <byte>(info.AssetId, frames));
    }
Example #6
0
        public IEnumerable <(byte[], uint[], ushort)> AllFrames8() // pixels, palette, delay
        {
            uint[] palette = new uint[0x100];
            byte[] buffer8 = new byte[Width * Height];

            foreach (var frame in Chunks.OfType <FlicFrame>())
            {
                ApiUtil.Assert(frame.Width == 0, "Frame width overrides are not currently handled");
                ApiUtil.Assert(frame.Height == 0, "Frame height overrides are not currently handled");

                foreach (var subChunk in frame.SubChunks)
                {
                    switch (subChunk)
                    {
                    case Palette8Chunk paletteChunk:
                        palette = paletteChunk.GetEffectivePalette(palette);
                        break;

                    case CopyChunk copy:
                        copy.PixelData.CopyTo(buffer8, 0);
                        break;

                    case DeltaFlcChunk delta:
                        delta.Apply(buffer8, Width);
                        break;

                    case FullByteOrientedRleChunk rle:
                        rle.PixelData.CopyTo(buffer8, 0);
                        break;
                    }
                }

                yield return(buffer8, palette, frame.Delay);
            }
        }
Example #7
0
        public object Serdes(object existing, AssetInfo config, AssetMapping mapping, ISerializer s)
        {
            if (s == null)
            {
                throw new ArgumentNullException(nameof(s));
            }
            var streamLength = s.BytesRemaining;

            ApiUtil.Assert(streamLength % StringSize == 0);
            var  results = new Dictionary <(int, GameLanguage), string>();
            long end     = s.Offset + streamLength;

            int i = 3;

            while (s.Offset < end)
            {
                var bytes    = s.ByteArray(null, null, StringSize);
                var language = (i % 3) switch
                {
                    0 => GameLanguage.German,
                    1 => GameLanguage.English,
                    _ => GameLanguage.French,
                };

                results[(i / 3, language)] = FormatUtil.BytesTo850String(bytes);
Example #8
0
        public static FlicChunk Load(ISerializer s, int width, int height)
        {
            if (s == null)
            {
                throw new ArgumentNullException(nameof(s));
            }

            var  chunkSizeOffset = s.Offset;
            uint chunkSize       = s.UInt32(null, 0);

            if ((chunkSize & 0x1) != 0)
            {
                chunkSize++;
            }
            FlicChunkType type = (FlicChunkType)s.UInt16(null, 0);

            // Chunk
            FlicChunk c = type switch
            {
                FlicChunkType.Palette8Bit => new Palette8Chunk(),
                FlicChunkType.DeltaWordOrientedRle => new DeltaFlcChunk(),
                FlicChunkType.FullByteOrientedRle => new FullByteOrientedRleChunk(width, height),
                FlicChunkType.FullUncompressed => new CopyChunk(),
                FlicChunkType.Frame => new FlicFrame(width, height),
                // FlicChunkType.Palette6Bit          => new Palette6Chunk(),
                // FlicChunkType.DeltaByteOrientedRle => new DeltaFliChunk(),
                // FlicChunkType.BlackFrameData       => new BlackFrameChunk(),
                // FlicChunkType.Thumbnail            => new ThumbnailChunk(),
                _ => new UnknownChunk(type)
            };

            // Fix export issue in Autodesk Animator
            if (type == FlicChunkType.FullUncompressed &&
                width * height + ChunkHeaderSize - 2 == chunkSize)
            {
                chunkSize += 2;
            }

            chunkSize = c.LoadChunk(chunkSize - ChunkHeaderSize, s) + ChunkHeaderSize;

            var actualChunkSize = s.Offset - chunkSizeOffset;

            if (actualChunkSize - chunkSize < 4) // pad
            {
                for (long i = chunkSize - actualChunkSize; i != 0; i--)
                {
                    s.UInt8(null, 0);
                }

                actualChunkSize = s.Offset - chunkSizeOffset;
            }

            ApiUtil.Assert(actualChunkSize == chunkSize);
            return(c);
        }
    }
Example #9
0
 public static VisitedEventList Serdes(int _, VisitedEventList c, AssetMapping mapping, ISerializer s)
 {
     if (s == null) throw new ArgumentNullException(nameof(s));
     c ??= new VisitedEventList();
     c.Size = s.UInt32(nameof(Size), c.Size);
     c.NumChunks = s.UInt16(nameof(NumChunks), c.NumChunks);
     ApiUtil.Assert(c.NumChunks == c.Size / VisitedEvent.SizeOnDisk);
     c.Contents ??= new VisitedEvent[(c.Size - 2) / VisitedEvent.SizeOnDisk];
     s.List(nameof(c.Contents), c.Contents, mapping, c.Contents.Length, VisitedEvent.Serdes);
     return c;
 }
Example #10
0
        public object Load(BinaryReader br, long streamLength, AssetKey key, AssetInfo config)
        {
            ApiUtil.Assert(config.Transposed != true);
            long initialPosition = br.BaseStream.Position;
            var  sizes           = ParseSpriteSizes(config.SubSprites);

            AlbionSprite sprite = new AlbionSprite
            {
                Name          = key.ToString(),
                Width         = 0,
                UniformFrames = false,
                Frames        = new List <AlbionSprite.Frame>()
            };

            int currentY   = 0;
            var frameBytes = new List <byte[]>();

            foreach (var(width, height) in sizes)
            {
                if (br.BaseStream.Position >= initialPosition + streamLength)
                {
                    break;
                }

                var bytes = br.ReadBytes(width * height);
                sprite.Frames.Add(new AlbionSprite.Frame(0, currentY, width, height));
                frameBytes.Add(bytes);

                currentY += height;
                if (width > sprite.Width)
                {
                    sprite.Width = width;
                }
            }

            sprite.Height    = currentY;
            sprite.PixelData = new byte[sprite.Frames.Count * sprite.Width * currentY];

            for (int n = 0; n < sprite.Frames.Count; n++)
            {
                var frame = sprite.Frames[n];

                for (int j = 0; j < frame.Height; j++)
                {
                    for (int i = 0; i < frame.Width; i++)
                    {
                        sprite.PixelData[(frame.Y + j) * sprite.Width + frame.X + i] = frameBytes[n][j * frame.Width + i];
                    }
                }
            }

            ApiUtil.Assert(br.BaseStream.Position == initialPosition + streamLength);
            return(sprite);
        }
Example #11
0
        static IReadOnlyTexture <byte> Read(AssetInfo info, ISerializer s)
        {
            int width     = s.UInt16("Width", 0);
            int height    = s.UInt16("Height", 0);
            int something = s.UInt8(null, 0);

            ApiUtil.Assert(something == 0);
            byte frameCount = s.UInt8("Frames", 1);

            // TODO: When writing, assert that uniform and the frame sizes match up
            var frames       = new (int, int, int)[frameCount];
Example #12
0
 public static VisitedEventList Serdes(int _, VisitedEventList c, ISerializer s)
 {
     c ??= new VisitedEventList();
     s.Begin();
     c.Size      = s.UInt32(nameof(Size), c.Size);
     c.NumChunks = s.UInt16(nameof(NumChunks), c.NumChunks);
     ApiUtil.Assert(c.NumChunks == c.Size / VisitedEvent.SizeOnDisk);
     c.Contents ??= new VisitedEvent[(c.Size - 2) / VisitedEvent.SizeOnDisk];
     s.List(nameof(c.Contents), c.Contents, c.Contents.Length, VisitedEvent.Serdes);
     s.End();
     return(c);
 }
Example #13
0
    Func <string[], Event> BuildParser(ParameterExpression partsParameter)
    {
        var constructor = Type.GetConstructors().Single();
        var parameters  = constructor.GetParameters();

        ApiUtil.Assert(parameters.Length == Parts.Count,
                       $"When building parser for {Type}, the public constructor had {parameters.Length} parameters but {Parts.Count} were expected.");

        return((Func <string[], Event>)Expression.Lambda(
                   Expression.Convert(
                       Expression.New(constructor, Parts.Select(x => x.Parser)), typeof(Event)),
                   partsParameter).Compile());
    }
Example #14
0
        public static SimpleChestEvent Serdes(SimpleChestEvent e, ISerializer s)
        {
            e ??= new SimpleChestEvent();
            s.Begin();
            e.ChestType = s.EnumU8(nameof(ChestType), e.ChestType);
            uint padding = s.UInt32(nameof(padding), 0);

            ApiUtil.Assert(padding == 0);
            e.ItemId = (ItemId)StoreIncremented.Serdes(nameof(e.ItemId), (ushort)e.ItemId, s.UInt16);
            e.Amount = s.UInt16(nameof(Amount), e.Amount);
            s.End();
            return(e);
        }
Example #15
0
        public static FlicChunk Load(BinaryReader br, int width, int height)
        {
            var  chunkSizeOffset = br.BaseStream.Position;
            uint chunkSize       = br.ReadUInt32();

            if ((chunkSize & 0x1) != 0)
            {
                chunkSize++;
            }
            FlicChunkType type = (FlicChunkType)br.ReadUInt16();

            // Chunk
            FlicChunk c = type switch
            {
                FlicChunkType.Palette8Bit => new Palette8Chunk(),
                FlicChunkType.DeltaWordOrientedRle => new DeltaFlcChunk(),
                FlicChunkType.FullByteOrientedRle => new FullByteOrientedRleChunk(width, height),
                FlicChunkType.FullUncompressed => new CopyChunk(),
                FlicChunkType.Frame => new FlicFrame(width, height),
                // FlicChunkType.Palette6Bit          => new Palette6Chunk(),
                // FlicChunkType.DeltaByteOrientedRle => new DeltaFliChunk(),
                // FlicChunkType.BlackFrameData       => new BlackFrameChunk(),
                // FlicChunkType.Thumbnail            => new ThumbnailChunk(),
                _ => new UnknownChunk(type)
            };

            // Fix export issue in Autodesk Animator
            if (type == FlicChunkType.FullUncompressed &&
                width * height + ChunkHeaderSize - 2 == chunkSize)
            {
                chunkSize += 2;
            }

            chunkSize = c.LoadChunk(chunkSize - ChunkHeaderSize, br) + ChunkHeaderSize;

            var actualChunkSize = br.BaseStream.Position - chunkSizeOffset;

            if (actualChunkSize - chunkSize < 4) // pad
            {
                for (long i = chunkSize - actualChunkSize; i != 0; i--)
                {
                    br.ReadByte();
                }

                actualChunkSize = br.BaseStream.Position - chunkSizeOffset;
            }

            ApiUtil.Assert(actualChunkSize == chunkSize);
            return(c);
        }
    }
Example #16
0
    public static EventNode SerdesNode(ushort id, EventNode node, ISerializer s, AssetId chainSource, TextId textAssetId, AssetMapping mapping)
    {
        if (s == null)
        {
            throw new ArgumentNullException(nameof(s));
        }
        var initialPosition = s.Offset;
        var mapEvent        = node?.Event as MapEvent;

        if (node?.Event != null && mapEvent == null)
        {
            throw new ArgumentOutOfRangeException($"Tried to serialise a non-map event \"{node.Event}\" to bytes");
        }

        var @event = SerdesEvent(mapEvent, s, chainSource, textAssetId, mapping);

        if (@event is IBranchingEvent be)
        {
            node ??= new BranchNode(id, be);
            var    branch       = (BranchNode)node;
            ushort?falseEventId = s.Transform <ushort, ushort?>(
                nameof(branch.NextIfFalse),
                branch.NextIfFalse?.Id,
                S.UInt16,
                MaxToNullConverter.Instance);

            if (falseEventId != null && branch.NextIfFalse == null)
            {
                branch.NextIfFalse = new DummyEventNode(falseEventId.Value);
            }
        }
        else
        {
            node ??= new EventNode(id, @event);
        }

        ushort?nextEventId = s.Transform <ushort, ushort?>(nameof(node.Next), node.Next?.Id, S.UInt16, MaxToNullConverter.Instance);

        if (nextEventId != null && node.Next == null)
        {
            node.Next = new DummyEventNode(nextEventId.Value);
        }

        long expectedPosition = initialPosition + 12;
        long actualPosition   = s.Offset;

        ApiUtil.Assert(expectedPosition == actualPosition,
                       $"Expected to have read {expectedPosition - initialPosition} bytes, but {actualPosition - initialPosition} have been read.");

        return(node);
    }
Example #17
0
        public static IQueryEvent Serdes(IQueryEvent genericEvent, ISerializer s, AssetType textType, ushort textSourceId)
        {
            var subType = s.EnumU8("SubType", genericEvent?.QueryType ?? QueryType.IsScriptDebugModeActive);

            switch (subType)
            {
            case QueryType.InventoryHasItem:
            case QueryType.UsedItemId:
                return(QueryItemEvent.Serdes((QueryItemEvent)genericEvent, s, subType));

            case QueryType.ChosenVerb:
                return(QueryVerbEvent.Serdes((QueryVerbEvent)genericEvent, s));

            case QueryType.IsPartyMemberConscious:
            case QueryType.IsPartyMemberLeader:
            case QueryType.HasPartyMember:
                return(QueryPartyEvent.Serdes((QueryPartyEvent)genericEvent, s, subType));

            case QueryType.PreviousActionResult: break;

            case QueryType.Ticker: break;

            case QueryType.CurrentMapId: break;

            case QueryType.TriggerType: break;

            case QueryType.PromptPlayer:
                return(PromptPlayerEvent.Serdes((PromptPlayerEvent)genericEvent, s, textType, textSourceId));

            case QueryType.PromptPlayerNumeric:
                return(PromptPlayerNumericEvent.Serdes((PromptPlayerNumericEvent)genericEvent, s));
            }

            var e = (QueryEvent)genericEvent ?? new QueryEvent {
                QueryType = subType
            };

            s.Begin();
            e.Operation = s.EnumU8(nameof(Operation), e.Operation);
            e.Immediate = s.UInt8(nameof(Immediate), e.Immediate);
            e.Unk4      = s.UInt8(nameof(Unk4), e.Unk4);
            e.Unk5      = s.UInt8(nameof(Unk5), e.Unk5);
            e.Argument  = s.UInt16(nameof(Argument), e.Argument);

            ApiUtil.Assert(e.Unk4 == 0);
            ApiUtil.Assert(e.Unk5 == 0);

            s.End();
            return(e);
        }
Example #18
0
        public static void RoundTripTest(string baseDir)
        {
            var mapping = AssetMapping.Global; // TODO: Base game mapping.
            var saveDir = Path.Combine(baseDir, "re", "TestSaves");
            var regex   = new Regex(@"\.[0-9][0-9][0-9]$");

            foreach (var file in Directory.EnumerateFiles(saveDir))
            {
                if (!regex.IsMatch(file))
                {
                    continue;
                }
                Console.WriteLine("Round-trip testing " + file);
                using var stream = File.Open(file, FileMode.Open);
                using var br     = new BinaryReader(stream);
                using var ar     = new AlbionReader(br, stream.Length);
                var save = SavedGame.Serdes(null, mapping, ar);

                using var ms = new MemoryStream();
                using var bw = new BinaryWriter(ms);
                using var aw = new AlbionWriter(bw);
                SavedGame.Serdes(save, mapping, aw);

                br.BaseStream.Position = 0;
                var originalBytes  = br.ReadBytes((int)stream.Length);
                var roundTripBytes = ms.ToArray();

                //* Save round-tripped and annotated text output for debugging
                File.WriteAllBytes(file + ".bin", roundTripBytes);
                using var ts  = new MemoryStream();
                using var tw  = new StreamWriter(ts);
                using var afw = new AnnotatedFormatWriter(tw);
                SavedGame.Serdes(save, mapping, afw);
                ts.Position = 0;
                File.WriteAllBytes(file + ".txt", ts.ToArray());
                //*/

                ApiUtil.Assert(originalBytes.Length == roundTripBytes.Length);
                ApiUtil.Assert(originalBytes.SequenceEqual(roundTripBytes));

                var sw = new StringWriter();
                using var jw = new JsonWriter(sw);
                SavedGame.Serdes(save, mapping, jw);
                File.WriteAllText(file + ".json", sw.ToString());
                break;
            }

            Console.WriteLine("Done");
            Console.ReadLine();
        }
Example #19
0
 public TrueColorTexture(string name, uint width, uint height, uint[] pixels)
 {
     Name    = name;
     IsDirty = true;
     Width   = width;
     Height  = height;
     ApiUtil.Assert(pixels.Length == width * height);
     _pixelData = pixels;
     _subImage  = new SubImage(
         Vector2.Zero,
         new Vector2(Width, Height),
         new Vector2(Width, Height),
         0);
 }