private static ArkFile ParseArkHeader(string input, Stream stream) { ArkFile ark = new ArkFile(); ark._encrypted = false; ark._xor = false; ark._arkPaths = new[] { Path.GetFullPath(input) }; using var ar = new AwesomeReader(stream); // Checks version int version = ar.ReadInt32(); if (!Enum.IsDefined(typeof(ArkVersion), version)) { throw new NotSupportedException($"Unsupported ark version 0x{version:X8}"); } ark._version = (ArkVersion)version; // Skip entries and come back later var entryOffset = ar.BaseStream.Position; var entryCount = ar.ReadInt32(); ar.BaseStream.Seek(entryCount * 20, SeekOrigin.Current); // Read string blob + string indicies var strings = ReadStringBlob(ar); int[] stringIndex = ReadStringIndicies(ar); // Read entries ar.BaseStream.Seek(entryOffset + 4, SeekOrigin.Begin); for (int i = 0; i < entryCount; i++) { uint offset = ar.ReadUInt32(); int fileIdx = ar.ReadInt32(); int dirIdx = ar.ReadInt32(); string filePath = (fileIdx >= 0) ? strings[stringIndex[fileIdx]] : ""; string direPath = (dirIdx >= 0) ? strings[stringIndex[dirIdx]] : ""; uint size = ar.ReadUInt32(); uint inflatedSize = ar.ReadUInt32(); ark._offsetEntries.Add(new OffsetArkEntry(offset, filePath, direPath, size, inflatedSize, 0, offset)); } return(ark); }
public static ArkFile Create(string hdrPath, ArkVersion version, int?key) { hdrPath = Path.GetFullPath(hdrPath); var ark = new ArkFile(); ark._encrypted = key.HasValue; ark._xor = (version >= ArkVersion.V10); // RB4/RBVR? if (key.HasValue) { ark._cryptKey = key.Value; } ark._version = version; var directory = Path.GetDirectoryName(hdrPath); var fileNameNoExt = Path.GetFileNameWithoutExtension(hdrPath); // TODO: Add additional parts dynamically var arkPaths = Enumerable.Range(0, 1) .Select(x => Path.Combine(directory, $"{fileNameNoExt}_{x}.ark")) .ToList(); // Create directory if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } // Create ark parts foreach (var partPath in arkPaths) { using var _ = File.Create(partPath); } ark._arkPaths = new[] { hdrPath, arkPaths.First() }; return(ark); }
private static ArkFile ParseHeader(string input, Stream stream) { ArkFile ark = new ArkFile(); ark._encrypted = false; ark._xor = false; using var ar = new AwesomeReader(stream); // Checks version int version = ar.ReadInt32(); if (Enum.IsDefined(typeof(ArkVersion), version)) { ark._version = (ArkVersion)version; } else { // Decrypt stream and re-checks version Crypt.DTBCrypt(ar.BaseStream, version, true); ark._cryptKey = version; // Set crypt key version = ar.ReadInt32(); ark._encrypted = true; if (!Enum.IsDefined(typeof(ArkVersion), version)) { version = (int)((uint)version ^ 0xFFFFFFFF); ark._xor = true; // Check one last time if (!Enum.IsDefined(typeof(ArkVersion), version)) { throw new Exception($"Ark version of \'{version}\' is unsupported"); } long start = ar.BaseStream.Position; int b; // 0xFF xor rest of stream while ((b = stream.ReadByte()) > -1) { stream.Seek(-1, SeekOrigin.Current); stream.WriteByte((byte)(b ^ 0xFF)); } ar.BaseStream.Seek(start, SeekOrigin.Begin); } ark._version = (ArkVersion)version; } if (version >= 6) { // TODO: Save 16-byte hashes uint hashCount = ar.ReadUInt32(); ar.BaseStream.Position += hashCount << 4; } uint arkFileCount = ar.ReadUInt32(); uint arkFileSizeCount = ar.ReadUInt32(); // Should be same as ark file count long[] partSizes = new long[arkFileSizeCount]; // Reads ark file sizes if (version != 4) { for (int i = 0; i < partSizes.Length; i++) { partSizes[i] = ar.ReadUInt32(); } } else { // Version 4 uses 64-bit sizes for (int i = 0; i < partSizes.Length; i++) { partSizes[i] = ar.ReadInt64(); } } // TODO: Verify the ark parts exist and the sizes match header listing if (version >= 5) { // Read ark names from hdr uint arkPathsCount = ar.ReadUInt32(); ark._arkPaths = new string[arkPathsCount + 1]; string directory = Path.GetDirectoryName(input); ark._arkPaths[0] = input; var hdrFileName = Path.GetFileNameWithoutExtension(input); for (int i = 0; i < arkPathsCount; i++) { ar.ReadString(); // Ehh just ignore what's in hdr. Sometimes it'll be absolute instead of relative ark._arkPaths[i + 1] = Path.Combine(directory, $"{hdrFileName}_{i}.ark"); } } else { // Make a good guess ark._arkPaths = GetPartNames(input, partSizes.Length); } if (version >= 6 && version <= 9) { // TODO: Save hashes? uint hash2Count = ar.ReadUInt32(); ar.BaseStream.Position += hash2Count << 2; } if (version >= 7) { // TODO: Save file collection paths uint fileCollectionCount = ar.ReadUInt32(); for (int i = 0; i < fileCollectionCount; i++) { uint fileCount = ar.ReadUInt32(); for (int j = 0; j < fileCount; j++) { ar.ReadString(); } } } if (version <= 7) { // Read string blob + string indicies var strings = ReadStringBlob(ar); int[] stringIndex = ReadStringIndicies(ar); // Reads entries uint entryCount = ar.ReadUInt32(); if (version >= 4) { for (int i = 0; i < entryCount; i++) { long entryOffset = ar.ReadInt64(); string filePath = strings[stringIndex[ar.ReadInt32()]]; string direPath = strings[stringIndex[ar.ReadInt32()]]; uint size = ar.ReadUInt32(); uint inflatedSize = ar.ReadUInt32(); (int partIdx, long partOffset) = GetArkOffsetForEntry(entryOffset, partSizes); ark._offsetEntries.Add(new OffsetArkEntry(entryOffset, filePath, direPath, size, inflatedSize, partIdx + 1, partOffset)); } } else { for (int i = 0; i < entryCount; i++) { uint entryOffset = ar.ReadUInt32(); string filePath = strings[stringIndex[ar.ReadInt32()]]; string direPath = strings[stringIndex[ar.ReadInt32()]]; uint size = ar.ReadUInt32(); uint inflatedSize = ar.ReadUInt32(); (int partIdx, long partOffset) = GetArkOffsetForEntry(entryOffset, partSizes); ark._offsetEntries.Add(new OffsetArkEntry(entryOffset, filePath, direPath, size, inflatedSize, partIdx + 1, partOffset)); } } } else { uint entryCount = ar.ReadUInt32(); var flags1 = new (string path, int nextIdx)[entryCount];