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)); }
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; } } }
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); }
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; }
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)); }
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); } }
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);
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); } }
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; }
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); }
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];
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); }
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()); }
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); }
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); } }
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); }
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); }
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(); }
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); }