| 1 | using System; | 
 
 
 
 
 | 2 | using System.Collections.Generic; | 
 
 
 
 
 | 3 | using System.IO; | 
 
 
 
 
 | 4 | using System.Text; | 
 
 
 
 
 | 5 |  | 
 
 
 
 
 | 6 | namespace Oni | 
 
 
 
 
 | 7 | { | 
 
 
 
 
 | 8 | internal sealed class ImporterFile | 
 
 
 
 
 | 9 | { | 
 
 
 
 
 | 10 | private static readonly byte[] txcaPadding = new byte[480]; | 
 
 
 
 
 | 11 | private readonly long templateChecksum = InstanceFileHeader.OniPCTemplateChecksum; | 
 
 
 
 
 | 12 | private MemoryStream rawStream; | 
 
 
 
 
 | 13 | private BinaryWriter rawWriter; | 
 
 
 
 
 | 14 | private List<ImporterFileDescriptor> descriptors; | 
 
 
 
 
 | 15 | private int nameOffset; | 
 
 
 
 
 | 16 |  | 
 
 
 
 
 | 17 | #region private class FileHeader | 
 
 
 
 
 | 18 |  | 
 
 
 
 
 | 19 | private class FileHeader | 
 
 
 
 
 | 20 | { | 
 
 
 
 
 | 21 | public const int Size = 64; | 
 
 
 
 
 | 22 |  | 
 
 
 
 
 | 23 | public long TemplateChecksum; | 
 
 
 
 
 | 24 | public int Version; | 
 
 
 
 
 | 25 | public int InstanceCount; | 
 
 
 
 
 | 26 | public int DataTableOffset; | 
 
 
 
 
 | 27 | public int DataTableSize; | 
 
 
 
 
 | 28 | public int NameTableOffset; | 
 
 
 
 
 | 29 | public int NameTableSize; | 
 
 
 
 
 | 30 | public int RawTableOffset; | 
 
 
 
 
 | 31 | public int RawTableSize; | 
 
 
 
 
 | 32 |  | 
 
 
 
 
 | 33 | public void Write(BinaryWriter writer) | 
 
 
 
 
 | 34 | { | 
 
 
 
 
 | 35 | writer.Write(TemplateChecksum); | 
 
 
 
 
 | 36 | writer.Write(Version); | 
 
 
 
 
 | 37 | writer.Write(InstanceFileHeader.Signature); | 
 
 
 
 
 | 38 | writer.Write(InstanceCount); | 
 
 
 
 
 | 39 | writer.Write(0ul); | 
 
 
 
 
 | 40 | writer.Write(DataTableOffset); | 
 
 
 
 
 | 41 | writer.Write(DataTableSize); | 
 
 
 
 
 | 42 | writer.Write(NameTableOffset); | 
 
 
 
 
 | 43 | writer.Write(NameTableSize); | 
 
 
 
 
 | 44 | writer.Write(RawTableOffset); | 
 
 
 
 
 | 45 | writer.Write(RawTableSize); | 
 
 
 
 
 | 46 | writer.Write(0ul); | 
 
 
 
 
 | 47 | } | 
 
 
 
 
 | 48 | } | 
 
 
 
 
 | 49 |  | 
 
 
 
 
 | 50 | #endregion | 
 
 
 
 
 | 51 |  | 
 
 
 
 
 | 52 | public ImporterFile() | 
 
 
 
 
 | 53 | { | 
 
 
 
 
 | 54 | } | 
 
 
 
 
 | 55 |  | 
 
 
 
 
 | 56 | public ImporterFile(long templateChecksum) | 
 
 
 
 
 | 57 | { | 
 
 
 
 
 | 58 | this.templateChecksum = templateChecksum; | 
 
 
 
 
 | 59 | } | 
 
 
 
 
 | 60 |  | 
 
 
 
 
 | 61 | public void BeginImport() | 
 
 
 
 
 | 62 | { | 
 
 
 
 
 | 63 | rawStream = null; | 
 
 
 
 
 | 64 | rawWriter = null; | 
 
 
 
 
 | 65 |  | 
 
 
 
 
 | 66 | descriptors = new List<ImporterFileDescriptor>(); | 
 
 
 
 
 | 67 | nameOffset = 0; | 
 
 
 
 
 | 68 | } | 
 
 
 
 
 | 69 |  | 
 
 
 
 
 | 70 | public BinaryWriter RawWriter | 
 
 
 
 
 | 71 | { | 
 
 
 
 
 | 72 | get | 
 
 
 
 
 | 73 | { | 
 
 
 
 
 | 74 | if (rawWriter == null) | 
 
 
 
 
 | 75 | { | 
 
 
 
 
 | 76 | rawStream = new MemoryStream(); | 
 
 
 
 
 | 77 | rawWriter = new BinaryWriter(rawStream); | 
 
 
 
 
 | 78 | rawWriter.Write(new byte[32]); | 
 
 
 
 
 | 79 | } | 
 
 
 
 
 | 80 |  | 
 
 
 
 
 | 81 | return rawWriter; | 
 
 
 
 
 | 82 | } | 
 
 
 
 
 | 83 | } | 
 
 
 
 
 | 84 |  | 
 
 
 
 
 | 85 | public ImporterDescriptor CreateInstance(TemplateTag tag, string name = null) | 
 
 
 
 
 | 86 | { | 
 
 
 
 
 | 87 | var descriptor = new ImporterFileDescriptor(this, tag, descriptors.Count, MakeInstanceName(tag, name)); | 
 
 
 
 
 | 88 |  | 
 
 
 
 
 | 89 | descriptors.Add(descriptor); | 
 
 
 
 
 | 90 |  | 
 
 
 
 
 | 91 | return descriptor; | 
 
 
 
 
 | 92 | } | 
 
 
 
 
 | 93 |  | 
 
 
 
 
 | 94 | public int WriteRawPart(byte[] data) | 
 
 
 
 
 | 95 | { | 
 
 
 
 
 | 96 | int offset = RawWriter.Align32(); | 
 
 
 
 
 | 97 | RawWriter.Write(data); | 
 
 
 
 
 | 98 | return offset; | 
 
 
 
 
 | 99 | } | 
 
 
 
 
 | 100 |  | 
 
 
 
 
 | 101 | public int WriteRawPart(string text) | 
 
 
 
 
 | 102 | { | 
 
 
 
 
 | 103 | return WriteRawPart(Encoding.UTF8.GetBytes(text)); | 
 
 
 
 
 | 104 | } | 
 
 
 
 
 | 105 |  | 
 
 
 
 
 | 106 | private sealed class ImporterFileDescriptor : ImporterDescriptor | 
 
 
 
 
 | 107 | { | 
 
 
 
 
 | 108 | public const int Size = 20; | 
 
 
 
 
 | 109 |  | 
 
 
 
 
 | 110 | private int nameOffset; | 
 
 
 
 
 | 111 | private int dataOffset; | 
 
 
 
 
 | 112 | private byte[] data; | 
 
 
 
 
 | 113 |  | 
 
 
 
 
 | 114 | public ImporterFileDescriptor(ImporterFile file, TemplateTag tag, int index, string name) | 
 
 
 
 
 | 115 | : base(file, tag, index, name) | 
 
 
 
 
 | 116 | { | 
 
 
 
 
 | 117 | if (!string.IsNullOrEmpty(name)) | 
 
 
 
 
 | 118 | { | 
 
 
 
 
 | 119 | nameOffset = file.nameOffset; | 
 
 
 
 
 | 120 | file.nameOffset += name.Length + 1; | 
 
 
 
 
 | 121 | } | 
 
 
 
 
 | 122 | } | 
 
 
 
 
 | 123 |  | 
 
 
 
 
 | 124 | public int NameOffset | 
 
 
 
 
 | 125 | { | 
 
 
 
 
 | 126 | get { return nameOffset; } | 
 
 
 
 
 | 127 | set { nameOffset = value; } | 
 
 
 
 
 | 128 | } | 
 
 
 
 
 | 129 |  | 
 
 
 
 
 | 130 | public int DataOffset | 
 
 
 
 
 | 131 | { | 
 
 
 
 
 | 132 | get { return dataOffset; } | 
 
 
 
 
 | 133 | set { dataOffset = value; } | 
 
 
 
 
 | 134 | } | 
 
 
 
 
 | 135 |  | 
 
 
 
 
 | 136 | public int DataSize | 
 
 
 
 
 | 137 | { | 
 
 
 
 
 | 138 | get | 
 
 
 
 
 | 139 | { | 
 
 
 
 
 | 140 | if (data == null) | 
 
 
 
 
 | 141 | return 0; | 
 
 
 
 
 | 142 |  | 
 
 
 
 
 | 143 | return data.Length + 8; | 
 
 
 
 
 | 144 | } | 
 
 
 
 
 | 145 | } | 
 
 
 
 
 | 146 |  | 
 
 
 
 
 | 147 | public byte[] Data | 
 
 
 
 
 | 148 | { | 
 
 
 
 
 | 149 | get { return data; } | 
 
 
 
 
 | 150 | } | 
 
 
 
 
 | 151 |  | 
 
 
 
 
 | 152 | public override BinaryWriter OpenWrite() | 
 
 
 
 
 | 153 | { | 
 
 
 
 
 | 154 | if (data != null) | 
 
 
 
 
 | 155 | throw new InvalidOperationException("Descriptor has already been written to"); | 
 
 
 
 
 | 156 |  | 
 
 
 
 
 | 157 | return new InstanceWriter(this); | 
 
 
 
 
 | 158 | } | 
 
 
 
 
 | 159 |  | 
 
 
 
 
 | 160 | public override BinaryWriter OpenWrite(int offset) | 
 
 
 
 
 | 161 | { | 
 
 
 
 
 | 162 | if (data != null) | 
 
 
 
 
 | 163 | throw new InvalidOperationException("Descriptor has already been written to"); | 
 
 
 
 
 | 164 |  | 
 
 
 
 
 | 165 | var writer = new InstanceWriter(this); | 
 
 
 
 
 | 166 | writer.Skip(offset); | 
 
 
 
 
 | 167 | return writer; | 
 
 
 
 
 | 168 | } | 
 
 
 
 
 | 169 |  | 
 
 
 
 
 | 170 | public void Close(byte[] data) | 
 
 
 
 
 | 171 | { | 
 
 
 
 
 | 172 | this.data = data; | 
 
 
 
 
 | 173 | } | 
 
 
 
 
 | 174 | } | 
 
 
 
 
 | 175 |  | 
 
 
 
 
 | 176 | private class InstanceWriter : BinaryWriter | 
 
 
 
 
 | 177 | { | 
 
 
 
 
 | 178 | private readonly ImporterFileDescriptor descriptor; | 
 
 
 
 
 | 179 |  | 
 
 
 
 
 | 180 | public InstanceWriter(ImporterFileDescriptor descriptor) | 
 
 
 
 
 | 181 | : base(new MemoryStream()) | 
 
 
 
 
 | 182 | { | 
 
 
 
 
 | 183 | this.descriptor = descriptor; | 
 
 
 
 
 | 184 | } | 
 
 
 
 
 | 185 |  | 
 
 
 
 
 | 186 | protected override void Dispose(bool disposing) | 
 
 
 
 
 | 187 | { | 
 
 
 
 
 | 188 | var stream = (MemoryStream)BaseStream; | 
 
 
 
 
 | 189 |  | 
 
 
 
 
 | 190 | if (descriptor.Tag == TemplateTag.TXCA) | 
 
 
 
 
 | 191 | stream.Write(txcaPadding, 0, txcaPadding.Length); | 
 
 
 
 
 | 192 | else if (stream.Position > stream.Length) | 
 
 
 
 
 | 193 | stream.SetLength(stream.Position); | 
 
 
 
 
 | 194 |  | 
 
 
 
 
 | 195 | descriptor.Close(stream.ToArray()); | 
 
 
 
 
 | 196 |  | 
 
 
 
 
 | 197 | base.Dispose(disposing); | 
 
 
 
 
 | 198 | } | 
 
 
 
 
 | 199 | } | 
 
 
 
 
 | 200 |  | 
 
 
 
 
 | 201 | public void Write(string outputDirPath) | 
 
 
 
 
 | 202 | { | 
 
 
 
 
 | 203 | var filePath = Path.Combine(outputDirPath, Importer.EncodeFileName(descriptors[0].Name) + ".oni"); | 
 
 
 
 
 | 204 |  | 
 
 
 
 
 | 205 | Directory.CreateDirectory(outputDirPath); | 
 
 
 
 
 | 206 |  | 
 
 
 
 
 | 207 | int nameTableOffset = Utils.Align32(FileHeader.Size + ImporterFileDescriptor.Size * descriptors.Count); | 
 
 
 
 
 | 208 | int nameTableSize = nameOffset; | 
 
 
 
 
 | 209 | int dataTableOffset = Utils.Align32(nameTableOffset + nameOffset); | 
 
 
 
 
 | 210 | int dataTableSize = 0; | 
 
 
 
 
 | 211 |  | 
 
 
 
 
 | 212 | foreach (var descriptor in descriptors.Where(d => d.Data != null)) | 
 
 
 
 
 | 213 | { | 
 
 
 
 
 | 214 | descriptor.DataOffset = dataTableSize + 8; | 
 
 
 
 
 | 215 |  | 
 
 
 
 
 | 216 | dataTableSize += Utils.Align32(descriptor.DataSize); | 
 
 
 
 
 | 217 | } | 
 
 
 
 
 | 218 |  | 
 
 
 
 
 | 219 | var header = new FileHeader | 
 
 
 
 
 | 220 | { | 
 
 
 
 
 | 221 | TemplateChecksum = templateChecksum, | 
 
 
 
 
 | 222 | Version = InstanceFileHeader.Version32, | 
 
 
 
 
 | 223 | InstanceCount = descriptors.Count, | 
 
 
 
 
 | 224 | DataTableOffset = dataTableOffset, | 
 
 
 
 
 | 225 | DataTableSize = dataTableSize, | 
 
 
 
 
 | 226 | NameTableOffset = nameTableOffset, | 
 
 
 
 
 | 227 | NameTableSize = nameTableSize | 
 
 
 
 
 | 228 | }; | 
 
 
 
 
 | 229 |  | 
 
 
 
 
 | 230 | using (var stream = File.Create(filePath)) | 
 
 
 
 
 | 231 | using (var writer = new BinaryWriter(stream)) | 
 
 
 
 
 | 232 | { | 
 
 
 
 
 | 233 | bool hasRawParts = (rawStream != null && rawStream.Length > 32); | 
 
 
 
 
 | 234 |  | 
 
 
 
 
 | 235 | if (hasRawParts) | 
 
 
 
 
 | 236 | { | 
 
 
 
 
 | 237 | header.RawTableOffset = Utils.Align32(header.DataTableOffset + header.DataTableSize); | 
 
 
 
 
 | 238 | header.RawTableSize = (int)rawStream.Length; | 
 
 
 
 
 | 239 | } | 
 
 
 
 
 | 240 |  | 
 
 
 
 
 | 241 | header.Write(writer); | 
 
 
 
 
 | 242 |  | 
 
 
 
 
 | 243 | foreach (var descriptor in descriptors) | 
 
 
 
 
 | 244 | { | 
 
 
 
 
 | 245 | WriteDescriptor(writer, descriptor); | 
 
 
 
 
 | 246 | } | 
 
 
 
 
 | 247 |  | 
 
 
 
 
 | 248 | writer.Position = header.NameTableOffset; | 
 
 
 
 
 | 249 |  | 
 
 
 
 
 | 250 | foreach (var entry in descriptors) | 
 
 
 
 
 | 251 | { | 
 
 
 
 
 | 252 | if (entry.Name != null) | 
 
 
 
 
 | 253 | writer.Write(entry.Name, entry.Name.Length + 1); | 
 
 
 
 
 | 254 | } | 
 
 
 
 
 | 255 |  | 
 
 
 
 
 | 256 | writer.Position = header.DataTableOffset; | 
 
 
 
 
 | 257 |  | 
 
 
 
 
 | 258 | foreach (var descriptor in descriptors.Where(d => d.Data != null)) | 
 
 
 
 
 | 259 | { | 
 
 
 
 
 | 260 | writer.Align32(); | 
 
 
 
 
 | 261 | writer.WriteInstanceId(descriptor.Index); | 
 
 
 
 
 | 262 | writer.Write(0); | 
 
 
 
 
 | 263 | writer.Write(descriptor.Data); | 
 
 
 
 
 | 264 | } | 
 
 
 
 
 | 265 |  | 
 
 
 
 
 | 266 | if (hasRawParts) | 
 
 
 
 
 | 267 | { | 
 
 
 
 
 | 268 | writer.Position = header.RawTableOffset; | 
 
 
 
 
 | 269 | rawStream.WriteTo(stream); | 
 
 
 
 
 | 270 | } | 
 
 
 
 
 | 271 | } | 
 
 
 
 
 | 272 | } | 
 
 
 
 
 | 273 |  | 
 
 
 
 
 | 274 | private void WriteDescriptor(BinaryWriter writer, ImporterFileDescriptor descriptor) | 
 
 
 
 
 | 275 | { | 
 
 
 
 
 | 276 | var flags = InstanceDescriptorFlags.None; | 
 
 
 
 
 | 277 |  | 
 
 
 
 
 | 278 | if (descriptor.Name == null) | 
 
 
 
 
 | 279 | flags |= InstanceDescriptorFlags.Private; | 
 
 
 
 
 | 280 |  | 
 
 
 
 
 | 281 | if (descriptor.Data == null) | 
 
 
 
 
 | 282 | flags |= InstanceDescriptorFlags.Placeholder; | 
 
 
 
 
 | 283 |  | 
 
 
 
 
 | 284 | if (descriptor.Name == null && descriptor.Data == null) | 
 
 
 
 
 | 285 | throw new InvalidOperationException("Link descriptors must have names"); | 
 
 
 
 
 | 286 |  | 
 
 
 
 
 | 287 | writer.Write((int)descriptor.Tag); | 
 
 
 
 
 | 288 | writer.Write(descriptor.DataOffset); | 
 
 
 
 
 | 289 | writer.Write(descriptor.NameOffset); | 
 
 
 
 
 | 290 | writer.Write(descriptor.DataSize); | 
 
 
 
 
 | 291 | writer.Write((int)flags); | 
 
 
 
 
 | 292 | } | 
 
 
 
 
 | 293 |  | 
 
 
 
 
 | 294 | private static string MakeInstanceName(TemplateTag tag, string name) | 
 
 
 
 
 | 295 | { | 
 
 
 
 
 | 296 | if (string.IsNullOrEmpty(name)) | 
 
 
 
 
 | 297 | return null; | 
 
 
 
 
 | 298 |  | 
 
 
 
 
 | 299 | var tagName = tag.ToString(); | 
 
 
 
 
 | 300 |  | 
 
 
 
 
 | 301 | if (!name.StartsWith(tagName, StringComparison.Ordinal)) | 
 
 
 
 
 | 302 | name = tagName + name; | 
 
 
 
 
 | 303 |  | 
 
 
 
 
 | 304 | return name; | 
 
 
 
 
 | 305 | } | 
 
 
 
 
 | 306 | } | 
 
 
 
 
 | 307 | } |