Vault
4.1
|
00001 /* 00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved. 00003 This file is part of the Code Vault version 4.1 00004 http://www.bombaydigital.com/ 00005 License: MIT. See LICENSE.md in the Vault top level directory. 00006 */ 00007 00010 #include "vhex.h" 00011 00012 #include "vtextiostream.h" 00013 #include "vbinaryiostream.h" 00014 #include "vchar.h" 00015 00016 // static 00017 void VHex::bufferToHexString(const Vu8* buffer, Vs64 bufferLength, VString& s, bool wantLeading0x) { 00018 int hexStringLength = (int)(bufferLength * 2); // note we don't support string lengths > 32 bits 00019 00020 if (wantLeading0x) 00021 hexStringLength += 2; 00022 00023 s.preflight(hexStringLength); 00024 00025 char* hexStringBuffer = s.buffer(); 00026 int hexStringIndex = 0; 00027 int bufferIndex = 0; 00028 char highNibbleChar; 00029 char lowNibbleChar; 00030 00031 if (wantLeading0x) { 00032 hexStringBuffer[hexStringIndex++] = '0'; 00033 hexStringBuffer[hexStringIndex++] = 'x'; 00034 } 00035 00036 while (bufferIndex < bufferLength) { 00037 VHex::byteToHexChars(buffer[bufferIndex++], &highNibbleChar, &lowNibbleChar); 00038 00039 hexStringBuffer[hexStringIndex++] = highNibbleChar; 00040 hexStringBuffer[hexStringIndex++] = lowNibbleChar; 00041 } 00042 00043 s.postflight(hexStringLength); 00044 } 00045 00046 // static 00047 void VHex::hexStringToBuffer(const VString& hexDigits, Vu8* buffer, bool hasLeading0x) { 00048 int digitsIndex = 0; 00049 int bufferIndex = 0; 00050 int numDigits = hexDigits.length(); 00051 bool oddNumHexDigits = (numDigits % 2) != 0; 00052 char highNibbleChar = '0'; 00053 char lowNibbleChar; 00054 00055 if (hasLeading0x) { 00056 digitsIndex += 2; 00057 } 00058 00059 while (digitsIndex < numDigits) { 00060 if (oddNumHexDigits) { 00061 // First time through, use high nibble of 0, treat next digit as low nibble. 00062 lowNibbleChar = hexDigits.charAt(digitsIndex++); 00063 oddNumHexDigits = false; // done compensating for odd length, don't do again 00064 } else { 00065 highNibbleChar = hexDigits.charAt(digitsIndex++); 00066 lowNibbleChar = hexDigits.charAt(digitsIndex++); 00067 } 00068 00069 buffer[bufferIndex++] = VHex::hexCharsToByte(highNibbleChar, lowNibbleChar); 00070 } 00071 } 00072 00073 // static 00074 void VHex::stringToHex(const VString& text, VString& hexDigits, bool wantLeading0x) { 00075 VHex::bufferToHexString(text.getDataBufferConst(), static_cast<Vs64>(text.length()), hexDigits, wantLeading0x); 00076 } 00077 00078 // static 00079 void VHex::hexToString(const VString& hexDigits, VString& text, bool hasLeading0x) { 00080 int outputLength = hexDigits.length() / 2; 00081 00082 if (hasLeading0x) { 00083 --outputLength; 00084 } 00085 00086 text.preflight(outputLength); 00087 VHex::hexStringToBuffer(hexDigits, text.getDataBuffer(), hasLeading0x); 00088 text.postflight(outputLength); 00089 } 00090 00091 // static 00092 void VHex::byteToHexString(Vu8 byteValue, VString& s) { 00093 char hexChars[3]; 00094 00095 hexChars[2] = 0; 00096 VHex::byteToHexChars(byteValue, &hexChars[0], &hexChars[1]); 00097 00098 s = hexChars; 00099 } 00100 00101 // static 00102 void VHex::byteToHexChars(Vu8 byteValue, char* highNibbleChar, char* lowNibbleChar) { 00103 *highNibbleChar = VHex::nibbleToHexChar(static_cast<Vu8>(byteValue >> 4)); 00104 *lowNibbleChar = VHex::nibbleToHexChar(static_cast<Vu8>(byteValue & 0x0F)); 00105 } 00106 00107 // static 00108 Vu8 VHex::hexStringToByte(const char* twoHexDigits) { 00109 Vu8 highNibble = VHex::hexCharToNibble(twoHexDigits[0]); 00110 Vu8 lowNibble = VHex::hexCharToNibble(twoHexDigits[1]); 00111 00112 return static_cast<Vu8>((highNibble << 4) | lowNibble); 00113 } 00114 00115 // static 00116 Vu8 VHex::hexCharsToByte(char highNibbleChar, char lowNibbleChar) { 00117 Vu8 highNibble = VHex::hexCharToNibble(highNibbleChar); 00118 Vu8 lowNibble = VHex::hexCharToNibble(lowNibbleChar); 00119 00120 return static_cast<Vu8>((highNibble << 4) | lowNibble); 00121 } 00122 00123 // static 00124 char VHex::nibbleToHexChar(Vu8 nibbleValue) { 00125 nibbleValue &= 0x0F; // ensure that the high 4 bits are zero 00126 00127 if (nibbleValue < 0x0A) 00128 return static_cast<char>('0' + nibbleValue); 00129 else 00130 return static_cast<char>('A' + (nibbleValue - 0x0A)); 00131 } 00132 00133 // static 00134 Vu8 VHex::hexCharToNibble(char hexChar) { 00135 if ((hexChar >= '0') && (hexChar <= '9')) 00136 return static_cast<Vu8>(hexChar - '0'); 00137 else if ((hexChar >= 'A') && (hexChar <= 'F')) 00138 return static_cast<Vu8>(0x0A + hexChar - 'A'); 00139 else if ((hexChar >= 'a') && (hexChar <= 'f')) 00140 return static_cast<Vu8>(0x0A + hexChar - 'a'); 00141 else 00142 return 0; 00143 } 00144 00145 // static 00146 void VHex::bufferToPrintableASCIIString(const Vu8* buffer, Vs64 bufferLength, VString& s) { 00147 for (int i = 0; i < (int) bufferLength; ++i) { 00148 char asciiValue = static_cast<char>(buffer[i]); 00149 if ((asciiValue <= 0x20) || (asciiValue > 0x7E)) { 00150 asciiValue = '.'; 00151 } 00152 s += asciiValue; 00153 } 00154 } 00155 00156 // static 00157 void VHex::readHexDump(VTextIOStream& inputStream, VBinaryIOStream& outputStream) { 00158 VString line; 00159 00160 do { 00161 inputStream.readLine(line); 00162 00163 if (line.isEmpty()) { 00164 break; 00165 } 00166 00167 // Remove typical leading indent spaces. 00168 line.trim(); 00169 00170 // Lines we want must start with either of these offset formats: 00171 // NNNNNNNN: (where N is a decimal digit) 00172 // 0xNNNNNNNN: (where N is a hexadecimal digit) 00173 // Anything else is a line to be skipped. 00174 00175 int nextHexByteOffset = -1; 00176 if (line.startsWith("0x") && (line.length() > 10) && (line[10] == ':')) { 00177 nextHexByteOffset = 11; 00178 } else if (line.startsWith("0") && (line.length() > 8) && (line[8] == ':')) { 00179 nextHexByteOffset = 9; 00180 } else { 00181 continue; // skip this line 00182 } 00183 00184 // Read each ' xx' triplet from the line. Space, hex digit, hex digit. 00185 // Anything else indicates end of this line's hex data. 00186 while (nextHexByteOffset > 0) { 00187 if (line.length() < nextHexByteOffset + 3) { 00188 break; 00189 } else if (line[nextHexByteOffset] != ' ') { 00190 break; 00191 } else if (!(VChar(line[nextHexByteOffset+1]).isHexadecimal()) || !(VChar(line[nextHexByteOffset+2]).isHexadecimal())) { 00192 break; 00193 } 00194 00195 // Now we know we have a space and two hex digits we can decode. 00196 Vu8 byteValue = VHex::hexCharsToByte(line[nextHexByteOffset+1], line[nextHexByteOffset+2]); 00197 outputStream.writeU8(byteValue); 00198 00199 nextHexByteOffset += 3; 00200 } 00201 00202 } while (line.isNotEmpty()); 00203 } 00204 00205 VHex::VHex(VTextIOStream* outputStream, int numBytesPerRow, int indentCount, bool labelsInHex, bool showASCIIValues) 00206 : mOutputStream(outputStream) 00207 , mNumBytesPerRow(numBytesPerRow) 00208 , mIndentCount(indentCount) 00209 , mLabelsInHex(labelsInHex) 00210 , mShowASCIIValues(showASCIIValues) 00211 , mStartColumn(0) 00212 , mOffset(0) 00213 , mPendingBufferUsed(0) 00214 , mPendingBuffer(NULL) 00215 , mLineBuffer() 00216 { 00217 mPendingBuffer = new Vu8[mNumBytesPerRow]; 00218 } 00219 00220 VHex::~VHex() { 00221 delete [] mPendingBuffer; 00222 mOutputStream = NULL; // we don't own it, so don't delete it 00223 } 00224 00225 void VHex::printHex(const Vu8* buffer, Vs64 length, Vs64 offset) { 00226 while (length > 0) { 00227 if (mPendingBufferUsed == mNumBytesPerRow - mStartColumn) { 00228 this->_printPending(); 00229 } 00230 00231 mPendingBuffer[mPendingBufferUsed++] = buffer[offset++]; 00232 00233 --length; 00234 } 00235 00236 this->flush(); 00237 } 00238 00239 void VHex::reset() { 00240 this->_printPending(); 00241 00242 mStartColumn = 0; 00243 mOffset = 0; 00244 } 00245 00246 void VHex::flush() { 00247 this->_printPending(); 00248 } 00249 00250 void VHex::_printPending() { 00251 if (mPendingBufferUsed > 0) { 00252 char highNibbleChar; 00253 char lowNibbleChar; 00254 00255 mLineBuffer = VString::EMPTY(); 00256 00257 // Add spaces to indent. 00258 for (int i = 0; i < mIndentCount; ++i) { 00259 mLineBuffer += ' '; 00260 } 00261 00262 // Add the label. 00263 if (mLabelsInHex) { 00264 VString label(VSTRING_ARGS("0x%08X: ", (int) mOffset)); 00265 mLineBuffer += label; 00266 } else { 00267 VString label(VSTRING_ARGS("%08lld: ", mOffset)); 00268 mLineBuffer += label; 00269 } 00270 00271 // If we're starting mid-row, add spaces to indent. 00272 for (int i = 0; i < mStartColumn; ++i) { 00273 mLineBuffer += " "; 00274 } 00275 00276 // Now append our hex data. 00277 for (int i = 0; i < mPendingBufferUsed; ++i) { 00278 VHex::byteToHexChars(mPendingBuffer[i], &highNibbleChar, &lowNibbleChar); 00279 mLineBuffer += highNibbleChar; 00280 mLineBuffer += lowNibbleChar; 00281 mLineBuffer += ' '; 00282 } 00283 00284 // Now do the ASCII stuff if necessary. 00285 if (mShowASCIIValues) { 00286 // If we're starting mid-row, add spaces to indent. 00287 for (int i = mPendingBufferUsed; i < mNumBytesPerRow - mStartColumn; ++i) { 00288 mLineBuffer += " "; 00289 } 00290 00291 mLineBuffer += " "; 00292 00293 for (int i = 0; i < mStartColumn; ++i) { 00294 mLineBuffer += ' '; 00295 } 00296 00297 // Append the ASCII data 00298 for (int i = 0; i < mPendingBufferUsed; ++i) { 00299 char asciiValue = (char) mPendingBuffer[i]; 00300 00301 if ((asciiValue <= 0x20) || (asciiValue > 0x7E)) { 00302 asciiValue = '.'; 00303 } 00304 00305 mLineBuffer += asciiValue; 00306 } 00307 } 00308 00309 // Keep track of column in case of split lines 00310 if (mNumBytesPerRow == 0) { 00311 mStartColumn = 0; 00312 } else { 00313 mStartColumn = (mStartColumn + mPendingBufferUsed) % mNumBytesPerRow; 00314 } 00315 00316 // Reset pending indicators 00317 mOffset += mPendingBufferUsed; 00318 mPendingBufferUsed = 0; 00319 00320 // Finally, shove the string out to the stream or output pipe. 00321 if (mOutputStream == NULL) { 00322 std::cout << mLineBuffer.chars() << std::endl; 00323 } else { 00324 mOutputStream->writeLine(mLineBuffer); 00325 } 00326 } 00327 } 00328