public byte[] GetFileFromIndex(int index) { FileListTab file = Table20.FileList[index]; string name = file.Path.GetText(); DirectoryListTab dir = file.Directory; DirectoryOffsetTable dirOffset = dir.DirOffset; FileOffsetTab offsetInfo = file.FileOffset; bool isLink = false; if (file.IsLink) { isLink = true; while (file.IsLink) { file = file.FileOffset.File; } dir = file.Directory; dirOffset = dir.DirOffset; offsetInfo = file.FileOffset; } if (offsetInfo.Flag3) { dirOffset = offsetInfo.LinkedDirOffset; offsetInfo = offsetInfo.LinkedOffset; } if (isLink) { return(new byte[0]); } if (offsetInfo.Size == 0) { return(new byte[0]); } long offset = Header.Field10 + dirOffset.Offset + offsetInfo.Offset * 4; var data = new byte[offsetInfo.Size]; Stream.Position = offset; if (offsetInfo.SizeCompressed == 0 || offsetInfo.SizeCompressed == offsetInfo.Size) { Stream.Read(data, 0, offsetInfo.Size); } else { using (var compStream = new ZstandardStream(Stream, CompressionMode.Decompress, true)) { compStream.Read(data, 0, offsetInfo.Size); } } return(data); }
public Table20(BinaryReader reader) { Header = new Table20Header(reader); StreamRoot = new StreamRootTable[Header.Field34]; for (int i = 0; i < Header.Field34; i++) { StreamRoot[i] = new StreamRootTable(reader); } StreamHashToNameIndex = new StreamHashToNameIndexTab[Header.Field38]; for (int i = 0; i < Header.Field38; i++) { StreamHashToNameIndex[i] = new StreamHashToNameIndexTab(reader); } StreamNameIndexToHash = new StreamNameIndexTab[Header.Field38]; for (int i = 0; i < Header.Field38; i++) { StreamNameIndexToHash[i] = new StreamNameIndexTab(reader); } StreamIndexToFile = new StreamIndexToFileTab[Header.Field3C]; for (int i = 0; i < Header.Field3C; i++) { StreamIndexToFile[i] = new StreamIndexToFileTab(reader); } StreamFiles = new StreamFilesTab[Header.StreamFileCount]; for (int i = 0; i < Header.StreamFileCount; i++) { StreamFiles[i] = new StreamFilesTab(reader); } Field30 = new Tab20F30[Header.Field30]; for (int i = 0; i < Header.Field30; i++) { Field30[i] = new Tab20F30(reader); } DirectoryList = new DirectoryListTab[Header.Field4]; for (int i = 0; i < Header.Field4; i++) { DirectoryList[i] = new DirectoryListTab(reader, i); } DirectoryOffsets = new DirectoryOffsetTable[Header.Field20 + Header.Field8]; for (int i = 0; i < Header.Field20 + Header.Field8; i++) { DirectoryOffsets[i] = new DirectoryOffsetTable(reader, i); } Field18 = new Tab20F18[Header.Field18]; for (int i = 0; i < Header.Field18; i++) { Field18[i] = new Tab20F18(reader); } FileList = new FileListTab[Header.FieldC]; for (int i = 0; i < Header.FieldC; i++) { FileList[i] = new FileListTab(reader, i); } FileOffsets = new FileOffsetTab[Header.Field24 + Header.Field10]; for (int i = 0; i < Header.Field24 + Header.Field10; i++) { FileOffsets[i] = new FileOffsetTab(reader, i); } DirectoryListLookup = new DirectoryListLookupTab[Header.Field4]; for (int i = 0; i < Header.Field4; i++) { DirectoryListLookup[i] = new DirectoryListLookupTab(reader, i); } int fileCount = reader.ReadInt32(); int groupCount = reader.ReadInt32(); FieldXX = new Tab20Fxx[groupCount]; for (int i = 0; i < groupCount; i++) { FieldXX[i] = new Tab20Fxx(reader); } FileListLookup = new FileListLookupTab[Header.Field14]; for (int i = 0; i < Header.Field14; i++) { FileListLookup[i] = new FileListLookupTab(reader, i); } Field0CB = new Tab20F0CB[Header.FieldC]; for (int i = 0; i < Header.FieldC; i++) { Field0CB[i] = new Tab20F0CB(reader); } HashSet <long> hashes = Hash.Hashes; foreach (var item in StreamRoot) { hashes.Add(item.Hash.GetHash()); } foreach (var item in StreamHashToNameIndex) { hashes.Add(item.Hash.GetHash()); } foreach (var item in StreamNameIndexToHash) { hashes.Add(item.Hash.GetHash()); } foreach (var item in Field30) { hashes.Add(item.Hash.GetHash()); } foreach (var item in DirectoryList) { hashes.Add(item.Path.GetHash()); hashes.Add(item.Name.GetHash()); hashes.Add(item.Parent.GetHash()); hashes.Add(item.Hash4.GetHash()); } foreach (var item in Field18) { hashes.Add(item.Hash.GetHash()); } foreach (var item in FileList) { hashes.Add(item.Path.GetHash()); hashes.Add(item.Extension.GetHash()); hashes.Add(item.Parent.GetHash()); hashes.Add(item.Name.GetHash()); } foreach (var item in DirectoryListLookup) { hashes.Add(item.Hash.GetHash()); } foreach (var item in FileListLookup) { hashes.Add(item.Hash.GetHash()); } SetReferences(); }
public void ExtractFileIndex(int index, string outDir, IProgressReport progress, StringBuilder sb = null) { FileListTab file = Table20.FileList[index]; string name = file.Path.GetText(); DirectoryListTab dir = file.Directory; DirectoryOffsetTable dirOffset = dir.DirOffset; FileOffsetTab offsetInfo = file.FileOffset; bool isLink = false; long offset = Header.Field10 + dirOffset.Offset + file.FileOffset.Offset * 4; string path; if (name != null) { path = Path.Combine(outDir, name); } else if (file.Parent.HasText()) { path = Path.Combine(outDir, file.Parent.GetText(), index.ToString()); } else { path = Path.Combine(outDir, "_", index.ToString()); } if (file.IsLink) { isLink = true; while (file.IsLink) { file = file.FileOffset.File; } dir = file.Directory; dirOffset = dir.DirOffset; offsetInfo = file.FileOffset; offset = Header.Field10 + dirOffset.Offset + offsetInfo.Offset * 4; } if (offsetInfo.Flag3) { dirOffset = offsetInfo.LinkedDirOffset; offsetInfo = offsetInfo.LinkedOffset; offset = Header.Field10 + dirOffset.Offset + offsetInfo.Offset * 4; } //sb?.AppendLine($"{name}, 0x{file.Flags:x2}, 0x{dirOffset.Offset:x}, 0x{file.FileOffset.Offset:x}, 0x{offset:x}, 0x{file.FileOffset.SizeCompressed:x}, 0x{file.FileOffset.Size:x}, 0x{file.FileOffset.Flags:x2}, 0x{file.FileOffset.LinkFileIndex:x}, {file.Flag1}, {file.Flag9}, {file.Flag17}, {file.IsLink}, {file.Flag21}, {file.FileOffset.IsCompressed}, {file.FileOffset.Flag3}, {file.FileOffset.Flag4}, {file.FileOffset.Flag5}, {file.FileOffset.Flag6},"); //return; try { //if (isLink) return; Directory.CreateDirectory(Path.GetDirectoryName(path)); using (var fileOut = new FileStream(path, FileMode.Create, FileAccess.ReadWrite)) //using (var fileOut = Stream.Null) { Stream.Position = offset; if (offsetInfo.Size == 0) { return; } if (offsetInfo.SizeCompressed == 0 || offsetInfo.SizeCompressed == offsetInfo.Size) { Stream.CopyStream(fileOut, offsetInfo.Size); } else { using (var compStream = new ZstandardStream(Stream, CompressionMode.Decompress, true)) { compStream.CopyStream(fileOut, offsetInfo.Size); } } } sb?.AppendLine($"{name}, 0x{file.Flags:x2}, 0x{dirOffset.Offset:x}, 0x{offsetInfo.Offset:x}, 0x{offset:x}, 0x{offsetInfo.SizeCompressed:x}, 0x{offsetInfo.Size:x}, 0x{offsetInfo.Flags:x2}, 0x{offsetInfo.LinkFileIndex:x}, {file.Flag1}, {file.Flag9}, {file.Flag17}, {file.IsLink}, {file.Flag21}, {offsetInfo.IsCompressed}, {offsetInfo.Flag3}, {offsetInfo.Flag4}, {offsetInfo.Flag5}, {offsetInfo.Flag6}, "); } catch (InvalidDataException) { progress?.LogMessage($"File index 0x{file.Index:x5} Offset 0x{offset:x9}: Can't decompress {path}"); try { File.Delete(path); sb?.AppendLine($"{name}, 0x{file.Flags:x2}, 0x{dirOffset.Offset:x}, 0x{offsetInfo.Offset:x}, 0x{offset:x}, 0x{offsetInfo.SizeCompressed:x}, 0x{offsetInfo.Size:x}, 0x{offsetInfo.Flags:x2}, 0x{offsetInfo.LinkFileIndex:x}, {file.Flag1}, {file.Flag9}, {file.Flag17}, {file.IsLink}, {file.Flag21}, {offsetInfo.IsCompressed}, {offsetInfo.Flag3}, {offsetInfo.Flag4}, {offsetInfo.Flag5}, {offsetInfo.Flag6}, X"); var badPath = Path.Combine(outDir, "bad", index.ToString()); Directory.CreateDirectory(Path.GetDirectoryName(badPath)); using (var fileOut = new FileStream(badPath, FileMode.Create, FileAccess.ReadWrite)) { Stream.Position = offset; var info = file.FileOffset; if (info.Size == 0) { return; } Stream.CopyStream(fileOut, info.Size); } } catch (Exception) { } } catch (Exception) { progress?.LogMessage($"File index 0x{file.Index:x5}: Bad path {path}"); try { File.Delete(path); } catch (Exception) { } } }