diff --git a/CMakeLists.txt b/CMakeLists.txt index 27d6eeb..7a661e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,8 +75,8 @@ if (Qt5Widgets_FOUND) set(EXTRALIBS ${EXTRALIBS} Qt5::Widgets) endif() -#qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} i18n/rpi-imager_en.ts i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts i18n/rpi-imager_tr.ts i18n/rpi-imager_fr.ts i18n/rpi-imager_de.ts i18n/rpi-imager_it.ts) -qt5_add_translation(QM_FILES i18n/rpi-imager_en.ts i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts i18n/rpi-imager_tr.ts i18n/rpi-imager_fr.ts i18n/rpi-imager_de.ts i18n/rpi-imager_it.ts) +#qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} i18n/rpi-imager_en.ts i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts i18n/rpi-imager_tr.ts i18n/rpi-imager_fr.ts i18n/rpi-imager_de.ts i18n/rpi-imager_sk.ts i18n/rpi-imager_it.ts) +qt5_add_translation(QM_FILES i18n/rpi-imager_en.ts i18n/rpi-imager_nl.ts i18n/rpi-imager_zh_cn.ts i18n/rpi-imager_tr.ts i18n/rpi-imager_fr.ts i18n/rpi-imager_de.ts i18n/rpi-imager_sk.ts i18n/rpi-imager_it.ts) configure_file(i18n/translations.qrc "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc ${QM_FILES}) diff --git a/downloadthread.cpp b/downloadthread.cpp index aee6a94..10b427b 100644 --- a/downloadthread.cpp +++ b/downloadthread.cpp @@ -91,8 +91,24 @@ size_t DownloadThread::_curl_header_callback( void *ptr, size_t size, size_t nme return len; } +QByteArray DownloadThread::_fileGetContentsTrimmed(const QString &filename) +{ + QByteArray result; + QFile f(filename); + + if (f.exists() && f.open(f.ReadOnly)) + { + result = f.readAll().trimmed(); + f.close(); + } + + return result; +} + bool DownloadThread::_openAndPrepareDevice() { + emit preparationStatusUpdate(tr("opening drive")); + if (_filename.startsWith("/dev/")) { unmount_disk(_filename.constData()); @@ -203,11 +219,65 @@ bool DownloadThread::_openAndPrepareDevice() } #endif +#ifdef Q_OS_LINUX + /* Optional optimizations for Linux */ + + if (_filename.startsWith("/dev/")) + { + QString devname = _filename.mid(5); + + /* On some internal SD card readers CID/CSD is available, print it for debugging purposes */ + QByteArray cid = _fileGetContentsTrimmed("/sys/block/"+devname+"/device/cid"); + QByteArray csd = _fileGetContentsTrimmed("/sys/block/"+devname+"/device/csd"); + if (!cid.isEmpty()) + qDebug() << "SD card CID:" << cid; + if (!csd.isEmpty()) + qDebug() << "SD card CSD:" << csd; + + QByteArray discardmax = _fileGetContentsTrimmed("/sys/block/"+devname+"/queue/discard_max_bytes"); + + if (discardmax.isEmpty() || discardmax == "0") + { + qDebug() << "BLKDISCARD not supported"; + } + else + { + /* DISCARD/TRIM the SD card */ + uint64_t devsize, range[2]; + int fd = _file.handle(); + + if (::ioctl(fd, BLKGETSIZE64, &devsize) == -1) { + qDebug() << "Error getting device/sector size with BLKGETSIZE64 ioctl():" << strerror(errno); + } + else + { + qDebug() << "Try to perform TRIM/DISCARD on device"; + range[0] = 0; + range[1] = devsize; + emit preparationStatusUpdate(tr("discarding existing data on drive")); + _timer.start(); + if (::ioctl(fd, BLKDISCARD, &range) == -1) + { + qDebug() << "BLKDISCARD failed."; + } + else + { + qDebug() << "BLKDISCARD successful. Discarding took" << _timer.elapsed() / 1000 << "seconds"; + } + } + } + } +#endif + #ifndef Q_OS_WIN // Zero out MBR qint64 knownsize = _file.size(); QByteArray emptyMB(1024*1024, 0); + emit preparationStatusUpdate(tr("zeroing out first and last MB of drive")); + qDebug() << "Zeroing out first and last MB of drive"; + _timer.start(); + if (!_file.write(emptyMB.data(), emptyMB.size()) || !_file.flush()) { emit error(tr("Write error while zero'ing out MBR")); @@ -229,36 +299,10 @@ bool DownloadThread::_openAndPrepareDevice() } emptyMB.clear(); _file.seek(0); + qDebug() << "Done zeroing out start and end of drive. Took" << _timer.elapsed() / 1000 << "seconds"; #endif #ifdef Q_OS_LINUX - /* Optional optimizations for Linux */ - - /* See if we can DISCARD/TRIM the SD card */ - uint64_t devsize, range[2]; - int fd = _file.handle(); - - if (::ioctl(fd, BLKGETSIZE64, &devsize) == -1) { - qDebug() << "Error getting device/sector size with BLKGETSIZE64 ioctl():" << strerror(errno); - } - else - { - int secsize; - ::ioctl(fd, BLKSSZGET, &secsize); - qDebug() << "Sector size:" << secsize << "Device size:" << devsize; - - qDebug() << "Try to perform TRIM/DISCARD on device"; - range[0] = 0; - range[1] = devsize; - if (::ioctl(fd, BLKDISCARD, &range) == -1) - { - qDebug() << "BLKDISCARD not supported"; - } - else - { - qDebug() << "BLKDISCARD successful"; - } - } _sectorsStart = _sectorsWritten(); #endif @@ -303,6 +347,7 @@ void DownloadThread::run() if (!_proxy.isEmpty()) curl_easy_setopt(_c, CURLOPT_PROXY, _proxy.constData()); + emit preparationStatusUpdate(tr("starting download")); _timer.start(); CURLcode ret = curl_easy_perform(_c); diff --git a/downloadthread.h b/downloadthread.h index 2f46b95..37f52a8 100644 --- a/downloadthread.h +++ b/downloadthread.h @@ -126,6 +126,7 @@ signals: void error(QString msg); void cacheFileUpdated(QByteArray sha256); void finalizing(); + void preparationStatusUpdate(QString msg); protected: virtual void run(); @@ -140,6 +141,7 @@ protected: void _writeCache(const char *buf, size_t len); qint64 _sectorsWritten(); void _closeFiles(); + QByteArray _fileGetContentsTrimmed(const QString &filename); /* * libcurl callbacks diff --git a/i18n/rpi-imager_sk.ts b/i18n/rpi-imager_sk.ts new file mode 100644 index 0000000..cbf096f --- /dev/null +++ b/i18n/rpi-imager_sk.ts @@ -0,0 +1,450 @@ + + + + + DownloadExtractThread + + + Error writing to storage + Chyba pri zápise na úložisko + + + + + Error extracting archive: %1 + Chyba pri rozbaľovaní archívu: %1 + + + + Error mounting FAT32 partition + Chyba pri pripájaní partície FAT32 + + + + Operating system did not mount FAT32 partition + Operačný systém nepripojil partíciu FAT32 + + + + Error changing to directory '%1' + Chyba pri vstupe do adresára '%1' + + + + DownloadThread + + + Error running diskpart: %1 + Chyba počas behu diskpart: %1 + + + + Error removing existing partitions + Chyba pri odstraňovaní existujúcich partiícií + + + + Authentication cancelled + Zrušená autentifikácia + + + + Error running authopen to gain access to disk device '%1' + Chyba pri spúšťaní authopen v snahe o získanie prístupu na diskové zariadenie '%1' + + + + Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). + Preverte, prosím, či má 'Raspberry Pi Imager' prístup k 'vymeniteľným nosičom' v nastaveniach súkromia (pod 'súbormi a priečinkami', prípadne mu udeľte 'plný prístup k diskom'). + + + + Cannot open storage device '%1'. + Nepodarilo sa otvoriť zariadenie úložiska '%1'. + + + + Write error while zero'ing out MBR + Chyba zápisu pri prepisovaní MBR nulami + + + + Write error while trying to zero out last part of card. +Card could be advertising wrong capacity (possible counterfeit) + Chyba zápisu pri prepisovaní poslednej časti karty nulami. +Karta pravdepodobne udáva nesprávnu kapacitu (a môže byť falošná) + + + + Access denied error while writing file to disk. + Odopretý prístup pri zápise súboru na disk. + + + + Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again. + Vyzerá, že máte zapnutý Controlled Folder Access. Pridajte, prosím, rpi-imager.exe a fat32format.exe do zoznamu povolených aplikácií a skúste to znovu. + + + + Error writing file to disk + Chyba pri zápise na disk + + + + Error writing to storage (while flushing) + Chyba pri zápise na úložisko (počas volania flush) + + + + Error writing to storage (while fsync) + Chyba pri zápise na úložisko (počas volania fsync) + + + + Download corrupt. Hash does not match + Stiahnutý súbor je poškodený. Kontrolný súčet nesedí + + + + Error writing first block (partition table) + Chyba pri zápise prvého bloku (tabuľky partícií) + + + + Error reading from storage. +SD card may be broken. + Chyba pri čítaní z úložiska. +Karta SD môže byť poškodená. + + + + Verifying write failed. Contents of SD card is different from what was written to it. + Overovanie zápisu skončilo s chybou. Obsah karty SD sa nezhoduje s tým, čo na ňu bolo zapísané. + + + + DriveFormatThread + + + + + Error partitioning: %1 + Chyba pri zápise partícií: %1 + + + + Error starting fat32format + Chyba pri spustení fat32format + + + + Error running fat32format: %1 + Chyba pri spustení fat32format: %1 + + + + Error determining new drive letter + Chyba pri zisťovaní písmena nového disku + + + + Invalid device: %1 + Neplatné zariadenie: %1 + + + + Error formatting (through udisks2) + Chyba pri formátovaní (pomocou udisks2) + + + + Error starting sfdisk + Chyba pri spustení sfdisk + + + + Error starting mkfs.fat + Chyba pri spustení mkfs.fat + + + + Error running mkfs.fat: %1 + Chyba pri spustení mkfs.fat: %1 + + + + Formatting not implemented for this platform + Formátovanie nie je na tejto platforme implementované + + + + ImageWriter + + + Storage capacity is not large enough. +Needs to be at least %1 GB + Kapacita úložiska je nedostatočná +Musí byť aspoň %1 GB + + + + Input file is not a valid disk image. +File size %1 bytes is not a multiple of 512 bytes. + Vstupný súbor nie je platným obrazom disku. +Veľkosť súboru %1 bajtov nie je násobkom 512 bajtov. + + + + Downloading and writing image + Sťahujem a zapisujem obraz + + + + Select image + Vyberte obraz + + + + LocalFileExtractThread + + + Error opening image file + Chyba pri otváraní súboru s obrazom + + + + MsgPopup + + + NO + NIE + + + + YES + ÁNO + + + + CONTINUE + POKRAČOVAŤ + + + + QObject + + + Internal SD card reader + Interná čítačka SD kariet + + + + main + + + Raspberry Pi Imager v%1 + Raspberry Pi Imager v%1 + + + + Are you sure you want to quit? + Skutočne chcete skončiť? + + + + Raspberry Pi Imager is still busy.<br>Are you sure you want to quit? + Raspberry Pi Imager ešte neskončil.<br>Ste si istý, že chcete skončiť? + + + + + Operating System + Operačný systém + + + + CHOOSE OS + VÝBER OS + + + + + SD Card + SD karta + + + + + CHOOSE SD CARD + VYBERTE SD KARTU + + + + WRITE + ZÁPIS + + + + + Writing... %1% + Zapisujem... %1% + + + + CANCEL WRITE + ZRUŠIŤ ZÁPIS + + + + Select this button to change the operating system + Pre zmenu operačného systému kliknite na toto tlačidlo + + + + Select this button to change the destination SD card + Pre zmenu cieľovej SD karty kliknite na toto tlačidlo + + + + Select this button to start writing the image + Kliknutím na toto tlačidlo spustíte zápis + + + + + Cancelling... + Ruším operáciu... + + + + CANCEL VERIFY + ZRUŠIŤ OVEROVANIE + + + + + + Finalizing... + Ukončujem... + + + + + Erase + Vymazať + + + + Format card as FAT32 + Formátovať kartu ako FAT32 + + + + Use custom + Použiť vlastný + + + + Select a custom .img from your computer + Použiť vlastný súbor img. na Vašom počítači + + + + Local file + Miestny súbor + + + + [WRITE PROTECTED] + [OCHRANA PROTI ZÁPISU] + + + + Warning + Varovanie + + + + All existing data on '%1' will be erased.<br>Are you sure you want to continue? + Všetky existujúce dáta na '%1' budú odstránené.<br>Naozaj chcete pokračovať? + + + + <b>%1</b> has been erased<br><br>You can now remove the SD card from the reader + <b>%1</b> bola vymazaná<br><br>Teraz môžete odstrániť SD kartu z čítačky + + + + + Error parsing os_list.json + Chyba pri spracovaní os_list.json + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Najprv pripojte USB kľúč, ktorý obsahuje diskové obrazy.<br>Obrazy sa musia nachádzať v koreňovom priečinku USB kľúča. + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD karta je chránená proti zápisu.<br>Presuňte prepínač zámku na ľavej strane karty smerom hore a skúste to znova. + + + + Back + Späť + + + + Go back to main menu + Prejsť do hlavnej ponuky + + + + Released: %1 + Vydané: %1 + + + + Cached on your computer + Uložené na počítači + + + + Online - %1 GB download + Online %1 GB na stiahnutie + + + + + + Mounted as %1 + Pripojená ako %1 + + + + Error downloading OS list from Internet + Chyba pri sťahovaní zoznamu OS z Internetu + + + + Verifying... %1% + Overujem... %1% + + + + Error + Chyba + + + + Write Successful + Zápis úspešne skončil + + + + <b>%1</b> has been written to <b>%2</b><br><br>You can now remove the SD card from the reader + <b>%1</b> bol zapísaný na <b>%2</b><br><br>Teraz môžete odstrániť SD kartu z čítačky + + + diff --git a/i18n/translations.qrc b/i18n/translations.qrc index 9ffb5bc..c0a603f 100644 --- a/i18n/translations.qrc +++ b/i18n/translations.qrc @@ -5,7 +5,8 @@ rpi-imager_fr.qm rpi-imager_it.qm rpi-imager_nl.qm - rpi-imager_tr.qm - rpi-imager_zh_cn.qm + rpi-imager_sk.qm + rpi-imager_tr.qm + rpi-imager_zh_cn.qm diff --git a/imagewriter.cpp b/imagewriter.cpp index 6c2aef6..57b8adf 100644 --- a/imagewriter.cpp +++ b/imagewriter.cpp @@ -201,6 +201,8 @@ void ImageWriter::startWrite() connect(_thread, SIGNAL(success()), SLOT(onSuccess())); connect(_thread, SIGNAL(error(QString)), SLOT(onError(QString))); + connect(_thread, SIGNAL(finalizing()), SLOT(onFinalizing())); + connect(_thread, SIGNAL(preparationStatusUpdate(QString)), SLOT(onPreparationStatusUpdate(QString))); _thread->setVerifyEnabled(_verifyEnabled); _thread->setUserAgent(QString("Mozilla/5.0 rpi-imager/%1").arg(constantVersion()).toUtf8()); @@ -252,6 +254,7 @@ void ImageWriter::startWrite() _thread->start(); } + _dlnow = 0; _verifynow = 0; _polltimer.start(PROGRESS_UPDATE_INTERVAL); } @@ -403,6 +406,11 @@ void ImageWriter::onFinalizing() emit finalizing(); } +void ImageWriter::onPreparationStatusUpdate(QString msg) +{ + emit preparationStatusUpdate(msg); +} + void ImageWriter::openFileDialog() { #ifndef QT_NO_WIDGETS diff --git a/imagewriter.h b/imagewriter.h index f0c6131..10fb143 100644 --- a/imagewriter.h +++ b/imagewriter.h @@ -99,6 +99,7 @@ signals: void cancelled(); void finalizing(); void networkOnline(); + void preparationStatusUpdate(QVariant msg); protected slots: @@ -112,6 +113,7 @@ protected slots: void onCacheFileUpdated(QByteArray sha256); void onFinalizing(); void onTimeSyncReply(QNetworkReply *reply); + void onPreparationStatusUpdate(QString msg); protected: QUrl _src, _repo; diff --git a/localfileextractthread.cpp b/localfileextractthread.cpp index cc5081e..0ac86a3 100644 --- a/localfileextractthread.cpp +++ b/localfileextractthread.cpp @@ -31,6 +31,7 @@ void LocalFileExtractThread::run() if (isImage() && !_openAndPrepareDevice()) return; + emit preparationStatusUpdate(tr("opening image file")); _timer.start(); _inputfile.setFileName( QUrl(_url).toLocalFile() ); if (!_inputfile.open(_inputfile.ReadOnly)) diff --git a/main.cpp b/main.cpp index 3c4f690..5587e4e 100644 --- a/main.cpp +++ b/main.cpp @@ -158,6 +158,7 @@ int main(int argc, char *argv[]) QObject *qmlwindow = engine.rootObjects().value(0); qmlwindow->connect(&imageWriter, SIGNAL(downloadProgress(QVariant,QVariant)), qmlwindow, SLOT(onDownloadProgress(QVariant,QVariant))); qmlwindow->connect(&imageWriter, SIGNAL(verifyProgress(QVariant,QVariant)), qmlwindow, SLOT(onVerifyProgress(QVariant,QVariant))); + qmlwindow->connect(&imageWriter, SIGNAL(preparationStatusUpdate(QVariant)), qmlwindow, SLOT(onPreparationStatusUpdate(QVariant))); qmlwindow->connect(&imageWriter, SIGNAL(error(QVariant)), qmlwindow, SLOT(onError(QVariant))); qmlwindow->connect(&imageWriter, SIGNAL(success()), qmlwindow, SLOT(onSuccess())); qmlwindow->connect(&imageWriter, SIGNAL(fileSelected(QVariant)), qmlwindow, SLOT(onFileSelected(QVariant))); diff --git a/main.qml b/main.qml index 816b39f..ea87424 100644 --- a/main.qml +++ b/main.qml @@ -749,7 +749,7 @@ ApplicationWindow { cancelwritebutton.enabled = true cancelwritebutton.visible = true cancelverifybutton.enabled = true - progressText.text = qsTr("Writing... %1%").arg("0") + progressText.text = qsTr("Preparing to write..."); progressText.visible = true progressBar.visible = true progressBar.indeterminate = true @@ -831,6 +831,10 @@ ApplicationWindow { } } + function onPreparationStatusUpdate(msg) { + progressText.text = qsTr("Preparing to write... (%1)").arg(msg) + } + function resetWriteButton() { progressText.visible = false progressBar.visible = false