diff --git a/DefinedStructs/ConstDataHolder.cpp b/DefinedStructs/ConstDataHolder.cpp index 638e3ac..ba55a03 100644 --- a/DefinedStructs/ConstDataHolder.cpp +++ b/DefinedStructs/ConstDataHolder.cpp @@ -1,4 +1,6 @@ #include "ConstDataHolder.hpp" +#include "DescriptorStruct.hpp" + DataHolder* DataHolder::holder = nullptr; @@ -26,6 +28,7 @@ DataHolder::DataHolder() TRANSFER_LEFTOVER_DATA = Qt::UserRole + 1; TRANSFER_OPTIONAL_HEADER = Qt::UserRole + 2; USBPCAP_HEADER_DATA = Qt::UserRole + 3; + DescriptorPath = std::filesystem::current_path().string() + "\\Descriptors\\"; } /// @@ -53,6 +56,28 @@ void DataHolder::FillDataColorsMap() DataColors.insert(std::pair(UNKNOWN_TRANSFER, { 0,0,255,255 })); } +/// +/// Tries to load new descriptor from file if description for given descriptor exists +/// +/// Type of descriptor we want to get +/// Pointer to loaded descriptor if exists, else nullptr +DescriptorStruct* DataHolder::TryLoadNewDescriptor(BYTE descType) +{ + std::stringstream stream(DescriptorPath, std::ios_base::app | std::ios_base::out); + + stream << "Desc"; + stream << std::setw(2) << std::setfill('0') << (int)descType; + stream << ".txt"; + + if (std::filesystem::exists(stream.str().c_str())) + { + descriptors.emplace_back(std::make_unique(stream.str().c_str(), descType)); + return descriptors[descriptors.size() - 1].get(); + } + + return nullptr; +} + /// /// Get string representation for USBPcap transfer. /// diff --git a/DefinedStructs/ConstDataHolder.hpp b/DefinedStructs/ConstDataHolder.hpp index d133aa9..ba445d9 100644 --- a/DefinedStructs/ConstDataHolder.hpp +++ b/DefinedStructs/ConstDataHolder.hpp @@ -1,9 +1,14 @@ #ifndef CONSTDATAHOLDER_HPP #define CONSTDATAHOLDER_HPP -#include "PacketExternStructs.hpp" #include #include +#include +#include +#include +#include "PacketExternStructs.hpp" + +class DescriptorStruct; /// /// Class used for holding global variables and for converting data constants to string. @@ -24,6 +29,8 @@ class DataHolder std::string GetUsage(const BYTE globalUsage, const BYTE value); std::string GetGenericDesktopUsage(const BYTE value); + DescriptorStruct* TryLoadNewDescriptor(BYTE descType); + /// /// Delete copy constructor due to Singleton pattern /// @@ -54,6 +61,10 @@ class DataHolder /// Map that associate HeaderDataType to its color highlightion /// std::map DataColors; + /// + /// Vector holding all yet known descriptor structs + /// + std::vector> descriptors; private: DataHolder(); void FillDataColorsMap(); @@ -62,6 +73,8 @@ class DataHolder /// Instance of this class. /// static DataHolder* holder; + + std::string DescriptorPath; }; #endif // !CONSTDATAHOLDER_HPP \ No newline at end of file diff --git a/DefinedStructs/DescriptorStruct.cpp b/DefinedStructs/DescriptorStruct.cpp new file mode 100644 index 0000000..36ae2a6 --- /dev/null +++ b/DefinedStructs/DescriptorStruct.cpp @@ -0,0 +1,98 @@ +#include "DescriptorStruct.hpp" +#include + + +/// +/// Fills up fields for this concrete descriptor struct +/// +void DescriptorStruct::FillUpFields() +{ + std::ifstream input; + input.open(filename); + std::string line; + //read first line to get descriptor type and name + if (input.good()) + { + if (std::getline(input, line)) + { + std::istringstream ss(line); + std::string bDescType; + ss >> bDescType; + if (std::stoi(bDescType) != descriptorType) + { + return; + } + std::string descName = ss.str(); + fields.emplace_back(std::make_unique>(descName)); + } + } + while (input.good()) + { + if (std::getline(input, line)) + { + //last added field has more detailed structure (on bit level of detail) + //with given range and values (representing more fields) so fill them up + if (line == "<") + { + fields[fields.size() - 1]->FillUpField(input); + } + else + { + std::istringstream ss(line); + std::string firstWord; + ss >> firstWord; + if (!std::isdigit(firstWord[0])) + { + //means input will be read by the end and has no fixed length (e.g. wstring in String Descriptor) + if (firstWord == "..") + { + std::string type; + ss >> type; + if (type == "wstring") + { + fields.emplace_back(std::make_unique>(ss.str())); + } + } + } + else + { + //according to length determine exact type + //currently supporting 2 most common - BYTE and USHORT + switch (std::stoi(firstWord)) + { + case 1: + { + fields.emplace_back(std::make_unique>(ss.str())); + break; + } + case 2: + { + fields.emplace_back(std::make_unique>(ss.str())); + break; + } + default: + break; + } + } + } + } + } +} + +/// +/// Interpret data according to concrete descriptor +/// +/// Root tree item of tree view +/// Data to be interpreted +/// Pointer to AdditionalDataModel +void DescriptorStruct::InterpretData(TreeItem* rootItem, const QByteArray& data, AdditionalDataModel* additionalDataModel) +{ + const char* packet = data.constData(); + std::size_t dataLeft = data.size(); + for (int i = 0; i < fields.size(); i++) + { + std::size_t value = fields[i]->InterpretField(rootItem, (const unsigned char*)packet, dataLeft, additionalDataModel); + packet += value; + dataLeft -= value; + } +} \ No newline at end of file diff --git a/DefinedStructs/DescriptorStruct.hpp b/DefinedStructs/DescriptorStruct.hpp new file mode 100644 index 0000000..cf1e08d --- /dev/null +++ b/DefinedStructs/DescriptorStruct.hpp @@ -0,0 +1,235 @@ +#ifndef DESCRIPTOR_STRUCT_HPP +#define DESCRIPTOR_STRUCT_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include "PacketExternStructs.hpp" +#include "../Models/AdditionalDataModel.hpp" + +/// +/// Abstract class representing 1 field in descriptor +/// +class AbstractDescriptorField +{ +public: + AbstractDescriptorField() {} + virtual ~AbstractDescriptorField() noexcept {} + virtual void FillUpField(std::ifstream& input) = 0; + virtual std::size_t InterpretField(TreeItem* rootItem, const unsigned char* data, std::size_t size, + AdditionalDataModel* additionalDataModel) = 0; +}; + +/// +/// Structure representing one segment of field which has bit level details +/// +template +struct BitField +{ + /// + /// starting position of analyzing bits + /// + int start; + /// + /// size of analyzing bits + /// + int size; + /// + /// map holding descriptions to given values + /// + std::map descriptions; +}; + +/// +/// Conrete class representing 1 field in descriptor +/// +/// type of concrete field (e.g. INT32, INT64, ...) +template +class DescriptorField : public AbstractDescriptorField +{ +public: + DescriptorField(std::string descr) : description(descr), value() {} + void FillUpField(std::ifstream& input) override; + std::size_t InterpretField(TreeItem* rootItem, const unsigned char* data, std::size_t sizeLeft, + AdditionalDataModel* additionalDataModel) override; +private: + void CharToNumberConvert(const unsigned char* addr) + { + if constexpr (!std::is_same_v && !std::is_same_v) + { + value = T(); + for (int i = sizeof(T); i > 0; i--) + { + value = (value << 8) | addr[i - 1]; + } + } + } + + void CharToConcreteNumberConvert(const unsigned char* addr, T& value) + { + if constexpr (!std::is_same_v && !std::is_same_v) + { + value = T(); + for (int i = sizeof(T); i > 0; i--) + { + value = (value << 8) | addr[i - 1]; + } + } + } + + T GetBitFieldValue(BitField& field); + + std::string description; + std::vector> bitFields; + T value; +}; + +/// +/// fill up one concrete fields along with its bit defined fields +/// +/// Type of descriptor field +/// input stream +template +void DescriptorField::FillUpField(std::ifstream& input) +{ + while (input.good()) + { + std::string line; + if (std::getline(input, line)) + { + //end of the whole field + if (line == ">") + { + return; + } + std::istringstream ss(line); + BitField b; + ss >> b.start; + ss.get(); //separator + ss >> b.size; + char separator = input.get(); + if (input.good() && separator == '{') + { + while (input.good()) + { + if (std::getline(input, line)) + { + //end of bit field + if (line == "}") + { + return; + } + //else fill up one bit field with value - description pairs + ss = std::istringstream(line); + T value; + CharToConcreteNumberConvert((const unsigned char*)(ss.str().c_str()), value); + b.descriptions[value] = ss.str(); + } + } + } + } + } +} + +/// +/// Interpret concrete field (and its possible bit fields) +/// +/// Type of descriptor field +/// Root tree item of tree view +/// Starting point of data to be interpreted +/// Size of data that are still valid and has not been proccessed yet +/// Pointer to AdditionalDataModel instance +/// Size of data that were interpreted +template +std::size_t DescriptorField::InterpretField(TreeItem* rootItem, const unsigned char* data, std::size_t sizeLeft, + AdditionalDataModel* additionalDataModel) +{ + QString hexData; + //wstring ... proccess all left data + if (std::is_same_v) + { + additionalDataModel->CharToHexConvert(&data, sizeLeft, hexData); + std::wstring wString(data, data + sizeLeft); + std::string bString(wString.begin(), wString.end()); + rootItem->AppendChild(new TreeItem(QVector{hexData, description.c_str(), bString.c_str()}, rootItem)); + return sizeLeft; + } + + //used so we can work with std::stringstream and its operator << (it wouldnt work with wstring) + if constexpr (!std::is_same_v && !std::is_same_v) + { + //some other data type + //convert char* to number and fill value data field + CharToNumberConvert(data); + additionalDataModel->CharToHexConvert(&data, sizeof(T), hexData); + std::stringstream ss; + ss << value; + rootItem->AppendChild(new TreeItem(QVector{hexData, description.c_str(), ss.str().c_str()}, rootItem)); + + //check if it carries some bit defined information + //if not, return proccessed size + if (bitFields.empty()) + { + return sizeof(T); + } + //else go through bitFields and interpret them + TreeItem* bitFieldParent = rootItem->Child(rootItem->ChildCount() - 1); + for (std::size_t i = 0; i < bitFields.size(); i++) + { + T bitValue = GetBitFieldValue(bitFields[i]); + auto fieldDesc = bitFields[i].descriptions[bitValue]; + ss.clear(); + ss << value; + std::stringstream ss2; + ss2 << bitValue; + bitFieldParent->AppendChild(new TreeItem(QVector{ + additionalDataModel->ShowBits(bitFields[i].start, bitFields[i].size, ss.str().c_str()), + fieldDesc.c_str(), ss2.str().c_str()}, bitFieldParent)); + } + } +} + +/// +/// Get value of given bit-field +/// +/// Type of descriptor field +/// Concrete field +/// Value of given bit-field +template +T DescriptorField::GetBitFieldValue(BitField& field) +{ + T bitmask = (1 << field.size) - 1; + bitmask = bitmask << field.start; + T bitValue = (value & bitmask) >> field.start; + + return bitValue; +} + +/// +/// Class representing concrete descriptor. +/// +class DescriptorStruct +{ +public: + DescriptorStruct(std::string fName, BYTE dType) : filename(fName), descriptorType(dType) { FillUpFields(); } + /// + /// Fill up concrete tree representing this descriptor + /// + /// + /// + void InterpretData(TreeItem* rootItem, const QByteArray& data, AdditionalDataModel* additionalDataModel); + BYTE descriptorType; +private: + void FillUpFields(); + + std::string filename; + std::vector> fields; +}; + +#endif // !DESCRIPTOR_STRUCT_HPP + diff --git a/Descriptors/Desc03.txt b/Descriptors/Desc03.txt new file mode 100644 index 0000000..d9a58b5 --- /dev/null +++ b/Descriptors/Desc03.txt @@ -0,0 +1,4 @@ +03 STRING DESCRIPTOR +1 bLength +1 bDescriptorType +.. wstring bString \ No newline at end of file diff --git a/Interpreters/ControlTransferInterpreter.cpp b/Interpreters/ControlTransferInterpreter.cpp new file mode 100644 index 0000000..994e168 --- /dev/null +++ b/Interpreters/ControlTransferInterpreter.cpp @@ -0,0 +1,70 @@ +#include "ControlTransferInterpreter.hpp" +#include "../DefinedStructs/DescriptorStruct.hpp" + + +ControlTransferInterpreter::ControlTransferInterpreter(TreeItem* rootItem, QTableWidgetItem* item, AdditionalDataModel* additionalDataModel) : + BaseInterpreter(rootItem, item, additionalDataModel) +{ + this->holder = DataHolder::GetDataHolder(); +} + +/// +/// Iterate through data and interpret known descriptors +/// +void ControlTransferInterpreter::Interpret() +{ + QByteArray data = item->data(holder->TRANSFER_LEFTOVER_DATA).toByteArray(); + const char* packet = data.constData(); + BYTE descriptorType = (BYTE) * (++packet); + DescriptorStruct* descStruct = GetDescriptorStruct(descriptorType); + if (descStruct == nullptr) + { + InterpretUnknownDescriptor((const unsigned char*)packet); + } + else + { + descStruct->InterpretData(rootItem, data, additionalDataModel); + } +} + +/// +/// Gets struct that represents given descriptor. If it doesnt exists yet, try to load it +/// +/// Type of descriptor we want to get struct of +/// +DescriptorStruct* ControlTransferInterpreter::GetDescriptorStruct(BYTE descriptorType) +{ + auto predicate = [descriptorType](std::unique_ptr& desc) {return desc->descriptorType == descriptorType; }; + auto descStructIterator = std::find_if(holder->descriptors.begin(), holder->descriptors.end(), predicate); + if (descStructIterator == holder->descriptors.end()) + { + DescriptorStruct* descStruct = holder->TryLoadNewDescriptor(descriptorType); + return descStruct; + } + else + { + return descStructIterator->get(); + } +} + +/// +/// Inteprets unknown descriptor +/// +/// Pointer to descriptor data +void ControlTransferInterpreter::InterpretUnknownDescriptor(const unsigned char* packet) +{ + rootItem->AppendChild(new TreeItem(QVector{"UNKNOWN_DESCRIPTOR", "", ""}, rootItem)); + TreeItem* unknownDescriptorChild = rootItem->Child(rootItem->ChildCount() - 1); + BYTE descriptorSize = (*packet); + + QString hexData; + additionalDataModel->CharToHexConvert(&packet, 1, hexData); + unknownDescriptorChild->AppendChild(new TreeItem(QVector{hexData, "bLength", descriptorSize}, unknownDescriptorChild)); + + BYTE descriptorType = (*packet); + additionalDataModel->CharToHexConvert(&packet, 1, hexData); + unknownDescriptorChild->AppendChild(new TreeItem(QVector{hexData, "bDescriptorType", descriptorType}, unknownDescriptorChild)); + + additionalDataModel->CharToHexConvert(&packet, descriptorSize - 1, hexData); // -1 for descriptorType + unknownDescriptorChild->AppendChild(new TreeItem(QVector{hexData, "unspecified"}, unknownDescriptorChild)); +} \ No newline at end of file diff --git a/Interpreters/ControlTransferInterpreter.hpp b/Interpreters/ControlTransferInterpreter.hpp new file mode 100644 index 0000000..b0cd112 --- /dev/null +++ b/Interpreters/ControlTransferInterpreter.hpp @@ -0,0 +1,21 @@ +#ifndef CONTROLTRANSFERINTERPRETER_HPP +#define CONTROLTRANSFERINTERPRETER_HPP + +#include "BaseInterpreter.hpp" + +#include + +class ControlTransferInterpreter : public BaseInterpreter +{ +public: + ControlTransferInterpreter(TreeItem* rootItem, QTableWidgetItem* item, AdditionalDataModel* additionalDataModel); + + void Interpret() override; +private: + DescriptorStruct* GetDescriptorStruct(BYTE descriptorType); + void InterpretUnknownDescriptor(const unsigned char* packet); + + DataHolder* holder; +}; + +#endif // !CONTROLTRANSFERINTERPRETER_HPP diff --git a/Interpreters/InterpreterFactory.cpp b/Interpreters/InterpreterFactory.cpp index cb1578c..adcf001 100644 --- a/Interpreters/InterpreterFactory.cpp +++ b/Interpreters/InterpreterFactory.cpp @@ -22,6 +22,23 @@ InterpreterFactory::InterpreterFactory(TreeItem* rootItem, QTableWidgetItem* ite /// Pointer to appropriate interpreter BaseInterpreter* InterpreterFactory::GetInterpreter() { + PUSBPCAP_BUFFER_PACKET_HEADER usbh = (PUSBPCAP_BUFFER_PACKET_HEADER)item-> + data(DataHolder::GetDataHolder()->USBPCAP_HEADER_DATA).toByteArray().constData(); + UCHAR transferType = usbh->transfer; + switch (transferType) + { + case 1: //interrupt + { + return new InterruptTransferInterpreter(rootItem, item, additionalDataModel); + } + case 2: // control + { + + } + default: + break; + } + switch (dataType) { case INTERR_TRANSFER: diff --git a/Models/AdditionalDataModel.cpp b/Models/AdditionalDataModel.cpp index 8773202..2107734 100644 --- a/Models/AdditionalDataModel.cpp +++ b/Models/AdditionalDataModel.cpp @@ -1,6 +1,7 @@ #include "AdditionalDataModel.hpp" -#include "../Interpreters/InterpreterFactory.hpp" +#include "../Interpreters/InterruptTransferInterpreter.hpp" +#include "../Interpreters/ControlTransferInterpreter.hpp" /// /// Constructor of AdditionaldataModel. @@ -56,10 +57,24 @@ QVariant AdditionalDataModel::headerData(int section, Qt::Orientation orientatio /// void AdditionalDataModel::SetupSpecifiedModelData() { - InterpreterFactory factory(rootItem.get(), item, this,dataType); - std::unique_ptr interpreter(factory.GetInterpreter()); - if (interpreter != nullptr) + PUSBPCAP_BUFFER_PACKET_HEADER usbh = (PUSBPCAP_BUFFER_PACKET_HEADER)item-> + data(DataHolder::GetDataHolder()->USBPCAP_HEADER_DATA).toByteArray().constData(); + UCHAR transferType = usbh->transfer; + switch (transferType) { + case 1: //interrupt + { + std::unique_ptr interpreter = std::make_unique(rootItem.get(), item, this); interpreter->Interpret(); + break; + } + case 2: // control + { + std::unique_ptr interpreter = std::make_unique(rootItem.get(), item, this); + interpreter->Interpret(); + break; + } + default: + break; } } \ No newline at end of file diff --git a/Models/TreeItemBaseModel.h b/Models/TreeItemBaseModel.h index c8391bf..747e4da 100644 --- a/Models/TreeItemBaseModel.h +++ b/Models/TreeItemBaseModel.h @@ -55,27 +55,30 @@ template QString TreeItemBaseModel::ShowBits(const uint32_t start, const size_t size, T number) const { std::stringstream stream; - for (int i = 0; i < sizeof(T) * 8; i++) + if constexpr (!std::is_same_v && !std::is_same_v && !std::is_same_v) { - if (i != 0 && i % 4 == 0) + for (int i = 0; i < sizeof(T) * 8; i++) { - stream << ' '; - } - if (i < start || i >= start + size) - { - stream << std::setw(2) << std::setfill(' ') << '.'; + if (i != 0 && i % 4 == 0) + { + stream << ' '; + } + if (i < start || i >= start + size) + { + stream << std::setw(2) << std::setfill(' ') << '.'; + number = number << 1; + continue; + } + if (number & (0x1 << ((sizeof(T) * 8) - 1))) + { + stream << '1'; + } + else + { + stream << '0'; + } number = number << 1; - continue; - } - if (number & (0x1 << ((sizeof(T) * 8) - 1))) - { - stream << '1'; - } - else - { - stream << '0'; } - number = number << 1; } return QString(stream.str().c_str());