public TOCFileInfo parseToc() { FileStream TBL = File.OpenRead(Properties.Settings.Default.inputPsarcTBLBinary); TOCFileInfo toc = new TOCFileInfo(); uint fileCount; List <uint> fileNameSizes = new List <uint>(); List <bool> fileSubFolderFlags = new List <bool>(); List <uint> fileIndexPointers = new List <uint>(); List <PACFileInfoV2> fileInfos = new List <PACFileInfoV2>(); changeStreamFile(TBL); Stream.Seek(0, SeekOrigin.Begin); int magic = readIntBigEndian(Stream.Position); if (magic != 0x54424C20) { throw new Exception("PATCH.TBL is not in TBL format!"); } Stream.Seek(0x04, SeekOrigin.Current); fileCount = readUIntBigEndian(Stream.Position); uint totalFileCount = readUIntBigEndian(Stream.Position); toc.totalFileEntries = totalFileCount; ushort subFolderFlag = readUShort(Stream.Position, true); bool hasSubFolder = subFolderFlag == 0x8000 ? true : false; fileSubFolderFlags.Add(hasSubFolder); ushort initFileNamePointer = readUShort(Stream.Position, true); uint prevFileNamePointer = initFileNamePointer; for (int i = 0; i < fileCount; i++) { if (i != fileCount - 1) { subFolderFlag = readUShort(Stream.Position, true); hasSubFolder = subFolderFlag == 0x8000 ? true : false; fileSubFolderFlags.Add(hasSubFolder); uint currentFileNamePointer = readUShort(Stream.Position, true); fileNameSizes.Add(currentFileNamePointer - prevFileNamePointer); prevFileNamePointer = currentFileNamePointer; } else { fileNameSizes.Add((uint)Stream.Length - prevFileNamePointer); } } for (int i = 0; i < totalFileCount; i++) { fileIndexPointers.Add(readUIntBigEndian(Stream.Position)); } uint fileNamePointer = initFileNamePointer; for (int i = 0; i < fileCount; i++) { PACFileInfoV2 pacFileInfo = new PACFileInfoV2(); uint nameSize = fileNameSizes[i]; Stream.Seek(fileNamePointer, SeekOrigin.Begin); string relativePatchPath = readString(Stream.Position, nameSize); pacFileInfo.relativePatchPath = relativePatchPath; string nameHash = Path.GetFileNameWithoutExtension(relativePatchPath); if (nameHash.Contains("PATCH")) { pacFileInfo.namePrefix = prefixEnum.PATCH; nameHash = nameHash.Replace("PATCH", ""); } else if (nameHash.Contains("STREAM")) { pacFileInfo.namePrefix = prefixEnum.STREAM; nameHash = nameHash.Replace("STREAM", ""); } else { pacFileInfo.namePrefix = prefixEnum.NONE; } if (!uint.TryParse(nameHash, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out uint res)) { throw new Exception("Failed to convert " + nameHash + " to uint!"); } pacFileInfo.hasRelativePatchSubPath = fileSubFolderFlags[i]; pacFileInfo.fileFlags = fileFlagsEnum.hasFileName; //pacFileInfo.relativePathIndex = (uint)i; pacFileInfo.nameHash = res; fileNamePointer += nameSize; fileInfos.Add(pacFileInfo); } List <uint> non_Zero_fileIndexPointers = fileIndexPointers.Where(s => !s.Equals(0)).ToList(); for (int i = 0; i < non_Zero_fileIndexPointers.Count; i++) { bool newInfo = false; Stream.Seek(non_Zero_fileIndexPointers[i], SeekOrigin.Begin); patchNoEnum patchNo = (patchNoEnum)readUIntBigEndian(Stream.Position); uint relativePathNo = readUIntBigEndian(Stream.Position); uint unk04 = readUIntBigEndian(Stream.Position); uint Size1 = readUIntBigEndian(Stream.Position); uint Size2 = readUIntBigEndian(Stream.Position); uint Size3 = readUIntBigEndian(Stream.Position); uint unk00 = readUIntBigEndian(Stream.Position); uint nameHash = readUIntBigEndian(Stream.Position); int fileIndex = fileIndexPointers.FindIndex(a => a.Equals(non_Zero_fileIndexPointers[i])); PACFileInfoV2 pacFileInfo = fileInfos.FirstOrDefault(a => a.nameHash.Equals(nameHash)); if (pacFileInfo == null) { pacFileInfo = new PACFileInfoV2(); newInfo = true; } else { //if (relativePathNo != pacFileInfo.relativePathIndex) //throw new Exception("Different relative Path Index between name and fileInfo for nameHash " + nameHash.ToString("X8")); } pacFileInfo.fileFlags |= fileFlagsEnum.hasFileInfo; pacFileInfo.patchNo = patchNo; //pacFileInfo.relativePathIndex = relativePathNo; pacFileInfo.unk04 = unk04; pacFileInfo.Size1 = Size1; pacFileInfo.Size2 = Size2; pacFileInfo.Size3 = Size3; pacFileInfo.unk00 = unk00; pacFileInfo.nameHash = nameHash; pacFileInfo.fileInfoIndex = fileIndex; if (Properties.Settings.Default.identifyPACFilesTBLParse) { // Trying to find the file inside the folder. string[] allFiles = Directory.GetFiles(Properties.Settings.Default.psarcTBLParseRepackFolder, "*", SearchOption.AllDirectories); string nameHashStr = nameHash.ToString("X8"); string path = allFiles.FirstOrDefault(s => s.Contains(nameHashStr)); if (path != null) { pacFileInfo.fileFlags |= fileFlagsEnum.hasFilePath; } pacFileInfo.filePath = path; } if (newInfo) { fileInfos.Add(pacFileInfo); } } toc.allFiles = fileInfos; return(toc); }
public MemoryStream writeToc(TOCFileInfo Toc) { List <PACFileInfoV2> fileInfos = Toc.allFiles; uint totalFileCount = Toc.totalFileEntries; Dictionary <int, long> fileInfoOffsets = new Dictionary <int, long>(); List <PACFileInfoV2> onlyFileInfoswithNames = fileInfos.Where(a => a.fileFlags.HasFlag(fileFlagsEnum.hasFileName)).ToList(); List <PACFileInfoV2> onlyFileInfoswithIndex = fileInfos.Where(a => a.fileFlags.HasFlag(fileFlagsEnum.hasFileInfo)).ToList(); // Depreceated as we just use data's position in JSON as is. /* * // Validating index data: * List<uint> relPathIndexes = onlyFileInfoswithNames.Select(s => s.relativePathIndex).ToList(); * List<int> castedIndex = relPathIndexes.ConvertAll(x => (int)x); * * // Check duplicate * List<int> duplicates = castedIndex.GroupBy(x => x) * .SelectMany(g => g.Skip(1)).ToList(); * * * //List<PACFileInfo> newFiles = fileInfos.Where(a => a.fileInfoIndex > 6144).ToList(); * //totalFileCount += (uint)newFiles.Count; * * * if (duplicates.Count >= 1) * { * string dupStr = string.Empty; * foreach (int dup in duplicates) * { * dupStr += " | " + dup; * } * throw new Exception("Found duplicate Relative Path Indexes! Duplicates: " + dupStr.ToString()); * } * * * // Check sequential * int missingNo = findMissing(castedIndex.ToArray(), castedIndex.Count()); * if (missingNo != -1) * throw new Exception("Relative Path Indexes is not Consecutive! Found missing non consecutive number: " + missingNo.ToString()); */ // Check if there is dulplicate hashes // Validating nameHash data: List <uint> nameHashes = fileInfos.Select(s => s.nameHash).ToList(); List <int> castedNameHashes = nameHashes.ConvertAll(x => (int)x); // Check duplicate List <int> duplicateNameHashes = castedNameHashes.GroupBy(x => x) .SelectMany(g => g.Skip(1)).ToList(); if (duplicateNameHashes.Count >= 1) { string dupStr = string.Empty; foreach (int dup in duplicateNameHashes) { dupStr += " | " + dup; } throw new Exception("Found duplicate Name Hash Indexes! Duplicates: " + dupStr.ToString()); } uint fileNo = (uint)onlyFileInfoswithNames.Count; MemoryStream TBL = new MemoryStream(); appendIntMemoryStream(TBL, 0x54424C20, true); appendIntMemoryStream(TBL, 0x01010000, true); appendUIntMemoryStream(TBL, fileNo, true); appendUIntMemoryStream(TBL, totalFileCount, true); MemoryStream fileNamePointersStream = new MemoryStream(); MemoryStream fileIndexPointersStream = new MemoryStream(); MemoryStream fileInfosStream = new MemoryStream(); MemoryStream fileNamesStream = new MemoryStream(); long fileInfoStart = TBL.Length + fileNo * 0x04 + totalFileCount * 0x04; uint startPointer = (uint)(0x10 + (onlyFileInfoswithNames.Count * 0x4) + (totalFileCount * 0x4)); uint paddingRequired = addHalfPaddingSizeCalculation(startPointer) - startPointer; long fileNameStartPointer = fileInfoStart + (onlyFileInfoswithIndex.Count * 0x20) + paddingRequired; //List<PACFileInfoV2> orderedRelativePathIndex = onlyFileInfoswithNames.OrderBy(s => s.relativePathIndex).ToList(); List <PACFileInfoV2> actualFileNameIndex = new List <PACFileInfoV2>(); for (int i = 0; i < onlyFileInfoswithNames.Count; i++) { PACFileInfoV2 fileInfo = onlyFileInfoswithNames[i]; //if (fileInfo.relativePathIndex != i) //throw new Exception("ordered Relative Path Index is not continious!"); string relativePath = fileInfo.relativePatchPath; long initPos = fileNamesStream.Position; ushort subFolderFlag = fileInfo.hasRelativePatchSubPath ? (ushort)0x8000 : (ushort)0; appendStringMemoryStream(fileNamesStream, relativePath, Encoding.Default); appendZeroMemoryStream(fileNamesStream, 1); appendUIntMemoryStream(fileNamePointersStream, (uint)(fileNameStartPointer + initPos), true); //appendUShortMemoryStream(fileNamePointersStream, subFolderFlag, true); actualFileNameIndex.Add(fileInfo); } for (int i = 0; i < onlyFileInfoswithIndex.Count; i++) { PACFileInfoV2 fileInfo = onlyFileInfoswithIndex[i]; int fileIndexes = fileInfo.fileInfoIndex; fileInfoOffsets[fileIndexes] = fileInfoStart + fileInfosStream.Position + paddingRequired; //foreach(int fileIndex in fileIndexes) //{ // fileInfoOffsets[fileIndex] = fileInfoStart + fileInfosStream.Position; //} appendUIntMemoryStream(fileInfosStream, (uint)fileInfo.patchNo, true); if (fileInfo.fileFlags.HasFlag(fileFlagsEnum.hasFileName)) { int trueIndex = actualFileNameIndex.FindIndex(x => x == fileInfo); appendUIntMemoryStream(fileInfosStream, (uint)trueIndex, true); } else { appendUIntMemoryStream(fileInfosStream, (uint)0, true); } appendIntMemoryStream(fileInfosStream, 0x00040000, true); appendUIntMemoryStream(fileInfosStream, fileInfo.Size1, true); appendUIntMemoryStream(fileInfosStream, fileInfo.Size2, true); appendUIntMemoryStream(fileInfosStream, fileInfo.Size3, true); appendIntMemoryStream(fileInfosStream, 0, true); appendUIntMemoryStream(fileInfosStream, fileInfo.nameHash, true); } for (int i = 0; i < totalFileCount; i++) { if (fileInfoOffsets.ContainsKey(i)) { appendUIntMemoryStream(fileIndexPointersStream, (uint)fileInfoOffsets[i], true); } else { appendZeroMemoryStream(fileIndexPointersStream, 0x04); } } // fileIndexPointer must be aligned to 8 MemoryStream combinedfileNamePointersandIndexPointersStream = new MemoryStream(); combinedfileNamePointersandIndexPointersStream.Write(fileNamePointersStream.ToArray(), 0, (int)fileNamePointersStream.Length); combinedfileNamePointersandIndexPointersStream.Write(fileIndexPointersStream.ToArray(), 0, (int)fileIndexPointersStream.Length); combinedfileNamePointersandIndexPointersStream = addHalfPaddingStream(combinedfileNamePointersandIndexPointersStream); //TBL.Write(fileNamePointersStream.ToArray(), 0, (int)fileNamePointersStream.Length); //TBL.Write(fileIndexPointersStream.ToArray(), 0, (int)fileIndexPointersStream.Length); TBL.Write(combinedfileNamePointersandIndexPointersStream.ToArray(), 0, (int)combinedfileNamePointersandIndexPointersStream.Length); TBL.Write(fileInfosStream.ToArray(), 0, (int)fileInfosStream.Length); TBL.Write(fileNamesStream.ToArray(), 0, (int)fileNamesStream.Length); return(TBL); }