> >>(1) Sample input and output
> local abc > > abc=repl('x', 256) > abcshort = '1234546' > a2345678900=.f. > a2345678901=.t. > a2345678902=804.3 > a2345678903={^2012/01/01} > a2345678904={^2012/01/01 01:02:03} > a2345678905=$-1234.5678 > a2345678906 = null > > > dime aa[2] > aa[1] = 1 > aa[2] = 2 > > dime LongNameArray[4, 3] > local i, j > for i = 1 to alen(LongNameArray, 1) > for j = 1 to alen(LongNameArray, 2) > LongNameArray[i, j] = i + j/100 > endfor > endfor > > save to 'd:\tmp\1.txt' >>
> class VfpVariableTest > { > public static void Main() > { > byte[] bytes = File.ReadAllBytes(@"d:\tmp\1.txt"); > > Dictionary<String, VfpVariable> dict = VfpMemoryRestore.GetVariables(bytes, false); > > foreach (var v in dict) > { > Console.Write("Name: {0} Type: {1}", v.Value.Name, v.Value.Type); > > if (v.Value.Type == VfpValueType.Array) > { > if (v.Value.Value is VfpVariable[]) > { // one dimensional array > VfpVariable[] vfpArray = v.Value.Value as VfpVariable[]; > Console.WriteLine(); > for (int i = 0; i < vfpArray.Length; i++) > Console.WriteLine("{0}[{1}] = {2}", vfpArray[i].Name, i, vfpArray[i].Value); > > } > else if (v.Value.Value is VfpVariable[,]) > { // two dimensional array > VfpVariable[,] vfpArray = v.Value.Value as VfpVariable[,]; > Console.WriteLine(); > for (int i = 0; i <= vfpArray.GetUpperBound(0); i++) > for (int j = 0; j <= vfpArray.GetUpperBound(1); j++) > Console.WriteLine("{0}[{1},{2}] = {3}", vfpArray[i, j].Name, i, j, vfpArray[i, j].Value); > } > } > else if (v.Value.Value == null) > { > Console.WriteLine(" Value: {0}", "null"); > } > else if (v.Value.Type == VfpValueType.Char) > { // treat as chars here > byte[] s = (byte[])v.Value.Value; > > Console.WriteLine(" Value: {0}", Encoding.Default.GetString(s, 0, s.Length)); > } > else // no array > { > Console.WriteLine(" Value: {0}", v.Value.Value); > } > } > Console.ReadLine(); > } > } >>output
>Name: ABC Type: Char Value: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx >xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx >Name: A2345678901 Type: Logical Value: True >Name: A2345678902 Type: Numeric Value: 804.3 >Name: A2345678903 Type: Date Value: 01/01/2012 00:00:00 >Name: A2345678904 Type: DateTime Value: 01/01/2012 01:02:03 >Name: I Type: Numeric Value: 5 >Name: AA Type: Array >AA[0] = 1 >AA[1] = 2 >Name: J Type: Numeric Value: 4 >Name: ABCSHORT Type: Char Value: 1234546 >Name: A2345678905 Type: Currency Value: -1234.5678 >Name: A2345678900 Type: Logical Value: False >Name: A2345678906 Type: Logical Value: null >Name: LONGNAMEARRAY Type: Array >LONGNAMEARRAY[0,0] = 1.01 >LONGNAMEARRAY[0,1] = 1.02 >LONGNAMEARRAY[0,2] = 1.03 >LONGNAMEARRAY[1,0] = 2.01 >LONGNAMEARRAY[1,1] = 2.02 >LONGNAMEARRAY[1,2] = 2.03 >LONGNAMEARRAY[2,0] = 3.01 >LONGNAMEARRAY[2,1] = 3.02 >LONGNAMEARRAY[2,2] = 3.03 >LONGNAMEARRAY[3,0] = 4.01 >LONGNAMEARRAY[3,1] = 4.02 >LONGNAMEARRAY[3,2] = 4.03 >>
>(2) Explanation, design, choices > >>We start off with a class
>class VfpVariable > { > public readonly string Name; > public VfpValueType Type { get; private set; } > public VfpVariableScope Scope { get; private set; } > > private object _Value; > public object Value { get; private set;} > // rest of code is at the end > > } >>
> enum VfpValueType > { > Logical = 0, // bool > Numeric, // double > Currency, // decimal > Char, // byte[] > Date, // datetime > DateTime, // datetime > Array, // VfpVariable[] or VfpVariable[ , ] > Object // not implemented > } >>
> enum VfpVariableScope > { > Public, > Private, > Local > } >>
>static class VfpMemoryRestore > { > // rest of the code at the end > > internal static Dictionary<String, VfpVariable> GetVariables(byte[] buffer, bool isMemoField) > { / code at the end > } > } >>
>(3) Full code at the end > >>
> enum VfpValueType > { > Logical = 0, // bool > Numeric, // double > Currency, // decimal > Char, // byte[] > Date, // datetime > DateTime, // datetime > Array, // VfpVariable[] or VfpVariable[ , ] > Object // not implemented > } >>
> enum VfpVariableScope > { > Public, > Private, > Local > } >>
>class VfpVariable > { > public readonly string Name; > public VfpValueType Type { get; private set; } > public VfpVariableScope Scope { get; private set; } > > private object _Value; > public object Value > { > get > { > return _Value; > } > private set > { > > if (value == null) > ; // ok > else switch(Type) > { > case VfpValueType.Array: > if( !(value is VfpVariable[]) && !(value is VfpVariable[,])) > throw new ArgumentException("Wrong value for type"); > break; > > case VfpValueType.Char: > if( !(value is byte[]) ) > throw new ArgumentException("Wrong value for type"); > break; > > case VfpValueType.Currency: > if( ! (value is decimal) ) > throw new ArgumentException("Wrong value for type"); > break; > > case VfpValueType.Date: > > if( !(value is DateTime) || ((DateTime)value).Date != (DateTime)value) > throw new ArgumentException("Wrong value for type"); > break; > case VfpValueType.DateTime: > if( !(value is DateTime) ) > throw new ArgumentException("Wrong value for type"); > break; > case VfpValueType.Logical: > if( !(value is bool) ) > throw new ArgumentException("Wrong value for type"); > break; > case VfpValueType.Numeric: > if (!(value is int) && !(value is uint) && !(value is Double)) > throw new ArgumentException("Wrong value for type"); > break; > > case VfpValueType.Object: > throw new NotImplementedException("Object type"); > break; > default: > throw new NotImplementedException("Type"); > > } > > _Value = value; > } > } > //______________________________________________________________________ > public VfpVariable(string name, VfpValueType type, object value, VfpVariableScope scope) > { > CheckName(name); > Name = name; > Type = type; // type must be assiged before value (see set accessor of Value) > Value = value; > Scope = scope; > } > //______________________________________________________________________ > // other checks to add > private void CheckName(string name) > { > if (String.IsNullOrWhiteSpace(name)) > throw new ArgumentException("Invalid name"); > } > //______________________________________________________________________ > } >>
> static class VfpMemoryRestore > { > /* > * 00-10 char[11] upper(name) '\0 terminated > * 11-11 char Type, if lower(): extended name, except 0 (Null) > * 12-15 length strings > 254 ( type H, includes \0) > * 16-16 Length for > * - char <= 255 chars ( includes \0' ) > * > * > * not for > * - logical > * - numeric > * - date > * - dateTime > * - null - not always > * > * > * > * 17-17 precision for numeric > * 20-23 ?? > * 24-24 0x00 = Private/public, 0x01 = local > * 25-25 constant : 0x03 > * 26-31 ? > && case extended name > * 32-33 len Name if extended name > * 34-... name > * ..-.. Value > && otherwise > * 32 - Value (or type if 11-11 == '0' > * > */ > private const int EndOfFileMarker = 0x1a; > private const int VariableTypeOffset = 11; > private const int ValueOffset = 0x20; > private const int ScopeOffset = 24; > private const int ContentLengthOffset = 16; > private const int LongStringContentLengthOffset = 12; > > //______________________________________________________________________ > internal static Dictionary<String, VfpVariable> GetVariables(byte[] buffer, bool isMemoField) > { > Dictionary<String, VfpVariable> dict = new Dictionary<string, VfpVariable>(); > > int offset = isMemoField ? 2 : 0; > > VfpVariable v; > > while (offset < buffer.Length) > { > if (offset == buffer.Length - 1 && buffer[offset] == EndOfFileMarker) > break; > > v = GetVariable(buffer, ref offset); > dict.Add(v.Name, v); > } > > return dict; > > } > //______________________________________________________________________ > private static VfpVariable GetVariable(byte[] buffer, ref int offset) > { > int valueOffset; > > // Name > // valueOffset depends on the length of the name > string name = GetName(buffer, offset, out valueOffset); > > // Scope > VfpVariableScope scope = GetScope(buffer[offset + ScopeOffset]); > > > //Type and value > VfpValueType type; > > object value; > int newOffset; > byte[] x; // for strings > int contentLength; > > byte varType = buffer[offset + VariableTypeOffset]; > > if (varType == (byte)'0') > { > type = GetVarType(buffer[offset + valueOffset]); > contentLength = 1; > value = null; > newOffset = offset + valueOffset + contentLength; > } > else > { > type = GetVarType(varType); > > switch (type) > { > case VfpValueType.Char: > if (varType == (byte)'C' || varType == (byte)'c') > { > contentLength = buffer[offset + ContentLengthOffset]; > x = new byte[contentLength - 1]; > Array.Copy(buffer, offset + valueOffset, x, 0, contentLength - 1); > value = x; > > } > else > { > contentLength = BitConverter.ToInt32(buffer, offset + LongStringContentLengthOffset); > x = new byte[contentLength - 1]; > Array.Copy(buffer, offset + valueOffset, x, 0, contentLength - 1); > value = x; > } > newOffset = offset + valueOffset + contentLength; > break; > > case VfpValueType.Logical: > contentLength = 1; > value = (buffer[offset +valueOffset] != 0x00); > newOffset = offset + valueOffset + contentLength; > break; > > case VfpValueType.Numeric: > contentLength = 8; > value = BitConverter.ToDouble(buffer, offset + valueOffset); > newOffset = offset + valueOffset + contentLength; > break; > > case VfpValueType.Date : > case VfpValueType.DateTime: > contentLength = 8; > double d = BitConverter.ToDouble(buffer, offset + valueOffset); > if (d == 0.0) > value = null; > else > { > if (type == VfpValueType.Date) > d = d < 0.0 ? Math.Ceiling(d) : Math.Floor(d); > value = DateTime.FromOADate(d - 2415019.0); //2415019 is julian day of 1899 dec 30 > } > newOffset = offset + valueOffset + contentLength; > break; > > case VfpValueType.Currency: > contentLength = 8; > Int64 v64 = BitConverter.ToInt64(buffer, offset + valueOffset); > value = (decimal)v64 / 10000.0m; > newOffset = offset + valueOffset + contentLength; > break; > > case VfpValueType.Array: > contentLength = 4; // for rowCount and columnCount > uint rowCount = BitConverter.ToUInt16(buffer, offset + valueOffset); > uint colCount = BitConverter.ToUInt16(buffer, offset + valueOffset + sizeof(UInt16)); > newOffset = offset + valueOffset + contentLength; > > if (colCount == 0) > { // one dimensional array > VfpVariable[] array = new VfpVariable[rowCount]; > for (int i = 0; i < rowCount; i++) > array[i] = GetVariable(buffer, ref newOffset); > > value = array; > } > else > { // two dimensional array > VfpVariable[,] array = new VfpVariable[rowCount, colCount]; > for (int i = 0; i < rowCount; i++) > for( int j = 0; j < colCount; j++) > array[i, j] = GetVariable(buffer, ref newOffset); > > value = array; > } > break; > > default: > throw new NotImplementedException(String.Format("Program error: type {0}", type)); > } > } > > VfpVariable v = new VfpVariable(name, type, value, scope); > offset = newOffset; > return v; > > } > //______________________________________________________________________ > private static VfpValueType GetVarType(byte varType) > { > VfpValueType type; > > switch (varType) > { > case (byte)'C': > case (byte)'c': > case (byte)'H': > case (byte)'h': > type = VfpValueType.Char; > break; > > case (byte)'L': > case (byte)'l': > type = VfpValueType.Logical; > break; > > case (byte)'N': > case (byte)'n': > type = VfpValueType.Numeric; > break; > > case (byte)'D': > case (byte)'d': > type = VfpValueType.Date; > break; > > case (byte)'T': > case (byte)'t': > type = VfpValueType.DateTime; > break; > > case (byte)'Y': > case (byte)'y': > type = VfpValueType.Currency; > break; > > case (byte)'A': > case (byte)'a': > type = VfpValueType.Array; > break; > > default: > throw new NotImplementedException(String.Format("Program error: GetVarType {0}", varType.ToString())); > } > return type; > } > //______________________________________________________________________ > private static string GetName(byte[] buffer, int offset, out int valueOffset) > { > int nameOffset, count; > > if (buffer[offset] != 0) //short name, null terminated > { // name <= 10 bytes > valueOffset = ValueOffset; > nameOffset = offset; > count = Array.IndexOf<byte>(buffer, 0, nameOffset) - nameOffset; > if (count < 0 || count > 10) > throw new Exception("Name inconsistency"); > > } > else > { > count = BitConverter.ToInt16(buffer, offset + ValueOffset); > valueOffset = ValueOffset + count + 2; > nameOffset = offset + ValueOffset + 2; > } > string name = Encoding.Default.GetString(buffer, nameOffset, count); > > return name; > } > //______________________________________________________________________ > private static VfpVariableScope GetScope(byte b) > { > VfpVariableScope scope; > switch (b) > { > case 0x00: > scope = VfpVariableScope.Private; > break; > > case 0x01: > scope = VfpVariableScope.Local; > break; > > default: > throw new NotImplementedException(String.Format("Program error: Scope {0}", b.ToString())); > } > return scope; > } > //______________________________________________________________________ > } >