-
Notifications
You must be signed in to change notification settings - Fork 0
/
MPF.cs
407 lines (350 loc) · 10.7 KB
/
MPF.cs
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
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
using Capricorn.IO;
namespace Capricorn.Drawing;
/// <summary>
/// MPF Image Class
/// </summary>
public class MPFImage
{
private int expectedFrames;
private int width;
private int height;
private MPFFrame[] frames;
private uint expectedDataSize;
private ushort unknown;
private uint ffUnknown;
private bool isNewFormat;
private bool isFFFormat;
private byte[] extendedHeader;
/// <summary>
/// Gets or sets the frame at the specified index.
/// </summary>
/// <param name="index">Zero-based index of the frame.</param>
/// <returns>MPF frame data.</returns>
public MPFFrame this[int index]
{
get { return frames[index]; }
set { frames[index] = value; }
}
/// <summary>
/// Gets the extended header bytes.
/// </summary>
public byte[] ExtendedHeader
{
get { return extendedHeader; }
}
/// <summary>
/// Gets whether the MPF is flagged using the FF header.
/// </summary>
public bool IsFFFormat
{
get { return isFFFormat; }
}
/// <summary>
/// Gets whether the MPF is using the newer header format.
/// </summary>
public bool IsNewFormat
{
get { return isNewFormat; }
}
/// <summary>
/// Gets the value of the FF Header unknown value.
/// </summary>
public uint FFUnknown
{
get { return ffUnknown; }
}
/// <summary>
/// Gets the unknown header value.
/// </summary>
public ushort Unknown
{
get { return unknown; }
}
/// <summary>
/// Gets the expected size of the data, excluding the header, in bytes.
/// </summary>
public uint ExpectedDataSize
{
get { return expectedDataSize; }
}
/// <summary>
/// Gets the frames of the image.
/// </summary>
public MPFFrame[] Frames
{
get { return frames; }
}
/// <summary>
/// Gets the height of the frames' containing canvas, in pixels.
/// </summary>
public int Height
{
get { return height; }
}
/// <summary>
/// Gets the width of the frames' containing canvas, in pixels.
/// </summary>
public int Width
{
get { return width; }
}
/// <summary>
/// Gets the numbers of frames expected in the image.
/// </summary>
public int ExpectedFrames
{
get { return expectedFrames; }
}
/// <summary>
/// Loads a MPF file from disk.
/// </summary>
/// <param name="file">MPF image file to load.</param>
/// <returns>MPF image object.</returns>
public static MPFImage FromFile(string file)
{
// Create File Stream
FileStream stream = new FileStream(file,
FileMode.Open, FileAccess.Read, FileShare.Read);
// Load MPF from File Stream
return LoadMPF(stream);
}
/// <summary>
/// Loads a MPF image file from raw data bytes.
/// </summary>
/// <param name="data">Raw data to use.</param>
/// <returns>MPF image.</returns>
public static MPFImage FromRawData(byte[] data)
{
// Create Memory Stream
MemoryStream stream = new MemoryStream(data);
// Load HPF from Memory Stream
return LoadMPF(stream);
}
/// <summary>
/// Loads a MPF image file from an archive (case-sensitive).
/// </summary>
/// <param name="file">MPF image to load.</param>
/// <param name="archive">Data archive to load from.</param>
/// <returns>MPF image.</returns>
public static MPFImage FromArchive(string file, DATArchive archive)
{
// Check if File Exists
if (!archive.Contains(file))
return null;
// Extract and Create File
return FromRawData(archive.ExtractFile(file));
}
/// <summary>
/// Loads a MPF image file from an archive.
/// </summary>
/// <param name="file">MPF image to load.</param>
/// <param name="ignoreCase">Ignore case (noncase-sensitive).</param>
/// <param name="archive">Data archive to load from.</param>
/// <returns>MPF image.</returns>
public static MPFImage FromArchive(string file, bool ignoreCase, DATArchive archive)
{
// Check if File Exists
if (!archive.Contains(file, ignoreCase))
return null;
// Extract and Create File
return FromRawData(archive.ExtractFile(file, ignoreCase));
}
/// <summary>
/// Internal function that loads a MPF image from a given data stream.
/// </summary>
/// <param name="stream">Data stream.</param>
/// <returns>MPF image.</returns>
private static MPFImage LoadMPF(Stream stream)
{
// Create Binary Reader
stream.Seek(0, SeekOrigin.Begin);
BinaryReader reader = new BinaryReader(stream);
// Create MPF File
MPFImage mpf = new MPFImage();
#region Check for FF Header Type
if (reader.ReadUInt32() == 0xFFFFFFFF)
{
mpf.isFFFormat = true;
mpf.ffUnknown = reader.ReadUInt32();
}
else
reader.BaseStream.Seek(-4, SeekOrigin.Current);
#endregion
// Get Frame Count
mpf.expectedFrames = reader.ReadByte();
// Allocate Frames
mpf.frames = new MPFFrame[mpf.expectedFrames];
#region Read MPF Header
mpf.width = reader.ReadUInt16();
mpf.height = reader.ReadUInt16();
mpf.expectedDataSize = reader.ReadUInt32();
mpf.unknown = reader.ReadUInt16();
// If 0xFFFF, then New Format
mpf.isNewFormat = (reader.ReadUInt16() == 0xFFFF);
// Get Extended Header Info
if (mpf.isNewFormat)
mpf.extendedHeader = reader.ReadBytes(10);
else
mpf.extendedHeader = reader.ReadBytes(4);
#endregion
// Get Data Start Address
long dataStart = reader.BaseStream.Length - mpf.expectedDataSize;
#region Read Frame Headers
for (int i = 0; i < mpf.expectedFrames; i++)
{
#region Read Variables
int left = reader.ReadUInt16();
int top = reader.ReadUInt16();
int right = reader.ReadUInt16();
int bottom = reader.ReadUInt16();
int width = right - left;
int height = bottom - top;
int xOffset = reader.ReadUInt16();
int yOffset = reader.ReadUInt16();
// Reverse Bytes
xOffset = ((xOffset % 256) << 8) + (xOffset / 256);
yOffset = ((yOffset % 256) << 8) + (yOffset / 256);
long startAddress = reader.ReadUInt32();
#endregion
byte[] frameData = null;
if (height > 0 && width > 0)
{
// Save Current Position
long position = reader.BaseStream.Position;
#region Read Raw Frame Data
reader.BaseStream.Seek(dataStart + startAddress, SeekOrigin.Begin);
frameData = reader.ReadBytes(height * width);
reader.BaseStream.Seek(position, SeekOrigin.Begin);
#endregion
}
// Create Frame
mpf.frames[i] = new MPFFrame(left, top, width, height, xOffset, yOffset, frameData);
} reader.Close();
#endregion
// Return MPF
return mpf;
}
/// <summary>
/// Gets the string representation of the object.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return "{Frames = " + expectedFrames.ToString() + ", Width = " +
width.ToString() + ", Height = " +
height.ToString() + ", Unknown = 0x" +
unknown.ToString("X").PadLeft(8, '0') + "}";
}
}
/// <summary>
/// MPF Image Frame Class
/// </summary>
public class MPFFrame
{
private int left;
private int top;
private int width;
private int height;
private byte[] rawData;
private int xOffset;
private int yOffset;
/// <summary>
/// Gets whether the frame is renderable or not.
/// </summary>
public bool IsValid
{
get
{
if (rawData == null || rawData.Length < 1)
return false;
else if (width < 1 || height < 1)
return false;
else if (width * height != rawData.Length)
return false;
else
return true;
}
}
/// <summary>
/// Gets the Y positional offset of the frame.
/// </summary>
public int OffsetY
{
get { return yOffset; }
}
/// <summary>
/// Gets the X positional offset of the frame.
/// </summary>
public int OffsetX
{
get { return xOffset; }
}
/// <summary>
/// Gets the raw byte data for the frame's image.
/// </summary>
public byte[] RawData
{
get { return rawData; }
}
/// <summary>
/// Gets the height of the frame image, in pixels.
/// </summary>
public int Height
{
get { return height; }
}
/// <summary>
/// Gets the width of the frame image, in pixels.
/// </summary>
public int Width
{
get { return width; }
}
/// <summary>
/// Gets or sets the Y position of the frame's location within the canvas.
/// </summary>
public int Top
{
get { return top; }
set { top = value; }
}
/// <summary>
/// Gets or sets the X position of the frame's location within the canvas.
/// </summary>
public int Left
{
get { return left; }
set { left = value; }
}
/// <summary>
/// Creates a frame with the specified values.
/// </summary>
/// <param name="left">X position of the frame within the canvas.</param>
/// <param name="top">Y position of the frame within the canvas.</param>
/// <param name="width">Width of the frame, in pixels.</param>
/// <param name="height">Height of the frame, in pixels.</param>
/// <param name="unknown">Unknown value.</param>
/// <param name="rawData">Raw data bytes of the frame.</param>
public MPFFrame(int left, int top, int width, int height, int xOffset, int yOffset, byte[] rawData)
{
this.left = left;
this.top = top;
this.width = width;
this.height = height;
this.xOffset = xOffset;
this.yOffset = yOffset;
this.rawData = rawData;
}
/// <summary>
/// Gets the string representation of the object.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return "{X = " + left.ToString() + ", Y = " +
top.ToString() + ", Width = " +
width.ToString() + ", Height = " +
height.ToString() + ", Offset = (" +
xOffset.ToString() + ", " + yOffset.ToString() + ")}";
}
}