/// <summary> /// Checks whether the buffer size for a file about to be loaded is sufficient and /// conditonally returns an address to a new buffer with adequate size. /// </summary> /// <returns>The address of a new buffer for the game to decompress a file to</returns> private static void *CheckBufferSize(int fileIndex, void *addressToDecompressTo, ONEFILE *thisPointer) { if (fileIndex >= 2) { // Get pointer and length. IntPtr onePointer = (IntPtr)thisPointer[0].InternalDataPointer - 0xC; // The start of file pointer is sometimes unused, so we use the InternalDataPointer instead and offset. int fileLength = thisPointer[0].FileLength; // Now the ONE File MemoryONEArchive memoryOneFile = MemoryONEArchive.ParseONEFromMemory(onePointer, fileLength); // Now we estimate the size of it. int actualFileIndex = fileIndex - 2; MemoryONEFile oneFile = memoryOneFile.Files[actualFileIndex]; byte[] oneFileCopy = GameProcess.ReadMemory((IntPtr)oneFile.CompressedDataPointer, oneFile.DataLength); int oneFileLength = Prs.Estimate(ref oneFileCopy); // Check if the size of allocation is sufficient. MemoryAddressDetails addressDetails = Allocator.GetAddressDetails((int)addressToDecompressTo); if (addressDetails.MemorySize < oneFileLength) { // Allocate some new data for me please addressToDecompressTo = (void *)Allocator.Allocate((int)addressToDecompressTo, oneFileLength); } } return(addressToDecompressTo); }
/// <summary> /// Parses the details of a minimal Heroes .ONE file from a supplied memory /// address and length. This is simply a modified version of HeroesONE-R's version. /// </summary> /// <returns>The structure of a ONE Archive.</returns> public static MemoryONEArchive ParseONEFromMemory(IntPtr filePointer, int fileLength) { // Creates a .ONE Archive instance ready to feed. MemoryONEArchive oneArchive = new MemoryONEArchive(); // Stores a pointer, increasing as we read the individual file structures. int pointer = 0; // Get the header and individual sections. oneArchive.FileHeader = (ONEHeader *)StructUtilities.MemoryOffsetToPointer <ONEHeader>(filePointer, pointer, ref pointer); oneArchive.FileNameSectionHeader = (ONEFileNameSectionHeader *)StructUtilities.MemoryOffsetToPointer <ONEFileNameSectionHeader>(filePointer, pointer, ref pointer); // Get the filenames. oneArchive.FileNameCount = (*oneArchive.FileNameSectionHeader).GetNameCount(); oneArchive.FileNames = (ONEFileName *)StructUtilities.MemoryOffsetToPointer <ONEFileName>(filePointer, pointer); pointer += (*oneArchive.FileNameSectionHeader).FileNameSectionLength; // Parse all of the files. oneArchive.Files = new List <MemoryONEFile>(oneArchive.FileNameCount); int fileCount = 0; for (int x = 0; x < oneArchive.FileNameCount; x++) { if (oneArchive.FileNames[x].ToString() != "") { fileCount += 1; } } // Some ONE files have been padded at the end of file, thus reading until the end of file may fail - only take as many files as we have filenames. while ((pointer < fileLength) && (oneArchive.Files.Count < fileCount)) { // Create ONE ArchiveFile MemoryONEFile oneFile = new MemoryONEFile(); // Parse the contents. oneFile.ONEFileHeader = (ONEFileHeader *)StructUtilities.MemoryOffsetToPointer <ONEFileHeader>(filePointer, pointer, ref pointer); oneFile.CompressedDataPointer = (byte *)StructUtilities.MemoryOffsetToPointer <ONEFileHeader>(filePointer, pointer); oneFile.DataLength = oneFile.ONEFileHeader[0].FileSize; pointer += oneFile.DataLength; // We're done here :3 oneArchive.Files.Add(oneFile); } return(oneArchive); }