mirror of
https://github.com/jankae/LibreVNA.git
synced 2026-01-05 08:10:06 +01:00
update LibreCAL API from LibreCAL repository
This commit is contained in:
parent
ef8cdeccd7
commit
c56fdf0ce5
|
|
@ -1,12 +1,54 @@
|
|||
#include "caldevice.h"
|
||||
|
||||
#include "Util/util.h"
|
||||
#include "ui_factoryUpdateDialog.h"
|
||||
#include "Util/QMicroz/qmicroz.h"
|
||||
#include "CustomWidgets/informationbox.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDateTime>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QUrl>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
|
||||
using namespace std;
|
||||
|
||||
static const QStringList factoryProblemSerials = {
|
||||
"5mNkR0s+NzMA", "5mNkR0s2DTcA", "5mNkR0s2gzcA", "5mNkR0s2HTMA", "5mNkR0s2STMA",
|
||||
"5mNkR0s3pzcA", "5mNkR0s8ijcA", "5mNkR0s9jjMA", "5mNkR0sGdCsA", "5mNkR0slqjEA",
|
||||
"5mNkR0srVDMA", "5mNkR0sssjcA", "5mNkR0suHDcA", "5mNkR0svOTcA", "5mNkR0svrzcA",
|
||||
"5mNkR0swkTcA", "5mNkR0s_JzMA", "5mNkR0t+HTUA", "5mNkR0t+JzcA", "5mNkR0t+mjUA",
|
||||
"5mNkR0t0IzIA", "5mNkR0t0TjAA", "5mNkR0t1bTAA", "5mNkR0t1cjUA", "5mNkR0t1HDAA",
|
||||
"5mNkR0t2oTIA", "5mNkR0t2rTAA", "5mNkR0t2uyoA", "5mNkR0t3HC0A", "5mNkR0t3HjIA",
|
||||
"5mNkR0t3MS0A", "5mNkR0t3vC0A", "5mNkR0t3VzUA", "5mNkR0t4aS0A", "5mNkR0t4nDUA",
|
||||
"5mNkR0t5fDcA", "5mNkR0t6cDcA", "5mNkR0t9kzcA", "5mNkR0t9OTcA", "5mNkR0t9UTUA",
|
||||
"5mNkR0tbPTUA", "5mNkR0tbqTUA", "5mNkR0tcHzUA", "5mNkR0tctDUA", "5mNkR0tcwjcA",
|
||||
"5mNkR0tdrDUA", "5mNkR0telDUA", "5mNkR0tgaTkA", "5mNkR0tgRzcA", "5mNkR0tgWTkA",
|
||||
"5mNkR0tikTUA", "5mNkR0tivDcA", "5mNkR0tIXS8A", "5mNkR0tLOTkA", "5mNkR0tqtTkA",
|
||||
"5mNkR0tshS0A", "5mNkR0tsnioA", "5mNkR0tSOzQA", "5mNkR0tSqjQA", "5mNkR0tThjUA",
|
||||
"5mNkR0tTxTUA", "5mNkR0tuVS0A", "5mNkR0tvGC0A", "5mNkR0tvYyoA", "5mNkR0tWljIA",
|
||||
"5mNkR0tXMDIA", "5mNkR0t_UzUA", "5mNkR0uBiC0A", "5mNkR0uBNS0A", "5mNkR0uCPTYA",
|
||||
"5mNkR0uCrTYA", "5mNkR0uDgDIA", "5mNkR0uFjTUA", "5mNkR0uFTjUA", "5mNkR0ugbCoA",
|
||||
"5mNkR0uGIy0A", "5mNkR0uGkzUA", "5mNkR0uGpi0A", "5mNkR0uGRC0A", "5mNkR0uGrjUA",
|
||||
"5mNkR0uGSzUA", "5mNkR0uGVCoA", "5mNkR0uHIyoA", "5mNkR0uHJjUA", "5mNkR0uHmC0A",
|
||||
"5mNkR0uHpzUA", "5mNkR0uHTzUA", "5mNkR0uIdzUA", "5mNkR0uIPDYA", "5mNkR0uIpy0A",
|
||||
"5mNkR0uIqioA", "5mNkR0uISioA", "5mNkR0uITjUA", "5mNkR0uIVi0A", "5mNkR0uJejYA",
|
||||
"5mNkR0uJSDUA", "5mNkR0uKNDYA", "5mNkR0uKnzYA", "5mNkR0uLfTIA", "5mNkR0uLPjIA",
|
||||
"5mNkR0uMbTIA", "5mNkR0uNpy0A", "5mNkR0uOTzAA", "5mNkR0uPhTAA", "5mOUNss0EysA",
|
||||
"5mOUNss1XisA", "5mOUNsseICwA", "5mOUNssiNiwA", "5mOUNssnISwA", "5mOUNssnIyUA",
|
||||
"5mOUNssrmCQA", "5mOUNssRmicA", "5mOUNssUlycA", "5mOUNssURCcA", "5mOUNssvjyQA",
|
||||
"5mOUNssvNSUA", "5mOUNssVXSwA", "5mOUNsszOiQA", "5mOUNst9liQA", "5mOUNstAhSsA",
|
||||
"5mOUNstFSysA", "5mOUNstFwCsA", "5mOUNstGmCUA", "5mOUNstHaCUA", "5mOUNstJkScA",
|
||||
"5mOUNstOxysA", "5mOUNstsYiIA", "5mOUNstYRi0A", "5mOUNsuHqioA", "5mOUNsuhVSgA",
|
||||
"5mOUNsuJLSoA", "5mOUNsuJrioA", "5mOUNsuVdCcA", "5mNkR0s1sjMA", "5mNkR0s3MjMA",
|
||||
"5mNkR0s3XDcA", "5mNkR0slWjEA", "5mNkR0sxkC8A", "5mNkR0t+bzcA", "5mNkR0t1azIA",
|
||||
"5mNkR0taKzUA", "5mNkR0tbbTcA", "5mNkR0tgGTkA", "5mNkR0thGDcA", "5mNkR0thiTcA",
|
||||
"5mNkR0thKDcA", "5mNkR0tRYzQA", "5mNkR0tWBTIA", "5mNkR0uCpDYA", "5mNkR0uDXDYA",
|
||||
"5mNkR0uJUDUA", "5mNkR0uLfjYA", "5mNkR0uMfjIA",
|
||||
};
|
||||
|
||||
static QString getLocalDateTimeWithUtcOffset()
|
||||
{
|
||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||
|
|
@ -35,6 +77,7 @@ CalDevice::CalDevice(QString serial) :
|
|||
usb(new USBDevice(serial))
|
||||
{
|
||||
loadThread = nullptr;
|
||||
transferActive = false;
|
||||
|
||||
// Check device identification
|
||||
auto id = usb->Query("*IDN?");
|
||||
|
|
@ -66,6 +109,74 @@ CalDevice::CalDevice(QString serial) :
|
|||
numPorts = 0;
|
||||
}
|
||||
connect(usb, &USBDevice::communicationFailure, this, &CalDevice::disconnected);
|
||||
connect(this, &CalDevice::updateCoefficientsDone, this, [=]() {
|
||||
transferActive = false;
|
||||
});
|
||||
|
||||
// Check if this device was affected by the factory calibration problem
|
||||
if(factoryProblemSerials.contains(usb->serial())) {
|
||||
// it was, check if it still has bad factory coefficients
|
||||
|
||||
struct point {
|
||||
uint16_t pointNum;
|
||||
double freq;
|
||||
};
|
||||
std::array<point, 3> pointsToCheck = {{
|
||||
{.pointNum = 451, .freq = 1.0},
|
||||
{.pointNum = 551, .freq = 2.0},
|
||||
{.pointNum = 651, .freq = 3.0},
|
||||
}};
|
||||
|
||||
bool hasBadData = false;
|
||||
for(auto point : pointsToCheck) {
|
||||
// grab the point (just checking P12_THROUGH is enough
|
||||
QString ret = usb->Query(":COEFF:GET? FACTORY P12_THROUGH "+QString::number(point.pointNum));
|
||||
auto parts = ret.split(",");
|
||||
if (parts.size() == 9 && parts[0].toDouble() == point.freq) {
|
||||
// got the correct data, check S12 phase
|
||||
auto S12 = std::complex(parts[3].toDouble(), parts[4].toDouble());
|
||||
auto constexpr expectedS12Delay = 498e-12;
|
||||
auto expectedS12Phase = expectedS12Delay * (point.freq * 1e9) * 2*M_PI;
|
||||
auto phase = -arg(S12);
|
||||
// unwrap to expected phase
|
||||
while(phase < expectedS12Phase - M_PI) {
|
||||
phase += 2*M_PI;
|
||||
}
|
||||
double S12Delay = phase / (2*M_PI) / (point.freq * 1e9);
|
||||
qDebug() << "expected delay:" << expectedS12Delay << "factory calibration delay:" << S12Delay;
|
||||
auto error = S12Delay - expectedS12Delay;
|
||||
double tolerance = 17e-12;
|
||||
if(abs(error) > tolerance) {
|
||||
hasBadData = true;
|
||||
break;
|
||||
}
|
||||
// QList<double> possibleErrors({0, -94.7e-12, 34.8e-12});
|
||||
// for(auto e : possibleErrors) {
|
||||
// if(abs(error-e) <= tolerance) {
|
||||
// // this is the error of the LibreCAL
|
||||
// if(e != 0) {
|
||||
// // still has the wrong coefficients
|
||||
// hasBadData = true;
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
} else {
|
||||
qWarning() << "Unexpected point frequency, factory calibration data is dubious";
|
||||
}
|
||||
if(hasBadData) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hasBadData) {
|
||||
if(InformationBox::AskQuestion("Update Factory Coefficients?", "Your LibreCAL with serial number "+usb->serial()+" is affected by "
|
||||
"a mistake in the factory calibration and its factory coefficients (specifically the phase of all "
|
||||
"THRU standard definitions) are slightly wrong. The mistake has been corrected and updated factory "
|
||||
"coefficients are available. Do you want to update the factory coefficients now?", false)) {
|
||||
factoryUpdateDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CalDevice::~CalDevice()
|
||||
|
|
@ -186,6 +297,7 @@ void CalDevice::loadCoefficientSets(QStringList names, QList<int> ports, bool fa
|
|||
}
|
||||
|
||||
abortLoading = false;
|
||||
transferActive = true;
|
||||
if(fast && Util::firmwareEqualOrHigher(firmware, "0.2.1")) {
|
||||
loadThread = new std::thread(&CalDevice::loadCoefficientSetsThreadFast, this, names, ports);
|
||||
} else {
|
||||
|
|
@ -199,6 +311,7 @@ void CalDevice::abortCoefficientLoading()
|
|||
abortLoading = true;
|
||||
loadThread->join();
|
||||
loadThread = nullptr;
|
||||
transferActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +321,7 @@ void CalDevice::saveCoefficientSets()
|
|||
// nothing to do, already done
|
||||
emit updateCoefficientsDone(true);
|
||||
} else {
|
||||
transferActive = true;
|
||||
new std::thread(&CalDevice::saveCoefficientSetsThread, this);
|
||||
}
|
||||
}
|
||||
|
|
@ -356,11 +470,11 @@ void CalDevice::loadCoefficientSetsThreadFast(QStringList names, QList<int> port
|
|||
QString line;
|
||||
if(!usb->receive(&line)) {
|
||||
// failed to receive something, abort
|
||||
return c;
|
||||
return nullptr;
|
||||
}
|
||||
if(line.startsWith("ERROR")) {
|
||||
// something went wront
|
||||
return c;
|
||||
return nullptr;
|
||||
}
|
||||
// ignore start, comments and option line
|
||||
if(line.startsWith("START") || line.startsWith("!") || line.startsWith("#")) {
|
||||
|
|
@ -388,7 +502,7 @@ void CalDevice::loadCoefficientSetsThreadFast(QStringList names, QList<int> port
|
|||
}
|
||||
c->t.AddDatapoint(p);
|
||||
} catch (...) {
|
||||
return c;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -523,19 +637,34 @@ void CalDevice::saveCoefficientSetsThread()
|
|||
for(int i=1;i<=numPorts;i++) {
|
||||
if(set.opens.count(i)) {
|
||||
success &= createCoefficient(set.name, "P"+QString::number(i)+"_OPEN", set.opens[i]->t, set.opens[i]->modified);
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(set.shorts.count(i)) {
|
||||
success &= createCoefficient(set.name, "P"+QString::number(i)+"_SHORT", set.shorts[i]->t, set.shorts[i]->modified);
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(set.loads.count(i)) {
|
||||
success &= createCoefficient(set.name, "P"+QString::number(i)+"_LOAD", set.loads[i]->t, set.loads[i]->modified);
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(int j=i+1;j<=numPorts;j++) {
|
||||
auto c = set.getThrough(i,j);
|
||||
if(c) {
|
||||
success &= createCoefficient(set.name, "P"+QString::number(i)+QString::number(j)+"_THROUGH", c->t, c->modified);
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// prune empty coefficient sets
|
||||
|
|
@ -600,6 +729,194 @@ bool CalDevice::hasModifiedCoefficients()
|
|||
return false;
|
||||
}
|
||||
|
||||
void CalDevice::factoryUpdateDialog()
|
||||
{
|
||||
auto d = new QDialog();
|
||||
auto ui = new Ui::FactoryUpdateDialog();
|
||||
ui->setupUi(d);
|
||||
|
||||
ui->progress->setValue(0);
|
||||
connect(this, &CalDevice::updateCoefficientsPercent, ui->progress, &QProgressBar::setValue, Qt::QueuedConnection);
|
||||
|
||||
auto updateEnableState = [=]() {
|
||||
ui->updateFile->setEnabled(true);
|
||||
ui->updateServer->setEnabled(true);
|
||||
if(ui->updateServer->isChecked()) {
|
||||
// can start
|
||||
ui->startUpdate->setEnabled(true);
|
||||
// file selection disabled
|
||||
ui->file->setEnabled(false);
|
||||
ui->browse->setEnabled(false);
|
||||
} else {
|
||||
// file selection enabled
|
||||
ui->file->setEnabled(true);
|
||||
ui->browse->setEnabled(true);
|
||||
// start possible if some file is selected
|
||||
ui->startUpdate->setEnabled(!ui->file->text().isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
connect(ui->updateServer, &QRadioButton::toggled, this, updateEnableState);
|
||||
connect(ui->file, &QLineEdit::textChanged, this, updateEnableState);
|
||||
connect(ui->browse, &QPushButton::clicked, this, [=](){
|
||||
auto filename = QFileDialog::getOpenFileName(nullptr, "Select factory coefficient file", "", "Zip files (*.zip)", nullptr, QFileDialog::DontUseNativeDialog);
|
||||
if(filename.isEmpty()) {
|
||||
// aborted selection
|
||||
return;
|
||||
} else {
|
||||
ui->file->setText(filename);
|
||||
updateEnableState();
|
||||
}
|
||||
});
|
||||
|
||||
auto addStatus = [=](QString status) {
|
||||
ui->msg->appendPlainText(status);
|
||||
};
|
||||
auto abortWithError = [=](QString error) {
|
||||
ui->progress->setValue(0);
|
||||
|
||||
QTextCharFormat tf;
|
||||
tf = ui->msg->currentCharFormat();
|
||||
tf.setForeground(QBrush(Qt::red));
|
||||
ui->msg->setCurrentCharFormat(tf);
|
||||
ui->msg->appendPlainText(error);
|
||||
tf.setForeground(QBrush(Qt::black));
|
||||
ui->msg->setCurrentCharFormat(tf);
|
||||
|
||||
updateEnableState();
|
||||
};
|
||||
|
||||
connect(this, &CalDevice::updateCoefficientsDone, this, [=](bool success) {
|
||||
// transfer complete
|
||||
|
||||
// pass on communication failures again
|
||||
connect(usb, &USBDevice::communicationFailure, this, &CalDevice::disconnected);
|
||||
if(success) {
|
||||
ui->progress->setValue(100);
|
||||
addStatus("...done");
|
||||
updateEnableState();
|
||||
} else {
|
||||
abortWithError("Transferring coefficients to LibreCAL failed");
|
||||
}
|
||||
});
|
||||
|
||||
auto updateFromFile = [=](QString filename) {
|
||||
addStatus("Unzipping file...");
|
||||
if(!QMicroz::extract(filename, ".")) {
|
||||
abortWithError("Extracting zip file failed");
|
||||
return;
|
||||
}
|
||||
// create coefficient set
|
||||
auto set = CoefficientSet();
|
||||
set.name = "FACTORY";
|
||||
set.ports = 4;
|
||||
set.createEmptyCoefficients();
|
||||
struct Coeff {
|
||||
QString description;
|
||||
QString filename;
|
||||
CoefficientSet::Coefficient *coeff;
|
||||
};
|
||||
std::array<Coeff, 18> coeffs = {{
|
||||
{.description = "Port 1 open", .filename = "P1_OPEN.s1p", .coeff = set.opens[1]},
|
||||
{.description = "Port 1 short", .filename = "P1_SHORT.s1p", .coeff = set.shorts[1]},
|
||||
{.description = "Port 1 load", .filename = "P1_LOAD.s1p", .coeff = set.loads[1]},
|
||||
{.description = "Port 2 open", .filename = "P2_OPEN.s1p", .coeff = set.opens[2]},
|
||||
{.description = "Port 2 short", .filename = "P2_SHORT.s1p", .coeff = set.shorts[2]},
|
||||
{.description = "Port 2 load", .filename = "P2_LOAD.s1p", .coeff = set.loads[2]},
|
||||
{.description = "Port 3 open", .filename = "P3_OPEN.s1p", .coeff = set.opens[3]},
|
||||
{.description = "Port 3 short", .filename = "P3_SHORT.s1p", .coeff = set.shorts[3]},
|
||||
{.description = "Port 3 load", .filename = "P3_LOAD.s1p", .coeff = set.loads[3]},
|
||||
{.description = "Port 4 open", .filename = "P4_OPEN.s1p", .coeff = set.opens[4]},
|
||||
{.description = "Port 4 short", .filename = "P4_SHORT.s1p", .coeff = set.shorts[4]},
|
||||
{.description = "Port 4 load", .filename = "P4_LOAD.s1p", .coeff = set.loads[4]},
|
||||
{.description = "Port 1 to 2 through", .filename = "P12_THROUGH.s2p", .coeff = set.throughs[set.portsToThroughIndex(1,2)]},
|
||||
{.description = "Port 1 to 3 through", .filename = "P13_THROUGH.s2p", .coeff = set.throughs[set.portsToThroughIndex(1,3)]},
|
||||
{.description = "Port 1 to 4 through", .filename = "P14_THROUGH.s2p", .coeff = set.throughs[set.portsToThroughIndex(1,4)]},
|
||||
{.description = "Port 2 to 3 through", .filename = "P23_THROUGH.s2p", .coeff = set.throughs[set.portsToThroughIndex(2,3)]},
|
||||
{.description = "Port 2 to 4 through", .filename = "P24_THROUGH.s2p", .coeff = set.throughs[set.portsToThroughIndex(2,4)]},
|
||||
{.description = "Port 3 to 4 through", .filename = "P34_THROUGH.s2p", .coeff = set.throughs[set.portsToThroughIndex(3,4)]},
|
||||
}};
|
||||
for(const Coeff &c : coeffs) {
|
||||
addStatus("Loading coefficient ("+c.description+")...");
|
||||
try {
|
||||
auto t = Touchstone::fromFile(c.filename.toStdString());
|
||||
c.coeff->t = t;
|
||||
c.coeff->modified = true;
|
||||
QFile rmfile(c.filename);
|
||||
rmfile.remove();
|
||||
} catch (const std::exception &e) {
|
||||
abortWithError("Failed: "+QString::fromStdString(e.what()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
// delete previous factory set if available
|
||||
for(unsigned int i=0;i<coeffSets.size();i++) {
|
||||
if(coeffSets[i].name == "FACTORY") {
|
||||
coeffSets.erase(coeffSets.begin() + i);
|
||||
}
|
||||
}
|
||||
// add set to device
|
||||
coeffSets.push_back(set);
|
||||
// enable factory writing on device
|
||||
addStatus("Enable factory coefficient writes...");
|
||||
bool success = usb->Cmd(":FACT:ENABLEWRITE I_AM_SURE");
|
||||
// delete all factory coefficients (this formats the factory partition)
|
||||
success &= usb->Cmd(":FACT:DEL", 5000);
|
||||
|
||||
if(success) {
|
||||
// start the transfer
|
||||
addStatus("Transferring new coefficients to LibreCAL...");
|
||||
// potential communication failures should not be passed on during this
|
||||
disconnect(usb, &USBDevice::communicationFailure, this, &CalDevice::disconnected);
|
||||
saveCoefficientSets();
|
||||
} else {
|
||||
abortWithError("Failed to erase previous factory calibration");
|
||||
}
|
||||
};
|
||||
|
||||
auto netw = new QNetworkAccessManager();
|
||||
connect(netw, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply) {
|
||||
if(reply->error() == QNetworkReply::NoError) {
|
||||
// success
|
||||
addStatus("Factory coefficients downloaded...");
|
||||
QFile file(serial()+".zip");
|
||||
if(!file.open(QIODevice::WriteOnly)) {
|
||||
abortWithError("Failed to create folder for zipped data");
|
||||
return;
|
||||
}
|
||||
file.write(reply->readAll());
|
||||
file.flush();
|
||||
file.close();
|
||||
updateFromFile(serial()+".zip");
|
||||
// remove zip file
|
||||
file.remove();
|
||||
} else {
|
||||
abortWithError("No factory coefficients found. Check internet access and serial number");
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->startUpdate, &QPushButton::clicked, this, [=](){
|
||||
ui->msg->clear();
|
||||
ui->startUpdate->setEnabled(false);
|
||||
ui->updateFile->setEnabled(false);
|
||||
ui->updateServer->setEnabled(false);
|
||||
ui->file->setEnabled(false);
|
||||
ui->browse->setEnabled(false);
|
||||
if(ui->updateServer->isChecked()) {
|
||||
addStatus("Looking up factory coefficient data...");
|
||||
QUrl url("https://librecal.kaeberich.com/calibrationdata/"+serial()+".zip");
|
||||
netw->get(QNetworkRequest(url));
|
||||
} else {
|
||||
// update from file
|
||||
updateFromFile(ui->file->text());
|
||||
}
|
||||
});
|
||||
|
||||
updateEnableState();
|
||||
|
||||
d->exec();
|
||||
}
|
||||
|
||||
CalDevice::CoefficientSet::Coefficient *CalDevice::CoefficientSet::getOpen(int port)
|
||||
{
|
||||
if(opens.count(port)) {
|
||||
|
|
|
|||
|
|
@ -96,6 +96,11 @@ public:
|
|||
|
||||
bool hasModifiedCoefficients();
|
||||
|
||||
bool coefficientTransferActive() { return transferActive; }
|
||||
|
||||
public slots:
|
||||
void factoryUpdateDialog();
|
||||
|
||||
signals:
|
||||
void updateCoefficientsPercent(int percent);
|
||||
// emitted when all coefficients have been received and it is safe to call all functions again
|
||||
|
|
@ -113,6 +118,7 @@ private:
|
|||
int numPorts;
|
||||
std::thread *loadThread;
|
||||
bool abortLoading;
|
||||
bool transferActive;
|
||||
|
||||
float firmware_major_minor;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,98 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FactoryUpdateDialog</class>
|
||||
<widget class="QDialog" name="FactoryUpdateDialog">
|
||||
<property name="windowModality">
|
||||
<enum>Qt::ApplicationModal</enum>
|
||||
</property>
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>635</width>
|
||||
<height>448</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Factory Coefficient Update</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Updating the factory coefficients is normally not necessary. But in case they have been lost or modified accidentally, they can be set to the original state again.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="updateServer">
|
||||
<property name="text">
|
||||
<string>Update coefficients from manufacturing server</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="updateFile">
|
||||
<property name="text">
|
||||
<string>Update from file</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">buttonGroup</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="file"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="browse">
|
||||
<property name="toolTip">
|
||||
<string>Select file</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="msg">
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progress">
|
||||
<property name="value">
|
||||
<number>24</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="startUpdate">
|
||||
<property name="text">
|
||||
<string>Update Factory Coefficients</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="buttonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
|
@ -72,6 +72,10 @@ USBDevice::USBDevice(QString serial)
|
|||
m_receiveThread = new std::thread(&USBDevice::USBHandleThread, this);
|
||||
usbBuffer = new USBInBuffer(m_handle, LIBUSB_ENDPOINT_IN | 0x03, 65536);
|
||||
connect(usbBuffer, &USBInBuffer::DataReceived, this, &USBDevice::ReceivedData, Qt::DirectConnection);
|
||||
|
||||
// clear any possible queued data in USB device
|
||||
QString dummy;
|
||||
while(receive(&dummy, 10));
|
||||
}
|
||||
|
||||
USBDevice::~USBDevice()
|
||||
|
|
@ -85,11 +89,11 @@ USBDevice::~USBDevice()
|
|||
delete m_receiveThread;
|
||||
}
|
||||
|
||||
bool USBDevice::Cmd(QString cmd)
|
||||
bool USBDevice::Cmd(QString cmd, unsigned int timeout)
|
||||
{
|
||||
QString rcv;
|
||||
flushReceived();
|
||||
bool success = send(cmd) && receive(&rcv);
|
||||
bool success = send(cmd) && receive(&rcv, timeout);
|
||||
if(success && rcv == "") {
|
||||
// empty response expected by commad
|
||||
return true;
|
||||
|
|
@ -100,12 +104,12 @@ bool USBDevice::Cmd(QString cmd)
|
|||
}
|
||||
}
|
||||
|
||||
QString USBDevice::Query(QString query)
|
||||
QString USBDevice::Query(QString query, unsigned int timeout)
|
||||
{
|
||||
flushReceived();
|
||||
if(send(query)) {
|
||||
QString rcv;
|
||||
if(receive(&rcv)) {
|
||||
if(receive(&rcv, timeout)) {
|
||||
return rcv;
|
||||
} else {
|
||||
emit communicationFailure();
|
||||
|
|
@ -212,6 +216,8 @@ void USBDevice::SearchDevices(std::function<bool (libusb_device_handle *, QStrin
|
|||
|
||||
bool USBDevice::send(const QString &s)
|
||||
{
|
||||
lineBuffer.clear();
|
||||
qDebug() << "USB send:" << s;
|
||||
unsigned char data[s.size()+2];
|
||||
memcpy(data, s.toLatin1().data(), s.size());
|
||||
memcpy(&data[s.size()], "\r\n", 2);
|
||||
|
|
@ -237,6 +243,7 @@ bool USBDevice::receive(QString *s, unsigned int timeout)
|
|||
}
|
||||
}
|
||||
*s = lineBuffer.takeFirst();
|
||||
qDebug() << "USB recv:" << *s;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@
|
|||
|
||||
#include "Util/usbinbuffer.h"
|
||||
|
||||
#include <QtGlobal>
|
||||
#ifdef Q_OS_MACOS
|
||||
#include <libusb.h>
|
||||
#else
|
||||
#include <libusb-1.0/libusb.h>
|
||||
#endif
|
||||
|
||||
#include <QString>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
|
@ -19,8 +26,8 @@ public:
|
|||
USBDevice(QString serial = QString());
|
||||
~USBDevice();
|
||||
|
||||
bool Cmd(QString cmd);
|
||||
QString Query(QString query);
|
||||
bool Cmd(QString cmd, unsigned int timeout = 2000);
|
||||
QString Query(QString query, unsigned int timeout = 2000);
|
||||
QString serial() const;
|
||||
|
||||
// Returns serial numbers of all connected devices
|
||||
|
|
|
|||
|
|
@ -130,6 +130,9 @@ HEADERS += \
|
|||
Traces/waterfallaxisdialog.h \
|
||||
Traces/xyplotaxisdialog.h \
|
||||
Traces/tracepolarchart.h \
|
||||
Util/QMicroz/miniz.h \
|
||||
Util/QMicroz/qmicroz.h \
|
||||
Util/QMicroz/tools.h \
|
||||
Util/prbs.h \
|
||||
Util/qpointervariant.h \
|
||||
Util/usbinbuffer.h \
|
||||
|
|
@ -280,6 +283,9 @@ SOURCES += \
|
|||
Traces/tracepolar.cpp \
|
||||
Traces/waterfallaxisdialog.cpp \
|
||||
Traces/xyplotaxisdialog.cpp \
|
||||
Util/QMicroz/miniz.c \
|
||||
Util/QMicroz/qmicroz.cpp \
|
||||
Util/QMicroz/tools.cpp \
|
||||
Util/prbs.cpp \
|
||||
Util/usbinbuffer.cpp \
|
||||
Util/util.cpp \
|
||||
|
|
@ -312,6 +318,7 @@ SOURCES += \
|
|||
LIBS += -lusb-1.0
|
||||
unix:LIBS += -L/usr/lib/
|
||||
win32:LIBS += -L"$$_PRO_FILE_PWD_" # Github actions placed libusb here
|
||||
win32:DEFINES += QMICROZ_LIBRARY
|
||||
osx:INCPATH += /usr/local/include
|
||||
osx:LIBS += -L/usr/local/lib $(shell pkg-config --libs libusb-1.0)
|
||||
|
||||
|
|
@ -330,6 +337,7 @@ FORMS += \
|
|||
Calibration/CalStandardReflectEditDialog.ui \
|
||||
Calibration/CalStandardShortEditDialog.ui \
|
||||
Calibration/CalStandardThroughEditDialog.ui \
|
||||
Calibration/LibreCAL/factoryUpdateDialog.ui \
|
||||
Calibration/LibreCAL/librecaldialog.ui \
|
||||
Calibration/calibrationdialogui.ui \
|
||||
Calibration/calkitdialog.ui \
|
||||
|
|
|
|||
22
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/LICENSE
Normal file
22
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/LICENSE
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
Copyright 2013-2014 RAD Game Tools and Valve Software
|
||||
Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
|
||||
|
||||
All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
7833
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/miniz.c
Normal file
7833
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/miniz.c
Normal file
File diff suppressed because it is too large
Load diff
1422
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/miniz.h
Normal file
1422
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/miniz.h
Normal file
File diff suppressed because it is too large
Load diff
550
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/qmicroz.cpp
Normal file
550
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/qmicroz.cpp
Normal file
|
|
@ -0,0 +1,550 @@
|
|||
/*
|
||||
* This file is part of QMicroz,
|
||||
* under the MIT License.
|
||||
* https://github.com/artemvlas/qmicroz
|
||||
*
|
||||
* Copyright (c) 2024 Artem Vlasenko
|
||||
*/
|
||||
#include "qmicroz.h"
|
||||
#include "tools.h"
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
|
||||
const QString QMicroz::s_zip_ext = QStringLiteral(u".zip");
|
||||
|
||||
QMicroz::QMicroz() {}
|
||||
|
||||
QMicroz::QMicroz(const char* zip_path)
|
||||
{
|
||||
setZipFile(QString(zip_path));
|
||||
}
|
||||
|
||||
QMicroz::QMicroz(const QString &zip_path)
|
||||
{
|
||||
setZipFile(zip_path);
|
||||
}
|
||||
|
||||
QMicroz::QMicroz(const QByteArray &buffered_zip)
|
||||
{
|
||||
setZipBuffer(buffered_zip);
|
||||
}
|
||||
|
||||
QMicroz::~QMicroz()
|
||||
{
|
||||
closeArchive();
|
||||
}
|
||||
|
||||
bool QMicroz::setZipFile(const QString &zip_path)
|
||||
{
|
||||
if (QFileInfo(zip_path).isFile()) {
|
||||
// try to open zip archive
|
||||
mz_zip_archive *_za = tools::za_new(zip_path, tools::ZaReader);
|
||||
|
||||
if (_za) {
|
||||
// close the currently opened one if any
|
||||
closeArchive();
|
||||
|
||||
m_archive = _za;
|
||||
m_zip_path = zip_path;
|
||||
updateZipContents();
|
||||
setOutputFolder(); // zip file's parent folder
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
qWarning() << "Wrong path:" << zip_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QMicroz::setZipBuffer(const QByteArray &buffered_zip)
|
||||
{
|
||||
// open zip archive
|
||||
mz_zip_archive *_za = new mz_zip_archive();
|
||||
|
||||
if (mz_zip_reader_init_mem(_za, buffered_zip.constData(), buffered_zip.size(), 0)) {
|
||||
// close the currently opened one if any
|
||||
closeArchive();
|
||||
|
||||
// set the new one
|
||||
m_archive = _za;
|
||||
updateZipContents();
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << "Failed to open buffered zip";
|
||||
delete _za;
|
||||
return false;
|
||||
}
|
||||
|
||||
void QMicroz::setOutputFolder(const QString &output_folder)
|
||||
{
|
||||
if (output_folder.isEmpty() && !m_zip_path.isEmpty()) {
|
||||
// set zip file's parent folder
|
||||
m_output_folder = QFileInfo(m_zip_path).absolutePath();
|
||||
return;
|
||||
}
|
||||
|
||||
m_output_folder = output_folder;
|
||||
}
|
||||
|
||||
const QString& QMicroz::outputFolder() const
|
||||
{
|
||||
if (m_output_folder.isEmpty())
|
||||
qWarning() << "No output folder setted";
|
||||
|
||||
return m_output_folder;
|
||||
}
|
||||
|
||||
void QMicroz::closeArchive()
|
||||
{
|
||||
if (m_archive) {
|
||||
tools::za_close(static_cast<mz_zip_archive *>(m_archive));
|
||||
m_archive = nullptr;
|
||||
m_zip_contents.clear();
|
||||
m_zip_path.clear();
|
||||
m_output_folder.clear();
|
||||
}
|
||||
}
|
||||
|
||||
const ZipContents& QMicroz::updateZipContents()
|
||||
{
|
||||
m_zip_contents.clear();
|
||||
|
||||
if (!m_archive)
|
||||
return m_zip_contents;
|
||||
|
||||
mz_zip_archive *_za = static_cast<mz_zip_archive *>(m_archive);
|
||||
|
||||
// iterating...
|
||||
for (int it = 0; it < count(); ++it) {
|
||||
const QString _filename = tools::za_item_name(_za, it);
|
||||
if (_filename.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_zip_contents[it] = _filename;
|
||||
}
|
||||
|
||||
return m_zip_contents;
|
||||
}
|
||||
|
||||
qint64 QMicroz::sizeUncompressed() const
|
||||
{
|
||||
qint64 _total_size = 0;
|
||||
|
||||
for (int it = 0; it < count(); ++it) {
|
||||
_total_size += sizeUncompressed(it);
|
||||
}
|
||||
|
||||
return _total_size;
|
||||
}
|
||||
|
||||
const QString& QMicroz::zipFilePath() const
|
||||
{
|
||||
return m_zip_path;
|
||||
}
|
||||
|
||||
const ZipContents& QMicroz::contents() const
|
||||
{
|
||||
return m_zip_contents;
|
||||
}
|
||||
|
||||
int QMicroz::count() const
|
||||
{
|
||||
return mz_zip_reader_get_num_files(static_cast<mz_zip_archive *>(m_archive));
|
||||
}
|
||||
|
||||
bool QMicroz::isFolder(int index) const
|
||||
{
|
||||
return name(index).endsWith(tools::s_sep); // '/'
|
||||
}
|
||||
|
||||
bool QMicroz::isFile(int index) const
|
||||
{
|
||||
const QString _name = name(index);
|
||||
return !_name.isEmpty() && !_name.endsWith(tools::s_sep);
|
||||
}
|
||||
|
||||
QString QMicroz::name(int index) const
|
||||
{
|
||||
return contents().value(index);
|
||||
}
|
||||
|
||||
qint64 QMicroz::sizeCompressed(int index) const
|
||||
{
|
||||
if (!m_archive)
|
||||
return 0;
|
||||
|
||||
return tools::za_file_stat(static_cast<mz_zip_archive *>(m_archive), index).m_comp_size;
|
||||
}
|
||||
|
||||
qint64 QMicroz::sizeUncompressed(int index) const
|
||||
{
|
||||
if (!m_archive)
|
||||
return 0;
|
||||
|
||||
return tools::za_file_stat(static_cast<mz_zip_archive *>(m_archive), index).m_uncomp_size;
|
||||
}
|
||||
|
||||
QDateTime QMicroz::lastModified(int index) const
|
||||
{
|
||||
if (!m_archive)
|
||||
return QDateTime();
|
||||
|
||||
const qint64 _sec = tools::za_file_stat(static_cast<mz_zip_archive *>(m_archive), index).m_time;
|
||||
|
||||
return _sec > 0 ? QDateTime::fromSecsSinceEpoch(_sec) : QDateTime();
|
||||
}
|
||||
|
||||
bool QMicroz::extractAll()
|
||||
{
|
||||
if (!m_archive) {
|
||||
qDebug() << "No zip archive setted";
|
||||
return false;
|
||||
}
|
||||
|
||||
return tools::extract_all_to_disk(static_cast<mz_zip_archive*>(m_archive), outputFolder());
|
||||
}
|
||||
|
||||
// !recreate_path >> place in the root of the output folder
|
||||
bool QMicroz::extractIndex(int index, bool recreate_path)
|
||||
{
|
||||
if (!m_archive) {
|
||||
qDebug() << "No zip archive setted";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index == -1 || outputFolder().isEmpty())
|
||||
return false;
|
||||
|
||||
qDebug() << "Extract:" << index << "from:" << m_zip_path;
|
||||
qDebug() << "Output folder:" << outputFolder();
|
||||
|
||||
// create output folder if it doesn't exist
|
||||
if (!tools::createFolder(outputFolder())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// extracting...
|
||||
// the name is also a relative path inside the archive
|
||||
const QString _filename = name(index);
|
||||
if (_filename.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Extracting:" << _filename;
|
||||
const QString _outpath = tools::joinPath(outputFolder(),
|
||||
recreate_path ? _filename : QFileInfo(_filename).fileName());
|
||||
|
||||
// create a new path on disk
|
||||
const QString _parent_folder = QFileInfo(_outpath).absolutePath();
|
||||
if (!tools::createFolder(_parent_folder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// subfolder, no data to extract
|
||||
if (_filename.endsWith(tools::s_sep)) {
|
||||
qDebug() << "Subfolder extracted";
|
||||
}
|
||||
// extract file
|
||||
else if (!tools::extract_to_file(static_cast<mz_zip_archive *>(m_archive), index, _outpath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Unzip complete.";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QMicroz::extractFile(const QString &file_name, bool recreate_path)
|
||||
{
|
||||
return extractIndex(findIndex(file_name), recreate_path);
|
||||
}
|
||||
|
||||
BufList QMicroz::extractToBuf() const
|
||||
{
|
||||
BufList _res;
|
||||
|
||||
if (!m_archive) {
|
||||
qWarning() << "No archive setted";
|
||||
return _res;
|
||||
}
|
||||
|
||||
qDebug() << "Extracting to RAM:" << (m_zip_path.isEmpty() ? "buffered zip" : m_zip_path);
|
||||
|
||||
// extracting...
|
||||
for (int it = 0; it < count(); ++it) {
|
||||
const QString _filename = name(it);
|
||||
if (_filename.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// subfolder, no data to extract
|
||||
if (_filename.endsWith(tools::s_sep))
|
||||
continue;
|
||||
|
||||
qDebug() << "Extracting:" << (it + 1) << '/' << count() << _filename;
|
||||
|
||||
// extract file
|
||||
const QByteArray _data = extractData(it);
|
||||
if (!_data.isNull())
|
||||
_res[_filename] = _data;
|
||||
}
|
||||
|
||||
qDebug() << "Unzipped:" << _res.size() << "files";
|
||||
return _res;
|
||||
}
|
||||
|
||||
BufFile QMicroz::extractToBuf(int index) const
|
||||
{
|
||||
BufFile _res;
|
||||
|
||||
if (!m_archive) {
|
||||
qWarning() << "No archive setted";
|
||||
return _res;
|
||||
}
|
||||
|
||||
if (index == -1)
|
||||
return _res;
|
||||
|
||||
qDebug() << "Extracting to RAM:" << (m_zip_path.isEmpty() ? "buffered zip" : m_zip_path);
|
||||
|
||||
const QString _filename = name(index);
|
||||
if (_filename.isEmpty())
|
||||
return _res;
|
||||
|
||||
// subfolder, no data to extract
|
||||
if (_filename.endsWith(tools::s_sep)) {
|
||||
qDebug() << "Subfolder, no data to extract:" << _filename;
|
||||
return _res;
|
||||
}
|
||||
|
||||
qDebug() << "Extracting:" << _filename;
|
||||
|
||||
// extract file
|
||||
const QByteArray _data = extractData(index);
|
||||
|
||||
if (!_data.isNull()) {
|
||||
_res.m_name = _filename;
|
||||
_res.m_data = _data;
|
||||
_res.m_modified = lastModified(index);
|
||||
|
||||
qDebug() << "Unzipped:" << _data.size() << "bytes";
|
||||
}
|
||||
|
||||
return _res;
|
||||
}
|
||||
|
||||
BufFile QMicroz::extractFileToBuf(const QString &file_name) const
|
||||
{
|
||||
return extractToBuf(findIndex(file_name));
|
||||
}
|
||||
|
||||
// Recommended in most cases if speed and memory requirements are not critical.
|
||||
QByteArray QMicroz::extractData(int index) const
|
||||
{
|
||||
return tools::extract_to_buffer(static_cast<mz_zip_archive *>(m_archive), index);
|
||||
}
|
||||
|
||||
// This function is faster and consumes less resources than the previous one,
|
||||
// but requires an additional delete operation to avoid memory leaks. ( delete _array.constData(); )
|
||||
QByteArray QMicroz::extractDataRef(int index) const
|
||||
{
|
||||
return tools::extract_to_buffer(static_cast<mz_zip_archive *>(m_archive),
|
||||
index, false);
|
||||
}
|
||||
|
||||
// STATIC functions ---->>>
|
||||
bool QMicroz::extract(const QString &zip_path)
|
||||
{
|
||||
// extract to parent folder
|
||||
return extract(zip_path, QFileInfo(zip_path).absolutePath());
|
||||
}
|
||||
|
||||
bool QMicroz::extract(const QString &zip_path, const QString &output_folder)
|
||||
{
|
||||
//qDebug() << "Extract:" << zip_path;
|
||||
//qDebug() << "Output folder:" << output_folder;
|
||||
|
||||
// open zip archive
|
||||
mz_zip_archive *__za = tools::za_new(zip_path, tools::ZaReader);
|
||||
if (!__za) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// extracting...
|
||||
bool is_success = tools::extract_all_to_disk(__za, output_folder);
|
||||
|
||||
// finish
|
||||
tools::za_close(__za);
|
||||
return is_success;
|
||||
}
|
||||
|
||||
bool QMicroz::compress_here(const QString &path)
|
||||
{
|
||||
QFileInfo __fi(path);
|
||||
|
||||
if (__fi.isFile()) {
|
||||
return compress_file(path);
|
||||
}
|
||||
else if (__fi.isDir()) {
|
||||
return compress_folder(path);
|
||||
}
|
||||
else {
|
||||
qDebug() << "QMicroz::compress | WRONG path:" << path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool QMicroz::compress_here(const QStringList &paths)
|
||||
{
|
||||
if (paths.isEmpty())
|
||||
return false;
|
||||
|
||||
const QString _rootfolder = QFileInfo(paths.first()).absolutePath();
|
||||
const QString _zipname = QFileInfo(_rootfolder).fileName() + s_zip_ext;
|
||||
const QString _zippath = tools::joinPath(_rootfolder, _zipname);
|
||||
|
||||
return compress_list(paths, _zippath);
|
||||
}
|
||||
|
||||
bool QMicroz::compress_file(const QString &source_path)
|
||||
{
|
||||
QFileInfo __fi(source_path);
|
||||
const QString _zip_name = __fi.completeBaseName() + s_zip_ext;
|
||||
const QString _out_path = tools::joinPath(__fi.absolutePath(), _zip_name);
|
||||
|
||||
return compress_file(source_path, _out_path);
|
||||
}
|
||||
|
||||
bool QMicroz::compress_file(const QString &source_path, const QString &zip_path)
|
||||
{
|
||||
if (!QFileInfo::exists(source_path)) {
|
||||
qWarning() << "File not found:" << source_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
return compress_list({ source_path }, zip_path);
|
||||
}
|
||||
|
||||
bool QMicroz::compress_folder(const QString &source_path)
|
||||
{
|
||||
QFileInfo __fi(source_path);
|
||||
const QString _file_name = __fi.fileName() + s_zip_ext;
|
||||
const QString _parent_folder = __fi.absolutePath();
|
||||
const QString _out_path = tools::joinPath(_parent_folder, _file_name);
|
||||
|
||||
return compress_folder(source_path, _out_path);
|
||||
}
|
||||
|
||||
bool QMicroz::compress_folder(const QString &source_path, const QString &zip_path)
|
||||
{
|
||||
if (!QFileInfo::exists(source_path)) {
|
||||
qWarning() << "Folder not found:" << source_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
return compress_list({ source_path }, zip_path);
|
||||
}
|
||||
|
||||
bool QMicroz::compress_list(const QStringList &paths, const QString &zip_path)
|
||||
{
|
||||
if (paths.isEmpty())
|
||||
return false;
|
||||
|
||||
const QString _root = QFileInfo(paths.first()).absolutePath();
|
||||
const QString _info = QStringLiteral(u"Zipping: ")
|
||||
+ (paths.size() == 1 ? paths.first() : (QString::number(paths.size()) + QStringLiteral(u" items")));
|
||||
qDebug() << _info;
|
||||
qDebug() << "Output:" << zip_path;
|
||||
|
||||
// check if all paths are in the same root
|
||||
if (paths.size() > 1) {
|
||||
for (const QString &_path : paths) {
|
||||
if (_root != QFileInfo(_path).absolutePath()) {
|
||||
qWarning() << "ERROR: all items must be in the same folder!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QStringList _worklist;
|
||||
|
||||
// parsing the path list
|
||||
for (const QString &_path : paths) {
|
||||
QFileInfo __fi(_path);
|
||||
if (!__fi.exists() || __fi.isSymLink()) {
|
||||
qWarning() << "Skipped:" << _path;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (__fi.isFile())
|
||||
_worklist << _path;
|
||||
else
|
||||
_worklist << tools::folderContent(_path);
|
||||
}
|
||||
|
||||
return tools::createArchive(zip_path, _worklist, _root);
|
||||
}
|
||||
|
||||
bool QMicroz::compress_buf(const BufList &buf_data, const QString &zip_path)
|
||||
{
|
||||
qDebug() << "Zipping buffered data to:" << zip_path;
|
||||
|
||||
if (buf_data.isEmpty()) {
|
||||
qDebug() << "No input data. Nothing to zip.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// create and open the output zip file
|
||||
mz_zip_archive *__za = tools::za_new(zip_path, tools::ZaWriter);
|
||||
if (!__za) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// process
|
||||
BufList::const_iterator it;
|
||||
for (it = buf_data.constBegin(); it != buf_data.constEnd(); ++it) {
|
||||
if (!tools::add_item_data(__za, it.key(), it.value())) {
|
||||
tools::za_close(__za);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup
|
||||
mz_zip_writer_finalize_archive(__za);
|
||||
tools::za_close(__za);
|
||||
qDebug() << "Done";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QMicroz::compress_buf(const QByteArray &data, const QString &file_name, const QString &zip_path)
|
||||
{
|
||||
BufList _buf;
|
||||
_buf[file_name] = data;
|
||||
return compress_buf(_buf, zip_path);
|
||||
}
|
||||
|
||||
int QMicroz::findIndex(const QString &file_name) const
|
||||
{
|
||||
ZipContents::const_iterator it;
|
||||
|
||||
// full path matching
|
||||
for (it = m_zip_contents.constBegin(); it != m_zip_contents.constEnd(); ++it) {
|
||||
if (file_name == it.value())
|
||||
return it.key();
|
||||
}
|
||||
|
||||
// deep search, matching only the name
|
||||
if (!file_name.contains(tools::s_sep)) {
|
||||
for (it = m_zip_contents.constBegin(); it != m_zip_contents.constEnd(); ++it) {
|
||||
if (!it.value().endsWith('/') // if not a subfolder
|
||||
&& file_name == QFileInfo(it.value()).fileName())
|
||||
{
|
||||
return it.key();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Index not found:" << file_name;
|
||||
return -1;
|
||||
}
|
||||
115
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/qmicroz.h
Normal file
115
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/qmicroz.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* This file is part of QMicroz,
|
||||
* under the MIT License.
|
||||
* https://github.com/artemvlas/qmicroz
|
||||
*
|
||||
* Copyright (c) 2024 Artem Vlasenko
|
||||
*/
|
||||
|
||||
#ifndef QMICROZ_H
|
||||
#define QMICROZ_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#if defined(QMICROZ_LIBRARY)
|
||||
#define QMICROZ_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
#define QMICROZ_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#include <QStringList>
|
||||
#include <QMap>
|
||||
#include <QDateTime>
|
||||
|
||||
// { "path inside zip" : data }
|
||||
using BufList = QMap<QString, QByteArray>;
|
||||
|
||||
struct BufFile {
|
||||
BufFile() {}
|
||||
BufFile(const QString &filename, const QByteArray &data)
|
||||
: m_name(filename), m_data(data) {}
|
||||
|
||||
explicit operator bool() const { return !m_name.isEmpty(); }
|
||||
|
||||
QString m_name; // file name (path inside the archive)
|
||||
QByteArray m_data; // file data (uncompressed)
|
||||
QDateTime m_modified; // date and time of the file's last modification
|
||||
}; // struct BufFile
|
||||
|
||||
// list of files {index : path} contained in the archive
|
||||
using ZipContents = QMap<int, QString>;
|
||||
|
||||
class QMICROZ_EXPORT QMicroz
|
||||
{
|
||||
public:
|
||||
QMicroz();
|
||||
explicit QMicroz(const char *zip_path); // to avoid ambiguity
|
||||
explicit QMicroz(const QString &zip_path); // path to existing zip file
|
||||
explicit QMicroz(const QByteArray &buffered_zip); // existing zip archive buffered in memory
|
||||
~QMicroz();
|
||||
|
||||
explicit operator bool() const { return (bool)m_archive; } // checks whether the archive is setted
|
||||
|
||||
bool setZipFile(const QString &zip_path); // sets and opens the zip for the current object
|
||||
bool setZipBuffer(const QByteArray &buffered_zip); // sets a buffered in memory zip archive
|
||||
void setOutputFolder(const QString &output_folder = QString()); // path to the folder where to place the extracted files; empty --> parent dir
|
||||
void closeArchive(); // closes the currently setted zip and clears the member values
|
||||
|
||||
// Info about the Archive
|
||||
const QString& zipFilePath() const; // returns the path to the current zip-file ("m_zip_path")
|
||||
const QString& outputFolder() const; // the path to place the extracted files
|
||||
qint64 sizeUncompressed() const; // total uncompressed data size (space required for extraction)
|
||||
|
||||
// Zipped Items Info
|
||||
const ZipContents& contents() const; // returns a list of files {index : path} contained in the archive
|
||||
int count() const; // returns the number of items in the archive
|
||||
int findIndex(const QString &file_name) const; // returns the index by the specified file name, -1 if not found
|
||||
bool isFolder(int index) const; // whether the specified index belongs to the folder
|
||||
bool isFile(int index) const; // ... to the file
|
||||
QString name(int index) const; // returns the name/path corresponding to the index
|
||||
qint64 sizeCompressed(int index) const; // returns the compressed size of the file at the specified index
|
||||
qint64 sizeUncompressed(int index) const; // the uncompressed size
|
||||
QDateTime lastModified(int index) const; // returns the file modification date stored in the archive
|
||||
|
||||
// Extraction
|
||||
bool extractAll(); // extracts the archive into the output folder (or the parent one)
|
||||
bool extractIndex(int index, bool recreate_path = true); // extracts the file with index to disk
|
||||
bool extractFile(const QString &file_name, bool recreate_path = true); // finds the file_name and extracts if any; slower than 'extractIndex'
|
||||
|
||||
BufList extractToBuf() const; // unzips all files into the RAM buffer { path : data }
|
||||
BufFile extractToBuf(int index) const; // extracts the selected index only
|
||||
BufFile extractFileToBuf(const QString &file_name) const; // finds the file_name and extracts to Buf; slower than (index)
|
||||
QByteArray extractData(int index) const; // returns the extracted file data; QByteArray owns the copied data
|
||||
QByteArray extractDataRef(int index) const; // QByteArray does NOT own the data! To free memory: delete _array.constData();
|
||||
|
||||
// STATIC functions
|
||||
static bool extract(const QString &zip_path); // extracting the zip into the parent dir
|
||||
static bool extract(const QString &zip_path, const QString &output_folder); // to output_folder
|
||||
|
||||
static bool compress_here(const QString &path); // zips a file or folder (path), >> parent dir
|
||||
static bool compress_here(const QStringList &paths); // paths to files or/and folders
|
||||
|
||||
static bool compress_file(const QString &source_path); // zips a file, >> parent dir
|
||||
static bool compress_file(const QString &source_path, const QString &zip_path); // >> zip_path
|
||||
static bool compress_folder(const QString &source_path); // zips a folder, >> parent dir
|
||||
static bool compress_folder(const QString &source_path, const QString &zip_path); // >> zip_path
|
||||
static bool compress_list(const QStringList &paths, const QString &zip_path); // zips a list of files or folders (paths), >> zip_path
|
||||
|
||||
static bool compress_buf(const BufList &buf_data, const QString &zip_path); // creates an archive with files from the listed paths and data
|
||||
static bool compress_buf(const QByteArray &data,
|
||||
const QString &file_name, const QString &zip_path); // creates an archive (zip_path) containing a file (file_name, data)
|
||||
|
||||
private:
|
||||
const ZipContents& updateZipContents(); // updates the list of current archive contents
|
||||
|
||||
// the void pointer is used to allow the miniz header not to be included
|
||||
void *m_archive = nullptr;
|
||||
|
||||
QString m_zip_path; // path to the current zip file
|
||||
QString m_output_folder; // folder to place the extracted files
|
||||
ZipContents m_zip_contents; // holds the list of current contents { index : filename (or path) }
|
||||
static const QString s_zip_ext; // ".zip"
|
||||
|
||||
}; // class QMicroz
|
||||
|
||||
#endif // QMICROZ_H
|
||||
274
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/tools.cpp
Normal file
274
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/tools.cpp
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* This file is part of QMicroz,
|
||||
* under the MIT License.
|
||||
* https://github.com/artemvlas/qmicroz
|
||||
*
|
||||
* Copyright (c) 2024 Artem Vlasenko
|
||||
*/
|
||||
#include "tools.h"
|
||||
#include <QDebug>
|
||||
#include <QDirIterator>
|
||||
#include <QStringBuilder>
|
||||
|
||||
namespace tools {
|
||||
mz_zip_archive* za_new(const QString &zip_path, ZaType za_type)
|
||||
{
|
||||
// open zip archive
|
||||
mz_zip_archive *_za = new mz_zip_archive();
|
||||
|
||||
// TODO: make a merge (for example, by pointer)
|
||||
bool result = za_type ? mz_zip_writer_init_file(_za, zip_path.toUtf8().constData(), 0)
|
||||
: mz_zip_reader_init_file(_za, zip_path.toUtf8().constData(), 0);
|
||||
|
||||
if (!result) {
|
||||
qWarning() << "Failed to open zip file:" << zip_path;
|
||||
delete _za;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return _za;
|
||||
}
|
||||
|
||||
mz_zip_archive_file_stat za_file_stat(mz_zip_archive* pZip, int file_index)
|
||||
{
|
||||
mz_zip_archive_file_stat file_stat;
|
||||
if (mz_zip_reader_file_stat(pZip, file_index, &file_stat)) {
|
||||
return file_stat;
|
||||
}
|
||||
|
||||
qWarning() << "Failed to get file info:" << file_index;
|
||||
return mz_zip_archive_file_stat();
|
||||
}
|
||||
|
||||
bool za_close(mz_zip_archive* pZip)
|
||||
{
|
||||
if (pZip && mz_zip_end(pZip)) {
|
||||
delete pZip;
|
||||
qDebug() << "Archive closed";
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << "Failed to close archive";
|
||||
return false;
|
||||
}
|
||||
|
||||
QString za_item_name(mz_zip_archive* pZip, int file_index)
|
||||
{
|
||||
return za_file_stat(pZip, file_index).m_filename;
|
||||
}
|
||||
|
||||
bool createArchive(const QString &zip_path, const QStringList &item_paths, const QString &zip_root)
|
||||
{
|
||||
if (item_paths.isEmpty()) {
|
||||
qDebug() << "No input paths. Nothing to zip.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// create and open the output zip file
|
||||
mz_zip_archive *_za = za_new(zip_path, ZaWriter);
|
||||
if (!_za) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// process
|
||||
const bool _res = add_item_list(_za, item_paths, zip_root);
|
||||
|
||||
if (_res) {
|
||||
mz_zip_writer_finalize_archive(_za);
|
||||
qDebug() << "Done";
|
||||
}
|
||||
|
||||
// cleanup
|
||||
za_close(_za);
|
||||
|
||||
return _res;
|
||||
}
|
||||
|
||||
bool add_item_data(mz_zip_archive *p_zip, const QString &_item_path, const QByteArray &_data)
|
||||
{
|
||||
qDebug() << "Adding:" << _item_path;
|
||||
|
||||
if (!mz_zip_writer_add_mem(p_zip,
|
||||
_item_path.toUtf8().constData(),
|
||||
_data.constData(),
|
||||
_data.size(),
|
||||
compressLevel(_data.size())))
|
||||
{
|
||||
qWarning() << "Failed to compress file:" << _item_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_item_folder(mz_zip_archive *p_zip, const QString &in_path)
|
||||
{
|
||||
return add_item_data(p_zip,
|
||||
in_path.endsWith(s_sep) ? in_path : in_path + s_sep,
|
||||
QByteArray());
|
||||
}
|
||||
|
||||
bool add_item_file(mz_zip_archive *p_zip, const QString &fs_path, const QString &in_path)
|
||||
{
|
||||
qDebug() << "Adding:" << in_path;
|
||||
return mz_zip_writer_add_file(p_zip, // zip archive
|
||||
in_path.toUtf8().constData(), // path inside the zip
|
||||
fs_path.toUtf8().constData(), // filesystem path
|
||||
NULL, 0,
|
||||
compressLevel(QFileInfo(fs_path).size()));
|
||||
}
|
||||
|
||||
bool add_item_list(mz_zip_archive *p_zip, const QStringList &items, const QString &rootFolder)
|
||||
{
|
||||
QDir __d(rootFolder);
|
||||
|
||||
// parsing a list of paths
|
||||
for (const QString &_item : items) {
|
||||
QFileInfo __fi(_item);
|
||||
const QString _relPath = __d.relativeFilePath(_item);
|
||||
|
||||
// adding item
|
||||
if (__fi.isFile() && !add_item_file(p_zip, _item, _relPath) // file
|
||||
|| (__fi.isDir() && !add_item_folder(p_zip, _relPath))) // subfolder
|
||||
{
|
||||
// adding failed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool extract_to_file(mz_zip_archive* pZip, int file_index, const QString &outpath)
|
||||
{
|
||||
if (mz_zip_reader_extract_to_file(pZip,
|
||||
file_index,
|
||||
outpath.toUtf8().constData(),
|
||||
0))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << "Failed to extract file:" << file_index;
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray extract_to_buffer(mz_zip_archive* pZip, int file_index, bool copy_data)
|
||||
{
|
||||
size_t __size = 0;
|
||||
char *_c = (char*)mz_zip_reader_extract_to_heap(pZip, file_index, &__size, 0);
|
||||
|
||||
if (_c) {
|
||||
if (copy_data) {
|
||||
// COPY data to QByteArray
|
||||
QByteArray __b(_c, __size);
|
||||
|
||||
// clear extracted from heap
|
||||
delete _c;
|
||||
|
||||
return __b;
|
||||
}
|
||||
|
||||
// Reference to the data in the QByteArray.
|
||||
// Data should be deleted on the caller side: delete _array.constData();
|
||||
return QByteArray::fromRawData(_c, __size);
|
||||
}
|
||||
|
||||
qWarning() << "Failed to extract file:" << file_index;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
bool extract_all_to_disk(mz_zip_archive *pZip, const QString &output_folder)
|
||||
{
|
||||
if (output_folder.isEmpty()) {
|
||||
qDebug() << "No output folder provided";
|
||||
return false;
|
||||
}
|
||||
|
||||
const int _num_items = mz_zip_reader_get_num_files(pZip);
|
||||
|
||||
if (_num_items == 0) {
|
||||
qDebug() << "No files to extract";
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "Extracting" << _num_items << "items to:" << output_folder;
|
||||
|
||||
// extracting...
|
||||
bool is_success = true;
|
||||
for (int it = 0; it < _num_items; ++it) {
|
||||
const QString _filename = za_item_name(pZip, it);
|
||||
|
||||
qDebug() << "Extracting:" << (it + 1) << '/' << _num_items << _filename;
|
||||
|
||||
const QString _outpath = joinPath(output_folder, _filename);
|
||||
|
||||
// create new path on the disk
|
||||
const QString _parent_folder = QFileInfo(_outpath).absolutePath();
|
||||
if (!createFolder(_parent_folder)) {
|
||||
is_success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// subfolder, no data to extract
|
||||
if (_filename.endsWith(s_sep))
|
||||
continue;
|
||||
|
||||
// extract file
|
||||
if (!extract_to_file(pZip, it, _outpath)) {
|
||||
is_success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << (is_success ? "Unzip complete." : "Unzip failed.");
|
||||
return is_success;
|
||||
}
|
||||
|
||||
QStringList folderContent(const QString &folder, bool addRoot)
|
||||
{
|
||||
QStringList _items;
|
||||
|
||||
if (addRoot) // add root folder
|
||||
_items << folder;
|
||||
|
||||
QDirIterator it(folder,
|
||||
QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Hidden,
|
||||
QDirIterator::Subdirectories);
|
||||
|
||||
while (it.hasNext()) {
|
||||
_items << it.next();
|
||||
}
|
||||
|
||||
return _items;
|
||||
}
|
||||
|
||||
bool createFolder(const QString &path)
|
||||
{
|
||||
if (QFileInfo::exists(path)
|
||||
|| QDir().mkpath(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
qWarning() << "Failed to create directory:" << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool endsWithSlash(const QString &path)
|
||||
{
|
||||
return (path.endsWith('/') || path.endsWith('\\'));
|
||||
}
|
||||
|
||||
QString joinPath(const QString &abs_path, const QString &rel_path)
|
||||
{
|
||||
return endsWithSlash(abs_path) ? abs_path + rel_path
|
||||
: abs_path % s_sep % rel_path;
|
||||
}
|
||||
|
||||
mz_uint compressLevel(qint64 data_size)
|
||||
{
|
||||
return (data_size > 40) ? MZ_DEFAULT_COMPRESSION : MZ_NO_COMPRESSION;
|
||||
}
|
||||
|
||||
}// namespace tools
|
||||
72
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/tools.h
Normal file
72
Software/PC_Application/LibreVNA-GUI/Util/QMicroz/tools.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* This file is part of QMicroz,
|
||||
* under the MIT License.
|
||||
* https://github.com/artemvlas/qmicroz
|
||||
*
|
||||
* Copyright (c) 2024 Artem Vlasenko
|
||||
*/
|
||||
#ifndef TOOLS_H
|
||||
#define TOOLS_H
|
||||
|
||||
#include <QString>
|
||||
#include "miniz.h"
|
||||
|
||||
namespace tools {
|
||||
static const QChar s_sep = u'/';
|
||||
enum ZaType { ZaReader, ZaWriter };
|
||||
|
||||
// creates and initializes a new archive
|
||||
mz_zip_archive* za_new(const QString &zip_path, ZaType za_type);
|
||||
|
||||
// returns info about the file contained in the archive
|
||||
mz_zip_archive_file_stat za_file_stat(mz_zip_archive* pZip, int file_index);
|
||||
|
||||
// closes and deletes the archive
|
||||
bool za_close(mz_zip_archive* pZip);
|
||||
|
||||
// returns the name (path) of a file or folder in the archive at the specified index
|
||||
QString za_item_name(mz_zip_archive* pZip, int file_index);
|
||||
|
||||
// creates an archive at the specified path and adds a list of files and folders to it
|
||||
// root is the part of the path relative to which paths in the archive will be created
|
||||
bool createArchive(const QString &zip_path, const QStringList &item_paths, const QString &zip_root);
|
||||
|
||||
// adds to the archive an entry with file or folder data
|
||||
bool add_item_data(mz_zip_archive *p_zip, const QString &_item_path, const QByteArray &_data);
|
||||
|
||||
// adds an empty subfolder item to the zip; 'in_path' is the path inside the archive
|
||||
bool add_item_folder(mz_zip_archive *p_zip, const QString &in_path);
|
||||
|
||||
// adds file item and data to zip; 'fs_path' is the filesystem path; 'in_path' is the path inside the archive
|
||||
bool add_item_file(mz_zip_archive *p_zip, const QString &fs_path, const QString &in_path);
|
||||
|
||||
// parses the list of file/folder paths and adds them to the archive
|
||||
bool add_item_list(mz_zip_archive *p_zip, const QStringList &items, const QString &rootFolder);
|
||||
|
||||
// extracts a file with the specified index from the archive to disk at the specified path
|
||||
bool extract_to_file(mz_zip_archive* pZip, int file_index, const QString &outpath);
|
||||
|
||||
// extracts file data at the specified index into a buffer
|
||||
QByteArray extract_to_buffer(mz_zip_archive* pZip, int file_index, bool copy_data = true);
|
||||
|
||||
// extracts the entire contents of the archive into the specified folder
|
||||
bool extract_all_to_disk(mz_zip_archive *pZip, const QString &output_folder);
|
||||
|
||||
// returns a list of folder content paths; addRoot: the root folder is added to the list
|
||||
QStringList folderContent(const QString &folder, bool addRoot = true);
|
||||
|
||||
// creates a folder at the specified path
|
||||
// if already exists or created successfully, returns true; otherwise false
|
||||
bool createFolder(const QString &path);
|
||||
|
||||
// does the path string end with a slash
|
||||
bool endsWithSlash(const QString &path);
|
||||
|
||||
// connects two parts of a path, checking for the presence of a separator
|
||||
QString joinPath(const QString &abs_path, const QString &rel_path);
|
||||
|
||||
// returns "no compression" for micro files, for others by default
|
||||
mz_uint compressLevel(qint64 data_size);
|
||||
}
|
||||
|
||||
#endif // TOOLS_H
|
||||
|
|
@ -124,6 +124,9 @@ SOURCES += \
|
|||
../LibreVNA-GUI/Traces/tracexyplot.cpp \
|
||||
../LibreVNA-GUI/Traces/waterfallaxisdialog.cpp \
|
||||
../LibreVNA-GUI/Traces/xyplotaxisdialog.cpp \
|
||||
../LibreVNA-GUI/Util/QMicroz/miniz.c \
|
||||
../LibreVNA-GUI/Util/QMicroz/qmicroz.cpp \
|
||||
../LibreVNA-GUI/Util/QMicroz/tools.cpp \
|
||||
../LibreVNA-GUI/Util/prbs.cpp \
|
||||
../LibreVNA-GUI/Util/util.cpp \
|
||||
../LibreVNA-GUI/Util/usbinbuffer.cpp \
|
||||
|
|
@ -318,6 +321,9 @@ HEADERS += \
|
|||
../LibreVNA-GUI/Traces/tracexyplot.h \
|
||||
../LibreVNA-GUI/Traces/waterfallaxisdialog.h \
|
||||
../LibreVNA-GUI/Traces/xyplotaxisdialog.h \
|
||||
../LibreVNA-GUI/Util/QMicroz/miniz.h \
|
||||
../LibreVNA-GUI/Util/QMicroz/qmicroz.h \
|
||||
../LibreVNA-GUI/Util/QMicroz/tools.h \
|
||||
../LibreVNA-GUI/Util/prbs.h \
|
||||
../LibreVNA-GUI/Util/util.h \
|
||||
../LibreVNA-GUI/Util/usbinbuffer.h \
|
||||
|
|
@ -436,5 +442,6 @@ unix:LIBS += -L/usr/lib/
|
|||
|
||||
REVISION = $$system(git rev-parse HEAD)
|
||||
DEFINES += GITHASH=\\"\"$$REVISION\\"\"
|
||||
DEFINES += FW_MAJOR=1 FW_MINOR=5 FW_PATCH=0 FW_SUFFIX=""#\\"\"-alpha.2\\"\"
|
||||
DEFINES += FW_MAJOR=1 FW_MINOR=6 FW_PATCH=1 FW_SUFFIX=""#\\"\"-alpha.2\\"\"
|
||||
DEFINES -= _UNICODE UNICODE
|
||||
win32:DEFINES += QMICROZ_LIBRARY
|
||||
|
|
|
|||
Loading…
Reference in a new issue