Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Includes the Record header, defining the FRecord struct and URecordHelper class for record-related functionality.
- #include "Record.h"
- // Includes the Subrecord header, providing utilities for parsing and managing subrecords within a record.
- #include "Subrecord.h"
- // Includes FileManager for creating file readers and writers, used in file-based record operations.
- #include "HAL/FileManager.h"
- // Includes MemoryReader for deserializing data from byte arrays, used in ReadRecordFromBytes.
- #include "Serialization/MemoryReader.h"
- // Includes MemoryWriter for serializing data to byte arrays, used in WriteRecordToBytes.
- #include "Serialization/MemoryWriter.h"
- // Core function to parse a record from an FArchive, populating OutRecord with data based on game-specific logic.
- // Parameters: OutRecord (output record), Archive (data source), GameID (game-specific format), bSkipSubrecords (skip subrecord parsing).
- // Returns true on success, false on failure (e.g., archive errors, invalid data).
- bool URecordHelper::ReadRecord(FRecord& OutRecord, FArchive& Archive, EGameID GameID, bool bSkipSubrecords)
- {
- // Clear existing subrecords to ensure OutRecord starts fresh, preventing data accumulation.
- OutRecord.Subrecords.Empty();
- // Store the starting position of the record for size tracking
- int64 RecordStartPos = Archive.Tell(); // Added to track the start of the record
- // Read the 4-byte record type (e.g., "NPC_", "WEAP") into a byte array.
- TArray<uint8> TypeBytes;
- TypeBytes.SetNumUninitialized(4); // Allocate 4 bytes without initialization for efficiency.
- Archive.Serialize(TypeBytes.GetData(), 4); // Serialize 4 bytes from the archive.
- if (Archive.IsError()) // Check for read errors (e.g., end of file, corruption).
- {
- if (Archive.AtEnd())
- {
- // Reached end of file, not an error for the last record
- return false;
- }
- UE_LOG(LogTemp, Warning, TEXT("Failed to read record type at position %lld"), RecordStartPos);
- return false;
- }
- // Convert the byte array to an FString using a null-terminated char buffer - Added to fix encoding issue with record type
- char TypeBuffer[5];
- FMemory::Memcpy(TypeBuffer, TypeBytes.GetData(), 4);
- TypeBuffer[4] = '\0'; // Null-terminate the string - Ensures proper C-string termination
- OutRecord.Type = FString(TypeBuffer); // Construct FString from ANSI string - Forces ASCII interpretation
- // Read the total size of the record's data (32-bit integer), excluding the type and size fields.
- int32 RecordSize = 0; // Renamed TotalSubrecordsSize to RecordSize for clarity - Reflects the entire record data size
- Archive.Serialize(&RecordSize, sizeof(int32));
- if (Archive.IsError()) // Check for read errors during size serialization.
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to read record size at position %lld"), Archive.Tell());
- return false;
- }
- UE_LOG(LogTemp, Log, TEXT("Record %s size: %d at position %lld"), *OutRecord.Type, RecordSize, RecordStartPos);
- // Skip 4 bytes for Morrowind-specific fields (e.g., version or unused data), not present in other games.
- if (GameID == EGameID::Morrowind)
- {
- Archive.Seek(Archive.Tell() + 4); // Advance the archive cursor by 4 bytes.
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to skip Morrowind-specific field at position %lld"), Archive.Tell());
- return false;
- }
- }
- // Read the record flags (32-bit integer), indicating properties like compression status.
- Archive.Serialize(&OutRecord.Flags, sizeof(int32));
- if (Archive.IsError()) // Check for read errors during flags serialization.
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to read record flags at position %lld"), Archive.Tell());
- return false;
- }
- // Handle FormID for non-Morrowind games (e.g., Skyrim, Oblivion), which uniquely identify records.
- if (GameID != EGameID::Morrowind)
- {
- // Read the 32-bit FormID value from the archive.
- int32 FormIdValue = 0;
- Archive.Serialize(&FormIdValue, sizeof(int32));
- if (Archive.IsError()) // Check for read errors during FormID serialization.
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to read FormID at position %lld"), Archive.Tell());
- return false;
- }
- // Construct an FFormID with the read value and assign it to OutRecord.
- OutRecord.FormId = FFormID(FormIdValue);
- // Skip padding bytes: 4 for Oblivion, 8 for other non-Morrowind games (e.g., Skyrim).
- int32 SkipLength = (GameID == EGameID::Oblivion) ? 4 : 8;
- Archive.Seek(Archive.Tell() + SkipLength); // Advance the cursor by the skip length.
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to skip padding bytes at position %lld"), Archive.Tell());
- return false;
- }
- }
- else
- {
- // For Morrowind, set FormID to 0, as it does not use FormIDs.
- OutRecord.FormId = FFormID(0);
- }
- // Calculate the fixed header size for the record (type, size, Morrowind skip, flags, and FormID/padding if applicable)
- int64 HeaderSize = 4 + sizeof(int32) + (GameID == EGameID::Morrowind ? 4 : 0) + sizeof(int32) + (GameID != EGameID::Morrowind ? sizeof(int32) + ((GameID == EGameID::Oblivion) ? 4 : 8) : 0); // Added to compute header size
- UE_LOG(LogTemp, Log, TEXT("Header size: %lld, starting subrecord parsing at position %lld"), HeaderSize, Archive.Tell());
- // If subrecords are to be skipped, advance the archive cursor to the end of the record
- if (bSkipSubrecords)
- {
- Archive.Seek(RecordStartPos + HeaderSize + RecordSize); // Updated to advance to the correct end of the record
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to seek to end of record (skipping subrecords) at position %lld"), Archive.Tell());
- return false;
- }
- return true; // Return success if no errors occurred during seeking.
- }
- // If the record is compressed (based on flags), skip subrecord parsing, as compression is not handled here.
- if (AreFieldsCompressed(OutRecord))
- {
- Archive.Seek(RecordStartPos + HeaderSize + RecordSize); // Updated to advance to the correct end of the record
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to seek to end of record (compressed) at position %lld"), Archive.Tell());
- return false;
- }
- return true; // Return success if no errors occurred during seeking.
- }
- // Read subrecords until the record size is consumed or an error occurs.
- int64 StartPos = Archive.Tell(); // Store the starting position for subrecord parsing.
- int32 LargeSubrecordSize = 0; // Tracks size override for large subrecords (via XXXX subrecord).
- UE_LOG(LogTemp, Log, TEXT("Starting subrecord loop for record %s, expected bytes to read: %d"), *OutRecord.Type, RecordSize);
- while (!Archive.IsError() && (Archive.Tell() - StartPos) < RecordSize) // Updated condition to use RecordSize directly for subrecord data
- {
- // Log position before reading each subrecord
- int64 SubrecordStartPos = Archive.Tell();
- UE_LOG(LogTemp, Log, TEXT("Reading subrecord at position %lld, bytes remaining: %lld"), SubrecordStartPos, RecordSize - (SubrecordStartPos - StartPos));
- // Create a new subrecord and attempt to parse it from the archive.
- FSubrecord Subrecord;
- if (!USubrecordHelper::ReadSubrecord(Subrecord, Archive, GameID, LargeSubrecordSize))
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to read subrecord at position %lld for record %s"), Archive.Tell(), *OutRecord.Type);
- return false; // Return false if subrecord parsing fails.
- }
- // Handle XXXX subrecord, which specifies the size of the next large subrecord.
- if (Subrecord.Type == TEXT("XXXX"))
- {
- // Ensure the subrecord has enough data to read a 32-bit integer.
- if (Subrecord.RawData.Num() >= sizeof(int32))
- {
- // Extract the size from the subrecord's raw data.
- LargeSubrecordSize = *reinterpret_cast<const int32*>(Subrecord.RawData.GetData());
- }
- else
- {
- // Reset size if XXXX subrecord is invalid.
- LargeSubrecordSize = 0;
- }
- }
- else
- {
- // Reset large subrecord size for normal subrecords and add the subrecord to the record.
- LargeSubrecordSize = 0;
- OutRecord.Subrecords.Add(Subrecord);
- UE_LOG(LogTemp, Log, TEXT("Added subrecord %s to record %s, total subrecords: %d"), *Subrecord.Type, *OutRecord.Type, OutRecord.Subrecords.Num());
- if (Subrecord.Type == TEXT("VHGT"))
- {
- UE_LOG(LogTemp, Log, TEXT("VHGT subrecord found, data size: %d"), Subrecord.RawData.Num());
- }
- }
- }
- // Ensure the archive position is at the end of the record
- Archive.Seek(RecordStartPos + HeaderSize + RecordSize); // Updated to ensure correct positioning after record
- if (Archive.IsError())
- {
- UE_LOG(LogTemp, Warning, TEXT("Failed to seek to end of record at position %lld"), Archive.Tell());
- return false;
- }
- // Log the record type and position for debugging
- UE_LOG(LogTemp, Log, TEXT("Parsed record type %s at position %lld, next record starts at %lld"), *OutRecord.Type, RecordStartPos, Archive.Tell());
- // Return true if no errors occurred during parsing, false otherwise.
- return true;
- }
- // Reads a record from a file, creating an FArchive and delegating to ReadRecord for parsing.
- // Parameters: OutRecord (output record), FilePath (file to read), GameID (game-specific format), bSkipSubrecords (skip subrecords).
- // Returns true on success, false on failure (e.g., file not found).
- bool URecordHelper::ReadRecordFromFile(FRecord& OutRecord, const FString& FilePath, EGameID GameID, bool bSkipSubrecords)
- {
- // Create a file reader archive for the specified file path.
- TUniquePtr<FArchive> FileArchive(IFileManager::Get().CreateFileReader(*FilePath));
- if (!FileArchive) // Check if the file could be opened.
- {
- // Log a warning if the file cannot be opened (e.g., does not exist, permissions issue).
- UE_LOG(LogTemp, Warning, TEXT("Failed to open file: %s"), *FilePath);
- return false;
- }
- // Delegate to ReadRecord to parse the record from the file archive.
- return ReadRecord(OutRecord, *FileArchive, GameID, bSkipSubrecords);
- }
- // Reads a record from a byte array, creating an FMemoryReader and delegating to ReadRecord.
- // Parameters: OutRecord (output record), ByteArray (input data), GameID (game-specific format), bSkipSubrecords (skip subrecords).
- // Returns true on success, false on failure (e.g., corrupt data).
- bool URecordHelper::ReadRecordFromBytes(FRecord& OutRecord, const TArray<uint8>& ByteArray, EGameID GameID, bool bSkipSubrecords)
- {
- // Create a memory reader archive from the byte array, enabling persistent data access.
- FMemoryReader MemoryArchive(ByteArray, true);
- // Delegate to ReadRecord to parse the record from the memory archive.
- return ReadRecord(OutRecord, MemoryArchive, GameID, bSkipSubrecords);
- }
- // Writes a record to a file, serializing its fields and subrecords in a game-specific format.
- // Parameters: Record (input record), FilePath (output file), GameID (game-specific format).
- // Returns true on success, false on failure (e.g., invalid path, write error).
- bool URecordHelper::WriteRecordToFile(const FRecord& Record, const FString& FilePath, EGameID GameID)
- {
- // Create a file writer archive for the specified file path.
- TUniquePtr<FArchive> FileArchive(IFileManager::Get().CreateFileWriter(*FilePath));
- if (!FileArchive) // Check if the file could be created/opened.
- {
- // Log a warning if the file cannot be created (e.g., permissions issue, invalid path).
- UE_LOG(LogTemp, Warning, TEXT("Failed to create file: %s"), *FilePath);
- return false;
- }
- // Serialize the 4-byte record type (e.g., "NPC_").
- TArray<uint8> TypeBytes;
- TypeBytes.SetNumZeroed(4); // Allocate and zero-initialize 4 bytes.
- for (int32 i = 0; i < 4 && i < Record.Type.Len(); ++i) // Copy up to 4 characters from Type.
- {
- TypeBytes[i] = static_cast<uint8>(Record.Type[i]); // Convert TCHAR to byte.
- }
- FileArchive->Serialize(TypeBytes.GetData(), 4); // Write the type bytes.
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- // Calculate the total size of subrecords, including type, length field, and data.
- int32 TotalSubrecordsSize = 0;
- for (const FSubrecord& Subrecord : Record.Subrecords)
- {
- TotalSubrecordsSize += 4; // 4 bytes for subrecord type.
- TotalSubrecordsSize += (GameID == EGameID::Morrowind) ? 4 : 2; // Length field size (4 for Morrowind, 2 for others).
- TotalSubrecordsSize += Subrecord.RawData.Num(); // Size of subrecord data.
- }
- FileArchive->Serialize(&TotalSubrecordsSize, sizeof(int32)); // Write the total size.
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- // Write 4 bytes of padding for Morrowind (unused field).
- if (GameID == EGameID::Morrowind)
- {
- int32 Padding = 0;
- FileArchive->Serialize(&Padding, sizeof(int32));
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Serialize the record flags (32-bit integer).
- FileArchive->Serialize(&const_cast<int32&>(Record.Flags), sizeof(int32));
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- // Serialize FormID for non-Morrowind games.
- if (GameID != EGameID::Morrowind)
- {
- // Access the FormID value (assumes FFormID has a public 'Value' field).
- int32 FormIdValue = Record.FormId.Value;
- FileArchive->Serialize(&FormIdValue, sizeof(int32)); // Write the FormID.
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- // Write padding bytes: 4 for Oblivion, 8 for other non-Morrowind games.
- int32 SkipLength = (GameID == EGameID::Oblivion) ? 4 : 8;
- TArray<uint8> Padding;
- Padding.SetNumZeroed(SkipLength); // Allocate and zero-initialize padding.
- FileArchive->Serialize(Padding.GetData(), SkipLength); // Write padding.
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Serialize each subrecord.
- for (const FSubrecord& Subrecord : Record.Subrecords)
- {
- // Serialize the 4-byte subrecord type.
- TArray<uint8> SubTypeBytes;
- SubTypeBytes.SetNumZeroed(4); // Allocate and zero-initialize 4 bytes.
- for (int32 i = 0; i < 4 && i < Subrecord.Type.Len(); ++i) // Copy up to 4 characters.
- {
- SubTypeBytes[i] = static_cast<uint8>(Subrecord.Type[i]); // Convert TCHAR to byte.
- }
- FileArchive->Serialize(SubTypeBytes.GetData(), 4); // Write the type bytes.
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- // Serialize the subrecord data length (4 bytes for Morrowind, 2 bytes for others).
- if (GameID == EGameID::Morrowind)
- {
- int32 DataLength = Subrecord.RawData.Num();
- FileArchive->Serialize(&DataLength, sizeof(int32)); // Write 32-bit length.
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- }
- else
- {
- int16 DataLength = static_cast<int16>(Subrecord.RawData.Num()); // Cast to 16-bit for non-Morrowind.
- FileArchive->Serialize(&DataLength, sizeof(int16)); // Write 16-bit length.
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Serialize the subrecord's raw data.
- FileArchive->Serialize(const_cast<uint8*>(Subrecord.RawData.GetData()), Subrecord.RawData.Num());
- if (FileArchive->IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Return true if all writes succeeded.
- return true;
- }
- // Serializes a record to a byte array, preserving its structure in a game-specific format.
- // Parameters: Record (input record), OutByteArray (output bytes), GameID (game-specific format).
- // Returns true on success, false on failure (e.g., serialization error).
- bool URecordHelper::WriteRecordToBytes(const FRecord& Record, TArray<uint8>& OutByteArray, EGameID GameID)
- {
- // Create a memory writer archive to serialize data into OutByteArray.
- FMemoryWriter MemoryArchive(OutByteArray, true);
- OutByteArray.Empty(); // Clear the output array to ensure a fresh start.
- // Serialize the 4-byte record type (e.g., "NPC_").
- TArray<uint8> TypeBytes;
- TypeBytes.SetNumZeroed(4); // Allocate and zero-initialize 4 bytes.
- for (int32 i = 0; i < 4 && i < Record.Type.Len(); ++i) // Copy up to 4 characters.
- {
- TypeBytes[i] = static_cast<uint8>(Record.Type[i]); // Convert TCHAR to byte.
- }
- MemoryArchive.Serialize(TypeBytes.GetData(), 4); // Write the type bytes.
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- // Calculate the total size of subrecords.
- int32 TotalSubrecordsSize = 0;
- for (const FSubrecord& Subrecord : Record.Subrecords)
- {
- TotalSubrecordsSize += 4; // 4 bytes for subrecord type.
- TotalSubrecordsSize += (GameID == EGameID::Morrowind) ? 4 : 2; // Length field size.
- TotalSubrecordsSize += Subrecord.RawData.Num(); // Size of subrecord data.
- }
- MemoryArchive.Serialize(&TotalSubrecordsSize, sizeof(int32)); // Write the total size.
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- // Write 4 bytes of padding for Morrowind.
- if (GameID == EGameID::Morrowind)
- {
- int32 Padding = 0;
- MemoryArchive.Serialize(&Padding, sizeof(int32));
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Serialize the record flags.
- MemoryArchive.Serialize(&const_cast<int32&>(Record.Flags), sizeof(int32));
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- // Serialize FormID for non-Morrowind games.
- if (GameID != EGameID::Morrowind)
- {
- // Access the FormID value (assumes FFormID has a public 'Value' field).
- int32 FormIdValue = Record.FormId.Value;
- MemoryArchive.Serialize(&FormIdValue, sizeof(int32)); // Write the FormID.
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- // Write padding bytes: 4 for Oblivion, 8 for others.
- int32 SkipLength = (GameID == EGameID::Oblivion) ? 4 : 8;
- TArray<uint8> Padding;
- Padding.SetNumZeroed(SkipLength); // Allocate and zero-initialize padding.
- MemoryArchive.Serialize(Padding.GetData(), SkipLength); // Write padding.
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Serialize each subrecord.
- for (const FSubrecord& Subrecord : Record.Subrecords)
- {
- // Serialize the 4-byte subrecord type.
- TArray<uint8> SubTypeBytes;
- SubTypeBytes.SetNumZeroed(4); // Allocate and zero-initialize 4 bytes.
- for (int32 i = 0; i < 4 && i < Subrecord.Type.Len(); ++i)
- {
- SubTypeBytes[i] = static_cast<uint8>(Subrecord.Type[i]); // Convert TCHAR to byte.
- }
- MemoryArchive.Serialize(SubTypeBytes.GetData(), 4); // Write the type bytes.
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- // Serialize the subrecord data length.
- if (GameID == EGameID::Morrowind)
- {
- int32 DataLength = Subrecord.RawData.Num();
- MemoryArchive.Serialize(&DataLength, sizeof(int32)); // Write 32-bit length.
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- }
- else
- {
- int16 DataLength = static_cast<int16>(Subrecord.RawData.Num()); // Cast to 16-bit.
- MemoryArchive.Serialize(&DataLength, sizeof(int16)); // Write 16-bit length.
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Serialize the subrecord's raw data.
- MemoryArchive.Serialize(const_cast<uint8*>(Subrecord.RawData.GetData()), Subrecord.RawData.Num());
- if (MemoryArchive.IsError()) // Check for write errors.
- {
- return false;
- }
- }
- // Return true if all writes succeeded.
- return true;
- }
- // Returns the record's 4-byte type identifier (e.g., "NPC_") as an FString.
- // Simple accessor for Blueprint and C++ use, with no side effects.
- FString URecordHelper::GetType(const FRecord& Record)
- {
- return Record.Type;
- }
- // Returns the record's flags as a 32-bit integer, indicating properties like compression.
- // Simple accessor for querying record metadata.
- int32 URecordHelper::GetFlags(const FRecord& Record)
- {
- return Record.Flags;
- }
- // Returns the record's FormID, uniquely identifying it in non-Morrowind games.
- // Simple accessor for accessing the FormID field.
- FFormID URecordHelper::GetFormId(const FRecord& Record)
- {
- return Record.FormId;
- }
- // Returns the array of subrecords stored in the record, providing access to detailed data.
- // Simple accessor for Blueprint and C++ use, returning a copy of the subrecords array.
- TArray<FSubrecord> URecordHelper::GetSubrecords(const FRecord& Record)
- {
- return Record.Subrecords;
- }
- // Checks if the record is compressed by examining its flags (bit 0x00040000).
- // Returns true if compressed, false otherwise, used to determine parsing behavior.
- bool URecordHelper::AreFieldsCompressed(const FRecord& Record)
- {
- return (Record.Flags & 0x00040000) != 0;
- }
- // Retrieves the first subrecord matching the specified SubrecordType (e.g., "NAME").
- // Parameters: Record (input record), SubrecordType (type to find), OutSubrecord (output subrecord).
- // Returns true if found, false otherwise, populating OutSubrecord on success.
- bool URecordHelper::GetSubrecordByType(const FRecord& Record, const FString& SubrecordType, FSubrecord& OutSubrecord)
- {
- // Iterate through subrecords to find a matching type (case-sensitive).
- for (const FSubrecord& Subrecord : Record.Subrecords)
- {
- if (Subrecord.Type.Equals(SubrecordType, ESearchCase::CaseSensitive))
- {
- OutSubrecord = Subrecord; // Assign the found subrecord.
- return true;
- }
- }
- return false; // Return false if no matching subrecord is found.
- }
- // Checks if a subrecord with the specified SubrecordType exists in the record.
- // Returns true if found, false otherwise, useful for conditional logic.
- bool URecordHelper::HasSubrecord(const FRecord& Record, const FString& SubrecordType)
- {
- // Iterate through subrecords to check for a matching type (case-sensitive).
- for (const FSubrecord& Subrecord : Record.Subrecords)
- {
- if (Subrecord.Type.Equals(SubrecordType, ESearchCase::CaseSensitive))
- {
- return true;
- }
- }
- return false;
- }
- // Returns the number of subrecords in the record, providing a count for iteration or validation.
- // Simple accessor returning the size of the Subrecords array.
- int32 URecordHelper::GetSubrecordCount(const FRecord& Record)
- {
- return Record.Subrecords.Num();
- }
- // Validates the record by checking for a non-empty Type and a valid FormID (non-zero for non-Morrowind).
- // Parameters: Record (input record), GameID (game-specific context).
- // Returns true if valid, false otherwise, ensuring data integrity.
- bool URecordHelper::IsValidRecord(const FRecord& Record, EGameID GameID)
- {
- // Check if the record type is empty (invalid record).
- if (Record.Type.IsEmpty())
- {
- return false;
- }
- // For non-Morrowind games, ensure FormID is non-zero (assumes FFormID has a 'Value' field).
- if (GameID != EGameID::Morrowind && Record.FormId.Value == 0)
- {
- return false;
- }
- return true;
- }
- // Clears all subrecords from the record, resetting the Subrecords array to empty.
- // Parameters: Record (modified record).
- // Used to reset a record's subrecord data, e.g., before repopulating.
- void URecordHelper::ClearSubrecords(FRecord& Record)
- {
- Record.Subrecords.Empty();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement