source: OniSplit/InstanceFile.cs@ 1178

Last change on this file since 1178 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: 8.7 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.IO;
4using Oni.Metadata;
5
6namespace Oni
7{
8 internal sealed class InstanceFile
9 {
10 private readonly InstanceFileManager fileManager;
11 private readonly string filePath;
12 private InstanceFileHeader header;
13 private Dictionary<int, int> rawParts;
14 private Dictionary<int, int> sepParts;
15 private string rawFilePath;
16 private string sepFilePath;
17 private List<InstanceDescriptor> descriptors;
18 private IList<InstanceDescriptor> readOnlyDescriptors;
19
20 private InstanceFile(InstanceFileManager fileManager, string filePath)
21 {
22 this.fileManager = fileManager;
23 this.filePath = filePath;
24 }
25
26 public static InstanceFile Read(InstanceFileManager fileManager, string filePath)
27 {
28 var file = new InstanceFile(fileManager, filePath);
29
30 using (var reader = new BinaryReader(filePath))
31 {
32 var header = InstanceFileHeader.Read(reader);
33 var descriptors = new List<InstanceDescriptor>(header.InstanceCount);
34
35 file.header = header;
36 file.descriptors = descriptors;
37
38 for (int i = 0; i < file.header.InstanceCount; i++)
39 descriptors.Add(InstanceDescriptor.Read(file, reader, i));
40
41 var names = ReadNames(header, reader);
42
43 for (int i = 0; i < file.header.InstanceCount; i++)
44 descriptors[i].ReadName(names);
45 }
46
47 //
48 // Force AGDB instances to have a name so they get exported into separate .oni files
49 // instead of being exported inside an AKEV.oni file. This code assumes that there
50 // is only 1 AGDB/AKEV file per .dat file (or none at all).
51 //
52
53 foreach (var descriptor in file.descriptors)
54 {
55 if (descriptor.Template.Tag != TemplateTag.AGDB)
56 continue;
57
58 var akevDescriptors = file.GetNamedDescriptors(TemplateTag.AKEV);
59
60 if (akevDescriptors.Count == 1)
61 {
62 string agdbName = "AGDB" + akevDescriptors[0].Name;
63 descriptor.SetName(agdbName);
64 break;
65 }
66 }
67
68 return file;
69 }
70
71 private static Dictionary<int, string> ReadNames(InstanceFileHeader header, BinaryReader reader)
72 {
73 reader.Position = header.NameTableOffset;
74
75 var nameTable = reader.ReadBytes(header.NameTableSize);
76 int nameOffset = 0;
77
78 var names = new Dictionary<int, string>(header.NameCount);
79 var buffer = new char[64];
80
81 while (nameOffset < nameTable.Length)
82 {
83 int i = 0;
84
85 while (true)
86 {
87 byte c = nameTable[nameOffset + i];
88
89 if (c == 0)
90 break;
91
92 buffer[i++] = (char)c;
93 }
94
95 names.Add(nameOffset, new string(buffer, 0, i));
96 nameOffset += i + 1;
97 }
98
99 return names;
100 }
101
102 public InstanceFileManager FileManager
103 {
104 get { return fileManager; }
105 }
106
107 public string FilePath
108 {
109 get { return filePath; }
110 }
111
112 public InstanceFileHeader Header
113 {
114 get { return header; }
115 }
116
117 public List<InstanceDescriptor> GetReferencedDescriptors(InstanceDescriptor descriptor)
118 {
119 var result = new List<InstanceDescriptor>();
120
121 result.Add(descriptor);
122
123 var stack = new Stack<InstanceDescriptor>();
124 var seen = new bool[descriptors.Count];
125
126 stack.Push(descriptor);
127 seen[descriptor.Index] = true;
128
129 using (var reader = new BinaryReader(filePath))
130 {
131 var linkVisitor = new LinkVisitor(reader);
132
133 while (stack.Count > 0)
134 {
135 descriptor = stack.Pop();
136 reader.Position = descriptor.DataOffset;
137 linkVisitor.Links.Clear();
138
139 descriptor.Template.Type.Accept(linkVisitor);
140
141 foreach (int id in linkVisitor.Links)
142 {
143 if (!seen[id >> 8])
144 {
145 var referencedDescriptor = GetDescriptor(id);
146
147 if (!referencedDescriptor.IsPlaceholder && !referencedDescriptor.HasName)
148 stack.Push(referencedDescriptor);
149
150 result.Add(referencedDescriptor);
151 seen[referencedDescriptor.Index] = true;
152 }
153 }
154 }
155 }
156
157 return result;
158 }
159
160 public int GetRawPartSize(int offset)
161 {
162 EnsureRawAndSepParts();
163 return rawParts[offset];
164 }
165
166 public int GetSepPartSize(int offset)
167 {
168 EnsureRawAndSepParts();
169 return sepParts[offset];
170 }
171
172 public string RawFilePath
173 {
174 get
175 {
176 if (rawFilePath == null)
177 {
178 if (header.Version == InstanceFileHeader.Version31)
179 rawFilePath = Path.ChangeExtension(filePath, ".raw");
180 else
181 rawFilePath = filePath;
182 }
183
184 return rawFilePath;
185 }
186 }
187
188 public string SepFilePath
189 {
190 get
191 {
192 if (sepFilePath == null)
193 sepFilePath = Path.ChangeExtension(filePath, ".sep");
194
195 return sepFilePath;
196 }
197 }
198
199 public BinaryReader GetRawReader(int offset)
200 {
201 return GetBinaryReader(offset, RawFilePath);
202 }
203
204 public BinaryReader GetSepReader(int offset)
205 {
206 return GetBinaryReader(offset, SepFilePath);
207 }
208
209 private void EnsureRawAndSepParts()
210 {
211 if (rawParts != null)
212 return;
213
214 rawParts = new Dictionary<int, int>();
215 sepParts = new Dictionary<int, int>();
216
217 InstanceMetadata.GetRawAndSepParts(this, rawParts, sepParts);
218 }
219
220 private BinaryReader GetBinaryReader(int offset, string binaryFilePath)
221 {
222 var reader = new BinaryReader(binaryFilePath);
223 reader.Position = offset + header.RawTableOffset;
224 return reader;
225 }
226
227 public InstanceDescriptor ResolveLink(int id)
228 {
229 var descriptor = GetDescriptor(id);
230
231 if (descriptor == null || !descriptor.IsPlaceholder)
232 return descriptor;
233
234 if (!descriptor.HasName)
235 return null;
236
237 var file = fileManager.FindInstance(descriptor.FullName, this);
238
239 if (file == null || file == this)
240 {
241 Console.Error.WriteLine("Cannot find instance '{0}'", descriptor.FullName);
242 return null;
243 }
244
245 if (file.header.Version == InstanceFileHeader.Version32)
246 return file.GetDescriptor(1);
247
248 foreach (var target in file.descriptors)
249 {
250 if (target.HasName && target.FullName == descriptor.FullName)
251 return target;
252 }
253
254 return null;
255 }
256
257 public InstanceDescriptor GetDescriptor(int id)
258 {
259 if (id == 0)
260 return null;
261
262 return descriptors[id >> 8];
263 }
264
265 public IList<InstanceDescriptor> Descriptors
266 {
267 get
268 {
269 if (readOnlyDescriptors == null)
270 readOnlyDescriptors = descriptors.AsReadOnly();
271
272 return readOnlyDescriptors;
273 }
274 }
275
276 public List<InstanceDescriptor> GetNamedDescriptors()
277 {
278 return descriptors.FindAll(x => x.HasName && !x.IsPlaceholder);
279 }
280
281 public List<InstanceDescriptor> GetNamedDescriptors(TemplateTag tag)
282 {
283 return descriptors.FindAll(x => x.Template.Tag == tag && x.HasName && !x.IsPlaceholder);
284 }
285
286 public List<InstanceDescriptor> GetPlaceholders()
287 {
288 return descriptors.FindAll(x => x.HasName && x.IsPlaceholder);
289 }
290 }
291}
Note: See TracBrowser for help on using the repository browser.