/// <summary> /// Initializes arc from binary reader /// </summary> /// <param name="reader"></param> private bool Init(ExtBinaryReader reader) { if (!HashDict.Initialized) { HashDict.Init(); } if (reader.BaseStream.Length < Marshal.SizeOf <_sArcHeader>()) { return(false); } header = reader.ReadType <_sArcHeader>(); if (header.Magic != Magic) { throw new InvalidDataException("ARC magic does not match"); } reader.BaseStream.Seek(header.FileSystemOffset, SeekOrigin.Begin); if (header.FileDataOffset < 0x8824AF68) { Version = 0x00010000; ReadFileSystemV1(ReadCompressedTable(reader)); } else { ReadFileSystem(ReadCompressedTable(reader)); } //reader.BaseStream.Seek(header.FileSystemSearchOffset, SeekOrigin.Begin); //ReadSearchTable(ReadCompressedTable(reader)); return(true); }
/// <summary> /// Parses filesystem from the 1.0.0 arc /// </summary> /// <param name="fileSystemTable"></param> private void ReadFileSystemV1(byte[] fileSystemTable) { using (ExtBinaryReader reader = new ExtBinaryReader(new MemoryStream(fileSystemTable))) { reader.BaseStream.Position = 0; var nodeHeader = reader.ReadType <_sFileSystemHeaderV1>(); reader.BaseStream.Seek(0x68, SeekOrigin.Begin); // Hash Table reader.BaseStream.Seek(0x8 * nodeHeader.Part1Count, SeekOrigin.Current); // Hash Table 2 System.Diagnostics.Debug.WriteLine("stream " + reader.BaseStream.Position.ToString("X")); streamNameToHash = reader.ReadType <_sStreamNameToHash>(nodeHeader.Part1Count); // Hash Table 3 System.Diagnostics.Debug.WriteLine("stream " + reader.BaseStream.Position.ToString("X")); streamIndexToFile = reader.ReadType <_sStreamIndexToOffset>(nodeHeader.Part2Count); // stream offsets System.Diagnostics.Debug.WriteLine("stream " + reader.BaseStream.Position.ToString("X")); streamOffsets = reader.ReadType <_sStreamOffset>(nodeHeader.MusicFileCount); // Another Hash Table System.Diagnostics.Debug.WriteLine("RegionalHashList " + reader.BaseStream.Position.ToString("X")); reader.BaseStream.Seek(0xC * 0xE, SeekOrigin.Current); //folders System.Diagnostics.Debug.WriteLine("FolderHashes " + reader.BaseStream.Position.ToString("X")); directoryList = reader.ReadType <_sDirectoryList>(nodeHeader.FolderCount); //file offsets System.Diagnostics.Debug.WriteLine("fileoffsets " + reader.BaseStream.Position.ToString("X")); directoryOffsets = reader.ReadType <_sDirectoryOffset>(nodeHeader.FileCount1 + nodeHeader.FileCount2); //DirectoryOffsets_2 = reader.ReadType<_sDirectoryOffsets>(R, NodeHeader.FileCount2); System.Diagnostics.Debug.WriteLine("subfileoffset " + reader.BaseStream.Position.ToString("X")); directoryChildHashGroup = reader.ReadType <_sHashIndexGroup>(nodeHeader.HashFolderCount); System.Diagnostics.Debug.WriteLine("subfileoffset " + reader.BaseStream.Position.ToString("X")); fileInfoV1 = reader.ReadType <_sFileInformationV1>(nodeHeader.FileInformationCount); System.Diagnostics.Debug.WriteLine("subfileoffset " + reader.BaseStream.Position.ToString("X")); subFiles = reader.ReadType <_sSubFileInfo>(nodeHeader.SubFileCount + nodeHeader.SubFileCount2); } }
/// <summary> /// Reads the search table information for V2 Arc /// </summary> /// <param name="searchTable"></param> private void ReadSearchTable(byte[] searchTable) { using (ExtBinaryReader r = new ExtBinaryReader(new MemoryStream(searchTable))) { var header = r.ReadType <_sSearchHashHeader>(); // paths var ActualPathHashSection = r.ReadType <_sHashIndexGroup>(header.FolderLengthHashCount); int MaxFolderIndex = 0; foreach (var section in ActualPathHashSection) { MaxFolderIndex = Math.Max(MaxFolderIndex, section.index >> 8); } Console.WriteLine("Max FolderIndex " + MaxFolderIndex.ToString("X")); // path lookup group var UnkHashGroup = r.ReadType <_sHashGroup>(header.FolderLengthHashCount); Console.WriteLine("End at: " + r.BaseStream.Position.ToString("X")); // file paths var ActualFullPath = r.ReadType <_sHashIndexGroup>(header.SomeCount3); Console.WriteLine("End at: " + r.BaseStream.Position.ToString("X")); // index to look up pathgroup? why they needed a separate index who knows... var IndexTable = r.ReadType <int>(header.SomeCount3); Console.WriteLine("End at: " + r.BaseStream.Position.ToString("X")); // file path lookup group var PathNameHashGroupLookup = r.ReadType <_sHashGroup>(header.SomeCount4); Console.WriteLine("End at: " + r.BaseStream.Position.ToString("X")); int MaxIntTableValue = 0; foreach (var g in UnkHashGroup) { MaxIntTableValue = Math.Max((int)g.ExtensionHash, MaxIntTableValue); //Console.WriteLine(HashDict.GetString(g.Hash)); } Console.WriteLine("Max table value: " + MaxIntTableValue.ToString("X")); /*using (StreamWriter writer = new StreamWriter(new FileStream("print.txt", FileMode.Create))) * foreach (var g in ActualFullPath) * { * writer.WriteLine(HashDict.GetString(g.Hash)); * }*/ } }
/// <summary> /// Arc versions other than 1.0 have compressed tables /// </summary> /// <param name="reader"></param> /// <returns></returns> private byte[] ReadCompressedTable(ExtBinaryReader reader) { var compHeader = reader.ReadType <_sCompressedTableHeader>(); if (compHeader.DataOffset > 0x10) { var tableStart = reader.BaseStream.Position - 0x10; reader.BaseStream.Seek(tableStart, SeekOrigin.Begin); var size = reader.ReadInt32(); reader.BaseStream.Seek(tableStart, SeekOrigin.Begin); return(reader.ReadBytes(size)); } else if (compHeader.DataOffset == 0x10) { return(reader.ReadZstdCompressed(compHeader.CompressedSize)); } else { return(reader.ReadBytes(compHeader.CompressedSize)); } }
/// <summary> /// Parses filesystem from the 1.0.0 arc /// </summary> /// <param name="fileSystemTable"></param> private void ReadFileSystemV1(byte[] fileSystemTable) { using (ExtBinaryReader reader = new ExtBinaryReader(new MemoryStream(fileSystemTable))) { reader.BaseStream.Position = 0; var NodeHeader = reader.ReadType <_sFileSystemHeaderV1>(); reader.BaseStream.Seek(0x68, SeekOrigin.Begin); // Hash Table reader.BaseStream.Seek(0x8 * NodeHeader.Part1Count, SeekOrigin.Current); // Hash Table 2 System.Diagnostics.Debug.WriteLine("stream " + reader.BaseStream.Position.ToString("X")); streamNameToHash = reader.ReadType <_sStreamNameToHash>(NodeHeader.Part1Count); // Hash Table 3 System.Diagnostics.Debug.WriteLine("stream " + reader.BaseStream.Position.ToString("X")); streamIndexToFile = reader.ReadType <_sStreamIndexToOffset>(NodeHeader.Part2Count); // stream offsets System.Diagnostics.Debug.WriteLine("stream " + reader.BaseStream.Position.ToString("X")); streamOffsets = reader.ReadType <_sStreamOffset>(NodeHeader.MusicFileCount); // Another Hash Table System.Diagnostics.Debug.WriteLine("RegionalHashList " + reader.BaseStream.Position.ToString("X")); reader.BaseStream.Seek(0xC * 0xE, SeekOrigin.Current); //folders System.Diagnostics.Debug.WriteLine("FolderHashes " + reader.BaseStream.Position.ToString("X")); directoryList = reader.ReadType <_sDirectoryList>(NodeHeader.FolderCount); //file offsets System.Diagnostics.Debug.WriteLine("fileoffsets " + reader.BaseStream.Position.ToString("X")); directoryOffsets = reader.ReadType <_sDirectoryOffset>(NodeHeader.FileCount1 + NodeHeader.FileCount2); //DirectoryOffsets_2 = reader.ReadType<_sDirectoryOffsets>(R, NodeHeader.FileCount2); System.Diagnostics.Debug.WriteLine("subfileoffset " + reader.BaseStream.Position.ToString("X")); directoryChildHashGroup = reader.ReadType <_sHashIndexGroup>(NodeHeader.HashFolderCount); System.Diagnostics.Debug.WriteLine("subfileoffset " + reader.BaseStream.Position.ToString("X")); fileInfoV1 = reader.ReadType <_sFileInformationV1>(NodeHeader.FileInformationCount); System.Diagnostics.Debug.WriteLine("subfileoffset " + reader.BaseStream.Position.ToString("X")); subFiles = reader.ReadType <_sSubFileInfo>(NodeHeader.SubFileCount + NodeHeader.SubFileCount2); /*SubFileInformationStart = R.BaseStream.Position; * SubFileInformation_1 = reader.ReadType<_SubFileInfo>(R, NodeHeader.SubFileCount); * SubFileInformationStart2 = R.BaseStream.Position; * SubFileInformation_2 = reader.ReadType<_SubFileInfo>(R, NodeHeader.SubFileCount2);*/ //_sHashInt[] HashInts = reader.ReadType<_sHashInt>(R, NodeHeader.FolderCount); // okay some more file information //uint FileHashCount = reader.ReadUInt32(); //uint UnknownTableCount = reader.ReadUInt32(); //_sExtraFITable[] Extra1 = reader.ReadType<_sExtraFITable>(R, UnknownTableCount); //_sExtraFITable2[] Extra2 = reader.ReadType<_sExtraFITable2>(R, FileHashCount); /*reader.BaseStream.Position += 8 * NodeHeader.FileInformationCount; * * FolderHashDict = new Dictionary<uint, _sDirectoryList>(); * foreach (_sDirectoryList fh in DirectoryLists) * { * FolderHashDict.Add(fh.HashID, fh); * } * * foreach (_sDirectoryOffsets chunk in DirectoryOffsets_1) * { * for (int i = 0; i < chunk.SubDataCount; i++) * if (!ChunkHash1.ContainsKey((int)chunk.SubDataStartIndex + i)) * ChunkHash1.Add((int)chunk.SubDataStartIndex + i, chunk); * } * foreach (_sDirectoryOffsets chunk in DirectoryOffsets_2) * { * for (int i = 0; i < chunk.SubDataCount; i++) * if (!ChunkHash2.ContainsKey((int)chunk.SubDataStartIndex + i)) * ChunkHash2.Add((int)chunk.SubDataStartIndex + i, chunk); * }*/ } }
/// <summary> /// Reads file system table from Arc /// </summary> /// <param name="fileSystemTable"></param> private void ReadFileSystem(byte[] fileSystemTable) { using (ExtBinaryReader reader = new ExtBinaryReader(new MemoryStream(fileSystemTable))) { FSHeader = reader.ReadType <_sFileSystemHeader>(); uint ExtraFolder = 0; uint ExtraCount = 0; if (fileSystemTable.Length >= 0x2992DD4) { // Version 3+ Version = reader.ReadInt32(); ExtraFolder = reader.ReadUInt32(); ExtraCount = reader.ReadUInt32(); reader.ReadBytes(0x10); // some extra thing :thinking } else { Version = 0x00020000; reader.BaseStream.Seek(0x3C, SeekOrigin.Begin); } // skip this for now regionalbytes = reader.ReadBytes(0xE * 12); // Streams StreamHeader = reader.ReadType <_sStreamHeader>(); streamUnk = reader.ReadType <_sStreamUnk>(StreamHeader.UnkCount); streamHashToName = reader.ReadType <_sStreamHashToName>(StreamHeader.StreamHashCount); streamNameToHash = reader.ReadType <_sStreamNameToHash>(StreamHeader.StreamHashCount); streamIndexToFile = reader.ReadType <_sStreamIndexToOffset>(StreamHeader.StreamIndexToOffsetCount); streamOffsets = reader.ReadType <_sStreamOffset>(StreamHeader.StreamOffsetCount); Console.WriteLine(reader.BaseStream.Position.ToString("X")); // Unknown uint UnkCount1 = reader.ReadUInt32(); uint UnkCount2 = reader.ReadUInt32(); fileInfoUnknownTable = reader.ReadType <_sFileInformationUnknownTable>(UnkCount2); filePathToIndexHashGroup = reader.ReadType <_sHashIndexGroup>(UnkCount1); // FileTables fileInfoPath = reader.ReadType <_sFileInformationPath>(FSHeader.FileInformationPathCount); fileInfoIndex = reader.ReadType <_sFileInformationIndex>(FSHeader.FileInformationIndexCount); // directory tables // directory hashes by length and index to directory probably 0x6000 something Console.WriteLine(reader.BaseStream.Position.ToString("X")); directoryHashGroup = reader.ReadType <_sHashIndexGroup>(FSHeader.DirectoryCount); directoryList = reader.ReadType <_sDirectoryList>(FSHeader.DirectoryCount); directoryOffsets = reader.ReadType <_sDirectoryOffset>(FSHeader.DirectoryOffsetCount1 + FSHeader.DirectoryOffsetCount2 + ExtraFolder); directoryChildHashGroup = reader.ReadType <_sHashIndexGroup>(FSHeader.DirectoryHashSearchCount); // file information tables Console.WriteLine(reader.BaseStream.Position.ToString("X")); fileInfoV2 = reader.ReadType <_sFileInformationV2>(FSHeader.FileInformationCount + FSHeader.SubFileCount2 + ExtraCount); fileInfoSubIndex = reader.ReadType <_sFileInformationSubIndex>(FSHeader.FileInformationSubIndexCount + FSHeader.SubFileCount2 + ExtraCount); subFiles = reader.ReadType <_sSubFileInfo>(FSHeader.SubFileCount + FSHeader.SubFileCount2); Console.WriteLine(reader.BaseStream.Position.ToString("X")); //uint max = 0; /*using (StreamWriter writer = new StreamWriter(new FileStream("FS1.txt", FileMode.Create))) * for (int i = 0; i < (int)FSHeader.FileInformationCount; i++) * { * var fileinfo = fileInfoV2[i]; * var path = fileInfoPath[fileinfo.PathIndex]; * var subindex = fileInfoSubIndex[fileinfo.SubIndexIndex]; * writer.WriteLine(fileinfo.Flags.ToString("X") + " " + fileinfo.PathIndex.ToString("X") + " " + subindex.SubFileIndex.ToString("X") + " " + HashDict.GetString(path.Path) + " " + HashDict.GetString(path.FileName)); * //max = Math.Max(max, fp.SomeIndex2); * } * using (StreamWriter writer = new StreamWriter(new FileStream("FS2.txt", FileMode.Create))) * for (int i = (int)FSHeader.FileInformationCount ;i < fileInfoV2.Length; i++) * { * var fileinfo = fileInfoV2[i]; * var path = fileInfoPath[fileinfo.PathIndex]; * var subindex = fileInfoSubIndex[fileinfo.SubIndexIndex]; * writer.WriteLine(fileinfo.Flags.ToString("X") + " " + fileinfo.PathIndex.ToString("X") + " " + subindex.SubFileIndex.ToString("X") + " " + HashDict.GetString(path.Path) + " " + HashDict.GetString(path.FileName)); * //max = Math.Max(max, fp.SomeIndex2); * }*/ //Console.WriteLine("Max: " + max.ToString("X")); /*int MaxIntTableValue = 0; * foreach (var g in fileInfoIndex) * { * MaxIntTableValue = Math.Max((int)g.FileInformationIndex, MaxIntTableValue); * //Console.WriteLine(HashDict.GetString(g.Hash)); * } * Console.WriteLine("Max table value: " + MaxIntTableValue.ToString("X"));*/ /*var flags = new System.Collections.Generic.List<uint>(); * foreach (var g in fileInfoV2) * { * if (!flags.Contains(g.Flags)) * flags.Add(g.Flags); * }*/ /*using (StreamWriter writer = new StreamWriter(new FileStream("print.txt", FileMode.Create))) * foreach (var g in PathNameHashLengthLookup) * { * writer.WriteLine(HashDict.GetString(g.FilePathHash) + " " + HashDict.GetString(PathNameHashGroupLookup[IndexTable[g.ExtensionHash]].FileNameHash)); * }*/ } }
/// <summary> /// Reads file system table from Arc /// </summary> /// <param name="fileSystemTable"></param> private void ReadFileSystem(byte[] fileSystemTable) { using (ExtBinaryReader reader = new ExtBinaryReader(new MemoryStream(fileSystemTable))) { fsHeader = reader.ReadType <_sFileSystemHeader>(); uint extraFolder = 0; uint extraCount = 0; uint extraCount2 = 0; uint extraSubCount = 0; if (fileSystemTable.Length >= 0x2992DD4) { // Version 3+ Version = reader.ReadInt32(); extraFolder = reader.ReadUInt32(); extraCount = reader.ReadUInt32(); reader.ReadBytes(8); // some extra thing :thinking extraCount2 = reader.ReadUInt32(); extraSubCount = reader.ReadUInt32(); } else { Version = 0x00020000; reader.BaseStream.Seek(0x3C, SeekOrigin.Begin); } // skip this for now regionalBytes = reader.ReadBytes(0xE * 12); // Streams streamHeader = reader.ReadType <_sStreamHeader>(); streamUnk = reader.ReadType <_sStreamUnk>(streamHeader.UnkCount); streamHashToName = reader.ReadType <_sStreamHashToName>(streamHeader.StreamHashCount); streamNameToHash = reader.ReadType <_sStreamNameToHash>(streamHeader.StreamHashCount); streamIndexToFile = reader.ReadType <_sStreamIndexToOffset>(streamHeader.StreamIndexToOffsetCount); streamOffsets = reader.ReadType <_sStreamOffset>(streamHeader.StreamOffsetCount); Console.WriteLine(reader.BaseStream.Position.ToString("X")); // Unknown uint unkCount1 = reader.ReadUInt32(); uint unkCount2 = reader.ReadUInt32(); fileInfoUnknownTable = reader.ReadType <_sFileInformationUnknownTable>(unkCount2); filePathToIndexHashGroup = reader.ReadType <_sHashIndexGroup>(unkCount1); // FileTables fileInfoPath = reader.ReadType <_sFileInformationPath>(fsHeader.FileInformationPathCount); fileInfoIndex = reader.ReadType <_sFileInformationIndex>(fsHeader.FileInformationIndexCount); // directory tables // directory hashes by length and index to directory probably 0x6000 something Console.WriteLine(reader.BaseStream.Position.ToString("X")); directoryHashGroup = reader.ReadType <_sHashIndexGroup>(fsHeader.DirectoryCount); directoryList = reader.ReadType <_sDirectoryList>(fsHeader.DirectoryCount); directoryOffsets = reader.ReadType <_sDirectoryOffset>(fsHeader.DirectoryOffsetCount1 + fsHeader.DirectoryOffsetCount2 + extraFolder); directoryChildHashGroup = reader.ReadType <_sHashIndexGroup>(fsHeader.DirectoryHashSearchCount); // file information tables Console.WriteLine(reader.BaseStream.Position.ToString("X")); fileInfoV2 = reader.ReadType <_sFileInformationV2>(fsHeader.FileInformationCount + fsHeader.SubFileCount2 + extraCount); fileInfoSubIndex = reader.ReadType <_sFileInformationSubIndex>(fsHeader.FileInformationSubIndexCount + fsHeader.SubFileCount2 + extraCount2); subFiles = reader.ReadType <_sSubFileInfo>(fsHeader.SubFileCount + fsHeader.SubFileCount2 + extraSubCount); Console.WriteLine("End:" + reader.BaseStream.Position.ToString("X")); } }