View difference between Paste ID: 8p6ggipk and GS3YhHYV
SHOW: | | - or go back to the newest paste.
1
using System;
2
using System.Collections.Generic;
3
using System.Drawing;
4
using System.Drawing.Imaging;
5
using System.IO;
6
using System.IO.Compression;
7
using System.Linq;
8
using System.Text;
9
10
namespace Mygod.LittleInferno.Resource.Unpacker
11
{
12
    public static class Program
13
    {
14
        private static void Main(string[] args)
15
        {
16
            if (args == null || args.Length == 0)
17
            {
18
                DoubleWriter.WriteLine("Drag files onto the program to start unpacking!");
19
                Console.ReadKey();
20
                return;
21
            }
22
            DoubleWriter.Output = new StreamWriter(File.Create("output.txt")) {AutoFlush = true};
23
            const string directory = "EXTRACTED";
24
            const string temp = directory + "_TEMP";
25
            Directory.CreateDirectory(directory);
26
            Directory.CreateDirectory(temp);
27
            Package lastPackage = null;
28
            foreach (var arg in args.Where(File.Exists))
29
            {
30
                DoubleWriter.WriteLine("INFO: Unpacking file: {0}", arg);
31
                ErrorCount = WarningCount = 0;
32
                InformationCount = 1;
33
                try
34
                {
35
                    (lastPackage = new Package(arg)).ExtractAll(temp);
36
                }
37
                catch (Exception exc)
38
                {
39
                    DoubleWriter.WriteLine("ERROR: {0}", exc.Message);
40
                    ErrorCount++;
41
                }
42
                DoubleWriter.WriteLine("Unpacking done. {0} error(s), {1} warning(s), {2} info(s).", ErrorCount, WarningCount, InformationCount);
43
                DoubleWriter.WriteLine();
44
            }
45
            if (lastPackage != null)
46
            {
47-
                lastPackage.MoveFiles(directory, temp);
47+
                DoubleWriter.WriteLine("INFO: Moving files into the correct path.");
48-
                TryDecodeFiles(directory);
48+
49
                InformationCount = 1;
50
                var decodePosition = lastPackage.MoveFiles(directory, temp) ? directory : temp;
51
                DoubleWriter.WriteLine("Moving done. {0} error(s), {1} warning(s), {2} info(s).", ErrorCount, WarningCount, InformationCount);
52
                DoubleWriter.WriteLine();
53
                DoubleWriter.WriteLine("INFO: Trying to decode images.");
54
                ErrorCount = WarningCount = 0;
55
                InformationCount = 1;
56
                TryDecodeFiles(decodePosition);
57
                DoubleWriter.WriteLine("Decoding done. {0} error(s), {1} warning(s), {2} info(s).", ErrorCount, WarningCount, InformationCount);
58
                DoubleWriter.WriteLine();
59
            }
60
            DoubleWriter.Output.Close();
61
        }
62
63
        public static void TryDecodeFiles(string directoryPath)
64
        {
65
            var queue = new Queue<DirectoryInfo>();
66
            queue.Enqueue(new DirectoryInfo(directoryPath));
67
            while (queue.Count > 0)
68
            {
69
                var dir = queue.Dequeue();
70
                foreach (var info in dir.EnumerateFileSystemInfos())
71
                {
72
                    var dirInfo = info as DirectoryInfo;
73
                    if (dirInfo != null)
74
                    {
75-
        private static readonly string[] ImpossibleImageFormats = ".raw,.xml,.flac".Split(',');
75+
76
                        continue;
77
                    }
78
                    var fileInfo = info as FileInfo;
79
                    if (fileInfo == null) continue;
80
                    TryDecodeImage(fileInfo);
81
                }
82-
                string path = fileInfo.FullName, lower = path.ToLower();
82+
83-
                if (ImpossibleImageFormats.Any(lower.EndsWith)) return;
83+
84
85
        private static void TryDecodeImage(FileInfo fileInfo)
86
        {
87
            RawImage image = null;
88
            var path = fileInfo.FullName;
89
            try
90-
                DoubleWriter.WriteLine("ERROR: {0}", exc.Message);
90+
91-
                ErrorCount++;
91+
                if (path.ToLower().EndsWith(".raw")) return;
92
                using (var stream = fileInfo.OpenRead()) image = new RawImage(stream);
93
                fileInfo.MoveTo(path + ".raw");
94
                image.SaveToFile(path);
95
            }
96
            catch (Exception exc)
97
            {
98
                DoubleWriter.WriteLine("WARNING: {0} is not an image. ({1})", path, exc.Message);
99
                WarningCount++;
100
            }
101
            finally
102
            {
103
                if (image != null) image.Dispose();
104
            }
105
        }
106
107
        public static int ErrorCount, WarningCount, InformationCount;
108
    }
109
110
    public class Package : IDisposable
111
    {
112
        public Package(string filePath)
113
        {
114
            path = filePath;
115
            stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
116
            if (!stream.CanRead || !stream.CanSeek) throw new NotSupportedException("The file stream is terrible!");
117
            reader = new BinaryReader(stream);
118
            PackageVersion = reader.ReadInt32();
119
            if (PackageVersion != 1) throw new NotSupportedException("Unsupported package version: " + PackageVersion);
120
            var count = reader.ReadInt32();
121
            Records = new List<FileRecord>(count);
122
            for (var i = 0; i < count; i++) Records.Add(new FileRecord(reader));
123
        }
124
125
        private static string ReadString(BinaryReader reader)
126
        {
127
            var strBytes = new List<byte>();
128
            int b;
129
            while ((b = reader.ReadByte()) > 0) strBytes.Add((byte)b);
130
            return Encoding.UTF8.GetString(strBytes.ToArray());
131
        }
132
133
        public void ExtractAll(string directory)
134
        {
135
            foreach (var record in Records) Extract(record, Path.Combine(directory, record.ID.ToString()));
136
        }
137
        public void Extract(FileRecord record, string filePath)
138
        {
139
            int unpackedSize;
140
            byte[] data;
141
            lock (reader)
142
            {
143
                stream.Seek(record.Offset, SeekOrigin.Begin);
144
                var packedSize = reader.ReadInt32();
145
                unpackedSize = reader.ReadInt32();
146
                data = reader.ReadBytes(packedSize);
147
            }
148
            if (record.Flags == 0)
149
            {
150
                File.WriteAllBytes(filePath, data);
151
                DoubleWriter.WriteLine("INFO: Unpacking item #{0} done.", record.ID);
152
                Program.InformationCount++;
153
                return;
154
            }
155
            using (var fileStream = File.Create(filePath))
156
            using (var memoryStream = new MemoryStream(data))
157
            using (var deflateStream = new DeflateStream(memoryStream, CompressionMode.Decompress))
158
            {
159
                memoryStream.ReadByte();
160
                memoryStream.ReadByte();
161
                try
162
                {
163
                    deflateStream.CopyTo(fileStream);
164
                    if (fileStream.Length != unpackedSize)
165
                    {
166
                        DoubleWriter.WriteLine("WARNING: File size does not match. (item #{0})", record.ID);
167
                        Program.WarningCount++;
168
                    }
169
                    DoubleWriter.WriteLine("INFO: Unpacking item #{0} done.", record.ID);
170
                    Program.InformationCount++;
171-
        public void MoveFiles(string directory, string temp)
171+
172
                catch (InvalidDataException)
173
                {
174
                    DoubleWriter.WriteLine("ERROR: Unpacking item #{0} failed.", record.ID);
175
                    Program.ErrorCount++;
176
                }
177
            }
178
        }
179
        public bool MoveFiles(string directory, string temp)
180
        {
181
            var map = Path.Combine(temp, "3085189639");
182
            if (!File.Exists(map))
183
            {
184
                DoubleWriter.WriteLine("ERROR: Maps not found!");
185
                Program.ErrorCount++;
186
                return false;
187
            }
188
            using (var fileStream = new FileStream(map, FileMode.Open, FileAccess.Read, FileShare.Read))
189
            {
190
                if (!fileStream.CanRead || !fileStream.CanSeek) throw new NotSupportedException("The file stream is terrible!");
191
                using (var mapReader = new BinaryReader(fileStream))
192
                {
193
                    BinaryHeaderPointer maps = new BinaryHeaderPointer(mapReader), stringTableBytes = new BinaryHeaderPointer(mapReader);
194
                    fileStream.Seek(maps.Offset, SeekOrigin.Begin);
195
                    var dic = new Dictionary<uint, int>(maps.Count);
196
                    for (var i = 0; i < maps.Count; i++) dic.Add(mapReader.ReadUInt32(), mapReader.ReadInt32());
197
                    fileStream.Seek(stringTableBytes.Offset, SeekOrigin.Begin);
198
                    int numStrings = mapReader.ReadInt32(), numPointers = mapReader.ReadInt32();
199
                    var stringPointers = new List<StringPointer>(numStrings);
200
                    for (var i = 0; i < numStrings; i++) stringPointers.Add(new StringPointer(mapReader));
201
                    var pointerPointers = new List<LanguagePointer>(numPointers);
202
                    for (var i = 0; i < numPointers; i++) pointerPointers.Add(new LanguagePointer(mapReader));
203
                    var baseOffset = fileStream.Position;
204
                    // DoubleWriter.WriteLine(pointerPointers.Min(p => p.Offset));   // for debugging purpose
205
206
                    foreach (var file in new DirectoryInfo(temp).EnumerateFiles())
207
                    {
208
                        try
209
                        {
210
                            var id = uint.Parse(file.Name);
211
                            var stringPointer = stringPointers[dic[id]];
212
                            var pointer = default(LanguagePointer);
213
                            for (var i = 0; i < stringPointer.Count; i++)
214
                            {
215
                                pointer = pointerPointers[stringPointer.Index + i];
216
                                if (pointer.LanguageID == 25966) break; // ne
217
                            }
218
                            if (pointer.LanguageID != 25966) throw new Exception("Language ne not found!");
219
                            fileStream.Seek(baseOffset + pointer.Offset, SeekOrigin.Begin);
220
                            string realPath = ReadString(mapReader), absolutePath = Path.Combine(directory, realPath);
221
                            // ReSharper disable AssignNullToNotNullAttribute
222
                            Directory.CreateDirectory(Path.GetDirectoryName(absolutePath));
223
                            // ReSharper restore AssignNullToNotNullAttribute
224
                            try
225
                            {
226
                                file.MoveTo(absolutePath);
227
                            }
228
                            catch (IOException)
229
                            {
230
                                file.CopyTo(absolutePath);  // copy it at least
231
                                file.Delete();
232
                            }
233
                            DoubleWriter.WriteLine("INFO: File moved successfully: {0}", realPath);
234
                            Program.InformationCount++;
235
                        }
236
                        catch (Exception exc)
237
                        {
238
                            DoubleWriter.WriteLine("ERROR: ({0}) {1}", exc.GetType(), exc.Message);
239
                            Program.ErrorCount++;
240
                        }
241
                    }
242
                }
243
            }
244
            return true;
245
        }
246
247
        public void Dispose()
248
        {
249
            reader.Close();
250
            stream.Close();
251
        }
252
253
        private readonly FileStream stream;
254
        private readonly BinaryReader reader;
255
        private readonly string path;
256
        public int PackageVersion;
257
        public List<FileRecord> Records;
258
    }
259
260
    public struct FileRecord
261
    {
262
        public uint ID, Flags;
263
        public int Offset, Size;
264
265
        public FileRecord(BinaryReader reader)
266
        {
267
            ID = reader.ReadUInt32();
268
            Flags = reader.ReadUInt32();
269
            if (Flags > 1)
270
            {
271
                //throw new NotSupportedException("Unsupported file record flags: " + Flags);
272
                DoubleWriter.WriteLine("WARNING: Unsupported file record flags value {0}. (item #{1})", Flags, ID);
273
                Program.WarningCount++;
274
            }
275
            Offset = reader.ReadInt32();
276
            Size = reader.ReadInt32();
277
        }
278
    }
279
280
    public struct BinaryHeaderPointer
281
    {
282
        public int Count, Offset;
283
284
        public BinaryHeaderPointer(BinaryReader reader)
285
        {
286
            Count = reader.ReadInt32();
287
            Offset = reader.ReadInt32();
288
        }
289
    }
290
291
    public struct StringPointer
292
    {
293
        public int Index, Count;
294
295
        public StringPointer(BinaryReader reader)
296
        {
297
            Index = reader.ReadInt32();
298
            Count = reader.ReadInt32();
299
        }
300
    }
301
302
    public struct LanguagePointer
303
    {
304
        public uint LanguageID;
305
        public int Offset;
306
307
        public LanguagePointer(BinaryReader reader)
308
        {
309
            LanguageID = reader.ReadUInt32();
310
            Offset = reader.ReadInt32();
311
        }
312
    }
313
314
    public class RawImage : IDisposable
315
    {
316
        public RawImage(Stream stream)
317
        {
318
            using (var reader = new BinaryReader(stream))
319
            {
320
                Width = reader.ReadInt32();
321
                Height = reader.ReadInt32();
322
                if (Width * Height * 4 + 12 != stream.Length) throw new Exception("Length incorrect.");
323
                Bitmap = new Bitmap(Width, Height, PixelFormat.Format32bppArgb);
324
                Type = (ImageType) reader.ReadInt32();
325
                switch (Type)
326
                {
327
                    case ImageType.RGBA:
328
                        for (var y = 0; y < Height; y++) for (var x = 0; x < Width; x++)
329
                        {
330
                            byte b = reader.ReadByte(), g = reader.ReadByte(), r = reader.ReadByte(), a = reader.ReadByte();
331
                            Bitmap.SetPixel(x, y, Color.FromArgb(a, r, g, b));
332
                        }
333
                        break;
334
                    case ImageType.Greyscale:
335
                        for (var y = 0; y < Height; y++) for (var x = 0; x < Width; x++)
336
                        {
337
                            byte ca = reader.ReadByte(), cc = reader.ReadByte(), ac = reader.ReadByte(), aa = reader.ReadByte();
338
                            Bitmap.SetPixel(x, y, Color.FromArgb((byte) (ca * aa * ac / 16581375.0), cc, cc, cc));
339
                        }
340
                        break;
341
                }
342
            }
343
        }
344
345
        public int Width, Height;
346
        public ImageType Type;
347
        public Bitmap Bitmap;
348
349
        public void SaveToFile(string filePath, ImageFormat format = null)
350
        {
351
            if (format == null) format = ImageFormat.Png;
352
            var extension = '.' + format.ToString().ToLower();
353
            if (!filePath.EndsWith(extension)) filePath += extension;
354
            Bitmap.Save(filePath, format);
355
        }
356
357
        public void Dispose()
358
        {
359
            Bitmap.Dispose();
360
        }
361
    }
362
363
    public enum ImageType
364
    {
365
        RGBA = 1, Greyscale = 8
366
    }
367
368
    public static class DoubleWriter
369
    {
370
        public static StreamWriter Output;
371
372
        public static void WriteLine(string value, params object[] arg)
373
        {
374
            Console.WriteLine(value, arg);
375
            if (Output != null) Output.WriteLine(value, arg);
376
        }
377
378
        public static void WriteLine()
379
        {
380
            Console.WriteLine();
381
            if (Output != null) Output.WriteLine();
382
        }
383
    }
384
}