committed by
GitHub
5 changed files with 103 additions and 98 deletions
@ -1,105 +1,116 @@ |
|||||
using System; |
using System; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
using System.IO; |
using System.IO; |
||||
using System.Runtime.Serialization; |
using System.Text; |
||||
using System.Xml.Linq; |
|
||||
using System.Linq; |
|
||||
using System.Diagnostics.CodeAnalysis; |
|
||||
|
|
||||
namespace Avalonia.Utilities |
namespace Avalonia.Utilities |
||||
{ |
{ |
||||
#if !BUILDTASK
|
#if !BUILDTASK
|
||||
public |
public |
||||
#endif
|
#endif
|
||||
static class AvaloniaResourcesIndexReaderWriter |
static class AvaloniaResourcesIndexReaderWriter |
||||
{ |
{ |
||||
private const int LastKnownVersion = 1; |
private const int XmlLegacyVersion = 1; |
||||
public static List<AvaloniaResourcesIndexEntry> Read(Stream stream) |
private const int BinaryCurrentVersion = 2; |
||||
|
|
||||
|
public static List<AvaloniaResourcesIndexEntry> ReadIndex(Stream stream) |
||||
{ |
{ |
||||
var ver = new BinaryReader(stream).ReadInt32(); |
using var reader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); |
||||
if (ver > LastKnownVersion) |
|
||||
throw new Exception("Resources index format version is not known"); |
|
||||
|
|
||||
var assetDoc = XDocument.Load(stream); |
|
||||
XNamespace assetNs = assetDoc.Root!.Attribute("xmlns")!.Value; |
|
||||
List<AvaloniaResourcesIndexEntry> entries = |
|
||||
(from entry in assetDoc.Root.Element(assetNs + "Entries")!.Elements(assetNs + "AvaloniaResourcesIndexEntry") |
|
||||
select new AvaloniaResourcesIndexEntry |
|
||||
{ |
|
||||
Path = entry.Element(assetNs + "Path")!.Value, |
|
||||
Offset = int.Parse(entry.Element(assetNs + "Offset")!.Value), |
|
||||
Size = int.Parse(entry.Element(assetNs + "Size")!.Value) |
|
||||
}).ToList(); |
|
||||
|
|
||||
return entries; |
var version = reader.ReadInt32(); |
||||
|
return version switch |
||||
|
{ |
||||
|
XmlLegacyVersion => ReadXmlIndex(), |
||||
|
BinaryCurrentVersion => ReadBinaryIndex(reader), |
||||
|
_ => throw new Exception($"Unknown resources index format version {version}") |
||||
|
}; |
||||
} |
} |
||||
|
|
||||
[RequiresUnreferencedCode("AvaloniaResources uses Data Contract Serialization, which might require unreferenced code")] |
private static List<AvaloniaResourcesIndexEntry> ReadXmlIndex() |
||||
public static void Write(Stream stream, List<AvaloniaResourcesIndexEntry> entries) |
=> throw new NotSupportedException("Found legacy resources index format: please recompile your XAML files"); |
||||
|
|
||||
|
private static List<AvaloniaResourcesIndexEntry> ReadBinaryIndex(BinaryReader reader) |
||||
{ |
{ |
||||
new BinaryWriter(stream).Write(LastKnownVersion); |
var entryCount = reader.ReadInt32(); |
||||
new DataContractSerializer(typeof(AvaloniaResourcesIndex)).WriteObject(stream, |
var entries = new List<AvaloniaResourcesIndexEntry>(entryCount); |
||||
new AvaloniaResourcesIndex() |
|
||||
{ |
for (var i = 0; i < entryCount; ++i) |
||||
Entries = entries |
{ |
||||
|
entries.Add(new AvaloniaResourcesIndexEntry { |
||||
|
Path = reader.ReadString(), |
||||
|
Offset = reader.ReadInt32(), |
||||
|
Size = reader.ReadInt32() |
||||
}); |
}); |
||||
|
} |
||||
|
|
||||
|
return entries; |
||||
} |
} |
||||
|
|
||||
[RequiresUnreferencedCode("AvaloniaResources uses Data Contract Serialization, which might require unreferenced code")] |
public static void WriteIndex(Stream output, List<AvaloniaResourcesIndexEntry> entries) |
||||
public static byte[] Create(Dictionary<string, byte[]> data) |
|
||||
{ |
{ |
||||
var sources = data.ToList(); |
using var writer = new BinaryWriter(output, Encoding.UTF8, leaveOpen: true); |
||||
var offsets = new Dictionary<string, int>(); |
|
||||
var coffset = 0; |
WriteIndex(writer, entries); |
||||
foreach (var s in sources) |
} |
||||
|
|
||||
|
private static void WriteIndex(BinaryWriter writer, List<AvaloniaResourcesIndexEntry> entries) |
||||
|
{ |
||||
|
writer.Write(BinaryCurrentVersion); |
||||
|
writer.Write(entries.Count); |
||||
|
|
||||
|
foreach (var entry in entries) |
||||
{ |
{ |
||||
offsets[s.Key] = coffset; |
writer.Write(entry.Path ?? string.Empty); |
||||
coffset += s.Value.Length; |
writer.Write(entry.Offset); |
||||
|
writer.Write(entry.Size); |
||||
} |
} |
||||
var index = sources.Select(s => new AvaloniaResourcesIndexEntry |
} |
||||
{ |
|
||||
Path = s.Key, |
public static void WriteResources(Stream output, List<(string Path, int Size, Func<Stream> Open)> resources) |
||||
Size = s.Value.Length, |
{ |
||||
Offset = offsets[s.Key] |
var entries = new List<AvaloniaResourcesIndexEntry>(resources.Count); |
||||
}).ToList(); |
var offset = 0; |
||||
var output = new MemoryStream(); |
|
||||
var ms = new MemoryStream(); |
foreach (var resource in resources) |
||||
AvaloniaResourcesIndexReaderWriter.Write(ms, index); |
|
||||
new BinaryWriter(output).Write((int)ms.Length); |
|
||||
ms.Position = 0; |
|
||||
ms.CopyTo(output); |
|
||||
foreach (var s in sources) |
|
||||
{ |
{ |
||||
output.Write(s.Value,0,s.Value.Length); |
entries.Add(new AvaloniaResourcesIndexEntry |
||||
|
{ |
||||
|
Path = resource.Path, |
||||
|
Offset = offset, |
||||
|
Size = resource.Size |
||||
|
}); |
||||
|
offset += resource.Size; |
||||
} |
} |
||||
|
|
||||
return output.ToArray(); |
using var writer = new BinaryWriter(output, Encoding.UTF8, leaveOpen: true); |
||||
} |
writer.Write(0); // index size placeholder, overwritten below
|
||||
} |
|
||||
|
|
||||
[DataContract] |
var posBeforeEntries = output.Position; |
||||
#if !BUILDTASK
|
WriteIndex(writer, entries); |
||||
public |
|
||||
#endif
|
var posAfterEntries = output.Position; |
||||
class AvaloniaResourcesIndex |
var indexSize = (int) (posAfterEntries - posBeforeEntries); |
||||
{ |
output.Position = 0L; |
||||
[DataMember] |
writer.Write(indexSize); |
||||
public List<AvaloniaResourcesIndexEntry> Entries { get; set; } = new List<AvaloniaResourcesIndexEntry>(); |
output.Position = posAfterEntries; |
||||
|
|
||||
|
foreach (var resource in resources) |
||||
|
{ |
||||
|
using var resourceStream = resource.Open(); |
||||
|
resourceStream.CopyTo(output); |
||||
|
} |
||||
|
} |
||||
} |
} |
||||
|
|
||||
[DataContract] |
|
||||
#if !BUILDTASK
|
#if !BUILDTASK
|
||||
public |
public |
||||
#endif
|
#endif
|
||||
class AvaloniaResourcesIndexEntry |
class AvaloniaResourcesIndexEntry |
||||
{ |
{ |
||||
[DataMember] |
|
||||
public string? Path { get; set; } |
public string? Path { get; set; } |
||||
|
|
||||
[DataMember] |
|
||||
public int Offset { get; set; } |
public int Offset { get; set; } |
||||
|
|
||||
[DataMember] |
|
||||
public int Size { get; set; } |
public int Size { get; set; } |
||||
} |
} |
||||
} |
} |
||||
|
|||||
Loading…
Reference in new issue