HexFile.cpp
Go to the documentation of this file.
00001 /*
00002         Aseba - an event-based framework for distributed robot control
00003         Copyright (C) 2007--2012:
00004                 Stephane Magnenat <stephane at magnenat dot net>
00005                 (http://stephane.magnenat.net)
00006                 and other contributors, see authors.txt for details
00007         
00008         This program is free software: you can redistribute it and/or modify
00009         it under the terms of the GNU Lesser General Public License as published
00010         by the Free Software Foundation, version 3 of the License.
00011         
00012         This program is distributed in the hope that it will be useful,
00013         but WITHOUT ANY WARRANTY; without even the implied warranty of
00014         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015         GNU Lesser General Public License for more details.
00016         
00017         You should have received a copy of the GNU Lesser General Public License
00018         along with this program. If not, see <http://www.gnu.org/licenses/>.
00019 */
00020 
00021 #include "HexFile.h"
00022 #include <istream>
00023 #include <ostream>
00024 #include <fstream>
00025 #include <iostream>
00026 #include <iomanip>
00027 #include <cassert>
00028 #include <algorithm>
00029 #include <valarray>
00030 
00031 namespace Aseba
00032 {
00033         std::string HexFile::EarlyEOF::toString() const
00034         {
00035                 return FormatableString("Early end of file after %0 lines").arg(line);
00036         }
00037         
00038         std::string HexFile::InvalidRecord::toString() const
00039         {
00040                 return FormatableString("Invalid record at line %0").arg(line);
00041         }
00042         
00043         std::string HexFile::WrongCheckSum::toString() const
00044         {
00045                 return FormatableString("Wrong checksum (%0 instead of %1) at line %2").arg(computedCheckSum, 0, 16).arg(recordCheckSum, 0, 16).arg(line);
00046         }
00047         
00048         std::string HexFile::UnknownRecordType::toString() const
00049         {
00050                 return FormatableString("Unknown record type (%1) at line %0").arg(line).arg(recordType, 0, 16);
00051         }
00052         
00053         std::string HexFile::FileOpeningError::toString() const
00054         {
00055                 return FormatableString("Can't open file %0").arg(fileName);
00056         }
00057         
00058         unsigned HexFile::getUint4(std::istream &stream)
00059         {
00060                 int c = stream.get();
00061                 if (c <= '9')
00062                         return c - '0';
00063                 else if (c <= 'F')
00064                         return c - 'A' + 10;
00065                 else
00066                         return c - 'a' + 10;
00067         }
00068         
00069         unsigned HexFile::getUint8(std::istream &stream)
00070         {
00071                 return (getUint4(stream) << 4) | getUint4(stream);
00072         }
00073         
00074         unsigned HexFile::getUint16(std::istream &stream)
00075         {
00076                 return (getUint8(stream) << 8) | getUint8(stream);
00077         }
00078         
00079         void HexFile::read(const std::string &fileName)
00080         {
00081                 std::ifstream ifs(fileName.c_str());
00082                 
00083                 if (ifs.bad())
00084                         throw FileOpeningError(fileName);
00085                 
00086                 int lineCounter = 0;
00087                 uint32 baseAddress = 0;
00088                 
00089                 while (ifs.good())
00090                 {
00091                         // leading ":" character
00092                         char c;
00093                         ifs >> c;
00094                         if (c != ':')
00095                                 throw InvalidRecord(lineCounter);
00096                         
00097                         uint8 computedCheckSum = 0;
00098                         
00099                         // record data length
00100                         uint8 dataLength = getUint8(ifs);
00101                         computedCheckSum += dataLength;
00102                         
00103                         // short address
00104                         uint16 lowAddress = getUint16(ifs);
00105                         computedCheckSum += lowAddress;
00106                         computedCheckSum += lowAddress >> 8;
00107                         
00108                         // record type
00109                         uint8 recordType = getUint8(ifs);
00110                         computedCheckSum += recordType;
00111                         
00112                         switch (recordType)
00113                         {
00114                                 case 0:
00115                                 // data record
00116                                 {
00117                                         // read data
00118                                         std::vector<uint8> recordData;
00119                                         for (int i = 0; i != dataLength; i++)
00120                                         {
00121                                                 uint8 d = getUint8(ifs);
00122                                                 computedCheckSum += d;
00123                                                 recordData.push_back(d);
00124                                                 //std::cout << "data " << std::hex << (unsigned)d << "\n";
00125                                         }
00126                                         
00127                                         // verify checksum
00128                                         uint8 checkSum = getUint8(ifs);
00129                                         computedCheckSum = 1 + ~computedCheckSum;
00130                                         if (checkSum != computedCheckSum)
00131                                                 throw WrongCheckSum(lineCounter, checkSum, computedCheckSum);
00132                                         
00133                                         // compute address
00134                                         uint32 address = lowAddress;
00135                                         address += baseAddress;
00136                                         //std::cout << "data record at address 0x" << std::hex << address << "\n";
00137                                         
00138                                         // is some place to add
00139                                         bool found = false;
00140                                         for (ChunkMap::iterator it = data.begin(); it != data.end(); ++it)
00141                                         {
00142                                                 size_t chunkSize = it->second.size();
00143                                                 if (address == it->first + chunkSize)
00144                                                 {
00145                                                         // copy new
00146                                                         std::copy(recordData.begin(), recordData.end(), std::back_inserter(it->second));
00147                                                         found = true;
00148                                                         //std::cout << "tail fusable chunk found\n";
00149                                                         break;
00150                                                 }
00151                                                 else if (address + recordData.size() == it->first)
00152                                                 {
00153                                                         // resize
00154                                                         it->second.resize(chunkSize + recordData.size());
00155                                                         // move
00156                                                         std::copy_backward(it->second.begin(), it->second.begin() + chunkSize, it->second.end());
00157                                                         // copy new
00158                                                         std::copy(recordData.begin(), recordData.end(), it->second.begin());
00159                                                         found = true;
00160                                                         //std::cout << "head fusable chunk found\n";
00161                                                         break;
00162                                                 }
00163                                         }
00164                                         if (!found)
00165                                                 data[address] = recordData;
00166                                 }
00167                                 break;
00168                                 
00169                                 case 1:
00170                                 // end of file record
00171                                 for (ChunkMap::iterator it = data.begin(); it != data.end(); ++it)
00172                                 {
00173                                         //std::cout << "End of file found. Address " << it->first << " size " << it->second.size() << "\n";
00174                                 }
00175                                 ifs.close();
00176                                 return;
00177                                 break;
00178                                 
00179                                 case 2:
00180                                 // extended segment address record
00181                                 {
00182                                         if (dataLength != 2)
00183                                                 throw InvalidRecord(lineCounter);
00184                                         
00185                                         // read data
00186                                         uint16 highAddress = getUint16(ifs);
00187                                         computedCheckSum += highAddress;
00188                                         computedCheckSum += highAddress >> 8;
00189                                         baseAddress = highAddress;
00190                                         baseAddress <<= 4;
00191                                         //std::cout << "Extended segment address record (?!): 0x" << std::hex << baseAddress << "\n";
00192                                         
00193                                         // verify checksum
00194                                         uint8 checkSum = getUint8(ifs);
00195                                         computedCheckSum = 1 + ~computedCheckSum;
00196                                         if (checkSum != computedCheckSum)
00197                                                 throw WrongCheckSum(lineCounter, checkSum, computedCheckSum);
00198                                 }
00199                                 break;
00200                                 
00201                                 case 4:
00202                                 // extended linear address record
00203                                 {
00204                                         if (dataLength != 2)
00205                                                 throw InvalidRecord(lineCounter);
00206                                         
00207                                         // read data
00208                                         uint16 highAddress = getUint16(ifs);
00209                                         computedCheckSum += highAddress;
00210                                         computedCheckSum += highAddress >> 8;
00211                                         baseAddress = highAddress;
00212                                         baseAddress <<= 16;
00213                                         //std::cout << "Linear address record: 0x" << std::hex << baseAddress << "\n";
00214                                         
00215                                         // verify checksum
00216                                         uint8 checkSum = getUint8(ifs);
00217                                         computedCheckSum = 1 + ~computedCheckSum;
00218                                         if (checkSum != computedCheckSum)
00219                                                 throw WrongCheckSum(lineCounter, checkSum, computedCheckSum);
00220                                 }
00221                                 break;
00222                                 
00223                                 default:
00224                                 throw UnknownRecordType(lineCounter, recordType);
00225                                 break;
00226                         }
00227                         
00228                         lineCounter++;
00229                 }
00230                 
00231                 throw EarlyEOF(lineCounter);
00232         }
00233         
00234         void HexFile::writeExtendedLinearAddressRecord(std::ofstream &stream, unsigned addr16) const
00235         {
00236                 assert(addr16 <= 65535);
00237                 
00238                 uint8 checkSum = 0;
00239                 
00240                 stream << ":02000004";
00241                 checkSum += 0x02;
00242                 checkSum += 0x00;
00243                 checkSum += 0x00;
00244                 checkSum += 0x04;
00245                 
00246                 stream << std::setw(2);
00247                 stream << (addr16 >> 8);
00248                 checkSum += (addr16 >> 8);
00249                 
00250                 stream << std::setw(2);
00251                 stream << (addr16 & 0xFF);
00252                 checkSum += (addr16 & 0xFF);
00253                 
00254                 checkSum = (~checkSum) + 1;
00255                 stream << std::setw(2);
00256                 stream << (unsigned)checkSum;
00257                 
00258                 stream << "\n";
00259         }
00260         
00261         void HexFile::writeData(std::ofstream &stream, unsigned addr16, unsigned count8, uint8 *data) const
00262         {
00263                 assert(addr16 <= 65535);
00264                 assert(count8 <= 255);
00265                 
00266                 uint8 checkSum = 0;
00267                 
00268                 stream << ":";
00269                 
00270                 stream << std::setw(2);
00271                 stream << count8;
00272                 checkSum += count8;
00273                 
00274                 stream << std::setw(2);
00275                 stream << (addr16 >> 8);
00276                 checkSum += (addr16 >> 8);
00277                 
00278                 stream << std::setw(2);
00279                 stream << (addr16 & 0xFF);
00280                 checkSum += (addr16 & 0xFF);
00281                 
00282                 stream << "00";
00283                 checkSum += 0x00;
00284                 
00285                 for (unsigned i = 0; i < count8; i++)
00286                 {
00287                         stream << std::setw(2);
00288                         stream << (unsigned)data[i];
00289                         checkSum += data[i];
00290                 }
00291                 
00292                 checkSum = (~checkSum) + 1;
00293                 stream << std::setw(2);
00294                 stream << (unsigned)checkSum;
00295                 
00296                 stream << "\n";
00297         }
00298 
00299         void HexFile::strip(unsigned pageSize)
00300         {
00301                 // Build a page map.
00302                 typedef std::map<uint32, std::vector<uint8> > PageMap;
00303                 PageMap pageMap;
00304                 for (ChunkMap::iterator it = data.begin(); it != data.end(); it ++)
00305                 {
00306                         // get page number
00307                         unsigned chunkAddress = it->first;
00308                         // index inside data chunk
00309                         unsigned chunkDataIndex = 0;
00310                         // size of chunk in bytes
00311                         unsigned chunkSize = it->second.size();
00312 
00313                         // copy data from chunk to page
00314                         do
00315                         {
00316                                 // get page number
00317                                 unsigned pageIndex = (chunkAddress + chunkDataIndex) / pageSize;
00318                                 // get address inside page
00319                                 unsigned byteIndex = (chunkAddress + chunkDataIndex) % pageSize;
00320 
00321                                 // if page does not exists, create it
00322                                 if (pageMap.find(pageIndex) == pageMap.end())
00323                                 {
00324                                 //      std::cout << "New page N° " << pageIndex << " for address 0x" << std::hex << chunkAddress << endl;
00325                                         pageMap[pageIndex] = std::vector<uint8>(pageSize, (uint8)0xFF); // New page is created uninitialized
00326                                 }
00327                                 // copy data
00328                                 unsigned amountToCopy = std::min(pageSize - byteIndex, chunkSize - chunkDataIndex);
00329                                 copy(it->second.begin() + chunkDataIndex, it->second.begin() + chunkDataIndex + amountToCopy, pageMap[pageIndex].begin() + byteIndex);
00330 
00331                                 // increment chunk data pointer
00332                                 chunkDataIndex += amountToCopy;
00333                         }
00334                         while (chunkDataIndex < chunkSize);
00335                 }
00336                 
00337                 // Now, for each page, drop it if empty
00338                 data.clear();
00339                 
00340                 for(PageMap::iterator it = pageMap.begin(); it != pageMap.end(); it++)
00341                 {
00342                         int isempty = 1;
00343                         unsigned int i;
00344                         for(i = 0; i < pageSize; i+=4)
00345                                 if(it->second[i] != 0xff || it->second[i+1] != 0xff || it->second[i+2] != 0xff) {
00346                                         isempty = 0;
00347                                         break;
00348                                 }
00349                         if(!isempty)
00350                                 data[it->first * pageSize] = it->second;
00351                 }
00352 
00353 
00354         }
00355         
00356         void HexFile::write(const std::string &fileName) const
00357         {
00358                 int first = 1;
00359                 unsigned highAddress = 0;
00360                 std::ofstream ofs(fileName.c_str());
00361                 
00362                 if (ofs.bad())
00363                         throw FileOpeningError(fileName);
00364                 
00365                 // set format
00366                 ofs.flags(std::ios::hex | std::ios::uppercase);
00367                 ofs.fill('0');
00368                 
00369                 // for each chunk
00370                 for (ChunkMap::const_iterator it = data.begin(); it != data.end(); it++)
00371                 {
00372                         // split address
00373                         unsigned address = it->first;
00374                         unsigned amount = it->second.size();
00375                         
00376                         for (unsigned count = 0; count < amount;)
00377                         {
00378                                 // check if we have changed 64 K boundary, if so, write new high address
00379                                 unsigned newHighAddress = (address + count) >> 16;
00380                                 if (newHighAddress != highAddress || first)
00381                                         writeExtendedLinearAddressRecord(ofs, newHighAddress);
00382                                 first = 0;
00383                                 highAddress = newHighAddress;
00384                                 
00385                                 // write data
00386                                 unsigned rowCount = std::min(amount - count, (unsigned)16);
00387                                 unsigned lowAddress = (address + count) & 0xFFFF;
00388                                 std::valarray<uint8> buffer(rowCount);
00389                                 std::copy(it->second.begin() + count, it->second.begin() + count + rowCount, &buffer[0]);
00390                                 writeData(ofs, lowAddress, rowCount , &buffer[0]);
00391                                 
00392                                 // increment counters
00393                                 count += rowCount;
00394                         }
00395                 }
00396                 
00397                 // write EOF
00398                 ofs << ":00000001FF";
00399         }
00400 }
00401 


aseba
Author(s): Stéphane Magnenat
autogenerated on Thu Jan 2 2014 11:17:16