Advertisement
malice936

Subrecord.cpp

Apr 13th, 2025 (edited)
244
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 6.54 KB | Gaming | 0 0
  1. // Includes the header for the Subrecord struct and USubrecordHelper class, defining the data structure and utility functions.
  2. #include "Subrecord.h"
  3.  
  4. // Includes the FileManager interface for file operations, such as creating a file reader archive.
  5. #include "HAL/FileManager.h"
  6.  
  7. // Includes the MemoryReader class, enabling reading from a byte array as if it were an archive.
  8. #include "Serialization/MemoryReader.h"
  9.  
  10. // Core function to read a subrecord from an FArchive, Unreal Engine's serialization object.
  11. // Parses the subrecord's 4-byte type identifier and raw data, with game-specific data length handling.
  12. // Adjusted logging to avoid editor freezes, focusing on INTV length debugging.
  13. bool USubrecordHelper::ReadSubrecord(FSubrecord& OutSubrecord, FArchive& Archive, EGameID GameID, int32 DataLengthOverride)
  14. {
  15.     // Initialize a byte array to store the 4-byte type identifier.
  16.     TArray<uint8> TypeBytes;
  17.     TypeBytes.SetNumUninitialized(4);
  18.  
  19.     // Read the 4 bytes of the type identifier from the archive.
  20.     int64 StartPos = Archive.Tell();
  21.     Archive.Serialize(TypeBytes.GetData(), 4);
  22.  
  23.     // Check for serialization errors (e.g., end of file or read failure).
  24.     if (Archive.IsError())
  25.     {
  26.         UE_LOG(LogTemp, Error, TEXT("Failed to read subrecord type at position %lld"), StartPos);
  27.         return false; // Return false to indicate failure.
  28.     }
  29.  
  30.     // Log the raw type bytes to debug encoding issues
  31.     FString TypeHex;
  32.     for (const uint8& Byte : TypeBytes)
  33.     {
  34.         TypeHex += FString::Printf(TEXT("%02X "), Byte);
  35.     }
  36.     UE_LOG(LogTemp, Log, TEXT("Raw subrecord type bytes at position %lld: %s"), StartPos, *TypeHex);
  37.  
  38.     // Convert the 4 bytes to an FString, ensuring proper ASCII interpretation
  39.     char TypeBuffer[5];
  40.     FMemory::Memcpy(TypeBuffer, TypeBytes.GetData(), 4);
  41.     TypeBuffer[4] = '\0'; // Null-terminate the string
  42.     OutSubrecord.Type = FString(TypeBuffer); // Force ASCII interpretation
  43.     UE_LOG(LogTemp, Log, TEXT("Interpreted subrecord type: %s"), *OutSubrecord.Type);
  44.  
  45.     // Variable to store the length of the raw data.
  46.     int32 RawDataLength = 0;
  47.  
  48.     // Determine raw data length based on game format or override.
  49.     if (GameID == EGameID::Morrowind)
  50.     {
  51.         // Morrowind uses a 32-bit integer for data length.
  52.         TArray<uint8> LengthBytes;
  53.         LengthBytes.SetNumUninitialized(4);
  54.         Archive.Serialize(LengthBytes.GetData(), 4);
  55.         if (Archive.IsError())
  56.         {
  57.             UE_LOG(LogTemp, Error, TEXT("Failed to read length for subrecord %s at position %lld"), *OutSubrecord.Type, Archive.Tell());
  58.             return false; // Return false if length read fails.
  59.         }
  60.  
  61.         // Interpret the length as little-endian (Morrowind format).
  62.         RawDataLength = (LengthBytes[3] << 24) | (LengthBytes[2] << 16) | (LengthBytes[1] << 8) | LengthBytes[0];
  63.         // Log only for INTV to minimize output and prevent freezes.
  64.         if (OutSubrecord.Type == TEXT("INTV"))
  65.         {
  66.             FString LengthHex;
  67.             for (const uint8& Byte : LengthBytes)
  68.             {
  69.                 LengthHex += FString::Printf(TEXT("%02X "), Byte);
  70.             }
  71.             UE_LOG(LogTemp, Log, TEXT("Raw length bytes for INTV: %s, interpreted length: %d"), *LengthHex, RawDataLength);
  72.         }
  73.     }
  74.     else if (DataLengthOverride != 0)
  75.     {
  76.         // Skip 2 bytes (e.g., flags or metadata) and use the provided override length.
  77.         Archive.Seek(Archive.Tell() + 2);
  78.         RawDataLength = DataLengthOverride;
  79.     }
  80.     else
  81.     {
  82.         // Other games (e.g., Skyrim, Fallout) use a 16-bit integer for data length.
  83.         int16 Length16 = 0;
  84.         Archive.Serialize(&Length16, sizeof(int16));
  85.         if (Archive.IsError())
  86.         {
  87.             return false; // Return false if length read fails.
  88.         }
  89.         RawDataLength = Length16; // Convert 16-bit length to 32-bit.
  90.     }
  91.  
  92.     // Allocate space in the RawData array for the specified number of bytes.
  93.     OutSubrecord.RawData.SetNumUninitialized(RawDataLength);
  94.  
  95.     // Read the raw data bytes from the archive into RawData.
  96.     Archive.Serialize(OutSubrecord.RawData.GetData(), RawDataLength);
  97.     if (Archive.IsError())
  98.     {
  99.         UE_LOG(LogTemp, Error, TEXT("Failed to read data for subrecord %s, expected %d bytes at position %lld"), *OutSubrecord.Type, RawDataLength, Archive.Tell());
  100.         return false; // Return false if data read fails.
  101.     }
  102.     // Log only for INTV to avoid excessive output.
  103.     if (OutSubrecord.Type == TEXT("INTV"))
  104.     {
  105.         UE_LOG(LogTemp, Log, TEXT("Read INTV subrecord, data size: %d"), OutSubrecord.RawData.Num());
  106.     }
  107.  
  108.     return true; // Subrecord successfully parsed.
  109. }
  110.  
  111. // Wrapper function to read a subrecord from a file specified by its path.
  112. // Opens the file as an FArchive and delegates to ReadSubrecord.
  113. bool USubrecordHelper::ReadSubrecordFromFile(FSubrecord& OutSubrecord, const FString& FilePath, EGameID GameID, int32 DataLengthOverride)
  114. {
  115.     // Create a unique pointer to an FArchive for file reading using IFileManager.
  116.     TUniquePtr<FArchive> FileArchive(IFileManager::Get().CreateFileReader(*FilePath));
  117.     if (!FileArchive)
  118.     {
  119.         // Log a warning and return false if the file cannot be opened.
  120.         UE_LOG(LogTemp, Warning, TEXT("Failed to open file: %s"), *FilePath);
  121.         return false;
  122.     }
  123.  
  124.     // Delegate to the core ReadSubrecord function with the file archive.
  125.     return ReadSubrecord(OutSubrecord, *FileArchive, GameID, DataLengthOverride);
  126. }
  127.  
  128. // Wrapper function to read a subrecord from a byte array.
  129. // Creates an FMemoryReader archive and delegates to ReadSubrecord.
  130. bool USubrecordHelper::ReadSubrecordFromBytes(FSubrecord& OutSubrecord, const TArray<uint8>& ByteArray, EGameID GameID, int32 DataLengthOverride)
  131. {
  132.     // Create an FMemoryReader archive from the byte array; 'true' avoids unnecessary copying.
  133.     FMemoryReader MemoryArchive(ByteArray, true);
  134.  
  135.     // Delegate to the core ReadSubrecord function with the memory archive.
  136.     return ReadSubrecord(OutSubrecord, MemoryArchive, GameID, DataLengthOverride);
  137. }
  138.  
  139. // Utility function to retrieve the Type field of a subrecord.
  140. // Returns the Type directly from the FSubrecord struct.
  141. FString USubrecordHelper::GetType(const FSubrecord& Subrecord)
  142. {
  143.     return Subrecord.Type;
  144. }
  145.  
  146. // Utility function to retrieve the RawData field of a subrecord.
  147. // Returns the RawData directly from the FSubrecord struct.
  148. TArray<uint8> USubrecordHelper::GetRawData(const FSubrecord& Subrecord)
  149. {
  150.     return Subrecord.RawData;
  151. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement