Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Includes the header for the Subrecord struct and USubrecordHelper class, defining the data structure and utility functions.
- #include "Subrecord.h"
- // Includes the FileManager interface for file operations, such as creating a file reader archive.
- #include "HAL/FileManager.h"
- // Includes the MemoryReader class, enabling reading from a byte array as if it were an archive.
- #include "Serialization/MemoryReader.h"
- // Core function to read a subrecord from an FArchive, Unreal Engine's serialization object.
- // Parses the subrecord's 4-byte type identifier and raw data, with game-specific data length handling.
- // Adjusted logging to avoid editor freezes, focusing on INTV length debugging.
- bool USubrecordHelper::ReadSubrecord(FSubrecord& OutSubrecord, FArchive& Archive, EGameID GameID, int32 DataLengthOverride)
- {
- // Initialize a byte array to store the 4-byte type identifier.
- TArray<uint8> TypeBytes;
- TypeBytes.SetNumUninitialized(4);
- // Read the 4 bytes of the type identifier from the archive.
- int64 StartPos = Archive.Tell();
- Archive.Serialize(TypeBytes.GetData(), 4);
- // Check for serialization errors (e.g., end of file or read failure).
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Error, TEXT("Failed to read subrecord type at position %lld"), StartPos);
- return false; // Return false to indicate failure.
- }
- // Log the raw type bytes to debug encoding issues
- FString TypeHex;
- for (const uint8& Byte : TypeBytes)
- {
- TypeHex += FString::Printf(TEXT("%02X "), Byte);
- }
- UE_LOG(LogTemp, Log, TEXT("Raw subrecord type bytes at position %lld: %s"), StartPos, *TypeHex);
- // Convert the 4 bytes to an FString, ensuring proper ASCII interpretation
- char TypeBuffer[5];
- FMemory::Memcpy(TypeBuffer, TypeBytes.GetData(), 4);
- TypeBuffer[4] = '\0'; // Null-terminate the string
- OutSubrecord.Type = FString(TypeBuffer); // Force ASCII interpretation
- UE_LOG(LogTemp, Log, TEXT("Interpreted subrecord type: %s"), *OutSubrecord.Type);
- // Variable to store the length of the raw data.
- int32 RawDataLength = 0;
- // Determine raw data length based on game format or override.
- if (GameID == EGameID::Morrowind)
- {
- // Morrowind uses a 32-bit integer for data length.
- TArray<uint8> LengthBytes;
- LengthBytes.SetNumUninitialized(4);
- Archive.Serialize(LengthBytes.GetData(), 4);
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Error, TEXT("Failed to read length for subrecord %s at position %lld"), *OutSubrecord.Type, Archive.Tell());
- return false; // Return false if length read fails.
- }
- // Interpret the length as little-endian (Morrowind format).
- RawDataLength = (LengthBytes[3] << 24) | (LengthBytes[2] << 16) | (LengthBytes[1] << 8) | LengthBytes[0];
- // Log only for INTV to minimize output and prevent freezes.
- if (OutSubrecord.Type == TEXT("INTV"))
- {
- FString LengthHex;
- for (const uint8& Byte : LengthBytes)
- {
- LengthHex += FString::Printf(TEXT("%02X "), Byte);
- }
- UE_LOG(LogTemp, Log, TEXT("Raw length bytes for INTV: %s, interpreted length: %d"), *LengthHex, RawDataLength);
- }
- }
- else if (DataLengthOverride != 0)
- {
- // Skip 2 bytes (e.g., flags or metadata) and use the provided override length.
- Archive.Seek(Archive.Tell() + 2);
- RawDataLength = DataLengthOverride;
- }
- else
- {
- // Other games (e.g., Skyrim, Fallout) use a 16-bit integer for data length.
- int16 Length16 = 0;
- Archive.Serialize(&Length16, sizeof(int16));
- if (Archive.IsError())
- {
- return false; // Return false if length read fails.
- }
- RawDataLength = Length16; // Convert 16-bit length to 32-bit.
- }
- // Allocate space in the RawData array for the specified number of bytes.
- OutSubrecord.RawData.SetNumUninitialized(RawDataLength);
- // Read the raw data bytes from the archive into RawData.
- Archive.Serialize(OutSubrecord.RawData.GetData(), RawDataLength);
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Error, TEXT("Failed to read data for subrecord %s, expected %d bytes at position %lld"), *OutSubrecord.Type, RawDataLength, Archive.Tell());
- return false; // Return false if data read fails.
- }
- // Log only for INTV to avoid excessive output.
- if (OutSubrecord.Type == TEXT("INTV"))
- {
- UE_LOG(LogTemp, Log, TEXT("Read INTV subrecord, data size: %d"), OutSubrecord.RawData.Num());
- }
- return true; // Subrecord successfully parsed.
- }
- // Wrapper function to read a subrecord from a file specified by its path.
- // Opens the file as an FArchive and delegates to ReadSubrecord.
- bool USubrecordHelper::ReadSubrecordFromFile(FSubrecord& OutSubrecord, const FString& FilePath, EGameID GameID, int32 DataLengthOverride)
- {
- // Create a unique pointer to an FArchive for file reading using IFileManager.
- TUniquePtr<FArchive> FileArchive(IFileManager::Get().CreateFileReader(*FilePath));
- if (!FileArchive)
- {
- // Log a warning and return false if the file cannot be opened.
- UE_LOG(LogTemp, Warning, TEXT("Failed to open file: %s"), *FilePath);
- return false;
- }
- // Delegate to the core ReadSubrecord function with the file archive.
- return ReadSubrecord(OutSubrecord, *FileArchive, GameID, DataLengthOverride);
- }
- // Wrapper function to read a subrecord from a byte array.
- // Creates an FMemoryReader archive and delegates to ReadSubrecord.
- bool USubrecordHelper::ReadSubrecordFromBytes(FSubrecord& OutSubrecord, const TArray<uint8>& ByteArray, EGameID GameID, int32 DataLengthOverride)
- {
- // Create an FMemoryReader archive from the byte array; 'true' avoids unnecessary copying.
- FMemoryReader MemoryArchive(ByteArray, true);
- // Delegate to the core ReadSubrecord function with the memory archive.
- return ReadSubrecord(OutSubrecord, MemoryArchive, GameID, DataLengthOverride);
- }
- // Utility function to retrieve the Type field of a subrecord.
- // Returns the Type directly from the FSubrecord struct.
- FString USubrecordHelper::GetType(const FSubrecord& Subrecord)
- {
- return Subrecord.Type;
- }
- // Utility function to retrieve the RawData field of a subrecord.
- // Returns the RawData directly from the FSubrecord struct.
- TArray<uint8> USubrecordHelper::GetRawData(const FSubrecord& Subrecord)
- {
- return Subrecord.RawData;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement