-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathLfdFile.cs
More file actions
251 lines (235 loc) · 9.84 KB
/
Copy pathLfdFile.cs
File metadata and controls
251 lines (235 loc) · 9.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
* Idmr.LfdReader.dll, Library file to read and write LFD resource files
* Copyright (C) 2009-2026 Michael Gaisser (mjgaisser@gmail.com)
* Licensed under the MPL v2.0 or later
*
* Full notice in help/Idmr.LfdReader.chm
* Version: 3.0
*/
/* CHANGE LOG
* v3.0, 260821
* [ADD] Name
* v2.5.2, 260517
* [FIX] Resource modified flag reset during Write
* v2.4, 250202
* [ADD] Enabled Mtrx
* v2.0, 210309
* [UPD] cleanup
* [ADD] Cplx, Crft, Ship to assignResource
* v1.2, 160712
* [UPD] Always zeroes out name before writing
* [UPD] Only calls _encode on children if they report being modified
* v1.1, 141215
* [UPD] changed license to MPL
* v1.0
*/
using System;
using System.IO;
namespace Idmr.LfdReader
{
/// <summary>Object to manage LFD resource files.</summary>
public class LfdFile
{
Rmap _rmp = null;
static readonly string _cockpitRmapErrorMessage = "Cockpit LFDs do not contain RMAPs";
string _tempPath => FilePath + ".tmp";
LfdCategory _lfdCategory = LfdCategory.Normal;
/// <summary>Preset Lfd structures.</summary>
public enum LfdCategory : byte {
/// <summary>Typical LFD file, unlocked structure, no restrictions.</summary>
Normal,
/// <summary>Cockpit view LFDs, locked structure, does not contain an RMAP.</summary>
Cockpit,
/// <summary>Battle#.LFD files, locked structure.</summary>
Battle }
#region constructors
/// <summary>Populates with an existing *.lfd file.</summary>
/// <param name="stream">Opened *.lfd.</param>
public LfdFile(FileStream stream)
{
read(stream);
}
/// <summary>Populates with an existing *.lfd file.</summary>
/// <param name="filePath">Full path to the unopened *.lfd.</param>
public LfdFile(string filePath)
{
FileStream stream = File.OpenRead(filePath);
read(stream);
stream.Close();
}
/// <summary>Creates an empty file with the appropriate file structure.</summary>
/// <param name="category">Preset type.</param>
/// <remarks><see cref="Rmap"/> and <see cref="Resources"/> are initialized accordingly.</remarks>
public LfdFile(LfdCategory category)
{
Resources = new ResourceCollection(category);
if (category == LfdCategory.Battle) _rmp = new Rmap(category);
_lfdCategory = category;
}
#endregion constructors
#region public methods
/// <summary>Initializes <see cref="Rmap"/> using the contents of the LfdFile. If Rmap is already defined, it is updated.</summary>
/// <remarks>Processes individual <see cref="Resource.EncodeResource"/> functions, thus updating all <see cref="Resource.RawData"/> properties.</remarks>
public void CreateRmap()
{
if (_lfdCategory == LfdCategory.Cockpit) throw new ArgumentException(_cockpitRmapErrorMessage);
string name = null;
if (_rmp != null) name = _rmp.Name;
encodeResources();
if (_lfdCategory == LfdCategory.Battle) _rmp = new Rmap(_lfdCategory);
else _rmp = new Rmap(Resources.Count);
for (int i = 0; i < _rmp.NumberOfHeaders; i++)
{
_rmp.SubHeaders[i].Type = Resources[i].Type;
_rmp.SubHeaders[i].Name = Resources[i].Name;
_rmp.SubHeaders[i].Length = Resources[i].Length;
_rmp.SubHeaders[i].Offset = Resource.HeaderLength +
(i > 0 ? _rmp.SubHeaders[i - 1].Offset + _rmp.SubHeaders[i - 1].Length : 0);
}
if (name != null) _rmp.Name = name;
_rmp.EncodeResource();
}
/// <summary>Writes the file to disk.</summary>
/// <exception cref="Common.SaveFileException">An error occured, file remains unchanged.</exception>
public void Write()
{
System.Diagnostics.Debug.WriteLine("Encoding Lfd...");
if (_rmp != null) CreateRmap();
else encodeResources();
FileStream stream = null;
try
{
if (File.Exists(FilePath)) File.Copy(FilePath, _tempPath); // create backup
stream = File.OpenWrite(FilePath);
BinaryWriter bw = new BinaryWriter(stream);
System.Diagnostics.Debug.WriteLine("Writing...");
if (_rmp != null)
{
bw.Write((int)_rmp.Type);
bw.Write(_rmp.Name.ToCharArray());
stream.Position = Resource.LengthOffset;
bw.Write(_rmp.Length);
bw.Write(_rmp.RawData);
_rmp._isModified = false;
}
for(int i=0;i<Resources.Count;i++)
{
long pos = stream.Position;
bw.Write((int)Resources[i].Type);
bw.Write((long)0);
stream.Position = pos + Resource.NameOffset;
bw.Write(Resources[i].Name.ToCharArray());
stream.Position = pos + Resource.LengthOffset;
bw.Write(Resources[i].Length);
bw.Write(Resources[i].RawData);
Resources[i]._isModified = false;
}
stream.SetLength(stream.Position);
stream.Close();
File.Delete(_tempPath); // delete backup if it exists
System.Diagnostics.Debug.WriteLine("Completed");
}
catch (Exception x)
{
System.Diagnostics.Debug.WriteLine("Write failure");
stream?.Close();
if (File.Exists(_tempPath)) File.Copy(_tempPath, FilePath); // restore backup if it exists
File.Delete(_tempPath); // delete backup if it exists
throw new Common.SaveFileException(x);
}
}
#endregion public methods
#region public properties
/// <summary>Resources contained within the file.</summary>
/// <remarks>Does <u>not</u> contain <see cref="Rmap"/> if applicable.</remarks>
public ResourceCollection Resources { get; set; } = new ResourceCollection();
/// <summary>Gets the full path to the file.</summary>
public string FilePath { get; private set; } = "resource.lfd";
/// <summary>Gets the file name and extension of the file.</summary>
public string FileName => Path.GetFileName(FilePath);
/// <summary>Gets if <see cref="Rmap"/> is defined.</summary>
public bool HasRmap => (_rmp != null);
/// <summary>Gets the file name without extension.</summary>
public string Name => Path.GetFileNameWithoutExtension(FilePath);
/// <summary>Gets or sets the Rmap resource for the file.</summary>
/// <exception cref="ArgumentException">Cannot set if file is defined by the <see cref="LfdCategory.Cockpit"/> preset.</exception>
public Rmap Rmap
{
get => _rmp;
set
{
if (_lfdCategory == LfdCategory.Cockpit) throw new ArgumentException(_cockpitRmapErrorMessage);
_rmp = value;
}
}
#endregion public properties
#region private methods
/// <summary>Loops through Resources and calls the individual EncodeResource() functions.</summary>
void encodeResources() { for (int i = 0; i < Resources.Count; i++) if (Resources[i]._isModified) Resources[i].EncodeResource(); }
void read(FileStream stream)
{
FilePath = stream.Name;
if (Resource.GetType(stream, 0) == Resource.ResourceType.Rmap)
{
_rmp = new Rmap(stream);
Resources = new ResourceCollection(_rmp.NumberOfHeaders);
for (int i = 0; i < Resources.Count; i++)
assignResource(i, _rmp.SubHeaders[i].Type, stream, _rmp.SubHeaders[i].Offset);
if (Resources.Count == 2 && Resources[0].Name.StartsWith("battle") && Resources[1].Name.EndsWith("gal"))
_lfdCategory = LfdCategory.Battle;
}
else if (Resource.GetType(stream, 0) == Resource.ResourceType.Panl)
{
_lfdCategory = LfdCategory.Cockpit;
Resources = new ResourceCollection(3)
{
[0] = new Panl(stream, 0)
};
Resources[1] = new Mask(stream, Resource.HeaderLength + Resources[0].Length);
Resources[2] = new Pltt(stream, Resource.HeaderLength * 2 + Resources[0].Length + Resources[1].Length);
}
else
{
Resources = new ResourceCollection(1);
assignResource(0, Resource.GetType(stream, 0), stream, 0);
}
}
void assignResource(int index, Resource.ResourceType type, FileStream stream, long offset)
{
// commented out types redirect to Resource to read and capture _rawData
if (type == Resource.ResourceType.Anim) Resources[index] = new Anim(stream, offset);
//TODO: else if (type == Resource.ResourceType.Adlb) Resources[index] = new Adlb(stream, offset);
else if (type == Resource.ResourceType.Blas || type == Resource.ResourceType.Voic) Resources[index] = new Blas(stream, offset);
//TODO: else if (type == Resource.ResourceType.Bmap) Resources[index] = new Bmap(stream, offset);
//TODO: else if (type == Resource.ResourceType.Btmp) Resources[index] = new Btmp(stream, offset);
else if (type == Resource.ResourceType.Cplx) Resources[index] = new Cplx(stream, offset);
else if (type == Resource.ResourceType.Crft) Resources[index] = new Crft(stream, offset);
//TODO: else if (type == Resource.ResourceType.Cust) Resources[index] = new Cust(stream, offset);
else if (type == Resource.ResourceType.Delt) Resources[index] = new Delt(stream, offset);
else if (type == Resource.ResourceType.Film) Resources[index] = new Film(stream, offset);
else if (type == Resource.ResourceType.Font) Resources[index] = new Font(stream, offset);
//TODO: else if (type == Resource.ResourceType.Gmid) Resources[index] = new Gmid(stream, offset);
else if (type == Resource.ResourceType.Mask) Resources[index] = new Mask(stream, offset);
else if (type == Resource.ResourceType.Mtrx) Resources[index] = new Mtrx(stream, offset);
else if (type == Resource.ResourceType.Panl) Resources[index] = new Panl(stream, offset);
else if (type == Resource.ResourceType.Pltt) Resources[index] = new Pltt(stream, offset);
//TODO: else if (type == Resource.ResourceType.Rlnd) Resources[index] = new Rlnd(stream, offset);
// skip Rmap
else if (type == Resource.ResourceType.Ship) Resources[index] = new Ship(stream, offset);
else if (type == Resource.ResourceType.Text) Resources[index] = new Text(stream, offset);
else if (type == Resource.ResourceType.Xact) Resources[index] = new Xact(stream, offset);
else Resources[index] = new Resource(stream, offset);
}
#endregion private methods
/// <summary>Gets if any of the resources are known to have been modified.</summary>
public bool IsModified
{
get
{
for (int i = 0; i < Resources.Count; i++)
if (Resources[i]._isModified) return true;
return false;
}
}
}
}