/// <summary> /// Loads Btrieve Key Definitions from the Btrieve DAT File Header /// </summary> private void LoadBtrieveKeyDefinitions() { ushort keyDefinitionBase = 0x110; const ushort keyDefinitionLength = 0x1E; ReadOnlySpan <byte> btrieveFileContentSpan = LoadedFile.Data; //Check for Log Key if (btrieveFileContentSpan[0x10C] == 1) { _logger.Warn($"Btireve Log Key Present in {LoadedFile.FileName}"); LoadedFile.LogKeyPresent = true; } ushort totalKeys = LoadedFile.KeyCount; ushort currentKeyNumber = 0; ushort previousKeyNumber = 0; while (currentKeyNumber < totalKeys) { var keyDefinition = new BtrieveKeyDefinition { Data = btrieveFileContentSpan.Slice(keyDefinitionBase, keyDefinitionLength).ToArray() }; if (keyDefinition.Attributes.HasFlag(EnumKeyAttributeMask.SegmentedKey)) { keyDefinition.SegmentOf = previousKeyNumber; keyDefinition.Number = previousKeyNumber; } else { keyDefinition.Number = currentKeyNumber; currentKeyNumber++; } #if DEBUG _logger.Info("----------------"); _logger.Info("Loaded Key Definition:"); _logger.Info("----------------"); _logger.Info($"Number: {keyDefinition.Number}"); _logger.Info($"Total Records: {keyDefinition.TotalRecords}"); _logger.Info($"Data Type: {keyDefinition.DataType}"); _logger.Info($"Attributes: {keyDefinition.Attributes}"); _logger.Info($"Position: {keyDefinition.Position}"); _logger.Info($"Length: {keyDefinition.Length}"); _logger.Info("----------------"); #endif if (!LoadedFile.Keys.TryGetValue(keyDefinition.Number, out var key)) { key = new BtrieveKey(keyDefinition); LoadedFile.Keys.Add(keyDefinition.Number, key); } else { key.Segments.Add(keyDefinition); } keyDefinitionBase += keyDefinitionLength; } }
/// <summary> /// Loads metadata from the loaded Sqlite table, such as RecordLength and all the key metadata. /// </summary> private void LoadSqliteMetadata() { using (var cmd = new SqliteCommand("SELECT record_length, page_length, variable_length_records FROM metadata_t;", _connection)) { using var reader = cmd.ExecuteReader(); try { if (!reader.Read()) { throw new ArgumentException($"Can't read metadata_t from {FullPath}"); } RecordLength = reader.GetInt32(0); PageLength = reader.GetInt32(1); VariableLengthRecords = reader.GetBoolean(2); } finally { reader.Close(); } } using (var cmd = new SqliteCommand( "SELECT number, segment, attributes, data_type, offset, length FROM keys_t ORDER BY number, segment;", _connection)) { using var reader = cmd.ExecuteReader(); while (reader.Read()) { var number = reader.GetInt32(0); var btrieveKeyDefinition = new BtrieveKeyDefinition() { Number = (ushort)number, Segment = reader.GetInt32(1) != 0, SegmentOf = reader.GetInt32(1) != 0 ? (ushort)number : (ushort)0, Attributes = (EnumKeyAttributeMask)reader.GetInt32(2), DataType = (EnumKeyDataType)reader.GetInt32(3), Offset = (ushort)reader.GetInt32(4), Length = (ushort)reader.GetInt32(5), }; if (!Keys.TryGetValue(btrieveKeyDefinition.Number, out var btrieveKey)) { btrieveKey = new BtrieveKey(); Keys[btrieveKeyDefinition.Number] = btrieveKey; } var index = btrieveKey.Segments.Count; btrieveKeyDefinition.SegmentIndex = index; btrieveKey.Segments.Add(btrieveKeyDefinition); if (btrieveKeyDefinition.DataType == EnumKeyDataType.AutoInc) { AutoincrementedKeys[btrieveKeyDefinition.Number] = btrieveKey; } } reader.Close(); } }
public BtrieveKey(BtrieveKeyDefinition keyDefinition) { Segments = new List <BtrieveKeyDefinition> { keyDefinition }; }
/// <summary> /// Loads Btrieve Key Definitions from the Btrieve DAT File Header /// </summary> private void LoadBtrieveKeyDefinitions(ILogger logger) { var keyDefinitionBase = 0x110; const ushort keyDefinitionLength = 0x1E; var btrieveFileContentSpan = Data.AsSpan(); LogKeyPresent = (btrieveFileContentSpan[0x10C] == 1); var totalKeys = KeyCount; var currentKeyNumber = (ushort)0; while (currentKeyNumber < totalKeys) { var data = btrieveFileContentSpan.Slice(keyDefinitionBase, keyDefinitionLength).ToArray(); EnumKeyDataType dataType; var attributes = (EnumKeyAttributeMask)BitConverter.ToUInt16(data, 0x8); if (attributes.HasFlag(EnumKeyAttributeMask.UseExtendedDataType)) { dataType = (EnumKeyDataType)data[0x1C]; } else { dataType = attributes.HasFlag(EnumKeyAttributeMask.OldStyleBinary) ? EnumKeyDataType.OldBinary : EnumKeyDataType.OldAscii; } var keyDefinition = new BtrieveKeyDefinition { Number = currentKeyNumber, Attributes = attributes, DataType = dataType, Offset = BitConverter.ToUInt16(data, 0x14), Length = BitConverter.ToUInt16(data, 0x16), Segment = attributes.HasFlag(EnumKeyAttributeMask.SegmentedKey), SegmentOf = attributes.HasFlag(EnumKeyAttributeMask.SegmentedKey) ? currentKeyNumber : (ushort)0, NullValue = data[0x1D], }; if (keyDefinition.RequiresACS) { if (ACS == null) { throw new ArgumentException($"Key {keyDefinition.Number} requires ACS, but none was read. This database is likely corrupt: {FileName}"); } keyDefinition.ACS = ACS; } //If it's a segmented key, don't increment so the next key gets added to the same ordinal as an additional segment if (!keyDefinition.Segment) { currentKeyNumber++; } #if DEBUG logger.Info("----------------"); logger.Info("Loaded Key Definition:"); logger.Info("----------------"); logger.Info($"Number: {keyDefinition.Number}"); logger.Info($"Data Type: {keyDefinition.DataType}"); logger.Info($"Attributes: {keyDefinition.Attributes}"); logger.Info($"Offset: {keyDefinition.Offset}"); logger.Info($"Length: {keyDefinition.Length}"); logger.Info($"Segment: {keyDefinition.Segment}"); logger.Info($"SegmentOf: {keyDefinition.SegmentOf}"); logger.Info("----------------"); #endif if (!Keys.TryGetValue(keyDefinition.Number, out var key)) { key = new BtrieveKey(keyDefinition); Keys.Add(keyDefinition.Number, key); } else { key.Segments.Add(keyDefinition); } keyDefinitionBase += keyDefinitionLength; } // update segment indices foreach (var key in Keys) { var i = 0; foreach (var segment in key.Value.Segments) { segment.SegmentIndex = i++; } } }