private void ReadUOPFiles(string pattern) { _uopReader = new BinaryReader(_map); _uopReader.BaseStream.Seek(0, SeekOrigin.Begin); if (_uopReader.ReadInt32() != 0x50594D) { throw new ArgumentException("Bad UOP file."); } _uopReader.ReadInt64(); // version + signature long nextBlock = _uopReader.ReadInt64(); _uopReader.ReadInt32(); // block capacity int count = _uopReader.ReadInt32(); UOPFiles = new UopFile[count]; var hashes = new Dictionary <ulong, int>(); for (int i = 0; i < count; i++) { string file = $"build/{pattern}/{i:D8}.dat"; ulong hash = UopUtils.HashFileName(file); if (!hashes.ContainsKey(hash)) { hashes.Add(hash, i); } } _uopReader.BaseStream.Seek(nextBlock, SeekOrigin.Begin); do { int filesCount = _uopReader.ReadInt32(); nextBlock = _uopReader.ReadInt64(); for (int i = 0; i < filesCount; i++) { long offset = _uopReader.ReadInt64(); int headerLength = _uopReader.ReadInt32(); int compressedLength = _uopReader.ReadInt32(); int decompressedLength = _uopReader.ReadInt32(); ulong hash = _uopReader.ReadUInt64(); _uopReader.ReadUInt32(); // Adler32 short flag = _uopReader.ReadInt16(); int length = flag == 1 ? compressedLength : decompressedLength; if (offset == 0) { continue; } if (hashes.TryGetValue(hash, out int idx)) { if (idx < 0 || idx > UOPFiles.Length) { throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!"); } UOPFiles[idx] = new UopFile(offset + headerLength, length); } else { throw new ArgumentException($"File with hash 0x{hash:X8} was not found in hashes dictionary! EA Mythic changed UOP format!"); } } }while (_uopReader.BaseStream.Seek(nextBlock, SeekOrigin.Begin) != 0); }
public FileIndex(string idxFile, string mulFile, string uopFile, int length, int file, string uopEntryExtension, int idxLength, bool hasExtra) { Index = new Entry3D[length]; string idxPath = null; string uopPath = null; _mulPath = null; if (Files.MulPath == null) { Files.LoadMulPath(); } if (Files.MulPath.Count > 0) { idxPath = Files.MulPath[idxFile.ToLower()]; _mulPath = Files.MulPath[mulFile.ToLower()]; if (!string.IsNullOrEmpty(uopFile) && Files.MulPath.ContainsKey(uopFile.ToLower())) { uopPath = Files.MulPath[uopFile.ToLower()]; } if (string.IsNullOrEmpty(idxPath)) { idxPath = null; } else { if (string.IsNullOrEmpty(Path.GetDirectoryName(idxPath))) { idxPath = Path.Combine(Files.RootDir, idxPath); } if (!File.Exists(idxPath)) { idxPath = null; } } if (string.IsNullOrEmpty(_mulPath)) { _mulPath = null; } else { if (string.IsNullOrEmpty(Path.GetDirectoryName(_mulPath))) { _mulPath = Path.Combine(Files.RootDir, _mulPath); } if (!File.Exists(_mulPath)) { _mulPath = null; } } if (!string.IsNullOrEmpty(uopPath)) { if (string.IsNullOrEmpty(Path.GetDirectoryName(uopPath))) { uopPath = Path.Combine(Files.RootDir, uopPath); } if (File.Exists(uopPath)) { _mulPath = uopPath; } } } /* UOP files support code, written by Wyatt (c) www.ruosi.org * idxLength variable was added for compatibility with legacy code for art (see art.cs) * At the moment the only UOP file having entries with extra field is gumpartlegacy.uop, * and it's two DWORDs in the beginning of the entry. * It's possible that UOP can include some entries with unknown hash: not really unknown for me, but * not useful for reading legacy entries. That's why i removed unknown hash exception throwing from this code */ if (_mulPath?.EndsWith(".uop") == true) { _stream = new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); var fi = new FileInfo(_mulPath); string uopPattern = fi.Name.Replace(fi.Extension, "").ToLowerInvariant(); using (var br = new BinaryReader(_stream)) { br.BaseStream.Seek(0, SeekOrigin.Begin); if (br.ReadInt32() != 0x50594D) { throw new ArgumentException("Bad UOP file."); } br.ReadInt64(); // version + signature long nextBlock = br.ReadInt64(); br.ReadInt32(); // block capacity _ = br.ReadInt32(); // TODO: check if we need value from here if (idxLength > 0) { IdxLength = idxLength * 12; } var hashes = new Dictionary <ulong, int>(); for (int i = 0; i < length; i++) { string entryName = $"build/{uopPattern}/{i:D8}{uopEntryExtension}"; ulong hash = UopUtils.HashFileName(entryName); if (!hashes.ContainsKey(hash)) { hashes.Add(hash, i); } } br.BaseStream.Seek(nextBlock, SeekOrigin.Begin); // There are no invalid entries in .uop so we have to initialize all entries // as invalid and then fill the valid ones for (var i = 0; i < Index.Length; i++) { Index[i].Lookup = -1; Index[i].Length = -1; Index[i].Extra = -1; } do { int filesCount = br.ReadInt32(); nextBlock = br.ReadInt64(); for (int i = 0; i < filesCount; i++) { long offset = br.ReadInt64(); int headerLength = br.ReadInt32(); int compressedLength = br.ReadInt32(); int decompressedLength = br.ReadInt32(); ulong hash = br.ReadUInt64(); br.ReadUInt32(); // Adler32 short flag = br.ReadInt16(); int entryLength = flag == 1 ? compressedLength : decompressedLength; if (offset == 0) { continue; } if (!hashes.TryGetValue(hash, out int idx)) { continue; } if (idx < 0 || idx > Index.Length) { throw new IndexOutOfRangeException("hashes dictionary and files collection have different count of entries!"); } Index[idx].Lookup = (int)(offset + headerLength); Index[idx].Length = entryLength; if (!hasExtra) { continue; } long curPos = br.BaseStream.Position; br.BaseStream.Seek(offset + headerLength, SeekOrigin.Begin); byte[] extra = br.ReadBytes(8); var extra1 = (short)((extra[3] << 24) | (extra[2] << 16) | (extra[1] << 8) | extra[0]); var extra2 = (short)((extra[7] << 24) | (extra[6] << 16) | (extra[5] << 8) | extra[4]); Index[idx].Lookup += 8; // changed from int b = extra1 << 16 | extra2; // int cast removes compiler warning Index[idx].Extra = extra1 << 16 | (int)extra2; br.BaseStream.Seek(curPos, SeekOrigin.Begin); } }while (br.BaseStream.Seek(nextBlock, SeekOrigin.Begin) != 0); } } else if ((idxPath != null) && (_mulPath != null)) { using (var index = new FileStream(idxPath, FileMode.Open, FileAccess.Read, FileShare.Read)) { _stream = new FileStream(_mulPath, FileMode.Open, FileAccess.Read, FileShare.Read); var count = (int)(index.Length / 12); IdxLength = index.Length; GCHandle gc = GCHandle.Alloc(Index, GCHandleType.Pinned); var buffer = new byte[index.Length]; index.Read(buffer, 0, (int)index.Length); Marshal.Copy(buffer, 0, gc.AddrOfPinnedObject(), (int)Math.Min(IdxLength, length * 12)); gc.Free(); for (int i = count; i < length; ++i) { Index[i].Lookup = -1; Index[i].Length = -1; Index[i].Extra = -1; } } } else { _stream = null; return; } if (file <= -1) { return; } Entry5D[] verdataPatches = Verdata.Patches; foreach (var patch in verdataPatches) { if (patch.File != file || patch.Index < 0 || patch.Index >= length) { continue; } Index[patch.Index].Lookup = patch.Lookup; Index[patch.Index].Length = patch.Length | (1 << 31); Index[patch.Index].Extra = patch.Extra; } }