source: OniSplit/ImporterFile.cs@ 1194

Last change on this file since 1194 was 1114, checked in by iritscen, 5 years ago

Adding OniSplit source code (v0.9.99.0). Many thanks to Neo for all his work over the years.

File size: 9.5 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Text;
5
6namespace 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}
Note: See TracBrowser for help on using the repository browser.