public void ReadIndexFile() { files = new ConcurrentDictionary <byte[], FileEntry>(new ByteArrayComparer()); indexFile = new IndexFile { Signature = indexFileReader.ReadFourCC(), Version = indexFileReader.ReadUInt32(), }; indexFileReader.Skip(512); indexFile.FileSize = indexFileReader.ReadUInt32(); indexFileReader.Skip(12); indexFile.FileSystemInfo = indexFileReader.ReadUInt32(); // Set to XDIA start. indexFileReader.BaseStream.Position = 0x240; indexFile.AIDX = new ArchiveIndex { Signature = indexFileReader.ReadFourCC(), Version = indexFileReader.ReadUInt32(), GameBuild = indexFileReader.ReadUInt32() }; indexFileReader.BaseStream.Position = indexFile.FileSystemInfo - 8; var aidxEntryCount = indexFileReader.ReadInt32() / 16; indexFileReader.Skip(4 + 16); indexFile.AIDX.Entries = new List <ArchiveIndexEntry>(aidxEntryCount - 1); folders = new Dictionary <uint, Tuple <SortedDictionary <uint, FolderEntry>, SortedDictionary <uint, FileEntry> > >(); for (var i = 0; i < aidxEntryCount - 1; i++) { var entryOffset = indexFileReader.ReadInt64(); var entrySize = indexFileReader.ReadInt64(); var nextEntryOffset = indexFileReader.BaseStream.Position; // Skip AIDX start. if (entryOffset <= 0x240 || entrySize == 0 || entryOffset == (aidxEntryCount * 16)) { continue; } indexFileReader.BaseStream.Position = entryOffset; var aidxEntry = new ArchiveIndexEntry { FolderEntryCount = indexFileReader.ReadUInt32(), FileEntryCount = indexFileReader.ReadUInt32() }; aidxEntry.Folders = new Dictionary <uint, FolderEntry>((int)aidxEntry.FolderEntryCount); aidxEntry.Files = new List <FileEntry>((int)aidxEntry.FileEntryCount); var lastStringLength = 0; var folderStringEnd = 0L; var fList = new SortedDictionary <uint, FolderEntry>(); for (var j = 0; j < aidxEntry.FolderEntryCount; j++) { var folderEntry = new FolderEntry(); folderEntry.LowestLevel = indexFileReader.ReadUInt32(); folderEntry.Level = indexFileReader.ReadUInt32(); var position = indexFileReader.BaseStream.Position; indexFileReader.BaseStream.Position = entryOffset + 8 + ((aidxEntry.FolderEntryCount * 8) + (aidxEntry.FileEntryCount * 56) + lastStringLength); folderEntry.Name = indexFileReader.ReadCString(); lastStringLength += folderEntry.Name.Length + 1; folderStringEnd = indexFileReader.BaseStream.Position; indexFileReader.BaseStream.Position = position; fList.Add(folderEntry.Level, folderEntry); aidxEntry.Folders.Add(folderEntry.Level, folderEntry); } var fList2 = new SortedDictionary <uint, FileEntry>(); for (var j = 0; j < aidxEntry.FileEntryCount; j++) { var fileEntry = new FileEntry(); fileEntry.Index = indexFileReader.ReadUInt32(); fileEntry.Option = (Constants.FileOptions)indexFileReader.ReadUInt32(); indexFileReader.ReadUInt32(); indexFileReader.ReadUInt32(); fileEntry.UncompressedSize = indexFileReader.ReadUInt32(); indexFileReader.ReadUInt32(); fileEntry.CompressedSize = indexFileReader.ReadUInt32(); indexFileReader.ReadUInt32(); fileEntry.Sha1 = indexFileReader.ReadBytes(20); indexFileReader.ReadUInt32(); var position = indexFileReader.BaseStream.Position; indexFileReader.BaseStream.Position = entryOffset + 8 + ((aidxEntry.FolderEntryCount * 8) + (aidxEntry.FileEntryCount * 56) + lastStringLength); fileEntry.Name = indexFileReader.ReadCString(); lastStringLength += fileEntry.Name.Length + 1; indexFileReader.BaseStream.Position = position; files.TryAdd(fileEntry.Sha1, fileEntry); fList2.Add(fileEntry.Index, fileEntry); } if (fList.Count > 0 || fList2.Count > 0) { folders.Add((uint)i, Tuple.Create(fList, fList2)); } indexFileReader.BaseStream.Position = nextEntryOffset; } }
public static void ExtractTmod(string file, string folder, bool createOverrideFolders, bool createYaml, Action <double> updateProgress) { var buffer = new byte[1048576]; var properties = new Dictionary <string, string>(); var archiveEntries = new List <ArchiveIndexEntry>(); ulong headerSize = 0; using (var stream = File.OpenRead(file)) { using (var reader = new MyBinaryReader(stream)) { headerSize = ReadProperties(properties, stream, reader); // Read archive index entries (remainder of header) while ((ulong)stream.Position < headerSize) { var entry = new ArchiveIndexEntry(); entry.file = reader.ReadString(); entry.archiveIndex = reader.Read7BitEncodedInt(); entry.byteOffset = reader.Read7BitEncodedInt(); entry.size = reader.Read7BitEncodedInt(); entry.hash = reader.Read7BitEncodedInt(); archiveEntries.Add(entry); } int offset = 0, byteRead = 0; double count = 0; if (stream.Position != (long)headerSize) { stream.Position = (long)headerSize; } using (InflaterInputStream decompressionStream = new InflaterInputStream(stream)) { foreach (var entry in archiveEntries.OrderBy(e => e.byteOffset)) { updateProgress(count / archiveEntries.Count * 100d); log.InfoFormat("Extracting {0}", entry.file); string extractPath = Path.Combine(folder, entry.file.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)); if (createOverrideFolders) { extractPath = Path.Combine(Path.GetDirectoryName(extractPath), TroveMod.OverrideFolder, Path.GetFileName(extractPath)); } SettingsDataProvider.ResolveFolder(Path.GetDirectoryName(extractPath)); // Advance data position to the next entry offset if needed while (offset < entry.byteOffset && byteRead != -1) { byteRead = decompressionStream.ReadByte(); offset++; } offset += SaveBytes(extractPath, stream, decompressionStream, Convert.ToInt64(headerSize) + Convert.ToInt64(entry.byteOffset), entry.size, buffer); } } } } try { if (createYaml) { string title = properties.ContainsKey(TitleValue) ? properties[TitleValue] : Path.GetFileNameWithoutExtension(file); string yamlPath = Path.Combine(folder, SettingsDataProvider.GetSafeFilename(title) + ".yaml"); log.InfoFormat("Generating YAML file: {0}", yamlPath); ModDetails details = new ModDetails() { Author = properties[AuthorValue], Title = properties[TitleValue], Notes = properties[NotesValue], PreviewPath = properties[PreviewPathValue], Files = archiveEntries.Select(e => e.file).ToList() }; if (properties.ContainsKey(TagsValue)) { string tags = properties[TagsValue]; if (!string.IsNullOrWhiteSpace(tags)) { details.Tags = tags.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList(); } } details.SaveYamlFile(yamlPath); } } catch (Exception ex) { log.Error("Error generating YAML file", ex); } log.InfoFormat("Completed extracting files from {0}", file); }