diff --git a/DOOMExtract/DOOMExtract.csproj b/DOOMExtract/DOOMExtract.csproj index 0c72fc9..8f31546 100644 --- a/DOOMExtract/DOOMExtract.csproj +++ b/DOOMExtract/DOOMExtract.csproj @@ -45,10 +45,12 @@ + - + + diff --git a/DOOMExtract/DOOMResourceEntry.cs b/DOOMExtract/DOOMResourceEntry.cs new file mode 100644 index 0000000..64b1135 --- /dev/null +++ b/DOOMExtract/DOOMResourceEntry.cs @@ -0,0 +1,115 @@ +using ICSharpCode.SharpZipLib.Zip.Compression.Streams; +using System; +using System.IO; + +namespace DOOMExtract +{ + public class DOOMResourceEntry + { + private DOOMResourceIndex _index; + + public int ID; + public string FileType; // type? + public string FileName2; // ?? + public string FileName3; // full path + + public long Offset; + public int Size; + public int CompressedSize; + public long Zero; + public byte PatchFileNumber; + + public bool IsCompressed { get { return Size != CompressedSize; } } + public DOOMResourceEntry(DOOMResourceIndex index) + { + _index = index; + } + + public override string ToString() + { + return GetFullName(); + } + + public string GetFullName() + { + if (!String.IsNullOrEmpty(FileName3)) + return FileName3.Replace("/", "\\"); // convert to windows path + + if (!String.IsNullOrEmpty(FileName2)) + return FileName2.Replace("/", "\\"); // convert to windows path + + return FileType.Replace("/", "\\"); // convert to windows path + + } + public void Read(EndianIO io) + { + io.BigEndian = true; + ID = io.Reader.ReadInt32(); + + io.BigEndian = false; + // fname1 + int size = io.Reader.ReadInt32(); + FileType = io.Reader.ReadAsciiString(size); + // fname2 + size = io.Reader.ReadInt32(); + FileName2 = io.Reader.ReadAsciiString(size); + // fname3 + size = io.Reader.ReadInt32(); + FileName3 = io.Reader.ReadAsciiString(size); + + io.BigEndian = true; + + Offset = io.Reader.ReadInt64(); + Size = io.Reader.ReadInt32(); + CompressedSize = io.Reader.ReadInt32(); + if (_index.Header_Version <= 4) + Zero = io.Reader.ReadInt64(); + else + Zero = io.Reader.ReadInt32(); // Zero field is 4 bytes instead of 8 in version 5+ + + PatchFileNumber = io.Reader.ReadByte(); + } + + public void Write(EndianIO io) + { + io.BigEndian = true; + io.Writer.Write(ID); + + io.BigEndian = false; + io.Writer.Write(FileType.Length); + io.Writer.WriteAsciiString(FileType, FileType.Length); + io.Writer.Write(FileName2.Length); + io.Writer.WriteAsciiString(FileName2, FileName2.Length); + io.Writer.Write(FileName3.Length); + io.Writer.WriteAsciiString(FileName3, FileName3.Length); + + io.BigEndian = true; + + io.Writer.Write(Offset); + io.Writer.Write(Size); + io.Writer.Write(CompressedSize); + if (_index.Header_Version <= 4) + io.Writer.Write(Zero); + else + io.Writer.Write((int)Zero); // Zero field is 4 bytes instead of 8 in version 5+ + io.Writer.Write(PatchFileNumber); + } + + public Stream GetDataStream(bool decompress) + { + if (Size == 0 && CompressedSize == 0) + return null; + + var io = _index.GetResourceIO(PatchFileNumber); + if (io == null) + return null; + + io.Stream.Position = Offset; + Stream dataStream = io.Stream; + if (IsCompressed && decompress) + dataStream = new InflaterInputStream(io.Stream, new ICSharpCode.SharpZipLib.Zip.Compression.Inflater(true), 4096); + + return dataStream; + } + } +} diff --git a/DOOMExtract/ResourceIndex.cs b/DOOMExtract/DOOMResourceIndex.cs similarity index 62% rename from DOOMExtract/ResourceIndex.cs rename to DOOMExtract/DOOMResourceIndex.cs index f41428a..bb3eaef 100644 --- a/DOOMExtract/ResourceIndex.cs +++ b/DOOMExtract/DOOMResourceIndex.cs @@ -1,155 +1,90 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.IO; -using ICSharpCode.SharpZipLib.Core; +using System.Linq; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; namespace DOOMExtract { - public class DOOMResourceEntry + public class DOOMResourceIndex { - private DOOMResourceIndex _index; - - public int ID; - public string FileType; // type? - public string FileName2; // ?? - public string FileName3; // full path - - public long Offset; - public int Size; - public int CompressedSize; - public long Zero; - public byte PatchFileIndex; + EndianIO indexIO; + Dictionary resourceIOs; - public DOOMResourceEntry(DOOMResourceIndex index) - { - _index = index; - } + public byte PatchFileNumber = 0; // highest PatchFileNumber found in the entries of this index - public override string ToString() + public string IndexFilePath; + public string BaseIndexFilePath { - return GetFullName(); + get + { + var sepString = "resources_"; + var indexPath = IndexFilePath; + var numSepIdx = indexPath.IndexOf(sepString); + if (numSepIdx < 0) + return indexPath; // IndexFilePath is the base index? + + var basePath = indexPath.Substring(0, numSepIdx + sepString.Length - 1); + return basePath + ".index"; + } } - public string GetFullName() - { - if (!String.IsNullOrEmpty(FileName3)) - return FileName3.Replace("/", "\\"); // convert to windows path - - if (!String.IsNullOrEmpty(FileName2)) - return FileName2.Replace("/", "\\"); // convert to windows path + public byte Header_Version; + public int Header_IndexSize; + public int Header_NumEntries; - return FileType.Replace("/", "\\"); // convert to windows path + public List Entries; - } - public void Read(EndianIO io) + public DOOMResourceIndex(string indexFilePath) { - io.BigEndian = true; - ID = io.Reader.ReadInt32(); - - io.BigEndian = false; - // fname1 - int size = io.Reader.ReadInt32(); - FileType = io.Reader.ReadAsciiString(size); - // fname2 - size = io.Reader.ReadInt32(); - FileName2 = io.Reader.ReadAsciiString(size); - // fname3 - size = io.Reader.ReadInt32(); - FileName3 = io.Reader.ReadAsciiString(size); - - io.BigEndian = true; - - Offset = io.Reader.ReadInt64(); - Size = io.Reader.ReadInt32(); - CompressedSize = io.Reader.ReadInt32(); - if (_index.Header_Version <= 4) - Zero = io.Reader.ReadInt64(); - else - Zero = io.Reader.ReadInt32(); // Zero field is 4 bytes instead of 8 in version 5+ - - PatchFileIndex = io.Reader.ReadByte(); + IndexFilePath = indexFilePath; } - public void Write(EndianIO io) + public string ResourceFilePath(int patchFileNumber) { - io.BigEndian = true; - io.Writer.Write(ID); - - io.BigEndian = false; - io.Writer.Write(FileType.Length); - io.Writer.WriteAsciiString(FileType, FileType.Length); - io.Writer.Write(FileName2.Length); - io.Writer.WriteAsciiString(FileName2, FileName2.Length); - io.Writer.Write(FileName3.Length); - io.Writer.WriteAsciiString(FileName3, FileName3.Length); - - io.BigEndian = true; - - io.Writer.Write(Offset); - io.Writer.Write(Size); - io.Writer.Write(CompressedSize); - if (_index.Header_Version <= 4) - io.Writer.Write(Zero); - else - io.Writer.Write((int)Zero); // Zero field is 4 bytes instead of 8 in version 5+ - io.Writer.Write(PatchFileIndex); - } - } - public class DOOMResourceIndex - { - EndianIO indexIO; - EndianIO resourceIO; - - public string IndexFilePath; - public string ResourceFilePath; + var path = Path.Combine(Path.GetDirectoryName(BaseIndexFilePath), Path.GetFileNameWithoutExtension(BaseIndexFilePath)); + if (patchFileNumber == 0) + return path + ".resources"; + if (patchFileNumber == 1) + return path + ".patch"; - public byte Header_Version; - public int Header_IndexSize; - public int Header_NumEntries; + return $"{path}_{patchFileNumber:D3}.patch"; + } - public List Entries; - public static long StreamCopy(Stream destStream, Stream sourceStream, int bufferSize, long length) + public EndianIO GetResourceIO(int patchFileNumber) { - long read = 0; - while (read < length) + var resPath = ResourceFilePath(patchFileNumber); + if (!resourceIOs.ContainsKey(resPath)) { - int toRead = bufferSize; - if (toRead > length - read) - toRead = (int)(length - read); - - var buf = new byte[toRead]; - int buf_read = sourceStream.Read(buf, 0, toRead); - destStream.Write(buf, 0, buf_read); - read += buf_read; + if (!File.Exists(resPath)) + return null; + + var io = new EndianIO(resPath, FileMode.Open); + io.Stream.Position = 0; + var magic = io.Reader.ReadUInt32(); + if ((magic & 0xFFFFFF00) != 0x52455300) + { + io.Close(); + return null; + } + + resourceIOs.Add(resPath, io); } - return read; - } - public DOOMResourceIndex(string indexFilePath) - { - IndexFilePath = indexFilePath; + return resourceIOs[resPath]; } public long CopyEntryDataToStream(DOOMResourceEntry entry, Stream destStream, bool decompress = true) { - if (entry.Size == 0 && entry.CompressedSize == 0) + var srcStream = entry.GetDataStream(decompress); + if (srcStream == null) return 0; - - resourceIO.Stream.Position = entry.Offset; - - Stream sourceStream = resourceIO.Stream; + long copyLen = entry.CompressedSize; - if (entry.Size != entry.CompressedSize && decompress) - { - sourceStream = new InflaterInputStream(resourceIO.Stream, new ICSharpCode.SharpZipLib.Zip.Compression.Inflater(true), 4096); + if (entry.IsCompressed && decompress) copyLen = entry.Size; - } - return StreamCopy(destStream, sourceStream, 40960, copyLen); + return Utility.StreamCopy(destStream, srcStream, 40960, copyLen); } /*public static byte[] CompressData(byte[] data, ZLibNet.CompressionLevel level = ZLibNet.CompressionLevel.Level9) @@ -198,6 +133,7 @@ private void addFilesFromFolder(string folder, string baseFolder, EndianIO destR var filePath = Path.GetFullPath(file).Substring(Path.GetFullPath(baseFolder).Length).Replace("\\", "/"); var fileEntry = new DOOMResourceEntry(this); + fileEntry.PatchFileNumber = PatchFileNumber; fileEntry.FileType = "file"; if(filePath.Contains(";")) // fileType is specified { @@ -208,15 +144,21 @@ private void addFilesFromFolder(string folder, string baseFolder, EndianIO destR fileEntry.FileName2 = filePath; fileEntry.FileName3 = filePath; - if (destResources.Stream.Length % 0x10 != 0) + bool needToPad = destResources.Stream.Length % 0x10 != 0; + if (PatchFileNumber > 0 && destResources.Stream.Length == 4) + needToPad = false; // for some reason patch files start at 0x4 instead of 0x10 + + if (needToPad) { int extra = 0x10 - ((int)destResources.Stream.Length % 0x10); destResources.Stream.SetLength(destResources.Stream.Length + extra); } + + fileEntry.Offset = destResources.Stream.Length; + byte[] fileData = File.ReadAllBytes(file); fileEntry.Size = fileEntry.CompressedSize = fileData.Length; - fileEntry.Offset = destResources.Stream.Length; fileEntry.ID = Entries.Count; // TODO: find out wtf the ID is needed for? destResources.Stream.Position = fileEntry.Offset; destResources.Writer.Write(fileData); @@ -245,12 +187,31 @@ public void Rebuild(string destResourceFile, string replaceFromFolder = "", bool var destResources = new EndianIO(destResourceFile, FileMode.CreateNew); byte[] header = { Header_Version, 0x53, 0x45, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + if(PatchFileNumber > 0) + header = new byte[]{ Header_Version, 0x53, 0x45, 0x52 }; // patch files start at 0x4 instead + destResources.Writer.Write(header); var addedFiles = new List(); foreach (var file in Entries) { - if (destResources.Stream.Length % 0x10 != 0) + var replacePath = (String.IsNullOrEmpty(replaceFromFolder) ? String.Empty : Path.Combine(replaceFromFolder, file.GetFullName())); + if (File.Exists(replacePath + ";" + file.FileType)) + replacePath += ";" + file.FileType; + + bool isReplacing = !string.IsNullOrEmpty(replaceFromFolder) && File.Exists(replacePath); + + if (file.PatchFileNumber != PatchFileNumber && !isReplacing) + continue; // file is located in a different patch resource and we aren't replacing it, so skip it + + bool needToPad = destResources.Stream.Length % 0x10 != 0; + if (PatchFileNumber > 0 && destResources.Stream.Length == 4) + needToPad = false; // for some reason patch files start at 0x4 instead of 0x10 + + if (file.IsCompressed && PatchFileNumber > 0 && !isReplacing) // compressed files not padded in patch files? + needToPad = false; + + if (needToPad) { int extra = 0x10 - ((int)destResources.Stream.Length % 0x10); destResources.Stream.SetLength(destResources.Stream.Length + extra); @@ -262,21 +223,21 @@ public void Rebuild(string destResourceFile, string replaceFromFolder = "", bool continue; } - var replacePath = (String.IsNullOrEmpty(replaceFromFolder) ? String.Empty : Path.Combine(replaceFromFolder, file.GetFullName())); - if (File.Exists(replacePath + ";" + file.FileType)) - replacePath += ";" + file.FileType; + var offset = destResources.Stream.Length; + destResources.Stream.Position = offset; - file.Offset = destResources.Stream.Length; - destResources.Stream.Position = file.Offset; - - if (!string.IsNullOrEmpty(replaceFromFolder) && File.Exists(replacePath)) + if (isReplacing) { + file.PatchFileNumber = PatchFileNumber; + addedFiles.Add(replacePath); using (var fs = File.OpenRead(replacePath)) - file.CompressedSize = file.Size = (int)StreamCopy(destResources.Stream, fs, 40960, fs.Length); + file.CompressedSize = file.Size = (int)Utility.StreamCopy(destResources.Stream, fs, 40960, fs.Length); } else file.CompressedSize = (int)CopyEntryDataToStream(file, destResources.Stream, !keepCompressed); + + file.Offset = offset; } // now add any files that weren't replaced @@ -294,10 +255,13 @@ public void Close() indexIO.Close(); indexIO = null; } - if(resourceIO != null) + if(resourceIOs != null) { - resourceIO.Close(); - resourceIO = null; + foreach (var kvp in resourceIOs) + kvp.Value.Close(); + + resourceIOs.Clear(); + resourceIOs = null; } } @@ -323,33 +287,31 @@ public void Save() public bool Load() { - if (!File.Exists(IndexFilePath) || Path.GetExtension(IndexFilePath) != ".index") + var indexExt = Path.GetExtension(IndexFilePath); + if (!File.Exists(IndexFilePath) || (indexExt != ".index" && indexExt != ".pindex")) return false; // not an index file - ResourceFilePath = Path.Combine(Path.GetDirectoryName(IndexFilePath), Path.GetFileNameWithoutExtension(IndexFilePath)) + ".resources"; - if (!File.Exists(ResourceFilePath)) - return false; + if (!File.Exists(ResourceFilePath(0))) + return false; // base resource data file not found! + + resourceIOs = new Dictionary(); indexIO = new EndianIO(IndexFilePath, FileMode.Open); - resourceIO = new EndianIO(ResourceFilePath, FileMode.Open); indexIO.Stream.Position = 0; var magic = indexIO.Reader.ReadInt32(); if ((magic & 0xFFFFFF00) != 0x52455300) { - indexIO.Close(); - resourceIO.Close(); + Close(); return false; // not a RES file. } Header_Version = (byte)(magic & 0xFF); Header_IndexSize = indexIO.Reader.ReadInt32(); - resourceIO.Stream.Position = 0; - magic = resourceIO.Reader.ReadInt32(); - if((magic & 0xFFFFFF00) != 0x52455300) + // init the base resource data file + if(GetResourceIO(0) == null) { - indexIO.Close(); - resourceIO.Close(); + Close(); return false; } @@ -363,6 +325,8 @@ public bool Load() var entry = new DOOMResourceEntry(this); entry.Read(indexIO); Entries.Add(entry); + if (entry.PatchFileNumber > PatchFileNumber) + PatchFileNumber = entry.PatchFileNumber; // highest PatchFileNumber must be our patch file index } return true; diff --git a/DOOMExtract/Program.cs b/DOOMExtract/Program.cs index 6f8fac3..5ecfc84 100644 --- a/DOOMExtract/Program.cs +++ b/DOOMExtract/Program.cs @@ -13,23 +13,31 @@ static void PrintUsage() { Console.WriteLine("Usage:"); Console.WriteLine("Extraction: DOOMExtract.exe [pathToIndexFile] "); - Console.WriteLine("If destFolder isn't specified a folder will be created next to the index file."); - Console.WriteLine("Files with fileType != \"file\" will have the fileType appended to the filename."); - Console.WriteLine("eg. allowoverlays.decl;renderParm for fileType \"renderParm\""); + Console.WriteLine(" If destFolder isn't specified a folder will be created next to the index/pindex file."); + Console.WriteLine(" Files with fileType != \"file\" will have the fileType appended to the filename."); + Console.WriteLine(" eg. allowoverlays.decl;renderParm for fileType \"renderParm\""); Console.WriteLine(); Console.WriteLine("Repacking: DOOMExtract.exe [pathToIndexFile] --repack [repackFolder]"); - Console.WriteLine("Will repack the resources with the files in the repack folder."); - Console.WriteLine("Note that files that don't already exist in the resources will be added."); - Console.WriteLine("To set a new files fileType append the fileType to its filename."); - Console.WriteLine("eg. allowoverlays.decl;renderParm for fileType \"renderParm\""); + Console.WriteLine(" Will repack the resources with the files in the repack folder."); + Console.WriteLine(" Files that don't already exist in the resources will be added."); + Console.WriteLine(" To set a new files fileType append the fileType to its filename."); + Console.WriteLine(" eg. allowoverlays.decl;renderParm for fileType \"renderParm\""); + Console.WriteLine(" Note that you should only rebuild the latest patch index file, as patches rely on the data in earlier files!"); + Console.WriteLine(); + Console.WriteLine("Patch creation: DOOMExtract.exe [pathToLatestPatchIndex] --createPatch [patchContentsFolder]"); + Console.WriteLine(" Allows you to create your own patch files."); + Console.WriteLine(" Works like repacking above, but the resulting patch files will only contain the files you've added/changed."); + Console.WriteLine(" Make sure to point it to the highest-numbered .pindex file!"); + Console.WriteLine(" Once completed a new .patch/.pindex file pair should be created."); Console.WriteLine(); Console.WriteLine("Deleting files: DOOMExtract.exe [pathToIndexFile] --delete [file1] ..."); - Console.WriteLine("Will delete files from the resources package. Full filepaths should be specified."); - Console.WriteLine("If a file isn't found in the package a warning will be given."); + Console.WriteLine(" Will delete files from the resources package. Full filepaths should be specified."); + Console.WriteLine(" If a file isn't found in the package a warning will be given."); + Console.WriteLine(" This should only be used on the latest patch file, as modifying earlier patch files may break later ones."); } static void Main(string[] args) { - Console.WriteLine("DOOMExtract 1.5.1 - by infogram"); + Console.WriteLine("DOOMExtract 1.6 - by infogram"); Console.WriteLine(); if (args.Length <= 0) { @@ -43,6 +51,12 @@ static void Main(string[] args) bool isRepacking = false; bool isDeleting = false; + bool isCreatingPatch = false; + bool quietMode = false; + + foreach (var arg in args) + if (arg == "--quiet") + quietMode = true; if (args.Length >= 2) { @@ -50,9 +64,11 @@ static void Main(string[] args) { isDeleting = true; } - else if(args[1] == "--repack") // repacking + else if(args[1] == "--repack" || args[1] == "--createPatch") // repacking { isRepacking = true; + isCreatingPatch = args[1] == "--createPatch"; + if (args.Length <= 2) // missing the repack folder arg { PrintUsage(); @@ -62,7 +78,7 @@ static void Main(string[] args) destFolder = Path.GetFullPath(args[2]); // use destFolder as the folder where repack files are if(!Directory.Exists(destFolder)) { - Console.WriteLine("Repack folder \"" + destFolder + "\" doesn't exist!"); + Console.WriteLine((isCreatingPatch ? "Patch" : "Repack") + $" folder \"{destFolder}\" doesn't exist!"); return; } } @@ -70,7 +86,7 @@ static void Main(string[] args) destFolder = Path.GetFullPath(args[1]); } - Console.WriteLine("Loading " + indexFilePath + "..."); + Console.WriteLine($"Loading {indexFilePath}..."); var index = new DOOMResourceIndex(indexFilePath); if(!index.Load()) { @@ -78,18 +94,72 @@ static void Main(string[] args) return; } - Console.WriteLine("Index loaded, Header_NumEntries = " + index.Header_NumEntries.ToString()); + Console.WriteLine($"Index loaded ({index.Entries.Count} files)" + (!quietMode ? ", data file contents:" : "")); + + if (!quietMode) + { + var pfis = new Dictionary(); + + foreach (var entry in index.Entries) + { + if (!pfis.ContainsKey(entry.PatchFileNumber)) + pfis.Add(entry.PatchFileNumber, 0); + pfis[entry.PatchFileNumber]++; + } + + var pfiKeys = pfis.Keys.ToList(); + pfiKeys.Sort(); + + int total = 0; + foreach (var key in pfiKeys) + { + var resName = Path.GetFileName(index.ResourceFilePath(key)); + Console.WriteLine($" {resName}: {pfis[key]} files"); + total += pfis[key]; + } + + Console.WriteLine(); + } + + if (isCreatingPatch) + { + // clone old index and increment the patch file number + + byte pfi = (byte)(index.PatchFileNumber + 1); + var destPath = Path.ChangeExtension(index.ResourceFilePath(pfi), ".pindex"); + index.Close(); + + if (File.Exists(destPath)) + File.Delete(destPath); // !!!! + + File.Copy(indexFilePath, destPath); + indexFilePath = destPath; + + index = new DOOMResourceIndex(destPath); + if(!index.Load()) + { + Console.WriteLine("Copied patch file failed to load? (this should never happen!)"); + return; + } + index.PatchFileNumber = pfi; + } if(isRepacking) { - // REPACK MODE!!! + // REPACK (and patch creation) MODE!!! - Console.WriteLine("Repacking/rebuilding resources file from folder " + destFolder + "..."); - index.Rebuild(index.ResourceFilePath + "_tmp", destFolder, true); + var resFile = index.ResourceFilePath(index.PatchFileNumber); + + Console.WriteLine((isCreatingPatch ? "Creating" : "Repacking") + $" {Path.GetFileName(indexFilePath)} from folder {destFolder}..."); + + index.Rebuild(resFile + "_tmp", destFolder, true); index.Close(); - File.Delete(index.ResourceFilePath); - File.Move(index.ResourceFilePath + "_tmp", index.ResourceFilePath); - Console.WriteLine("Repack complete!"); + + if(File.Exists(resFile)) + File.Delete(resFile); + + File.Move(resFile + "_tmp", resFile); + Console.WriteLine(isCreatingPatch ? "Patch file created!" : "Repack complete!"); return; } @@ -118,12 +188,12 @@ static void Main(string[] args) } if (delIdx == -1) - Console.WriteLine("Failed to find file " + args[i] + " in package."); + Console.WriteLine($"Failed to find file {args[i]} in package."); else { index.Entries.RemoveAt(delIdx); deleted++; - Console.WriteLine("Deleted " + args[i] + "!"); + Console.WriteLine($"Deleted {args[i]}!"); } } @@ -131,12 +201,12 @@ static void Main(string[] args) if (deleted > 0) { Console.WriteLine("Repacking/rebuilding resources file..."); - index.Rebuild(index.ResourceFilePath + "_tmp", String.Empty, true); + index.Rebuild(index.ResourceFilePath(index.PatchFileNumber) + "_tmp", String.Empty, true); index.Close(); - File.Delete(index.ResourceFilePath); - File.Move(index.ResourceFilePath + "_tmp", index.ResourceFilePath); + File.Delete(index.ResourceFilePath(index.PatchFileNumber)); + File.Move(index.ResourceFilePath(index.PatchFileNumber) + "_tmp", index.ResourceFilePath(index.PatchFileNumber)); } - Console.WriteLine("Deleted " + deleted.ToString() + " files from resources."); + Console.WriteLine($"Deleted {deleted} files from resources."); return; } @@ -145,14 +215,17 @@ static void Main(string[] args) if (!Directory.Exists(destFolder)) Directory.CreateDirectory(destFolder); + Console.WriteLine("Extracting contents to:"); + Console.WriteLine("\t" + destFolder); + int numExtracted = 0; - var warned = new List(); foreach(var entry in index.Entries) { if(entry.Size == 0 && entry.CompressedSize == 0) // blank entry? continue; - - Console.WriteLine("Extracting " + entry.GetFullName() + " (type: " + entry.FileType + " size: " + entry.Size.ToString() + " compressed: " + entry.CompressedSize.ToString()); + + Console.WriteLine($"Extracting {entry.GetFullName()}..."); + Console.WriteLine($"\ttype: {entry.FileType}, size: {entry.Size} ({entry.CompressedSize} bytes compressed), source file: {Path.GetFileName(index.ResourceFilePath(entry.PatchFileNumber))}"); var destFilePath = Path.Combine(destFolder, entry.GetFullName()); if (entry.FileType != "file") @@ -160,37 +233,18 @@ static void Main(string[] args) var destFileFolder = Path.GetDirectoryName(destFilePath); - /*var data = index.GetEntryData(entry); - if(data.Length <= 0) - { - Console.WriteLine("Decompression failed!"); - continue; - } - - if (data.Length != entry.Size) - { - Console.WriteLine("WARNING: Decompression resulted in " + data.Length + " bytes, but we expected " + entry.Size + " bytes!"); - warned.Add(entry); - } - - if (!Directory.Exists(destFileFolder)) - Directory.CreateDirectory(destFileFolder); - - File.WriteAllBytes(destFilePath, data);*/ - if (!Directory.Exists(destFileFolder)) Directory.CreateDirectory(destFileFolder); using (FileStream fs = File.OpenWrite(destFilePath)) index.CopyEntryDataToStream(entry, fs); - + Console.WriteLine("----------------------------------------------------"); + numExtracted++; } Console.WriteLine("Extraction complete! Extracted " + numExtracted.ToString() + " files."); - if (warned.Count > 0) - Console.WriteLine("... with " + warned.Count + " warnings!"); } } } diff --git a/DOOMExtract/Properties/AssemblyInfo.cs b/DOOMExtract/Properties/AssemblyInfo.cs index be086d9..23a5e5c 100644 --- a/DOOMExtract/Properties/AssemblyInfo.cs +++ b/DOOMExtract/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.1.0")] -[assembly: AssemblyFileVersion("1.5.1.0")] +[assembly: AssemblyVersion("1.6.0.0")] +[assembly: AssemblyFileVersion("1.6.0.0")] diff --git a/DOOMExtract/Utility.cs b/DOOMExtract/Utility.cs new file mode 100644 index 0000000..3e8315e --- /dev/null +++ b/DOOMExtract/Utility.cs @@ -0,0 +1,27 @@ +using System; +using System.IO; + +namespace DOOMExtract +{ + public static class Utility + { + public static long StreamCopy(Stream destStream, Stream sourceStream, int bufferSize, long length) + { + long read = 0; + while (read < length) + { + int toRead = bufferSize; + if (toRead > length - read) + toRead = (int)(length - read); + + var buf = new byte[toRead]; + int buf_read = sourceStream.Read(buf, 0, toRead); + destStream.Write(buf, 0, buf_read); + read += buf_read; + if (buf_read == 0) + break; // no more to be read.. + } + return read; + } + } +}