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 EndianIO GetResourceIO(int patchFileNumber) { var resPath = ResourceFilePath(patchFileNumber); if (!resourceIOs.ContainsKey(resPath)) { 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(resourceIOs[resPath]); }
/*public static byte[] CompressData(byte[] data, ZLibNet.CompressionLevel level = ZLibNet.CompressionLevel.Level9) * { * using (var dest = new MemoryStream()) * { * using (var source = new MemoryStream(data)) * { * using (var deflateStream = new ZLibNet.DeflateStream(dest, ZLibNet.CompressionMode.Compress, level, true)) * { * source.CopyTo(deflateStream); * * // DOOM's compressed resources all end with 00 00 FF FF * dest.SetLength(dest.Length + 4); * dest.Position = dest.Length - 2; * dest.WriteByte(0xFF); * dest.WriteByte(0xFF); * * /* DOOM's compressed resources all seem to have the first bit unset * tested by decompressing data and then recompressing using this Compress method, data is 1:1 except for the first bit (eg. our compressed data started with 0x7D, the games data would be 0x7C) * in one test, keeping the bit set made the games graphics screw up and trying to open multiplayer would crash * but unsetting this bit let the game work normally (using a slightly modified decl file also) * another test like this using data that was heavily modded still resulted in a glitched game, even with this bit unset * results inconclusive :( * /*dest.Position = 0; * byte b = (byte)ms.ReadByte(); * b &= byte.MaxValue ^ (1 << 0); * ms.Position = 0; * ms.WriteByte((byte)b); * * return dest.ToArray(); * } * } * } * }*/ private void addFilesFromFolder(string folder, string baseFolder, EndianIO destResources, ref List <string> addedFiles) { var dirs = Directory.GetDirectories(folder); var files = Directory.GetFiles(folder); foreach (var file in files) { if (addedFiles.Contains(Path.GetFullPath(file))) { continue; } if (folder == baseFolder && Path.GetFileName(file).ToLower() == "fileids.txt") { continue; // don't want to add fileIds.txt from base } 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 { var idx = filePath.IndexOf(";"); fileEntry.FileType = filePath.Substring(idx + 1); filePath = filePath.Substring(0, idx); } fileEntry.FileName2 = filePath; fileEntry.FileName3 = filePath; 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) { long numPadding = 0x10 - (destResources.Stream.Length % 0x10); destResources.Stream.SetLength(destResources.Stream.Length + numPadding); } fileEntry.Offset = destResources.Stream.Length; byte[] fileData = File.ReadAllBytes(file); fileEntry.Size = fileEntry.CompressedSize = fileData.Length; fileEntry.ID = Entries.Count; // TODO: find out wtf the ID is needed for? destResources.Stream.Position = fileEntry.Offset; destResources.Writer.Write(fileData); Entries.Add(fileEntry); addedFiles.Add(Path.GetFullPath(file)); } foreach (var dir in dirs) { addFilesFromFolder(dir, baseFolder, destResources, ref addedFiles); } }
public bool Load() { var indexExt = Path.GetExtension(IndexFilePath); if (!File.Exists(IndexFilePath) || (indexExt != ".index" && indexExt != ".pindex")) { return(false); // not an index file } if (!File.Exists(ResourceFilePath(0))) { return(false); // base resource data file not found! } resourceIOs = new Dictionary <string, EndianIO>(); indexIO = new EndianIO(IndexFilePath, FileMode.Open); indexIO.Stream.Position = 0; var magic = indexIO.Reader.ReadInt32(); if ((magic & 0xFFFFFF00) != 0x52455300) { Close(); return(false); // not a RES file. } Header_Version = (byte)(magic & 0xFF); Header_IndexSize = indexIO.Reader.ReadInt32(); // init the base resource data file if (GetResourceIO(0) == null) { Close(); return(false); } indexIO.Stream.Position = 0x20; indexIO.BigEndian = true; Header_NumEntries = indexIO.Reader.ReadInt32(); Entries = new List <DOOMResourceEntry>(); for (var i = 0; i < Header_NumEntries; i++) { 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); }
public void Close() { if (indexIO != null) { indexIO.Close(); indexIO = null; } if (resourceIOs != null) { foreach (var kvp in resourceIOs) { kvp.Value.Close(); } resourceIOs.Clear(); resourceIOs = null; } }
internal EndianWriter(EndianIO _io) : base(_io.Stream) { this._io = _io; }
internal EndianReader(EndianIO io) : base(io.Stream) { _io = io; }
public void Rebuild(string destResourceFile, string replaceFromFolder = "", bool keepCompressed = false) { if (File.Exists(destResourceFile)) { File.Delete(destResourceFile); } if (!String.IsNullOrEmpty(replaceFromFolder)) { replaceFromFolder = replaceFromFolder.Replace("/", "\\"); if (!replaceFromFolder.EndsWith("\\")) { replaceFromFolder += "\\"; } } 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 <string>(); foreach (var file in Entries) { 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 && !isReplacing && PatchFileNumber > 0) // compressed files not padded in patch files? { needToPad = false; } if (needToPad) { long numPadding = 0x10 - (destResources.Stream.Length % 0x10); destResources.Stream.SetLength(destResources.Stream.Length + numPadding); } if (file.Size <= 0 && file.CompressedSize <= 0) { // patch indexes have offsets for 0-byte files set to 0, but in normal indexes it's the current resource file length file.Offset = PatchFileNumber > 0 ? 0 : destResources.Stream.Length; continue; } var offset = destResources.Stream.Length; destResources.Stream.Position = offset; if (isReplacing) { file.PatchFileNumber = PatchFileNumber; addedFiles.Add(replacePath); using (var fs = File.OpenRead(replacePath)) 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 if (!String.IsNullOrEmpty(replaceFromFolder)) { addFilesFromFolder(replaceFromFolder, replaceFromFolder, destResources, ref addedFiles); } // read the fileIds.txt file if it exists, and set the IDs var idFile = Path.Combine(replaceFromFolder, "fileIds.txt"); if (File.Exists(idFile)) { var lines = File.ReadAllLines(idFile); foreach (var line in lines) { if (String.IsNullOrEmpty(line.Trim())) { continue; } var sepIdx = line.LastIndexOf('='); if (sepIdx < 0) { continue; // todo: warn user? } var fileName = line.Substring(0, sepIdx).Trim(); var fileId = line.Substring(sepIdx + 1).Trim(); int id = -1; if (!int.TryParse(fileId, out id)) { Console.WriteLine($"Warning: file {fileName} defined in fileIds.txt but has invalid id!"); continue; } var file = Entries.Find(s => s.GetFullName() == fileName); if (file == null) { file = Entries.Find(s => s.GetFullName().Replace("\\", "/") == fileName); } if (file != null) { file.ID = id; } else { Console.WriteLine($"Warning: file {fileName} defined in fileIds.txt but doesn't exist?"); } } } destResources.Close(); Save(); }