/// <summary> /// Scans through the array and estimates the byte size of the field in this case. /// </summary> /// <param name="data">The data.</param> /// <param name="dataOffset">The data offset.</param> /// <returns>The byte size of the field.</returns> public int GetInstBytes(byte[] data, long dataOffset) { int nCount; int nInstBytes = 0; long offset = dataOffset; // Hard sized fields just return their constant size if (NumBytes > -1) { return(NumBytes); } // Pointers have a 4 byte integer count and a 4 byte uint offset if (Pointer != '\0') { nCount = Hfa.ReadInt32(data, offset); offset += 8; nInstBytes += 8; } else { // Anything other than a pointer only can have one item. nCount = 1; } if (ItemType == 'b' && nCount != 0) { // BASEDATA int nRows = Hfa.ReadInt32(data, offset); offset += 4; int nColumns = Hfa.ReadInt32(data, offset); offset += 4; HfaEpt baseItemType = (HfaEpt)Hfa.ReadInt16(data, offset); nInstBytes += 12; nInstBytes += ((baseItemType.GetBitCount() + 7) / 8) * nRows * nColumns; } else if (ItemObjectType == null) { nInstBytes += nCount * HfaDictionary.GetItemSize(ItemType); } else { for (int i = 0; i < nCount; i++) { nInstBytes += ItemObjectType.GetInstBytes(data, offset + nInstBytes); } } return(nInstBytes); }
/// <summary> /// Gets the count for a particular instance of a field. This will normally be /// the built in value, but for variable fields, this is extracted from the /// data itself. /// </summary> /// <param name="data">The data.</param> /// <param name="dataOffset">The data offset.</param> /// <returns>The count for a particular instance of a field.</returns> public int GetInstCount(byte[] data, long dataOffset) { if (Pointer == '\0') { return(ItemCount); } if (ItemType == 'b') { int numRows = Hfa.ReadInt32(data, dataOffset + 8); int numColumns = Hfa.ReadInt32(data, dataOffset + 12); return(numRows * numColumns); } return(Hfa.ReadInt32(data, dataOffset)); }
/// <summary> /// Sets the value. /// </summary> /// <param name="sField">The field.</param> /// <param name="indexValue">The index value.</param> /// <param name="data">The data.</param> /// <param name="dataOffset">The data offset.</param> /// <param name="dataSize">The data size.</param> /// <param name="reqType">The req type.</param> /// <param name="value">The value.</param> /// <exception cref="HfaPointerInsertNotSupportedException">Attempting to insert a pointer is not supported.</exception> /// <exception cref="HfaEnumerationNotFoundException">Occurs if the specified value is not a valid member of the enumeration for this field.</exception> public void SetInstValue(string sField, int indexValue, byte[] data, long dataOffset, int dataSize, char reqType, object value) { // If this field contains a pointer, then we wil adjust the data offset relative to it. // This updates the offset info, but doesn't change the first four bytes. if (Pointer != '\0') { int nCount; if (NumBytes > -1) { nCount = ItemCount; } else if (reqType == 's' && (ItemType == 'c' || ItemType == 'C')) { // Either a string or a character array nCount = 0; IEnumerable <char> strVal = value as IEnumerable <char>; if (strVal != null) { nCount = strVal.Count() + 1; } } else { nCount = indexValue + 1; } uint nOffset = (uint)nCount; Array.Copy(Hfa.LittleEndian(nOffset), 0, data, dataOffset + 4, 4); dataOffset += 8; dataSize -= 8; } // Pointers to char or uchar arrays requested as strings are handled as a special case if ((ItemType == 'c' || ItemType == 'C') && reqType == 's') { int nBytesToCopy = NumBytes; var strVal = (value as IEnumerable <char>)?.ToArray(); if (strVal != null) { nBytesToCopy = strVal.Length; } if (NumBytes == -1 && strVal != null) { nBytesToCopy = strVal.Length; } // Force a blank erase to remove previous characters byte[] blank = new byte[nBytesToCopy]; Array.Copy(blank, 0, data, dataOffset, nBytesToCopy); if (strVal != null) { ASCIIEncoding ascii = new ASCIIEncoding(); string str = new string(strVal); byte[] charData = ascii.GetBytes(str); Array.Copy(charData, 0, data, dataOffset, charData.Length); } return; } // Translate the passed type into different representations int nIntValue; double dfDoubleValue; if (reqType == 's') { nIntValue = int.Parse((string)value); dfDoubleValue = double.Parse((string)value); } else if (reqType == 'd') { dfDoubleValue = (double)value; nIntValue = Convert.ToInt32((double)value); } else if (reqType == 'i') { dfDoubleValue = Convert.ToDouble((int)value); nIntValue = (int)value; } else if (reqType == 'p') { throw new HfaPointerInsertNotSupportedException(); } else { return; } // Handle by type switch (ItemType) { case 'c': // Char64 case 'C': // Char128 if (reqType == 's') { // handled earlier as special case, } else { data[dataOffset + nIntValue] = (byte)nIntValue; } break; case 'e': // enums are stored as ushort case 's': // little s = ushort type if (ItemType == 'e' && reqType == 's') { nIntValue = EnumNames.IndexOf((string)value); if (nIntValue == -1) { throw new HfaEnumerationNotFoundException((string)value); } } // Each enumeration is stored as a 2-bit unsigned short entry. ushort num = (ushort)nIntValue; Array.Copy(Hfa.LittleEndian(num), 0, data, dataOffset + (2 * indexValue), 2); break; case 'S': // signed short Array.Copy(Hfa.LittleEndian((short)nIntValue), 0, data, dataOffset + (indexValue * 2), 2); break; case 't': case 'l': Array.Copy(Hfa.LittleEndian((uint)nIntValue), 0, data, dataOffset + (indexValue * 4), 4); break; case 'L': // Int32 Array.Copy(Hfa.LittleEndian(nIntValue), 0, data, dataOffset + (indexValue * 4), 4); break; case 'f': // Float (32 bit) float dfNumber = Convert.ToSingle(dfDoubleValue); Array.Copy(Hfa.LittleEndian(dfNumber), 0, data, dataOffset + (indexValue * 4), 4); break; case 'd': // Double (float 64) Array.Copy(Hfa.LittleEndian(dfDoubleValue), 0, data, dataOffset + (8 * indexValue), 8); break; case 'o': // object if (ItemObjectType == null) { break; } int nExtraOffset = 0; if (ItemObjectType.NumBytes > 0) { nExtraOffset = ItemObjectType.NumBytes * indexValue; } else { for (int iIndexCounter = 0; iIndexCounter < indexValue; iIndexCounter++) { nExtraOffset += ItemObjectType.GetInstBytes(data, dataOffset + nExtraOffset); } } if (!string.IsNullOrEmpty(sField)) { ItemObjectType.SetInstValue(sField, data, dataOffset + nExtraOffset, dataSize - nExtraOffset, reqType, value); } break; default: throw new ArgumentException(); } }
/// <summary> /// Extracts the value. /// </summary> /// <param name="pszField">The psz field.</param> /// <param name="nIndexValue">The index value.</param> /// <param name="data">The data.</param> /// <param name="dataOffset">The offset to start from.</param> /// <param name="nDataSize">The data size.</param> /// <param name="reqType">The req type.</param> /// <param name="pReqReturn">The req return.</param> /// <param name="extraOffset">This is used in the case of 'object' pointers where the indexed object is further in the data block.</param> /// <returns>True, if the value could be extracted.</returns> /// <exception cref="HfaInvalidCountException">Occurs if the count is less than zero for the header of a block of base data</exception> public bool ExtractInstValue(string pszField, int nIndexValue, byte[] data, long dataOffset, int nDataSize, char reqType, out object pReqReturn, out int extraOffset) { extraOffset = 0; pReqReturn = null; string returnString = null; int nIntRet = 0; double dfDoubleRet = 0.0; // it doesn't appear like remove this line will have side effects. // int size = GetInstBytes(data, dataOffset); int nInstItemCount = GetInstCount(data, dataOffset); byte[] rawData = null; long offset = dataOffset; // Check the index value is valid. Eventually this will have to account for variable fields. if (nIndexValue < 0 || nIndexValue >= nInstItemCount) { return(false); } // if this field contains a pointer then we will adjust the data offset relative to it. if (Pointer != '\0') { long nOffset = Hfa.ReadUInt32(data, dataOffset + 4); if (nOffset != (uint)(dataOffset + 8)) { // It seems there was originally an exception that would have gone here. // Original exception would have been pszFieldname.pszField points at nOffset, not nDataOffset +8 as expected. } offset += 8; dataOffset += 8; nDataSize -= 8; } // pointers to chara or uchar arrays are read as strings and then handled as a special case. if ((ItemType == 'c' || ItemType == 'C') && reqType == 's') { // Ok, nasty, the original code simply "pointed" to the byte data at this point and cast the pointer. // We probably need to cycle through until we reach the null character. List <char> chars = new List <char>(); while ((char)data[offset] != '\0') { chars.Add((char)data[offset]); offset++; dataOffset++; } pReqReturn = new string(chars.ToArray()); } switch (ItemType) { case 'c': case 'C': nIntRet = data[dataOffset + nIndexValue]; dfDoubleRet = nIntRet; break; case 'e': case 's': { int nNumber = Hfa.ReadUInt16(data, dataOffset + (nIndexValue * 2)); nIntRet = nNumber; dfDoubleRet = nIntRet; if (ItemType == 'e' && nIntRet >= 0 && nIntRet < EnumNames.Count) { returnString = EnumNames[nIntRet]; } } break; case 'S': { short nNumber = Hfa.ReadInt16(data, dataOffset + (nIndexValue * 2)); nIntRet = nNumber; dfDoubleRet = nNumber; } break; case 't': case 'l': { long nNumber = Hfa.ReadUInt32(data, dataOffset + (nIndexValue * 2)); nIntRet = (int)nNumber; dfDoubleRet = nNumber; } break; case 'L': { int nNumber = Hfa.ReadInt32(data, dataOffset + (nIndexValue * 2)); nIntRet = nNumber; dfDoubleRet = nNumber; } break; case 'f': { float fNumber = Hfa.ReadSingle(data, dataOffset + (nIndexValue * 4)); dfDoubleRet = fNumber; nIntRet = Convert.ToInt32(fNumber); } break; case 'd': { dfDoubleRet = Hfa.ReadDouble(data, dataOffset + (nInstItemCount * 8)); nIntRet = Convert.ToInt32(dfDoubleRet); } break; case 'b': { // BASE DATA int nRows = Hfa.ReadInt32(data, dataOffset); int nColumns = Hfa.ReadInt32(data, dataOffset + 4); if (nIndexValue < 0 || nIndexValue >= nRows * nColumns) { return(false); } HfaEpt type = (HfaEpt)Hfa.ReadUInt16(data, dataOffset + 8); // Ignore the 2 byte objecttype value dataOffset += 12; if (nRows < 0 || nColumns < 0) { throw new HfaInvalidCountException(nRows, nColumns); } switch (type) { case HfaEpt.U8: dfDoubleRet = data[dataOffset + nIndexValue]; nIntRet = data[offset + nIndexValue]; break; case HfaEpt.S16: short tShort = Hfa.ReadInt16(data, dataOffset + (nIndexValue * 2)); dfDoubleRet = tShort; nIntRet = tShort; break; case HfaEpt.U16: int tUShort = Hfa.ReadUInt16(data, dataOffset + (nIndexValue * 2)); dfDoubleRet = tUShort; nIntRet = tUShort; break; case HfaEpt.Single: float tSingle = Hfa.ReadSingle(data, dataOffset + (nIndexValue * 4)); dfDoubleRet = tSingle; nIntRet = Convert.ToInt32(tSingle); break; case HfaEpt.Double: dfDoubleRet = Hfa.ReadDouble(data, dataOffset + (nIndexValue * 8)); nIntRet = Convert.ToInt32(dfDoubleRet); break; default: pReqReturn = null; return(false); } } break; case 'o': if (ItemObjectType != null) { if (ItemObjectType.NumBytes > 0) { extraOffset = ItemObjectType.NumBytes * nIndexValue; } else { for (int iIndexCounter = 0; iIndexCounter < nIndexValue; iIndexCounter++) { extraOffset += ItemObjectType.GetInstBytes(data, dataOffset + extraOffset); } } int len = ItemObjectType.GetInstBytes(data, dataOffset + extraOffset); rawData = new byte[len]; Array.Copy(data, dataOffset + extraOffset, rawData, 0, len); if (!string.IsNullOrEmpty(pszField)) { return(ItemObjectType.ExtractInstValue(pszField, rawData, 0, rawData.Length, reqType, out pReqReturn)); } } break; default: return(false); } // Handle appropriate representations switch (reqType) { case 's': { if (returnString == null) { returnString = nIntRet.ToString(); } pReqReturn = returnString; return(true); } case 'd': pReqReturn = dfDoubleRet; return(true); case 'i': pReqReturn = nIntRet; return(true); case 'p': pReqReturn = rawData; return(true); default: return(false); } }
/// <summary> /// Dumps the fields value. /// </summary> /// <param name="fpOut">The output stream.</param> /// <param name="data">The data.</param> /// <param name="dataOffset">The offset to start from.</param> /// <param name="dataSize">The data size.</param> /// <param name="prefix">The prefix.</param> public void DumpInstValue(Stream fpOut, byte[] data, long dataOffset, int dataSize, string prefix) { StreamWriter sw = new StreamWriter(fpOut); int extraOffset; int nEntries = GetInstCount(data, dataOffset); // Special case for arrays of characters or uchars which are printed as a string if ((ItemType == 'c' || ItemType == 'C') && nEntries > 0) { object value; if (ExtractInstValue(null, 0, data, dataOffset, dataSize, 's', out value, out extraOffset)) { sw.WriteLine(prefix + FieldName + " = '" + (string)value + "'"); } else { sw.WriteLine(prefix + FieldName + " = (access failed)"); } } for (int iEntry = 0; iEntry < Math.Min(MaxEntryReport, nEntries); iEntry++) { sw.Write(prefix + FieldName); if (nEntries > 1) { sw.Write("[" + iEntry + "]"); } sw.Write(" = "); object value; switch (ItemType) { case 'f': case 'd': if (ExtractInstValue(null, iEntry, data, dataOffset, dataSize, 'd', out value, out extraOffset)) { sw.Write(value + "\n"); } else { sw.Write("(access failed)\n"); } break; case 'b': int nRows = Hfa.ReadInt32(data, dataOffset + 8); int nColumns = Hfa.ReadInt32(data, dataOffset + 12); HfaEpt type = Hfa.ReadType(data, dataOffset + 16); sw.Write(nRows + "x" + nColumns + " basedata of type " + type); break; case 'e': if (ExtractInstValue(null, iEntry, data, dataOffset, dataSize, 's', out value, out extraOffset)) { sw.Write((string)value); } else { sw.Write("(accessfailed)"); } break; case 'o': if (!ExtractInstValue(null, iEntry, data, dataOffset, dataSize, 'p', out value, out extraOffset)) { sw.WriteLine("(access failed)"); } else { // the pointer logic here is death! Originally the pointer address was used // nByteOffset = ((GByte*) val) - pabyData; // This doesn't work in a safe context. sw.Write("\n"); string szLongFieldName = prefix; if (prefix.Length > 256) { szLongFieldName = prefix.Substring(0, 256); } ItemObjectType.DumpInstValue(fpOut, data, dataOffset + extraOffset, dataSize - extraOffset, szLongFieldName); } break; default: if (ExtractInstValue(null, iEntry, data, dataOffset, dataSize, 'i', out value, out extraOffset)) { sw.WriteLine(value); } else { sw.WriteLine("(access failed)\n"); } break; } } if (nEntries > MaxEntryReport) { sw.Write(prefix + " ... remaining instances omitted ...\n"); } if (nEntries == 0) { sw.Write(prefix + FieldName + " = (no values)\n"); } }