From b44e70d58dac92a21d905565370357d296942209 Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Fri, 6 Sep 2013 18:48:41 +0200 Subject: started kill instance feature --- logic/MinecraftProcess.cpp | 5 +++++ logic/MinecraftProcess.h | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'logic') diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index d34be835..3d82008b 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -138,6 +138,11 @@ void MinecraftProcess::finish(int code, ExitStatus status) emit ended(); } +void MinecraftProcess::killMinecraft() +{ + killed = true; +} + void MinecraftProcess::launch() { if (!m_instance->settings().get("PreLaunchCommand").toString().isEmpty()) diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h index 516bf986..248ad807 100644 --- a/logic/MinecraftProcess.h +++ b/logic/MinecraftProcess.h @@ -59,6 +59,8 @@ public: void setMinecraftArguments(QStringList args); + void killMinecraft(); + signals: /** * @brief emitted when mc has finished and the PostLaunchCommand was run @@ -83,4 +85,6 @@ protected slots: void finish(int, QProcess::ExitStatus status); void on_stdErr(); void on_stdOut(); +private: + bool killed; }; -- cgit From f897a200e2607fd99116a3ab4bb9ba757a52139b Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Fri, 6 Sep 2013 22:40:50 +0200 Subject: Made instace killing actually work --- MultiMC.pro | 3 ++- gui/consolewindow.cpp | 24 ++++++++++++++++++++++-- gui/consolewindow.h | 4 +++- gui/consolewindow.ui | 7 +++++++ gui/mainwindow.cpp | 2 +- logic/MinecraftProcess.cpp | 8 +++++++- main.cpp | 2 +- 7 files changed, 43 insertions(+), 7 deletions(-) (limited to 'logic') diff --git a/MultiMC.pro b/MultiMC.pro index b3b7faff..b82b2294 100644 --- a/MultiMC.pro +++ b/MultiMC.pro @@ -40,7 +40,8 @@ FORMS += gui/mainwindow.ui \ gui/settingsdialog.ui \ gui/modeditwindow.ui \ gui/instancesettings.ui \ - gui/logindialog.ui + gui/logindialog.ui \ + gui/consolewindow.ui RESOURCES += \ multimc.qrc diff --git a/gui/consolewindow.cpp b/gui/consolewindow.cpp index 811900a2..f2bc662a 100644 --- a/gui/consolewindow.cpp +++ b/gui/consolewindow.cpp @@ -2,11 +2,13 @@ #include "ui_consolewindow.h" #include +#include -ConsoleWindow::ConsoleWindow(QWidget *parent) : +ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent) : QDialog(parent), ui(new Ui::ConsoleWindow), - m_mayclose(true) + m_mayclose(true), + proc(mcproc) { ui->setupUi(this); } @@ -40,6 +42,9 @@ void ConsoleWindow::write(QString data, MessageLevel::Enum mode) else if (mode == MessageLevel::Error) while(iter.hasNext()) writeColor(iter.next(), "red"); + else if (mode == MessageLevel::Warning) + while(iter.hasNext()) + writeColor(iter.next(), "orange"); // TODO: implement other MessageLevels else while(iter.hasNext()) @@ -72,3 +77,18 @@ void ConsoleWindow::closeEvent(QCloseEvent * event) else QDialog::closeEvent(event); } + +void ConsoleWindow::on_btnKillMinecraft_clicked() +{ + ui->btnKillMinecraft->setEnabled(false); + QMessageBox r_u_sure; + r_u_sure.setText("Kill Minecraft?"); + r_u_sure.setInformativeText("This can cause the instance to get corrupted and should only be used if Minecraft is frozen for some reason"); + r_u_sure.setStandardButtons(QMessageBox::Yes | QMessageBox::No); + r_u_sure.setDefaultButton(QMessageBox::Yes); + if (r_u_sure.exec() == QMessageBox::Yes) + proc->killMinecraft(); + else + ui->btnKillMinecraft->setEnabled(true); + r_u_sure.close(); +} \ No newline at end of file diff --git a/gui/consolewindow.h b/gui/consolewindow.h index 60bec69f..d4485a45 100644 --- a/gui/consolewindow.h +++ b/gui/consolewindow.h @@ -13,7 +13,7 @@ class ConsoleWindow : public QDialog Q_OBJECT public: - explicit ConsoleWindow(QWidget *parent = 0); + explicit ConsoleWindow(MinecraftProcess *proc, QWidget *parent = 0); ~ConsoleWindow(); /** @@ -48,12 +48,14 @@ public slots: private slots: void on_closeButton_clicked(); + void on_btnKillMinecraft_clicked(); protected: void closeEvent(QCloseEvent *); private: Ui::ConsoleWindow *ui; + MinecraftProcess *proc; bool m_mayclose; }; diff --git a/gui/consolewindow.ui b/gui/consolewindow.ui index 9a766543..8dc80015 100644 --- a/gui/consolewindow.ui +++ b/gui/consolewindow.ui @@ -62,6 +62,13 @@ + + + + Kill Minecraft + + + diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 4ccc12b6..75996c5d 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -538,7 +538,7 @@ void MainWindow::launchInstance(BaseInstance *instance, LoginResponse response) if(!proc) return; - console = new ConsoleWindow(); + console = new ConsoleWindow(proc); console->show(); connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, SLOT(write(QString, MessageLevel::Enum))); diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index 3d82008b..6ac5b886 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -120,7 +120,12 @@ void MinecraftProcess::finish(int code, ExitStatus status) //TODO: error handling } - emit log("Minecraft exited."); + // TODO: Localization + + if (!killed) + emit log("Minecraft exited."); + else + emit log("Minecraft was killed by user.", MessageLevel::Error); m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); @@ -141,6 +146,7 @@ void MinecraftProcess::finish(int code, ExitStatus status) void MinecraftProcess::killMinecraft() { killed = true; + kill(); } void MinecraftProcess::launch() diff --git a/main.cpp b/main.cpp index 72b0f225..12ef1ce7 100644 --- a/main.cpp +++ b/main.cpp @@ -71,7 +71,7 @@ private slots: //FIXME: report error return; } - console = new ConsoleWindow(); + console = new ConsoleWindow(proc); console->show(); connect(proc, SIGNAL(ended()), SLOT(onTerminated())); -- cgit From 6bea4ec988b7caeac63353fb9d2a354f2fd47dad Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 02:15:20 +0200 Subject: Use HttpMetaCache to minimize network use. --- CMakeLists.txt | 7 ++ MultiMC.cpp | 21 +++++- MultiMC.h | 8 ++ logic/LegacyUpdate.cpp | 4 +- logic/OneSixAssets.cpp | 29 +++----- logic/OneSixUpdate.cpp | 20 +++-- logic/net/ByteArrayDownload.cpp | 62 ++++++++++++++++ logic/net/ByteArrayDownload.h | 24 ++++++ logic/net/CacheDownload.cpp | 122 +++++++++++++++++++++++++++++++ logic/net/CacheDownload.h | 34 +++++++++ logic/net/Download.h | 54 ++++++++++++++ logic/net/DownloadJob.cpp | 146 +++++-------------------------------- logic/net/DownloadJob.h | 95 ++++-------------------- logic/net/FileDownload.cpp | 111 ++++++++++++++++++++++++++++ logic/net/FileDownload.h | 35 +++++++++ logic/net/HttpMetaCache.cpp | 157 ++++++++++++++++++++++++++++++++-------- logic/net/HttpMetaCache.h | 33 +++++++-- 17 files changed, 693 insertions(+), 269 deletions(-) create mode 100644 logic/net/ByteArrayDownload.cpp create mode 100644 logic/net/ByteArrayDownload.h create mode 100644 logic/net/CacheDownload.cpp create mode 100644 logic/net/CacheDownload.h create mode 100644 logic/net/Download.h create mode 100644 logic/net/FileDownload.cpp create mode 100644 logic/net/FileDownload.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index 27f8eaba..cbc3d842 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,10 @@ logic/ModList.h logic/InstanceLauncher.h # network stuffs +logic/net/Download.h +logic/net/FileDownload.h +logic/net/ByteArrayDownload.h +logic/net/CacheDownload.h logic/net/DownloadJob.h logic/net/HttpMetaCache.h @@ -252,6 +256,9 @@ logic/ModList.cpp logic/InstanceLauncher.cpp # network stuffs - to be moved into a depend lib ~_~ +logic/net/FileDownload.cpp +logic/net/ByteArrayDownload.cpp +logic/net/CacheDownload.cpp logic/net/DownloadJob.cpp logic/net/HttpMetaCache.cpp diff --git a/MultiMC.cpp b/MultiMC.cpp index 88eb3e62..f212d830 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -8,6 +8,7 @@ #include "logic/lists/InstanceList.h" #include "logic/lists/IconList.h" #include "logic/InstanceLauncher.h" +#include "logic/net/HttpMetaCache.h" #include "pathutils.h" @@ -112,11 +113,16 @@ MultiMC::MultiMC ( int& argc, char** argv ) // load settings initGlobalSettings(); + // and instances m_instances = new InstanceList(m_settings->get("InstanceDir").toString(),this); std::cout << "Loading Instances..." << std::endl; m_instances->loadList(); - // network manager + + // init the http meta cache + initHttpMetaCache(); + + // create the global network manager m_qnam = new QNetworkAccessManager(this); // Register meta types. @@ -137,11 +143,12 @@ MultiMC::MultiMC ( int& argc, char** argv ) MultiMC::~MultiMC() { delete m_settings; + delete m_metacache; } void MultiMC::initGlobalSettings() { - m_settings = new INISettingsObject(applicationDirPath() + "/multimc.cfg", this); + m_settings = new INISettingsObject("multimc.cfg", this); // Updates m_settings->registerSetting(new Setting("UseDevBuilds", false)); m_settings->registerSetting(new Setting("AutoUpdate", true)); @@ -189,6 +196,16 @@ void MultiMC::initGlobalSettings() m_settings->registerSetting(new Setting("TheCat", false)); } +void MultiMC::initHttpMetaCache() +{ + m_metacache = new HttpMetaCache("metacache"); + m_metacache->addBase("assets", QDir("assets").absolutePath()); + m_metacache->addBase("versions", QDir("versions").absolutePath()); + m_metacache->addBase("libraries", QDir("libraries").absolutePath()); + m_metacache->Load(); +} + + IconList* MultiMC::icons() { if ( !m_icons ) diff --git a/MultiMC.h b/MultiMC.h index 99d90b99..0c8b673b 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -4,6 +4,7 @@ #include "MultiMCVersion.h" #include "config.h" +class HttpMetaCache; class SettingsObject; class InstanceList; class IconList; @@ -55,14 +56,21 @@ public: { return m_qnam; } + + HttpMetaCache * metacache() + { + return m_metacache; + } private: void initGlobalSettings(); + void initHttpMetaCache(); private: SettingsObject * m_settings = nullptr; InstanceList * m_instances = nullptr; IconList * m_icons = nullptr; QNetworkAccessManager * m_qnam = nullptr; + HttpMetaCache * m_metacache = nullptr; Status m_status = MultiMC::Failed; MultiMCVersion m_version = {VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD}; }; \ No newline at end of file diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 626ad1e0..c75a0011 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -227,7 +227,9 @@ void LegacyUpdate::jarStart() QString intended_version_id = inst->intendedVersionId(); urlstr += intended_version_id + "/" + intended_version_id + ".jar"; - legacyDownloadJob.reset(new DownloadJob(QUrl(urlstr), inst->defaultBaseJar())); + auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); + dljob->add(QUrl(urlstr), inst->defaultBaseJar()); + legacyDownloadJob.reset(); connect(legacyDownloadJob.data(), SIGNAL(finished()), SLOT(jarFinished())); connect(legacyDownloadJob.data(), SIGNAL(failed()), SLOT(jarFailed())); connect(legacyDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp index c65ee607..5bdd29d7 100644 --- a/logic/OneSixAssets.cpp +++ b/logic/OneSixAssets.cpp @@ -3,6 +3,8 @@ #include #include "OneSixAssets.h" #include "net/DownloadJob.h" +#include "net/HttpMetaCache.h" +#include "MultiMC.h" inline QDomElement getDomElementByTagName(QDomElement parent, QString tagname) { @@ -65,7 +67,7 @@ void OneSixAssets::fetchXMLFinished() nuke_whitelist.clear(); auto firstJob = index_job->first(); - QByteArray ba = firstJob->m_data; + QByteArray ba = firstJob.dynamicCast()->m_data; QString xmlErrorMsg; QDomDocument doc; @@ -76,10 +78,12 @@ void OneSixAssets::fetchXMLFinished() //QRegExp etag_match(".*([a-f0-9]{32}).*"); QDomNodeList contents = doc.elementsByTagName ( "Contents" ); - DownloadJob *job = new DownloadJob(); + DownloadJob *job = new DownloadJob("Assets"); connect ( job, SIGNAL(succeeded()), SLOT(downloadFinished()) ); connect ( job, SIGNAL(failed()), SIGNAL(failed()) ); + auto metacache = MMC->metacache(); + for ( int i = 0; i < contents.length(); i++ ) { QDomElement element = contents.at ( i ).toElement(); @@ -104,22 +108,12 @@ void OneSixAssets::fetchXMLFinished() if ( sizeStr == "0" ) continue; - QString filename = fprefix + keyStr; - QFile check_file ( filename ); - QString client_etag = "nonsense"; - // if there already is a file and md5 checking is in effect and it can be opened - if ( check_file.exists() && check_file.open ( QIODevice::ReadOnly ) ) - { - // check the md5 against the expected one - client_etag = QCryptographicHash::hash ( check_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); - check_file.close(); - } - - QString trimmedEtag = etagStr.remove ( '"' ); nuke_whitelist.append ( keyStr ); - if(trimmedEtag != client_etag) + + auto entry = metacache->resolveEntry("assets", keyStr, etagStr); + if(entry->stale) { - job->add ( QUrl ( prefix + keyStr ), filename ); + job->add(QUrl(prefix + keyStr), entry); } } if(job->size()) @@ -135,7 +129,8 @@ void OneSixAssets::fetchXMLFinished() } void OneSixAssets::start() { - DownloadJob * job = new DownloadJob(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" )); + auto job = new DownloadJob("Assets index"); + job->add(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" )); connect ( job, SIGNAL(succeeded()), SLOT ( fetchXMLFinished() ) ); index_job.reset ( job ); job->start(); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 428d6ef7..ce71bde0 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -12,7 +12,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#include "MultiMC.h" #include "OneSixUpdate.h" #include @@ -72,7 +72,9 @@ void OneSixUpdate::versionFileStart() QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); urlstr += targetVersion->descriptor + "/" + targetVersion->descriptor + ".json"; - specificVersionDownloadJob.reset(new DownloadJob(QUrl(urlstr))); + auto job = new DownloadJob("Version index"); + job->add(QUrl(urlstr)); + specificVersionDownloadJob.reset(job); connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), SLOT(versionFileFinished())); connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); @@ -92,7 +94,7 @@ void OneSixUpdate::versionFileFinished() // FIXME: detect errors here, download to a temp file, swap QFile vfile1 (version1); vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly ); - vfile1.write(DlJob->m_data); + vfile1.write(DlJob.dynamicCast()->m_data); vfile1.close(); } @@ -134,16 +136,22 @@ void OneSixUpdate::jarlibStart() QString targetstr ("versions/"); targetstr += version->id + "/" + version->id + ".jar"; - jarlibDownloadJob.reset(new DownloadJob(QUrl(urlstr), targetstr)); + auto job = new DownloadJob("Libraries for instance " + inst->name()); + job->add(QUrl(urlstr), targetstr); + jarlibDownloadJob.reset(job); auto libs = version->getActiveNativeLibs(); libs.append(version->getActiveNormalLibs()); + auto metacache = MMC->metacache(); for(auto lib: libs) { QString download_path = lib->downloadPath(); - QString storage_path = "libraries/" + lib->storagePath(); - jarlibDownloadJob->add(download_path, storage_path); + auto entry = metacache->resolveEntry("libraries", lib->storagePath()); + if(entry->stale) + { + jarlibDownloadJob->add(download_path, entry); + } } connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished())); connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp new file mode 100644 index 00000000..6ae3f121 --- /dev/null +++ b/logic/net/ByteArrayDownload.cpp @@ -0,0 +1,62 @@ +#include "ByteArrayDownload.h" +#include "MultiMC.h" +#include + +ByteArrayDownload::ByteArrayDownload ( QUrl url ) + : Download() +{ + m_url = url; + m_status = Job_NotStarted; +} + +void ByteArrayDownload::start() +{ + qDebug() << "Downloading " << m_url.toString(); + QNetworkRequest request ( m_url ); + auto worker = MMC->qnam(); + QNetworkReply * rep = worker->get ( request ); + + m_reply = QSharedPointer ( rep, &QObject::deleteLater ); + connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); + connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) ); + connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) ); + connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) ); +} + +void ByteArrayDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +{ + emit progress (index_within_job, bytesReceived, bytesTotal ); +} + +void ByteArrayDownload::downloadError ( QNetworkReply::NetworkError error ) +{ + // error happened during download. + // TODO: log the reason why + m_status = Job_Failed; +} + +void ByteArrayDownload::downloadFinished() +{ + // if the download succeeded + if ( m_status != Job_Failed ) + { + // nothing went wrong... + m_status = Job_Finished; + m_data = m_reply->readAll(); + m_reply.clear(); + emit succeeded(index_within_job); + return; + } + // else the download failed + else + { + m_reply.clear(); + emit failed(index_within_job); + return; + } +} + +void ByteArrayDownload::downloadReadyRead() +{ + // ~_~ +} diff --git a/logic/net/ByteArrayDownload.h b/logic/net/ByteArrayDownload.h new file mode 100644 index 00000000..428b21db --- /dev/null +++ b/logic/net/ByteArrayDownload.h @@ -0,0 +1,24 @@ +#pragma once +#include "Download.h" + +class ByteArrayDownload: public Download +{ + Q_OBJECT +public: + ByteArrayDownload(QUrl url); + +public: + /// if not saving to file, downloaded data is placed here + QByteArray m_data; + +public slots: + virtual void start(); + +protected slots: + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void downloadError(QNetworkReply::NetworkError error); + void downloadFinished(); + void downloadReadyRead(); +}; + +typedef QSharedPointer ByteArrayDownloadPtr; diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp new file mode 100644 index 00000000..c0074574 --- /dev/null +++ b/logic/net/CacheDownload.cpp @@ -0,0 +1,122 @@ +#include "MultiMC.h" +#include "CacheDownload.h" +#include + +#include +#include +#include +#include + +CacheDownload::CacheDownload (QUrl url, MetaEntryPtr entry ) + :Download(), md5sum(QCryptographicHash::Md5) +{ + m_url = url; + m_entry = entry; + m_target_path = entry->getFullPath(); + m_status = Job_NotStarted; + m_opened_for_saving = false; +} + +void CacheDownload::start() +{ + if(!m_entry->stale) + { + emit succeeded(index_within_job); + return; + } + m_output_file.setFileName(m_target_path); + // if there already is a file and md5 checking is in effect and it can be opened + if(!ensureFilePathExists(m_target_path)) + { + emit failed(index_within_job); + return; + } + qDebug() << "Downloading " << m_url.toString(); + QNetworkRequest request ( m_url ); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + + auto worker = MMC->qnam(); + QNetworkReply * rep = worker->get ( request ); + + m_reply = QSharedPointer ( rep, &QObject::deleteLater ); + connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); + connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) ); + connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) ); + connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) ); +} + +void CacheDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +{ + emit progress (index_within_job, bytesReceived, bytesTotal ); +} + +void CacheDownload::downloadError ( QNetworkReply::NetworkError error ) +{ + // error happened during download. + // TODO: log the reason why + m_status = Job_Failed; +} +void CacheDownload::downloadFinished() +{ + // if the download succeeded + if ( m_status != Job_Failed ) + { + + // nothing went wrong... + m_status = Job_Finished; + if(m_opened_for_saving) + { + // save the data to the downloadable if we aren't saving to file + m_output_file.close(); + m_entry->md5sum = md5sum.result().toHex().constData(); + } + else + { + if ( m_output_file.open ( QIODevice::ReadOnly ) ) + { + m_entry->md5sum = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); + m_output_file.close(); + } + } + QFileInfo output_file_info(m_target_path); + + + m_entry->etag = m_reply->rawHeader("ETag").constData(); + m_entry->last_changed_timestamp = output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); + m_entry->stale = false; + MMC->metacache()->updateEntry(m_entry); + + m_reply.clear(); + emit succeeded(index_within_job); + return; + } + // else the download failed + else + { + m_output_file.close(); + m_output_file.remove(); + m_reply.clear(); + emit failed(index_within_job); + return; + } +} + +void CacheDownload::downloadReadyRead() +{ + if(!m_opened_for_saving) + { + if ( !m_output_file.open ( QIODevice::WriteOnly ) ) + { + /* + * Can't open the file... the job failed + */ + m_reply->abort(); + emit failed(index_within_job); + return; + } + m_opened_for_saving = true; + } + QByteArray ba = m_reply->readAll(); + md5sum.addData(ba); + m_output_file.write ( ba ); +} diff --git a/logic/net/CacheDownload.h b/logic/net/CacheDownload.h new file mode 100644 index 00000000..f95dabd5 --- /dev/null +++ b/logic/net/CacheDownload.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Download.h" +#include "HttpMetaCache.h" +#include +#include + +class CacheDownload : public Download +{ + Q_OBJECT +public: + MetaEntryPtr m_entry; + /// is the saving file already open? + bool m_opened_for_saving; + /// if saving to file, use the one specified in this string + QString m_target_path; + /// this is the output file, if any + QFile m_output_file; + /// the hash-as-you-download + QCryptographicHash md5sum; +public: + explicit CacheDownload(QUrl url, MetaEntryPtr entry); + +protected slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +public slots: + virtual void start(); +}; + +typedef QSharedPointer CacheDownloadPtr; diff --git a/logic/net/Download.h b/logic/net/Download.h new file mode 100644 index 00000000..91f09dec --- /dev/null +++ b/logic/net/Download.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include +#include + + +enum JobStatus +{ + Job_NotStarted, + Job_InProgress, + Job_Finished, + Job_Failed +}; + +class Download : public QObject +{ + Q_OBJECT +protected: + explicit Download(): QObject(0){}; +public: + virtual ~Download() {}; + +public: + /// the network reply + QSharedPointer m_reply; + + /// source URL + QUrl m_url; + + /// The file's status + JobStatus m_status; + + /// index within the parent job + int index_within_job = 0; + +signals: + void started(int index); + void progress(int index, qint64 current, qint64 total); + void succeeded(int index); + void failed(int index); + +protected slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) = 0; + virtual void downloadError(QNetworkReply::NetworkError error) = 0; + virtual void downloadFinished() = 0; + virtual void downloadReadyRead() = 0; + +public slots: + virtual void start() = 0; +}; + +typedef QSharedPointer DownloadPtr; diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index cad9ae72..cbc2c5fe 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -1,136 +1,29 @@ #include "DownloadJob.h" #include "pathutils.h" #include "MultiMC.h" +#include "FileDownload.h" +#include "ByteArrayDownload.h" +#include "CacheDownload.h" -Download::Download (QUrl url, QString target_path, QString expected_md5 ) - :Job() +ByteArrayDownloadPtr DownloadJob::add ( QUrl url ) { - m_url = url; - m_target_path = target_path; - m_expected_md5 = expected_md5; - - m_check_md5 = m_expected_md5.size(); - m_save_to_file = m_target_path.size(); - m_status = Job_NotStarted; - m_opened_for_saving = false; -} - -void Download::start() -{ - if ( m_save_to_file ) - { - QString filename = m_target_path; - m_output_file.setFileName ( filename ); - // if there already is a file and md5 checking is in effect and it can be opened - if ( m_output_file.exists() && m_output_file.open ( QIODevice::ReadOnly ) ) - { - // check the md5 against the expected one - QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); - m_output_file.close(); - // skip this file if they match - if ( m_check_md5 && hash == m_expected_md5 ) - { - qDebug() << "Skipping " << m_url.toString() << ": md5 match."; - emit succeeded(index_within_job); - return; - } - else - { - m_expected_md5 = hash; - } - } - if(!ensureFilePathExists(filename)) - { - emit failed(index_within_job); - return; - } - } - qDebug() << "Downloading " << m_url.toString(); - QNetworkRequest request ( m_url ); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); - - auto worker = MMC->qnam(); - QNetworkReply * rep = worker->get ( request ); - - m_reply = QSharedPointer ( rep, &QObject::deleteLater ); - connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); - connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) ); - connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) ); - connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) ); -} - -void Download::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) -{ - emit progress (index_within_job, bytesReceived, bytesTotal ); -} - -void Download::downloadError ( QNetworkReply::NetworkError error ) -{ - // error happened during download. - // TODO: log the reason why - m_status = Job_Failed; -} - -void Download::downloadFinished() -{ - // if the download succeeded - if ( m_status != Job_Failed ) - { - // nothing went wrong... - m_status = Job_Finished; - // save the data to the downloadable if we aren't saving to file - if ( !m_save_to_file ) - { - m_data = m_reply->readAll(); - } - else - { - m_output_file.close(); - } - - //TODO: check md5 here! - m_reply.clear(); - emit succeeded(index_within_job); - return; - } - // else the download failed - else - { - if ( m_save_to_file ) - { - m_output_file.close(); - m_output_file.remove(); - } - m_reply.clear(); - emit failed(index_within_job); - return; - } + ByteArrayDownloadPtr ptr (new ByteArrayDownload(url)); + ptr->index_within_job = downloads.size(); + downloads.append(ptr); + return ptr; } -void Download::downloadReadyRead() +FileDownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path) { - if( m_save_to_file ) - { - if(!m_opened_for_saving) - { - if ( !m_output_file.open ( QIODevice::WriteOnly ) ) - { - /* - * Can't open the file... the job failed - */ - m_reply->abort(); - emit failed(index_within_job); - return; - } - m_opened_for_saving = true; - } - m_output_file.write ( m_reply->readAll() ); - } + FileDownloadPtr ptr (new FileDownload(url, rel_target_path)); + ptr->index_within_job = downloads.size(); + downloads.append(ptr); + return ptr; } -DownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path, QString expected_md5 ) +CacheDownloadPtr DownloadJob::add ( QUrl url, MetaEntryPtr entry) { - DownloadPtr ptr (new Download(url, rel_target_path, expected_md5)); + CacheDownloadPtr ptr (new CacheDownload(url, entry)); ptr->index_within_job = downloads.size(); downloads.append(ptr); return ptr; @@ -139,16 +32,17 @@ DownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path, QString expect void DownloadJob::partSucceeded ( int index ) { num_succeeded++; + qDebug() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" << downloads.size(); if(num_failed + num_succeeded == downloads.size()) { if(num_failed) { - qDebug() << "Download JOB failed: " << this; + qDebug() << m_job_name.toLocal8Bit() << " failed."; emit failed(); } else { - qDebug() << "Download JOB succeeded: " << this; + qDebug() << m_job_name.toLocal8Bit() << " succeeded."; emit succeeded(); } } @@ -159,7 +53,7 @@ void DownloadJob::partFailed ( int index ) num_failed++; if(num_failed + num_succeeded == downloads.size()) { - qDebug() << "Download JOB failed: " << this; + qDebug() << m_job_name.toLocal8Bit() << " failed."; emit failed(); } } @@ -172,7 +66,7 @@ void DownloadJob::partProgress ( int index, qint64 bytesReceived, qint64 bytesTo void DownloadJob::start() { - qDebug() << "Download JOB started: " << this; + qDebug() << m_job_name.toLocal8Bit() << " started."; for(auto iter: downloads) { connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index adeef646..71466282 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -1,98 +1,28 @@ #pragma once #include +#include "Download.h" +#include "ByteArrayDownload.h" +#include "FileDownload.h" +#include "CacheDownload.h" +#include "HttpMetaCache.h" class DownloadJob; -class Download; typedef QSharedPointer DownloadJobPtr; -typedef QSharedPointer DownloadPtr; - -enum JobStatus -{ - Job_NotStarted, - Job_InProgress, - Job_Finished, - Job_Failed -}; - -class Job : public QObject -{ - Q_OBJECT -protected: - explicit Job(): QObject(0){}; -public: - virtual ~Job() {}; - -public slots: - virtual void start() = 0; -}; - -class Download: public Job -{ - friend class DownloadJob; - Q_OBJECT -protected: - Download(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); -public: - /// the network reply - QSharedPointer m_reply; - /// source URL - QUrl m_url; - - /// if true, check the md5sum against a provided md5sum - /// also, if a file exists, perform an md5sum first and don't download only if they don't match - bool m_check_md5; - /// the expected md5 checksum - QString m_expected_md5; - - /// save to file? - bool m_save_to_file; - /// is the saving file already open? - bool m_opened_for_saving; - /// if saving to file, use the one specified in this string - QString m_target_path; - /// this is the output file, if any - QFile m_output_file; - /// if not saving to file, downloaded data is placed here - QByteArray m_data; - - int currentProgress = 0; - int totalProgress = 0; - - /// The file's status - JobStatus m_status; - - int index_within_job = 0; -signals: - void started(int index); - void progress(int index, qint64 current, qint64 total); - void succeeded(int index); - void failed(int index); -public slots: - virtual void start(); - -private slots: - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);; - void downloadError(QNetworkReply::NetworkError error); - void downloadFinished(); - void downloadReadyRead(); -}; /** * A single file for the downloader/cache to process. */ -class DownloadJob : public Job +class DownloadJob : public QObject { Q_OBJECT public: - explicit DownloadJob() - :Job(){}; - explicit DownloadJob(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()) - :Job() - { - add(url, rel_target_path, expected_md5); - }; + explicit DownloadJob(QString job_name) + :QObject(), m_job_name(job_name){}; + + ByteArrayDownloadPtr add(QUrl url); + FileDownloadPtr add(QUrl url, QString rel_target_path); + CacheDownloadPtr add(QUrl url, MetaEntryPtr entry); - DownloadPtr add(QUrl url, QString rel_target_path = QString(), QString expected_md5 = QString()); DownloadPtr operator[](int index) { return downloads[index]; @@ -119,6 +49,7 @@ private slots: void partSucceeded(int index); void partFailed(int index); private: + QString m_job_name; QList downloads; int num_succeeded = 0; int num_failed = 0; diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp new file mode 100644 index 00000000..aad4a991 --- /dev/null +++ b/logic/net/FileDownload.cpp @@ -0,0 +1,111 @@ +#include "MultiMC.h" +#include "FileDownload.h" +#include +#include +#include + + +FileDownload::FileDownload ( QUrl url, QString target_path ) + :Download() +{ + m_url = url; + m_target_path = target_path; + m_check_md5 = false; + m_status = Job_NotStarted; + m_opened_for_saving = false; +} + +void FileDownload::start() +{ + QString filename = m_target_path; + m_output_file.setFileName ( filename ); + // if there already is a file and md5 checking is in effect and it can be opened + if ( m_output_file.exists() && m_output_file.open ( QIODevice::ReadOnly ) ) + { + // check the md5 against the expected one + QString hash = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); + m_output_file.close(); + // skip this file if they match + if ( m_check_md5 && hash == m_expected_md5 ) + { + qDebug() << "Skipping " << m_url.toString() << ": md5 match."; + emit succeeded(index_within_job); + return; + } + else + { + m_expected_md5 = hash; + } + } + if(!ensureFilePathExists(filename)) + { + emit failed(index_within_job); + return; + } + + qDebug() << "Downloading " << m_url.toString(); + QNetworkRequest request ( m_url ); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); + + auto worker = MMC->qnam(); + QNetworkReply * rep = worker->get ( request ); + + m_reply = QSharedPointer ( rep, &QObject::deleteLater ); + connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); + connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) ); + connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) ); + connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) ); +} + +void FileDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +{ + emit progress (index_within_job, bytesReceived, bytesTotal ); +} + +void FileDownload::downloadError ( QNetworkReply::NetworkError error ) +{ + // error happened during download. + // TODO: log the reason why + m_status = Job_Failed; +} + +void FileDownload::downloadFinished() +{ + // if the download succeeded + if ( m_status != Job_Failed ) + { + // nothing went wrong... + m_status = Job_Finished; + m_output_file.close(); + + m_reply.clear(); + emit succeeded(index_within_job); + return; + } + // else the download failed + else + { + m_output_file.close(); + m_reply.clear(); + emit failed(index_within_job); + return; + } +} + +void FileDownload::downloadReadyRead() +{ + if(!m_opened_for_saving) + { + if ( !m_output_file.open ( QIODevice::WriteOnly ) ) + { + /* + * Can't open the file... the job failed + */ + m_reply->abort(); + emit failed(index_within_job); + return; + } + m_opened_for_saving = true; + } + m_output_file.write ( m_reply->readAll() ); +} diff --git a/logic/net/FileDownload.h b/logic/net/FileDownload.h new file mode 100644 index 00000000..190bee58 --- /dev/null +++ b/logic/net/FileDownload.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Download.h" +#include + +class FileDownload : public Download +{ + Q_OBJECT +public: + /// if true, check the md5sum against a provided md5sum + /// also, if a file exists, perform an md5sum first and don't download only if they don't match + bool m_check_md5; + /// the expected md5 checksum + QString m_expected_md5; + /// is the saving file already open? + bool m_opened_for_saving; + /// if saving to file, use the one specified in this string + QString m_target_path; + /// this is the output file, if any + QFile m_output_file; + +public: + explicit FileDownload(QUrl url, QString target_path); + +protected slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +public slots: + virtual void start(); +}; + +typedef QSharedPointer FileDownloadPtr; diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp index 87741dc9..50a1136e 100644 --- a/logic/net/HttpMetaCache.cpp +++ b/logic/net/HttpMetaCache.cpp @@ -1,38 +1,136 @@ +#include "MultiMC.h" #include "HttpMetaCache.h" #include + +#include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +QString MetaEntry::getFullPath() +{ + return PathCombine(MMC->metacache()->getBasePath(base), path); +} + HttpMetaCache::HttpMetaCache(QString path) + :QObject() { m_index_file = path; + saveBatchingTimer.setSingleShot(true); + saveBatchingTimer.setTimerType(Qt::VeryCoarseTimer); + connect(&saveBatchingTimer,SIGNAL(timeout()),SLOT(SaveNow())); } + HttpMetaCache::~HttpMetaCache() { - Save(); + saveBatchingTimer.stop(); + SaveNow(); } -void HttpMetaCache::addEntry ( QString base, QString resource_path, QString etag ) +void HttpMetaCache::SaveEventually() +{ + saveBatchingTimer.stop(); + saveBatchingTimer.start(30000); +} + +MetaEntryPtr HttpMetaCache::getEntry ( QString base, QString resource_path ) { // no base. no base path. can't store if(!m_entries.contains(base)) - return; - QString real_path = PathCombine(m_entries[base].base_path, resource_path); + { + // TODO: log problem + return MetaEntryPtr(); + } + EntryMap & map = m_entries[base]; + if(map.entry_list.contains(resource_path)) + { + return map.entry_list[resource_path]; + } + return MetaEntryPtr(); +} + +MetaEntryPtr HttpMetaCache::resolveEntry ( QString base, QString resource_path, QString expected_etag ) +{ + auto entry = getEntry(base, resource_path); + // it's not present? generate a default stale entry + if(!entry) + { + return staleEntry(base, resource_path); + } + + auto & selected_base = m_entries[base]; + QString real_path = PathCombine(selected_base.base_path, resource_path); QFileInfo finfo(real_path); - // just ignore it, it's garbage if it's not a proper file + // is the file really there? if not -> stale if(!finfo.isFile() || !finfo.isReadable()) { - // TODO: log problem - return; + // if the file doesn't exist, we disown the entry + selected_base.entry_list.remove(resource_path); + return staleEntry(base, resource_path); + } + + if(!expected_etag.isEmpty() && expected_etag != entry->etag) + { + // if the etag doesn't match expected, we disown the entry + selected_base.entry_list.remove(resource_path); + return staleEntry(base, resource_path); } - Save(); + // if the file changed, check md5sum + qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch(); + if(file_last_changed != entry->last_changed_timestamp) + { + QFile input(real_path); + input.open(QIODevice::ReadOnly); + QString md5sum = QCryptographicHash::hash(input.readAll(), QCryptographicHash::Md5).toHex().constData(); + if(entry->md5sum != md5sum) + { + selected_base.entry_list.remove(resource_path); + return staleEntry(base, resource_path); + } + // md5sums matched... keep entry and save the new state to file + entry->last_changed_timestamp = file_last_changed; + SaveEventually(); + } + + // entry passed all the checks we cared about. + return entry; +} + +bool HttpMetaCache::updateEntry ( MetaEntryPtr stale_entry ) +{ + if(!m_entries.contains(stale_entry->base)) + { + qDebug() << "Cannot add entry with unknown base: " << stale_entry->base.toLocal8Bit(); + return false; + } + if(stale_entry->stale) + { + qDebug() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); + return false; + } + m_entries[stale_entry->base].entry_list[stale_entry->path] = stale_entry; + SaveEventually(); + return true; +} + +MetaEntryPtr HttpMetaCache::staleEntry(QString base, QString resource_path) +{ + auto foo = new MetaEntry; + foo->base = base; + foo->path = resource_path; + foo->stale = true; + return MetaEntryPtr(foo); } void HttpMetaCache::addBase ( QString base, QString base_root ) @@ -46,6 +144,16 @@ void HttpMetaCache::addBase ( QString base, QString base_root ) m_entries[base] = foo; } +QString HttpMetaCache::getBasePath ( QString base ) +{ + if(m_entries.contains(base)) + { + return m_entries[base].base_path; + } + return QString(); +} + + void HttpMetaCache::Load() { QFile index(m_index_file); @@ -65,12 +173,12 @@ void HttpMetaCache::Load() // read the entry array auto entries_val =root.value("entries"); - if(!version_val.isArray()) + if(!entries_val.isArray()) return; - QJsonArray array = json.array(); + QJsonArray array = entries_val.toArray(); for(auto element: array) { - if(!element.isObject()); + if(!element.isObject()) return; auto element_obj = element.toObject(); QString base = element_obj.value("base").toString(); @@ -83,11 +191,13 @@ void HttpMetaCache::Load() foo->md5sum = element_obj.value("md5sum").toString(); foo->etag = element_obj.value("etag").toString(); foo->last_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble(); + // presumed innocent until closer examination + foo->stale = false; entrymap.entry_list[path] = MetaEntryPtr( foo ); } } -void HttpMetaCache::Save() +void HttpMetaCache::SaveNow() { QSaveFile tfile(m_index_file); if(!tfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) @@ -118,14 +228,3 @@ void HttpMetaCache::Save() return; tfile.commit(); } - - -MetaEntryPtr HttpMetaCache::getEntryForResource ( QString base, QString resource_path ) -{ - if(!m_entries.contains(base)) - return MetaEntryPtr(); - auto & entrymap = m_entries[base]; - if(!entrymap.entry_list.contains(resource_path)) - return MetaEntryPtr(); - return entrymap.entry_list[resource_path]; -} diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index 161483ad..fac6bec3 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -2,6 +2,7 @@ #include #include #include +#include struct MetaEntry { @@ -9,23 +10,42 @@ struct MetaEntry QString path; QString md5sum; QString etag; - quint64 last_changed_timestamp = 0; + qint64 last_changed_timestamp = 0; + bool stale = true; + QString getFullPath(); }; typedef QSharedPointer MetaEntryPtr; -class HttpMetaCache +class HttpMetaCache : public QObject { + Q_OBJECT public: // supply path to the cache index file HttpMetaCache(QString path); ~HttpMetaCache(); - MetaEntryPtr getEntryForResource(QString base, QString resource_path); - void addEntry(QString base, QString resource_path, QString etag); + + // get the entry solely from the cache + // you probably don't want this, unless you have some specific caching needs. + MetaEntryPtr getEntry(QString base, QString resource_path); + + // get the entry from cache and verify that it isn't stale (within reason) + MetaEntryPtr resolveEntry(QString base, QString resource_path, QString expected_etag = QString()); + + // add a previously resolved stale entry + bool updateEntry(MetaEntryPtr stale_entry); + void addBase(QString base, QString base_root); -private: - void Save(); + + // (re)start a timer that calls SaveNow later. + void SaveEventually(); void Load(); + QString getBasePath ( QString base ); +public slots: + void SaveNow(); +private: + // create a new stale entry, given the parameters + MetaEntryPtr staleEntry(QString base, QString resource_path); struct EntryMap { QString base_path; @@ -33,4 +53,5 @@ private: }; QMap m_entries; QString m_index_file; + QTimer saveBatchingTimer; }; \ No newline at end of file -- cgit From dab2bbe4e79cfd8b3b72b8d2fd0e5bd66e0281a9 Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Sun, 8 Sep 2013 15:02:52 +0200 Subject: Added console coloring and made the log not contain any usernames/session ids --- gui/consolewindow.cpp | 8 +++++++- gui/mainwindow.cpp | 1 + logic/MinecraftProcess.cpp | 30 +++++++++++++++++++++--------- logic/MinecraftProcess.h | 5 +++++ 4 files changed, 34 insertions(+), 10 deletions(-) (limited to 'logic') diff --git a/gui/consolewindow.cpp b/gui/consolewindow.cpp index aba876c8..8ea90d45 100644 --- a/gui/consolewindow.cpp +++ b/gui/consolewindow.cpp @@ -23,7 +23,7 @@ void ConsoleWindow::writeColor(QString text, const char *color) { // append a paragraph if (color != nullptr) - ui->text->appendHtml(QString("%2").arg(color).arg(text)); + ui->text->appendHtml(QString("%2").arg(color).arg(text)); else ui->text->appendPlainText(text); // scroll down @@ -46,6 +46,12 @@ void ConsoleWindow::write(QString data, MessageLevel::Enum mode) else if (mode == MessageLevel::Warning) while(iter.hasNext()) writeColor(iter.next(), "orange"); + else if (mode == MessageLevel::Fatal) + while(iter.hasNext()) + writeColor(iter.next(), "pink"); + else if (mode == MessageLevel::Debug) + while(iter.hasNext()) + writeColor(iter.next(), "green"); // TODO: implement other MessageLevels else while(iter.hasNext()) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 82ae41d9..152773e7 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -556,6 +556,7 @@ void MainWindow::launchInstance(BaseInstance *instance, LoginResponse response) connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, SLOT(write(QString, MessageLevel::Enum))); connect(proc, SIGNAL(ended()), this, SLOT(instanceEnded())); + proc->setLogin(m_activeLogin.username, m_activeLogin.sessionID); proc->launch(); } diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index 6ac5b886..c33d34a8 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -84,17 +84,14 @@ void MinecraftProcess::on_stdErr() for(int i = 0; i < lines.size() - 1; i++) { QString & line = lines[i]; - MessageLevel::Enum level = MessageLevel::Error; - if(line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]") ) - level = MessageLevel::Message; - if(line.contains("[SEVERE]") || line.contains("[WARNING]") || line.contains("[STDERR]")) - level = MessageLevel::Error; - emit log(lines[i].toLocal8Bit(), level); + emit log(line.replace(username, "").replace(sessionID, "").toLocal8Bit(), getLevel(line, MessageLevel::Error)); } if(!complete) m_err_leftover = lines.last(); } + + void MinecraftProcess::on_stdOut() { QByteArray data = readAllStandardOutput(); @@ -106,7 +103,7 @@ void MinecraftProcess::on_stdOut() for(int i = 0; i < lines.size() - 1; i++) { QString & line = lines[i]; - emit log(lines[i].toLocal8Bit(), MessageLevel::Message); + emit log(line.replace(username, "").replace(sessionID, "").toLocal8Bit(), getLevel(line, MessageLevel::Message)); } if(!complete) m_out_leftover = lines.last(); @@ -167,7 +164,7 @@ void MinecraftProcess::launch() emit log(QString("Minecraft folder is: '%1'").arg(workingDirectory())); QString JavaPath = m_instance->settings().get("JavaPath").toString(); emit log(QString("Java path: '%1'").arg(JavaPath)); - emit log(QString("Arguments: '%1'").arg(m_args.join("' '"))); + emit log(QString("Arguments: '%1'").arg(m_args.join("' '").replace(username, "").replace(sessionID, ""))); start(JavaPath, m_args); if (!waitForStarted()) { @@ -177,4 +174,19 @@ void MinecraftProcess::launch() } } - +MessageLevel::Enum MinecraftProcess::getLevel(const QString &line, MessageLevel::Enum level) +{ + + if(line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]") ) + level = MessageLevel::Message; + if(line.contains("[SEVERE]") || line.contains("[STDERR]")) + level = MessageLevel::Error; + if(line.contains("[WARNING]")) + level = MessageLevel::Warning; + if(line.contains("Exception in thread") || line.contains(" at ")) + level = MessageLevel::Fatal; + if(line.contains("[DEBUG]")) + level = MessageLevel::Debug; + return level; + +} \ No newline at end of file diff --git a/logic/MinecraftProcess.h b/logic/MinecraftProcess.h index 248ad807..a1dfa23f 100644 --- a/logic/MinecraftProcess.h +++ b/logic/MinecraftProcess.h @@ -61,6 +61,8 @@ public: void killMinecraft(); + inline void setLogin(QString user, QString sid) { username = user; sessionID = sid; } + signals: /** * @brief emitted when mc has finished and the PostLaunchCommand was run @@ -87,4 +89,7 @@ protected slots: void on_stdOut(); private: bool killed; + MessageLevel::Enum getLevel(const QString &message, MessageLevel::Enum defaultLevel); + QString sessionID; + QString username; }; -- cgit From 7e1cf22ce61e8a5450e5dc74993e471c20b2742f Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 15:59:50 +0200 Subject: Use youtrack for bugs --- gui/mainwindow.cpp | 2 +- logic/net/HttpMetaCache.cpp | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'logic') diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 82483bf2..c0b79108 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -361,7 +361,7 @@ void MainWindow::on_actionSettings_triggered() void MainWindow::on_actionReportBug_triggered() { - openWebPage ( QUrl ( "http://jira.forkk.net/browse/MMC" ) ); + openWebPage ( QUrl ( "http://multimc.myjetbrains.com/youtrack/dashboard#newissue=yes" ) ); } void MainWindow::on_actionNews_triggered() diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp index 50a1136e..46801ab3 100644 --- a/logic/net/HttpMetaCache.cpp +++ b/logic/net/HttpMetaCache.cpp @@ -36,12 +36,6 @@ HttpMetaCache::~HttpMetaCache() SaveNow(); } -void HttpMetaCache::SaveEventually() -{ - saveBatchingTimer.stop(); - saveBatchingTimer.start(30000); -} - MetaEntryPtr HttpMetaCache::getEntry ( QString base, QString resource_path ) { // no base. no base path. can't store @@ -197,6 +191,13 @@ void HttpMetaCache::Load() } } +void HttpMetaCache::SaveEventually() +{ + // reset the save timer + saveBatchingTimer.stop(); + saveBatchingTimer.start(30000); +} + void HttpMetaCache::SaveNow() { QSaveFile tfile(m_index_file); -- cgit From cbf3238f0e4d761ff34c9469f1ec88bd937152a5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 16:25:02 +0200 Subject: Fix build --- logic/InstanceLauncher.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp index 312f4c69..a0557b37 100644 --- a/logic/InstanceLauncher.cpp +++ b/logic/InstanceLauncher.cpp @@ -31,7 +31,7 @@ void InstanceLauncher::onLoginComplete() //FIXME: report error return; } - console = new ConsoleWindow(); + console = new ConsoleWindow(proc); console->show(); connect ( proc, SIGNAL ( ended() ), SLOT ( onTerminated() ) ); -- cgit From 31e5a0fe6d75e124bc772faafcef2618e16c3dbf Mon Sep 17 00:00:00 2001 From: Stiepen22 Date: Sun, 8 Sep 2013 18:13:09 +0200 Subject: Changed all strings displayed to end user to use qts localization system --- gui/EditNotesDialog.cpp | 2 +- gui/IconPickerDialog.cpp | 9 ++++++--- gui/LegacyModEditDialog.cpp | 12 ++++++++---- gui/OneSixModEditDialog.cpp | 4 ++-- gui/consolewindow.cpp | 3 ++- gui/logindialog.cpp | 8 ++++---- gui/lwjglselectdialog.cpp | 2 +- gui/mainwindow.cpp | 4 ++-- gui/settingsdialog.cpp | 12 ++++++------ logic/MinecraftProcess.cpp | 9 ++++++--- logic/tasks/LoginTask.cpp | 18 +++++++++--------- 11 files changed, 47 insertions(+), 36 deletions(-) (limited to 'logic') diff --git a/gui/EditNotesDialog.cpp b/gui/EditNotesDialog.cpp index 6cc389f6..5b90ef53 100644 --- a/gui/EditNotesDialog.cpp +++ b/gui/EditNotesDialog.cpp @@ -12,7 +12,7 @@ EditNotesDialog::EditNotesDialog( QString notes, QString name, QWidget* parent ) { ui->setupUi(this); ui->noteEditor->setText(notes); - setWindowTitle("Edit notes of " + m_instance_name); + setWindowTitle(tr("Edit notes of %1").arg(m_instance_name)); //connect(ui->closeButton, SIGNAL(clicked()), SLOT(close())); } diff --git a/gui/IconPickerDialog.cpp b/gui/IconPickerDialog.cpp index 2dd80292..f3947d21 100644 --- a/gui/IconPickerDialog.cpp +++ b/gui/IconPickerDialog.cpp @@ -41,8 +41,8 @@ IconPickerDialog::IconPickerDialog(QWidget *parent) : contentsWidget->setModel(MMC->icons()); - auto buttonAdd = ui->buttonBox->addButton("Add Icon",QDialogButtonBox::ResetRole); - auto buttonRemove = ui->buttonBox->addButton("Remove Icon",QDialogButtonBox::ResetRole); + auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"),QDialogButtonBox::ResetRole); + auto buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"),QDialogButtonBox::ResetRole); connect(buttonAdd,SIGNAL(clicked(bool)),SLOT(addNewIcon())); @@ -87,7 +87,10 @@ bool IconPickerDialog::eventFilter ( QObject* obj, QEvent* evt) void IconPickerDialog::addNewIcon() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Icons", QString(), "Icons (*.png *.jpg *.jpeg)"); + //: The title of the select icons open file dialog + QString selectIcons = tr("Select Icons"); + //: The type of icon files + QStringList fileNames = QFileDialog::getOpenFileNames(this, selectIcons, QString(), tr("Icons") + "(*.png *.jpg *.jpeg)"); MMC->icons()->installIcons(fileNames); } diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index 616fc050..c336f837 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -183,7 +183,8 @@ bool LegacyModEditDialog::eventFilter ( QObject* obj, QEvent* ev ) void LegacyModEditDialog::on_addCoreBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Core Mods"); + //: Title of core mod selection dialog + QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Core Mods")); for(auto filename:fileNames) { m_coremods->stopWatching(); @@ -197,7 +198,8 @@ void LegacyModEditDialog::on_addForgeBtn_clicked() } void LegacyModEditDialog::on_addJarBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Jar Mods"); + //: Title of jar mod selection dialog + QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Jar Mods")); for(auto filename:fileNames) { m_jarmods->stopWatching(); @@ -207,7 +209,8 @@ void LegacyModEditDialog::on_addJarBtn_clicked() } void LegacyModEditDialog::on_addModBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Loader Mods"); + //: Title of regular mod selection dialog + QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Loader Mods")); for(auto filename:fileNames) { m_mods->stopWatching(); @@ -217,7 +220,8 @@ void LegacyModEditDialog::on_addModBtn_clicked() } void LegacyModEditDialog::on_addTexPackBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Texture Packs"); + //: Title of texture pack selection dialog + QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Texture Packs")); for(auto filename:fileNames) { m_texturepacks->stopWatching(); diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index 8e738fa1..15ff09a5 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -112,7 +112,7 @@ void OneSixModEditDialog::on_buttonBox_rejected() void OneSixModEditDialog::on_addModBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Loader Mods"); + QStringList fileNames = QFileDialog::getOpenFileNames(this, QApplication::translate("LegacyModEditDialog", "Select Loader Mods"); for(auto filename:fileNames) { m_mods->stopWatching(); @@ -139,7 +139,7 @@ void OneSixModEditDialog::on_viewModBtn_clicked() void OneSixModEditDialog::on_addResPackBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, "Select Resource Packs"); + QStringList fileNames = QFileDialog::getOpenFileNames(this, QApplication::translate("LegacyModEditDialog", "Select Resource Packs")); for(auto filename:fileNames) { m_resourcepacks->stopWatching(); diff --git a/gui/consolewindow.cpp b/gui/consolewindow.cpp index 8ea90d45..392eb50d 100644 --- a/gui/consolewindow.cpp +++ b/gui/consolewindow.cpp @@ -89,7 +89,8 @@ void ConsoleWindow::on_btnKillMinecraft_clicked() { ui->btnKillMinecraft->setEnabled(false); QMessageBox r_u_sure; - r_u_sure.setText("Kill Minecraft?"); + //: Main question of the kill confirmation dialog + r_u_sure.setText(tr("Kill Minecraft?")); r_u_sure.setInformativeText("This can cause the instance to get corrupted and should only be used if Minecraft is frozen for some reason"); r_u_sure.setStandardButtons(QMessageBox::Yes | QMessageBox::No); r_u_sure.setDefaultButton(QMessageBox::Yes); diff --git a/gui/logindialog.cpp b/gui/logindialog.cpp index a4dad1c1..37e30c85 100644 --- a/gui/logindialog.cpp +++ b/gui/logindialog.cpp @@ -24,8 +24,8 @@ LoginDialog::LoginDialog(QWidget *parent, const QString& loginErrMsg) : { ui->setupUi(this); - //TODO: make translateable - offlineButton = new QPushButton("Offline Once"); + //: Use offline mode one time + offlineButton = new QPushButton(tr("Offline Once")); ui->loginButtonBox->addButton(offlineButton, QDialogButtonBox::ActionRole); @@ -33,8 +33,8 @@ LoginDialog::LoginDialog(QWidget *parent, const QString& loginErrMsg) : isOnline_ = true; onlineForced = false; - //FIXME: translateable? - ui->usernameTextBox->lineEdit()->setPlaceholderText(QApplication::translate("LoginDialog", "Name", 0)); + //: The username during login (placeholder) + ui->usernameTextBox->lineEdit()->setPlaceholderText(tr("Name")); connect(ui->usernameTextBox, SIGNAL(currentTextChanged(QString)), this, SLOT(userTextChanged(QString))); connect(ui->forgetButton, SIGNAL(clicked(bool)), this, SLOT(forgetCurrentUser())); diff --git a/gui/lwjglselectdialog.cpp b/gui/lwjglselectdialog.cpp index 9de92754..c3215b7b 100644 --- a/gui/lwjglselectdialog.cpp +++ b/gui/lwjglselectdialog.cpp @@ -54,7 +54,7 @@ void LWJGLSelectDialog::loadingStateUpdated(bool loading) setEnabled(!loading); if (loading) { - ui->labelStatus->setText("Loading LWJGL version list..."); + ui->labelStatus->setText(tr("Loading LWJGL version list...")); ui->labelStatus->setStyleSheet("QLabel { color: black; }"); } ui->labelStatus->setVisible(loading); diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 7ddc66a9..747df047 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -587,7 +587,7 @@ void MainWindow::on_actionMakeDesktopShortcut_triggered() Util::createShortCut ( Util::getDesktopDir(), QApplication::instance()->applicationFilePath(), QStringList() << "-dl" << QDir::currentPath() << "test", name, "application-x-octet-stream" ); - QMessageBox::warning ( this, "Not useful", "A Dummy Shortcut was created. it will not do anything productive" ); + QMessageBox::warning ( this, tr("Not useful"), tr("A Dummy Shortcut was created. it will not do anything productive") ); } // BrowserDialog @@ -658,7 +658,7 @@ void MainWindow::selectionBad() QString iconKey = "infinity"; statusBar()->clearMessage(); ui->instanceToolBar->setEnabled(false); - renameButton->setText("Rename Instance"); + renameButton->setText(tr("Rename Instance")); auto ico = MMC->icons()->getIcon(iconKey); ui->actionChangeInstIcon->setIcon(ico); } diff --git a/gui/settingsdialog.cpp b/gui/settingsdialog.cpp index a1fbb8e7..a5283b36 100644 --- a/gui/settingsdialog.cpp +++ b/gui/settingsdialog.cpp @@ -49,7 +49,7 @@ void SettingsDialog::updateCheckboxStuff() void SettingsDialog::on_instDirBrowseBtn_clicked() { - QString dir = QFileDialog::getExistingDirectory(this, "Instance Directory", + QString dir = QFileDialog::getExistingDirectory(this, tr("Instance Directory"), ui->instDirTextBox->text()); if (!dir.isEmpty()) ui->instDirTextBox->setText(dir); @@ -57,7 +57,7 @@ void SettingsDialog::on_instDirBrowseBtn_clicked() void SettingsDialog::on_modsDirBrowseBtn_clicked() { - QString dir = QFileDialog::getExistingDirectory(this, "Mods Directory", + QString dir = QFileDialog::getExistingDirectory(this, tr("Mods Directory"), ui->modsDirTextBox->text()); if (!dir.isEmpty()) ui->modsDirTextBox->setText(dir); @@ -65,7 +65,7 @@ void SettingsDialog::on_modsDirBrowseBtn_clicked() void SettingsDialog::on_lwjglDirBrowseBtn_clicked() { - QString dir = QFileDialog::getExistingDirectory(this, "LWJGL Directory", + QString dir = QFileDialog::getExistingDirectory(this, tr("LWJGL Directory"), ui->lwjglDirTextBox->text()); if (!dir.isEmpty()) ui->lwjglDirTextBox->setText(dir); @@ -99,9 +99,9 @@ void SettingsDialog::applySettings(SettingsObject *s) } else if (!s->get("UseDevBuilds").toBool()) { - int response = QMessageBox::question(this, "Development builds", - "Development builds contain experimental features " - "and may be unstable. Are you sure you want to enable them?"); + int response = QMessageBox::question(this, tr("Development builds"), + tr("Development builds contain experimental features " + "and may be unstable. Are you sure you want to enable them?")); if (response == QMessageBox::Yes) { s->set("UseDevBuilds", true); diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index c33d34a8..299f00be 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -120,9 +120,11 @@ void MinecraftProcess::finish(int code, ExitStatus status) // TODO: Localization if (!killed) - emit log("Minecraft exited."); + //: Message displayed on instance exit + emit log(tr("Minecraft exited with exitcode %1.").arg(status)); else - emit log("Minecraft was killed by user.", MessageLevel::Error); + //: Message displayed after the instance exits due to kill request + emit log(tr("Minecraft was killed by user."), MessageLevel::Error); m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); @@ -168,7 +170,8 @@ void MinecraftProcess::launch() start(JavaPath, m_args); if (!waitForStarted()) { - emit log("Could not launch minecraft!"); + //: Error message displayed if instace can't start + emit log(tr("Could not launch minecraft!")); return; //TODO: error handling } diff --git a/logic/tasks/LoginTask.cpp b/logic/tasks/LoginTask.cpp index ad9de7f5..859827bc 100644 --- a/logic/tasks/LoginTask.cpp +++ b/logic/tasks/LoginTask.cpp @@ -28,7 +28,7 @@ LoginTask::LoginTask( const UserInfo& uInfo, QObject* parent ) : Task(parent), u void LoginTask::executeTask() { - setStatus("Logging in..."); + setStatus(tr("Logging in...")); auto worker = MMC->qnam(); connect(worker, SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*))); @@ -76,36 +76,36 @@ void LoginTask::processNetReply(QNetworkReply *reply) } else { - emitFailed("Failed to parse Minecraft version string."); + emitFailed(tr("Failed to parse Minecraft version string.")); } } else { if (responseStr.toLower() == "bad login") - emitFailed("Invalid username or password."); + emitFailed(tr("Invalid username or password.")); else if (responseStr.toLower() == "old version") - emitFailed("Launcher outdated, please update."); + emitFailed(tr("Launcher outdated, please update.")); else - emitFailed("Login failed: " + responseStr); + emitFailed(tr("Login failed: %1").arg(responseStr)); } } else if (responseCode == 503) { - emitFailed("The login servers are currently unavailable. Check http://help.mojang.com/ for more info."); + emitFailed(tr("The login servers are currently unavailable. Check http://help.mojang.com/ for more info.")); } else { - emitFailed(QString("Login failed: Unknown HTTP error %1 occurred.").arg(QString::number(responseCode))); + emitFailed(tr("Login failed: Unknown HTTP error %1 occurred.").arg(QString::number(responseCode))); } break; } case QNetworkReply::OperationCanceledError: - emitFailed("Login canceled."); + emitFailed(tr("Login canceled.")); break; default: - emitFailed("Login failed: " + reply->errorString()); + emitFailed(tr("Login failed: %1").arg(reply->errorString())); break; } } -- cgit From cffdc4e045d4e72376d83c30ad063c290b071d62 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 19:35:36 +0200 Subject: Fix legacy instance update segfault --- logic/LegacyUpdate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index c75a0011..7bf12faa 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -229,7 +229,7 @@ void LegacyUpdate::jarStart() auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); dljob->add(QUrl(urlstr), inst->defaultBaseJar()); - legacyDownloadJob.reset(); + legacyDownloadJob.reset(dljob); connect(legacyDownloadJob.data(), SIGNAL(finished()), SLOT(jarFinished())); connect(legacyDownloadJob.data(), SIGNAL(failed()), SLOT(jarFailed())); connect(legacyDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); -- cgit From 8062b73074b8e3c94fd97b77b05e7dbb61205d41 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 8 Sep 2013 20:16:38 +0200 Subject: Fix one more legacy update bug --- logic/LegacyUpdate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'logic') diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 7bf12faa..b8e179a5 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -230,9 +230,9 @@ void LegacyUpdate::jarStart() auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); dljob->add(QUrl(urlstr), inst->defaultBaseJar()); legacyDownloadJob.reset(dljob); - connect(legacyDownloadJob.data(), SIGNAL(finished()), SLOT(jarFinished())); - connect(legacyDownloadJob.data(), SIGNAL(failed()), SLOT(jarFailed())); - connect(legacyDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); + connect(dljob, SIGNAL(failed()), SLOT(jarFailed())); + connect(dljob, SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); legacyDownloadJob->start(); } -- cgit From 91a3e650e98c289ec183e8c4091da0dae0968f88 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 10 Sep 2013 00:29:25 +0200 Subject: Fix download progress (bars) --- logic/net/DownloadJob.cpp | 19 ++++++++++++++++++- logic/net/DownloadJob.h | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'logic') diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index cbc2c5fe..090df260 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -10,6 +10,8 @@ ByteArrayDownloadPtr DownloadJob::add ( QUrl url ) ByteArrayDownloadPtr ptr (new ByteArrayDownload(url)); ptr->index_within_job = downloads.size(); downloads.append(ptr); + parts_progress.append(QPair(0,1)); + total_progress++; return ptr; } @@ -18,6 +20,8 @@ FileDownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path) FileDownloadPtr ptr (new FileDownload(url, rel_target_path)); ptr->index_within_job = downloads.size(); downloads.append(ptr); + parts_progress.append(QPair(0,1)); + total_progress++; return ptr; } @@ -26,6 +30,8 @@ CacheDownloadPtr DownloadJob::add ( QUrl url, MetaEntryPtr entry) CacheDownloadPtr ptr (new CacheDownload(url, entry)); ptr->index_within_job = downloads.size(); downloads.append(ptr); + parts_progress.append(QPair(0,1)); + total_progress++; return ptr; } @@ -60,7 +66,16 @@ void DownloadJob::partFailed ( int index ) void DownloadJob::partProgress ( int index, qint64 bytesReceived, qint64 bytesTotal ) { - // PROGRESS? DENIED! + auto & slot = parts_progress[index]; + + current_progress -= slot.first; + slot.first = bytesReceived; + current_progress += slot.first; + + total_progress -= slot.second; + slot.second = bytesTotal; + total_progress += slot.second; + emit progress(current_progress, total_progress); } @@ -70,6 +85,8 @@ void DownloadJob::start() for(auto iter: downloads) { connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); + connect(iter.data(), SIGNAL(failed(int)), SLOT(partFailed(int))); + connect(iter.data(), SIGNAL(progress(int,qint64,qint64)), SLOT(partProgress(int,qint64,qint64))); iter->start(); } } diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index 71466282..69a49e59 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -51,6 +51,9 @@ private slots: private: QString m_job_name; QList downloads; + QList> parts_progress; + qint64 current_progress = 0; + qint64 total_progress = 0; int num_succeeded = 0; int num_failed = 0; }; -- cgit From 108a5a677c4bf248b70e77046502ea96bd9cfe65 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 10 Sep 2013 00:34:34 +0200 Subject: Progress works for null downloads (header checks) --- logic/net/DownloadJob.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'logic') diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index 090df260..9b083b6b 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -37,6 +37,10 @@ CacheDownloadPtr DownloadJob::add ( QUrl url, MetaEntryPtr entry) void DownloadJob::partSucceeded ( int index ) { + // do progress. all slots are 1 in size at least + auto & slot = parts_progress[index]; + partProgress ( index, slot.second , slot.second ); + num_succeeded++; qDebug() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" << downloads.size(); if(num_failed + num_succeeded == downloads.size()) -- cgit From 7721c57e5e1093a3d8597b6b6f30c97d2aa3d8a5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 11 Sep 2013 23:43:17 +0200 Subject: Split OneSixVersion into parts. --- CMakeLists.txt | 11 +++- logic/OneSixInstance_p.h | 1 + logic/OneSixLibrary.cpp | 84 ++++++++++++++++++++++++++ logic/OneSixLibrary.h | 68 +++++++++++++++++++++ logic/OneSixRule.cpp | 10 ++++ logic/OneSixRule.h | 70 ++++++++++++++++++++++ logic/OneSixUpdate.cpp | 1 + logic/OneSixVersion.cpp | 112 ++-------------------------------- logic/OneSixVersion.h | 153 ++--------------------------------------------- logic/OpSys.cpp | 12 ++++ logic/OpSys.h | 21 +++++++ logic/VersionFactory.cpp | 3 +- 12 files changed, 287 insertions(+), 259 deletions(-) create mode 100644 logic/OneSixLibrary.cpp create mode 100644 logic/OneSixLibrary.h create mode 100644 logic/OneSixRule.cpp create mode 100644 logic/OneSixRule.h create mode 100644 logic/OpSys.cpp create mode 100644 logic/OpSys.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index 62f878c7..feb46278 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,7 +205,11 @@ logic/OneSixInstance.h logic/OneSixInstance_p.h logic/OneSixUpdate.h logic/OneSixVersion.h +logic/OneSixLibrary.h +logic/OneSixRule.h logic/VersionFactory.h +logic/OpSys.h + # Nostalgia logic/NostalgiaInstance.h @@ -272,9 +276,12 @@ logic/LegacyForge.cpp # 1.6 instances logic/OneSixAssets.cpp logic/OneSixInstance.cpp -logic/OneSixVersion.cpp logic/OneSixUpdate.cpp +logic/OneSixVersion.cpp +logic/OneSixLibrary.cpp +logic/OneSixRule.cpp logic/VersionFactory.cpp +logic/OpSys.cpp # Nostalgia logic/NostalgiaInstance.cpp @@ -497,4 +504,4 @@ endif (UPDATE_TRANSLATIONS) add_custom_target (translations_target DEPENDS ${QM_FILES}) -install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/translations) \ No newline at end of file +install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/translations) diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index c098c9e2..7b1ca82e 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -2,6 +2,7 @@ #include "BaseInstance_p.h" #include "OneSixVersion.h" +#include "OneSixLibrary.h" #include "ModList.h" struct OneSixInstancePrivate: public BaseInstancePrivate diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp new file mode 100644 index 00000000..a109a7f0 --- /dev/null +++ b/logic/OneSixLibrary.cpp @@ -0,0 +1,84 @@ +#include "OneSixLibrary.h" +#include "OneSixRule.h" + +void OneSixLibrary::finalize() +{ + QStringList parts = m_name.split ( ':' ); + QString relative = parts[0]; + relative.replace ( '.','/' ); + relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; + if ( !m_is_native ) + relative += ".jar"; + else + { + if ( m_native_suffixes.contains ( currentSystem ) ) + { + relative += "-" + m_native_suffixes[currentSystem] + ".jar"; + } + else + { + // really, bad. + relative += ".jar"; + } + } + m_storage_path = relative; + m_download_path = m_base_url + relative; + + if ( m_rules.empty() ) + { + m_is_active = true; + } + else + { + RuleAction result = Disallow; + for ( auto rule: m_rules ) + { + RuleAction temp = rule->apply ( this ); + if ( temp != Defer ) + result = temp; + } + m_is_active = ( result == Allow ); + } + if ( m_is_native ) + { + m_is_active = m_is_active && m_native_suffixes.contains ( currentSystem ); + } +} + +void OneSixLibrary::setName ( QString name ) +{ + m_name = name; +} +void OneSixLibrary::setBaseUrl ( QString base_url ) +{ + m_base_url = base_url; +} +void OneSixLibrary::setIsNative() +{ + m_is_native = true; +} +void OneSixLibrary::addNative ( OpSys os, QString suffix ) +{ + m_is_native = true; + m_native_suffixes[os] = suffix; +} +void OneSixLibrary::setRules ( QList< QSharedPointer< Rule > > rules ) +{ + m_rules = rules; +} +bool OneSixLibrary::isActive() +{ + return m_is_active; +} +bool OneSixLibrary::isNative() +{ + return m_is_native; +} +QString OneSixLibrary::downloadPath() +{ + return m_download_path; +} +QString OneSixLibrary::storagePath() +{ + return m_storage_path; +} diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h new file mode 100644 index 00000000..856e409c --- /dev/null +++ b/logic/OneSixLibrary.h @@ -0,0 +1,68 @@ +#pragma once +#include +#include +#include +#include +#include "OpSys.h" + +class Rule; + +class OneSixLibrary +{ +private: + // basic values used internally (so far) + QString m_name; + QString m_base_url; + QList > m_rules; + + // derived values used for real things + /// where to store the lib locally + QString m_storage_path; + /// where to download the lib from + QString m_download_path; + /// is this lib actually active on the current OS? + bool m_is_active; + /// is the library a native? + bool m_is_native; + /// native suffixes per OS + QMap m_native_suffixes; +public: + QStringList extract_excludes; + +public: + /// Constructor + OneSixLibrary(QString name) + { + m_is_native = false; + m_is_active = false; + m_name = name; + m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; + } + + /** + * finalize the library, processing the input values into derived values and state + * + * This SHALL be called after all the values are parsed or after any further change. + */ + void finalize(); + + /// Set the library composite name + void setName(QString name); + /// Set the url base for downloads + void setBaseUrl(QString base_url); + /// Call this to mark the library as 'native' (it's a zip archive with DLLs) + void setIsNative(); + /// Attach a name suffix to the specified OS native + void addNative(OpSys os, QString suffix); + /// Set the load rules + void setRules(QList > rules); + + /// Returns true if the library should be loaded (or extracted, in case of natives) + bool isActive(); + /// Returns true if the library is native + bool isNative(); + /// Get the URL to download the library from + QString downloadPath(); + /// Get the relative path where the library should be saved + QString storagePath(); +}; diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp new file mode 100644 index 00000000..85f7d434 --- /dev/null +++ b/logic/OneSixRule.cpp @@ -0,0 +1,10 @@ +#include "OneSixRule.h" + +RuleAction RuleAction_fromString(QString name) +{ + if(name == "allow") + return Allow; + if(name == "disallow") + return Disallow; + return Defer; +} \ No newline at end of file diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h new file mode 100644 index 00000000..465c963f --- /dev/null +++ b/logic/OneSixRule.h @@ -0,0 +1,70 @@ +#pragma once +#include +#include + +class OneSixLibrary; +#include "OneSixLibrary.h" + +enum RuleAction +{ + Allow, + Disallow, + Defer +}; + +RuleAction RuleAction_fromString(QString); + +class Rule +{ +protected: + RuleAction m_result; + virtual bool applies(OneSixLibrary * parent) = 0; +public: + Rule(RuleAction result) + :m_result(result) {} + virtual ~Rule(){}; + RuleAction apply(OneSixLibrary * parent) + { + if(applies(parent)) + return m_result; + else + return Defer; + }; +}; + +class OsRule : public Rule +{ +private: + // the OS + OpSys m_system; + // the OS version regexp + QString m_version_regexp; +protected: + virtual bool applies ( OneSixLibrary* ) + { + return (m_system == currentSystem); + } + OsRule(RuleAction result, OpSys system, QString version_regexp) + : Rule(result), m_system(system), m_version_regexp(version_regexp) {} +public: + static QSharedPointer create(RuleAction result, OpSys system, QString version_regexp) + { + return QSharedPointer (new OsRule(result, system, version_regexp)); + } +}; + +class ImplicitRule : public Rule +{ +protected: + virtual bool applies ( OneSixLibrary* ) + { + return true; + } + ImplicitRule(RuleAction result) + : Rule(result) {} +public: + static QSharedPointer create(RuleAction result) + { + return QSharedPointer (new ImplicitRule(result)); + } +}; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index ce71bde0..a58a9626 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -28,6 +28,7 @@ #include "lists/MinecraftVersionList.h" #include "VersionFactory.h" #include "OneSixVersion.h" +#include "OneSixLibrary.h" #include "OneSixInstance.h" #include "pathutils.h" diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 56e272e2..7ffe9a94 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -1,111 +1,9 @@ #include "OneSixVersion.h" +#include "OneSixLibrary.h" -RuleAction RuleAction_fromString(QString name) +QList > OneSixVersion::getActiveNormalLibs() { - if(name == "allow") - return Allow; - if(name == "disallow") - return Disallow; - return Defer; -} - -OpSys OpSys_fromString(QString name) -{ - if(name == "linux") - return Os_Linux; - if(name == "windows") - return Os_Windows; - if(name == "osx") - return Os_OSX; - return Os_Other; -} - -void Library::finalize() -{ - QStringList parts = m_name.split ( ':' ); - QString relative = parts[0]; - relative.replace ( '.','/' ); - relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; - if ( !m_is_native ) - relative += ".jar"; - else - { - if ( m_native_suffixes.contains ( currentSystem ) ) - { - relative += "-" + m_native_suffixes[currentSystem] + ".jar"; - } - else - { - // really, bad. - relative += ".jar"; - } - } - m_storage_path = relative; - m_download_path = m_base_url + relative; - - if ( m_rules.empty() ) - { - m_is_active = true; - } - else - { - RuleAction result = Disallow; - for ( auto rule: m_rules ) - { - RuleAction temp = rule->apply ( this ); - if ( temp != Defer ) - result = temp; - } - m_is_active = ( result == Allow ); - } - if ( m_is_native ) - { - m_is_active = m_is_active && m_native_suffixes.contains ( currentSystem ); - } -} - -void Library::setName ( QString name ) -{ - m_name = name; -} -void Library::setBaseUrl ( QString base_url ) -{ - m_base_url = base_url; -} -void Library::setIsNative() -{ - m_is_native = true; -} -void Library::addNative ( OpSys os, QString suffix ) -{ - m_is_native = true; - m_native_suffixes[os] = suffix; -} -void Library::setRules ( QList< QSharedPointer< Rule > > rules ) -{ - m_rules = rules; -} -bool Library::isActive() -{ - return m_is_active; -} -bool Library::isNative() -{ - return m_is_native; -} -QString Library::downloadPath() -{ - return m_download_path; -} -QString Library::storagePath() -{ - return m_storage_path; -} - - -QList > OneSixVersion::getActiveNormalLibs() -{ - QList > output; + QList > output; for ( auto lib: libraries ) { if (lib->isActive() && !lib->isNative()) @@ -116,9 +14,9 @@ QList > OneSixVersion::getActiveNormalLibs() return output; } -QList > OneSixVersion::getActiveNativeLibs() +QList > OneSixVersion::getActiveNativeLibs() { - QList > output; + QList > output; for ( auto lib: libraries ) { if (lib->isActive() && lib->isNative()) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 89b7c911..8f01f82d 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -1,151 +1,6 @@ #pragma once #include - -class Library; - -enum OpSys -{ - Os_Windows, - Os_Linux, - Os_OSX, - Os_Other -}; - -OpSys OpSys_fromString(QString); - -#ifdef Q_OS_WIN32 - #define currentSystem Os_Windows -#else - #ifdef Q_OS_MAC - #define currentSystem Os_OSX - #else - #define currentSystem Os_Linux - #endif -#endif -enum RuleAction -{ - Allow, - Disallow, - Defer -}; - -RuleAction RuleAction_fromString(QString); - -class Rule -{ -protected: - RuleAction m_result; - virtual bool applies(Library * parent) = 0; -public: - Rule(RuleAction result) - :m_result(result) {} - virtual ~Rule(){}; - RuleAction apply(Library * parent) - { - if(applies(parent)) - return m_result; - else - return Defer; - }; -}; - -class OsRule : public Rule -{ -private: - // the OS - OpSys m_system; - // the OS version regexp - QString m_version_regexp; -protected: - virtual bool applies ( Library* ) - { - return (m_system == currentSystem); - } - OsRule(RuleAction result, OpSys system, QString version_regexp) - : Rule(result), m_system(system), m_version_regexp(version_regexp) {} -public: - static QSharedPointer create(RuleAction result, OpSys system, QString version_regexp) - { - return QSharedPointer (new OsRule(result, system, version_regexp)); - } -}; - -class ImplicitRule : public Rule -{ -protected: - virtual bool applies ( Library* ) - { - return true; - } - ImplicitRule(RuleAction result) - : Rule(result) {} -public: - static QSharedPointer create(RuleAction result) - { - return QSharedPointer (new ImplicitRule(result)); - } -}; - -class Library -{ -private: - // basic values used internally (so far) - QString m_name; - QString m_base_url; - QList > m_rules; - - // derived values used for real things - /// where to store the lib locally - QString m_storage_path; - /// where to download the lib from - QString m_download_path; - /// is this lib actually active on the current OS? - bool m_is_active; - /// is the library a native? - bool m_is_native; - /// native suffixes per OS - QMap m_native_suffixes; -public: - QStringList extract_excludes; - -public: - /// Constructor - Library(QString name) - { - m_is_native = false; - m_is_native = false; - m_name = name; - m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; - } - - /** - * finalize the library, processing the input values into derived values and state - * - * This SHALL be called after all the values are parsed or after any further change. - */ - void finalize(); - - /// Set the library composite name - void setName(QString name); - /// Set the url base for downloads - void setBaseUrl(QString base_url); - /// Call this to mark the library as 'native' (it's a zip archive with DLLs) - void setIsNative(); - /// Attach a name suffix to the specified OS native - void addNative(OpSys os, QString suffix); - /// Set the load rules - void setRules(QList > rules); - - /// Returns true if the library should be loaded (or extracted, in case of natives) - bool isActive(); - /// Returns true if the library is native - bool isNative(); - /// Get the URL to download the library from - QString downloadPath(); - /// Get the relative path where the library should be saved - QString storagePath(); -}; - +class OneSixLibrary; class OneSixVersion { @@ -180,7 +35,7 @@ public: QString mainClass; /// the list of libs - both active and inactive, native and java - QList > libraries; + QList > libraries; /* FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. @@ -208,6 +63,6 @@ public: minimumLauncherVersion = 0xDEADBEEF; } - QList > getActiveNormalLibs(); - QList > getActiveNativeLibs(); + QList > getActiveNormalLibs(); + QList > getActiveNativeLibs(); }; \ No newline at end of file diff --git a/logic/OpSys.cpp b/logic/OpSys.cpp new file mode 100644 index 00000000..559479fd --- /dev/null +++ b/logic/OpSys.cpp @@ -0,0 +1,12 @@ +#include "OpSys.h" + +OpSys OpSys_fromString(QString name) +{ + if(name == "linux") + return Os_Linux; + if(name == "windows") + return Os_Windows; + if(name == "osx") + return Os_OSX; + return Os_Other; +} \ No newline at end of file diff --git a/logic/OpSys.h b/logic/OpSys.h new file mode 100644 index 00000000..c68c437a --- /dev/null +++ b/logic/OpSys.h @@ -0,0 +1,21 @@ +#pragma once +#include +enum OpSys +{ + Os_Windows, + Os_Linux, + Os_OSX, + Os_Other +}; + +OpSys OpSys_fromString(QString); + +#ifdef Q_OS_WIN32 + #define currentSystem Os_Windows +#else + #ifdef Q_OS_MAC + #define currentSystem Os_OSX + #else + #define currentSystem Os_Linux + #endif +#endif \ No newline at end of file diff --git a/logic/VersionFactory.cpp b/logic/VersionFactory.cpp index 71c4d747..4fa5ad3f 100644 --- a/logic/VersionFactory.cpp +++ b/logic/VersionFactory.cpp @@ -1,5 +1,6 @@ #include "VersionFactory.h" #include "OneSixVersion.h" +#include "OneSixRule.h" // Library rules (if any) QList > FullVersionFactory::parse4rules(QJsonObject & baseObj) @@ -103,7 +104,7 @@ QSharedPointer FullVersionFactory::parse4(QJsonObject root, QShar auto nameVal = libObj.value("name"); if(!nameVal.isString()) continue; - QSharedPointer library(new Library(nameVal.toString())); + QSharedPointer library(new OneSixLibrary(nameVal.toString())); auto urlVal = libObj.value("url"); if(urlVal.isString()) -- cgit From d38b90530b3ba3a49c4eb072eb344ae2b0836913 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 16 Sep 2013 00:54:39 +0200 Subject: Forge version list implementation. Needs integration and testing. --- CMakeLists.txt | 18 +++- MultiMC.cpp | 45 ++++++++ MultiMC.h | 13 +++ gui/ModListView.cpp | 3 +- gui/OneSixModEditDialog.cpp | 12 ++- gui/OneSixModEditDialog.h | 1 + gui/OneSixModEditDialog.ui | 135 +++++++++++++++++++---- gui/lwjglselectdialog.cpp | 16 +-- gui/mainwindow.cpp | 12 +-- gui/newinstancedialog.cpp | 14 +-- gui/newinstancedialog.h | 8 +- gui/versionselectdialog.cpp | 14 +-- gui/versionselectdialog.h | 10 +- logic/BaseInstance.cpp | 5 +- logic/BaseInstance.h | 4 +- logic/BaseVersion.h | 45 ++++++++ logic/EnabledItemFilter.cpp | 30 ++++++ logic/EnabledItemFilter.h | 16 +++ logic/InstanceFactory.cpp | 10 +- logic/InstanceFactory.h | 6 +- logic/InstanceVersion.h | 68 ------------ logic/LegacyUpdate.cpp | 8 +- logic/MinecraftVersion.h | 29 +++-- logic/OneSixInstance.cpp | 21 +++- logic/OneSixInstance.h | 2 + logic/OneSixLibrary.cpp | 13 ++- logic/OneSixLibrary.h | 22 ++++ logic/OneSixUpdate.cpp | 47 +++++--- logic/OneSixVersion.cpp | 71 +++++++++++++ logic/OneSixVersion.h | 11 +- logic/lists/BaseVersionList.cpp | 123 +++++++++++++++++++++ logic/lists/BaseVersionList.h | 121 +++++++++++++++++++++ logic/lists/ForgeVersionList.cpp | 200 +++++++++++++++++++++++++++++++++++ logic/lists/ForgeVersionList.h | 98 +++++++++++++++++ logic/lists/InstVersionList.cpp | 129 ---------------------- logic/lists/InstVersionList.h | 121 --------------------- logic/lists/LwjglVersionList.cpp | 8 -- logic/lists/LwjglVersionList.h | 2 - logic/lists/MinecraftVersionList.cpp | 34 ++---- logic/lists/MinecraftVersionList.h | 21 ++-- 40 files changed, 1091 insertions(+), 475 deletions(-) create mode 100644 logic/BaseVersion.h create mode 100644 logic/EnabledItemFilter.cpp create mode 100644 logic/EnabledItemFilter.h delete mode 100644 logic/InstanceVersion.h create mode 100644 logic/lists/BaseVersionList.cpp create mode 100644 logic/lists/BaseVersionList.h create mode 100644 logic/lists/ForgeVersionList.cpp create mode 100644 logic/lists/ForgeVersionList.h delete mode 100644 logic/lists/InstVersionList.cpp delete mode 100644 logic/lists/InstVersionList.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index feb46278..d4f0cbbf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,7 +172,7 @@ gui/LabeledToolButton.h gui/EditNotesDialog.h # Base classes and infrastructure -logic/InstanceVersion.h +logic/BaseVersion.h logic/MinecraftVersion.h logic/InstanceFactory.h logic/BaseUpdate.h @@ -216,10 +216,14 @@ logic/NostalgiaInstance.h # Lists logic/lists/InstanceList.h -logic/lists/InstVersionList.h +logic/lists/IconList.h +logic/lists/BaseVersionList.h logic/lists/MinecraftVersionList.h logic/lists/LwjglVersionList.h -logic/lists/IconList.h +logic/lists/ForgeVersionList.h + +# misc model/view +logic/EnabledItemFilter.h # Tasks logic/tasks/Task.h @@ -288,10 +292,14 @@ logic/NostalgiaInstance.cpp # Lists logic/lists/InstanceList.cpp -logic/lists/InstVersionList.cpp +logic/lists/IconList.cpp +logic/lists/BaseVersionList.cpp logic/lists/MinecraftVersionList.cpp logic/lists/LwjglVersionList.cpp -logic/lists/IconList.cpp +logic/lists/ForgeVersionList.cpp + +# misc model/view +logic/EnabledItemFilter.cpp # Tasks logic/tasks/Task.cpp diff --git a/MultiMC.cpp b/MultiMC.cpp index b49773a1..08e5ed25 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -9,6 +9,10 @@ #include "gui/mainwindow.h" #include "logic/lists/InstanceList.h" #include "logic/lists/IconList.h" +#include "logic/lists/LwjglVersionList.h" +#include "logic/lists/MinecraftVersionList.h" +#include "logic/lists/ForgeVersionList.h" + #include "logic/InstanceLauncher.h" #include "logic/net/HttpMetaCache.h" @@ -158,6 +162,21 @@ MultiMC::~MultiMC() delete m_qt_translator; m_qt_translator = nullptr; } + if(m_icons) + { + delete m_icons; + m_icons = nullptr; + } + if(m_lwjgllist) + { + delete m_lwjgllist; + m_lwjgllist = nullptr; + } + if(m_minecraftlist) + { + delete m_minecraftlist; + m_minecraftlist = nullptr; + } delete m_settings; delete m_metacache; } @@ -276,6 +295,32 @@ IconList* MultiMC::icons() return m_icons; } +LWJGLVersionList* MultiMC::lwjgllist() +{ + if ( !m_lwjgllist ) + { + m_lwjgllist = new LWJGLVersionList(); + } + return m_lwjgllist; +} +ForgeVersionList* MultiMC::forgelist() +{ + if ( !m_forgelist ) + { + m_forgelist = new ForgeVersionList(); + } + return m_forgelist; +} + +MinecraftVersionList* MultiMC::minecraftlist() +{ + if ( !m_minecraftlist ) + { + m_minecraftlist = new MinecraftVersionList(); + } + return m_minecraftlist; +} + int main(int argc, char *argv[]) { diff --git a/MultiMC.h b/MultiMC.h index 216c670b..d3e92584 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -4,11 +4,14 @@ #include "MultiMCVersion.h" #include "config.h" +class MinecraftVersionList; +class LWJGLVersionList; class HttpMetaCache; class SettingsObject; class InstanceList; class IconList; class QNetworkAccessManager; +class ForgeVersionList; #if defined(MMC) #undef MMC @@ -61,6 +64,12 @@ public: { return m_metacache; } + + LWJGLVersionList * lwjgllist(); + + ForgeVersionList * forgelist(); + + MinecraftVersionList * minecraftlist(); private: void initGlobalSettings(); @@ -76,5 +85,9 @@ private: QNetworkAccessManager * m_qnam = nullptr; HttpMetaCache * m_metacache = nullptr; Status m_status = MultiMC::Failed; + LWJGLVersionList * m_lwjgllist = nullptr; + ForgeVersionList * m_forgelist = nullptr; + MinecraftVersionList * m_minecraftlist = nullptr; + MultiMCVersion m_version = {VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD}; }; \ No newline at end of file diff --git a/gui/ModListView.cpp b/gui/ModListView.cpp index 34bd4af2..1d0e834c 100644 --- a/gui/ModListView.cpp +++ b/gui/ModListView.cpp @@ -30,6 +30,7 @@ void ModListView::setModel ( QAbstractItemModel* model ) auto head = header(); head->setStretchLastSection(false); head->setSectionResizeMode(0, QHeaderView::Stretch); - head->setSectionResizeMode(1, QHeaderView::ResizeToContents); + for(int i = 1; i < head->count(); i++) + head->setSectionResizeMode(i, QHeaderView::ResizeToContents); dropIndicatorPosition(); } diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index ab6ad5f0..f778127f 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -22,6 +22,8 @@ #include #include #include +#include "logic/OneSixVersion.h" +#include OneSixModEditDialog::OneSixModEditDialog(OneSixInstance * inst, QWidget *parent): m_inst(inst), @@ -29,9 +31,15 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance * inst, QWidget *parent) ui(new Ui::OneSixModEditDialog) { ui->setupUi(this); - //TODO: libraries! + //libraries! { - // yeah... here be the real dragons. + m_version = m_inst->getFullVersion(); + + auto filter = new EnabledItemFilter(this); + filter->setActive(true); + filter->setSourceModel(m_version.data()); + ui->libraryTreeView->setModel(filter); + ui->libraryTreeView->installEventFilter( this ); } // Loader mods { diff --git a/gui/OneSixModEditDialog.h b/gui/OneSixModEditDialog.h index 3430bd26..c637df01 100644 --- a/gui/OneSixModEditDialog.h +++ b/gui/OneSixModEditDialog.h @@ -46,6 +46,7 @@ protected: bool resourcePackListFilter( QKeyEvent* ev ); private: Ui::OneSixModEditDialog *ui; + QSharedPointer m_version; QSharedPointer m_mods; QSharedPointer m_resourcepacks; OneSixInstance * m_inst; diff --git a/gui/OneSixModEditDialog.ui b/gui/OneSixModEditDialog.ui index 3feca726..bffcaed0 100644 --- a/gui/OneSixModEditDialog.ui +++ b/gui/OneSixModEditDialog.ui @@ -6,15 +6,15 @@ 0 0 - 543 - 423 + 555 + 463 Dialog - - + + true @@ -28,26 +28,121 @@ 0 - - Qt::ElideNone - - - false - - Library + Version - - - Qt::ScrollBarAlwaysOn - - - Qt::ScrollBarAlwaysOff - - + + + + + Qt::ScrollBarAlwaysOn + + + Qt::ScrollBarAlwaysOff + + + + + + + + + Main Class: + + + + + + + + + + + + + + + + Replace any current custom version with Minecraft Forge + + + Install Forge + + + + + + + Create an customized copy of the base version + + + Customize + + + + + + + Revert to original base version + + + Revert + + + + + + + QFrame::Sunken + + + Qt::Horizontal + + + + + + + false + + + Add new libraries + + + &Add + + + + + + + false + + + Remove selected libraries + + + &Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + @@ -163,7 +258,7 @@ - + false diff --git a/gui/lwjglselectdialog.cpp b/gui/lwjglselectdialog.cpp index c3215b7b..7c424a6c 100644 --- a/gui/lwjglselectdialog.cpp +++ b/gui/lwjglselectdialog.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "MultiMC.h" #include "lwjglselectdialog.h" #include "ui_lwjglselectdialog.h" @@ -24,11 +25,12 @@ LWJGLSelectDialog::LWJGLSelectDialog(QWidget *parent) : { ui->setupUi(this); ui->labelStatus->setVisible(false); - ui->lwjglListView->setModel(&LWJGLVersionList::get()); + auto lwjgllist = MMC->lwjgllist(); + ui->lwjglListView->setModel(lwjgllist); - connect(&LWJGLVersionList::get(), SIGNAL(loadingStateUpdated(bool)), SLOT(loadingStateUpdated(bool))); - connect(&LWJGLVersionList::get(), SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString))); - loadingStateUpdated(LWJGLVersionList::get().isLoading()); + connect(lwjgllist, SIGNAL(loadingStateUpdated(bool)), SLOT(loadingStateUpdated(bool))); + connect(lwjgllist, SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString))); + loadingStateUpdated(lwjgllist->isLoading()); } LWJGLSelectDialog::~LWJGLSelectDialog() @@ -38,15 +40,15 @@ LWJGLSelectDialog::~LWJGLSelectDialog() QString LWJGLSelectDialog::selectedVersion() const { - return LWJGLVersionList::get().data( + return MMC->lwjgllist()->data( ui->lwjglListView->selectionModel()->currentIndex(), Qt::DisplayRole).toString(); } void LWJGLSelectDialog::on_refreshButton_clicked() { - if (!LWJGLVersionList::get().isLoading()) - LWJGLVersionList::get().loadList(); + if (!MMC->lwjgllist()->isLoading()) + MMC->lwjgllist()->loadList(); } void LWJGLSelectDialog::loadingStateUpdated(bool loading) diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 747df047..241df383 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -154,14 +154,14 @@ MainWindow::MainWindow ( QWidget *parent ) // run the things that load and download other things... FIXME: this is NOT the place // FIXME: invisible actions in the background = NOPE. { - if (!MinecraftVersionList::getMainList().isLoaded()) + if (!MMC->minecraftlist()->isLoaded()) { - m_versionLoadTask = MinecraftVersionList::getMainList().getLoadTask(); + m_versionLoadTask = MMC->minecraftlist()->getLoadTask(); startTask(m_versionLoadTask); } - if (!LWJGLVersionList::get().isLoaded()) + if (!MMC->lwjgllist()->isLoaded()) { - LWJGLVersionList::get().loadList(); + MMC->lwjgllist()->loadList(); } assets_downloader = new OneSixAssets(); assets_downloader->start(); @@ -245,7 +245,7 @@ void MainWindow::instanceActivated ( QModelIndex index ) void MainWindow::on_actionAddInstance_triggered() { - if (!MinecraftVersionList::getMainList().isLoaded() && + if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && m_versionLoadTask->isRunning()) { QEventLoop waitLoop; @@ -604,7 +604,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() VersionSelectDialog vselect(m_selectedInstance->versionList(), this); if (vselect.exec() && vselect.selectedVersion()) { - m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor); + m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor()); } } diff --git a/gui/newinstancedialog.cpp b/gui/newinstancedialog.cpp index ac3bcd7d..af2b11c5 100644 --- a/gui/newinstancedialog.cpp +++ b/gui/newinstancedialog.cpp @@ -18,7 +18,7 @@ #include "ui_newinstancedialog.h" #include "logic/InstanceFactory.h" -#include "logic/InstanceVersion.h" +#include "logic/BaseVersion.h" #include "logic/lists/IconList.h" #include "logic/lists/MinecraftVersionList.h" #include "logic/tasks/Task.h" @@ -48,7 +48,7 @@ NewInstanceDialog::NewInstanceDialog(QWidget *parent) : taskDlg->exec(loadTask); } */ - setSelectedVersion(MinecraftVersionList::getMainList().getLatestStable()); + setSelectedVersion(MMC->minecraftlist()->getLatestStable()); InstIconKey = "infinity"; ui->iconButton->setIcon(MMC->icons()->getIcon(InstIconKey)); } @@ -63,13 +63,13 @@ void NewInstanceDialog::updateDialogState() ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!instName().isEmpty() && m_selectedVersion); } -void NewInstanceDialog::setSelectedVersion(InstVersionPtr version) +void NewInstanceDialog::setSelectedVersion(BaseVersionPtr version) { m_selectedVersion = version; if (m_selectedVersion) { - ui->versionTextBox->setText(version->name); + ui->versionTextBox->setText(version->name()); } else { @@ -89,18 +89,18 @@ QString NewInstanceDialog::iconKey() const return InstIconKey; } -InstVersionPtr NewInstanceDialog::selectedVersion() const +BaseVersionPtr NewInstanceDialog::selectedVersion() const { return m_selectedVersion; } void NewInstanceDialog::on_btnChangeVersion_clicked() { - VersionSelectDialog vselect(&MinecraftVersionList::getMainList(), this); + VersionSelectDialog vselect(MMC->minecraftlist(), this); vselect.exec(); if (vselect.result() == QDialog::Accepted) { - InstVersionPtr version = vselect.selectedVersion(); + BaseVersionPtr version = vselect.selectedVersion(); if (version) setSelectedVersion(version); } diff --git a/gui/newinstancedialog.h b/gui/newinstancedialog.h index e8c57024..408cf757 100644 --- a/gui/newinstancedialog.h +++ b/gui/newinstancedialog.h @@ -17,7 +17,7 @@ #define NEWINSTANCEDIALOG_H #include -#include "logic/InstanceVersion.h" +#include "logic/BaseVersion.h" namespace Ui { class NewInstanceDialog; @@ -33,13 +33,13 @@ public: void updateDialogState(); - void setSelectedVersion(InstVersionPtr version); + void setSelectedVersion(BaseVersionPtr version); void loadVersionList(); QString instName() const; QString iconKey() const; - InstVersionPtr selectedVersion() const; + BaseVersionPtr selectedVersion() const; private slots: void on_btnChangeVersion_clicked(); @@ -49,7 +49,7 @@ private slots: private: Ui::NewInstanceDialog *ui; - InstVersionPtr m_selectedVersion; + BaseVersionPtr m_selectedVersion; QString InstIconKey; }; diff --git a/gui/versionselectdialog.cpp b/gui/versionselectdialog.cpp index 66d772b0..b14956fd 100644 --- a/gui/versionselectdialog.cpp +++ b/gui/versionselectdialog.cpp @@ -22,11 +22,11 @@ #include -#include -#include +#include +#include #include -VersionSelectDialog::VersionSelectDialog(InstVersionList *vlist, QWidget *parent) : +VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QWidget *parent) : QDialog(parent), ui(new Ui::VersionSelectDialog) { @@ -69,11 +69,11 @@ void VersionSelectDialog::loadList() taskDlg->exec(loadTask); } -InstVersionPtr VersionSelectDialog::selectedVersion() const +BaseVersionPtr VersionSelectDialog::selectedVersion() const { auto currentIndex = ui->listView->selectionModel()->currentIndex(); - auto variant = m_proxyModel->data(currentIndex, InstVersionList::VersionPointerRole); - return variant.value(); + auto variant = m_proxyModel->data(currentIndex, BaseVersionList::VersionPointerRole); + return variant.value(); } void VersionSelectDialog::on_refreshButton_clicked() @@ -83,7 +83,7 @@ void VersionSelectDialog::on_refreshButton_clicked() void VersionSelectDialog::updateFilterState() { - m_proxyModel->setFilterKeyColumn(InstVersionList::TypeColumn); + m_proxyModel->setFilterKeyColumn(BaseVersionList::TypeColumn); QStringList filteredTypes; if (!ui->filterSnapshotsCheckbox->isChecked()) diff --git a/gui/versionselectdialog.h b/gui/versionselectdialog.h index b864aee1..57e4d0df 100644 --- a/gui/versionselectdialog.h +++ b/gui/versionselectdialog.h @@ -19,9 +19,9 @@ #include #include -#include "logic/InstanceVersion.h" +#include "logic/BaseVersion.h" -class InstVersionList; +class BaseVersionList; namespace Ui { @@ -33,7 +33,7 @@ class VersionSelectDialog : public QDialog Q_OBJECT public: - explicit VersionSelectDialog(InstVersionList *vlist, QWidget *parent = 0); + explicit VersionSelectDialog(BaseVersionList *vlist, QWidget *parent = 0); ~VersionSelectDialog(); virtual int exec(); @@ -41,7 +41,7 @@ public: //! Starts a task that loads the list. void loadList(); - InstVersionPtr selectedVersion() const; + BaseVersionPtr selectedVersion() const; private slots: void on_refreshButton_clicked(); @@ -51,7 +51,7 @@ private slots: private: Ui::VersionSelectDialog *ui; - InstVersionList *m_vlist; + BaseVersionList *m_vlist; QSortFilterProxyModel *m_proxyModel; }; diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index e166449f..10bb4573 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "MultiMC.h" #include "BaseInstance.h" #include "BaseInstance_p.h" @@ -131,9 +132,9 @@ InstanceList *BaseInstance::instList() const return NULL; } -InstVersionList *BaseInstance::versionList() const +BaseVersionList *BaseInstance::versionList() const { - return &MinecraftVersionList::getMainList(); + return MMC->minecraftlist(); } SettingsObject &BaseInstance::settings() const diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index cc9422be..fa317ba1 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -21,7 +21,7 @@ #include #include "inifile.h" -#include "lists/InstVersionList.h" +#include "lists/BaseVersionList.h" class QDialog; class BaseUpdate; @@ -134,7 +134,7 @@ public: * \brief Gets a pointer to this instance's version list. * \return A pointer to the available version list for this instance. */ - virtual InstVersionList *versionList() const; + virtual BaseVersionList *versionList() const; /*! * \brief Gets this instance's settings object. diff --git a/logic/BaseVersion.h b/logic/BaseVersion.h new file mode 100644 index 00000000..be717fee --- /dev/null +++ b/logic/BaseVersion.h @@ -0,0 +1,45 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include + +/*! + * An abstract base class for versions. + */ +struct BaseVersion +{ + /*! + * A string used to identify this version in config files. + * This should be unique within the version list or shenanigans will occur. + */ + virtual QString descriptor() = 0; + + /*! + * The name of this version as it is displayed to the user. + * For example: "1.5.1" + */ + virtual QString name() = 0; + + /*! + * This should return a string that describes + * the kind of version this is (Stable, Beta, Snapshot, whatever) + */ + virtual QString typeString() const = 0; +}; + +typedef QSharedPointer BaseVersionPtr; + +Q_DECLARE_METATYPE( BaseVersionPtr ) \ No newline at end of file diff --git a/logic/EnabledItemFilter.cpp b/logic/EnabledItemFilter.cpp new file mode 100644 index 00000000..6ecd0271 --- /dev/null +++ b/logic/EnabledItemFilter.cpp @@ -0,0 +1,30 @@ +#include "EnabledItemFilter.h" + +EnabledItemFilter::EnabledItemFilter(QObject* parent) + :QSortFilterProxyModel(parent) +{ + +} + +void EnabledItemFilter::setActive(bool active) +{ + m_active = active; + invalidateFilter(); +} + +bool EnabledItemFilter::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + if(!m_active) + return true; + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + if(sourceModel()->flags(index) & Qt::ItemIsEnabled) + { + return true; + } + return false; +} + +bool EnabledItemFilter::lessThan(const QModelIndex& left, const QModelIndex& right) const +{ + return QSortFilterProxyModel::lessThan(left, right); +} diff --git a/logic/EnabledItemFilter.h b/logic/EnabledItemFilter.h new file mode 100644 index 00000000..cb6d4041 --- /dev/null +++ b/logic/EnabledItemFilter.h @@ -0,0 +1,16 @@ +#pragma once +#include + +class EnabledItemFilter : public QSortFilterProxyModel +{ + Q_OBJECT +public: + EnabledItemFilter(QObject *parent = 0); + void setActive(bool active); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; +private: + bool m_active = false; +}; \ No newline at end of file diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index f0630568..b5832ce5 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -22,7 +22,7 @@ #include "LegacyInstance.h" #include "OneSixInstance.h" #include "NostalgiaInstance.h" -#include "InstanceVersion.h" +#include "BaseVersion.h" #include "MinecraftVersion.h" #include "inifile.h" @@ -68,7 +68,7 @@ InstanceFactory::InstLoadError InstanceFactory::loadInstance(BaseInstance *&inst } -InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*& inst, InstVersionPtr version, const QString& instDir ) +InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*& inst, BaseVersionPtr version, const QString& instDir ) { QDir rootDir(instDir); @@ -89,19 +89,19 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*& case MinecraftVersion::Legacy: m_settings->set("InstanceType", "Legacy"); inst = new LegacyInstance(instDir, m_settings, this); - inst->setIntendedVersionId(version->descriptor); + inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; case MinecraftVersion::OneSix: m_settings->set("InstanceType", "OneSix"); inst = new OneSixInstance(instDir, m_settings, this); - inst->setIntendedVersionId(version->descriptor); + inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; case MinecraftVersion::Nostalgia: m_settings->set("InstanceType", "Nostalgia"); inst = new NostalgiaInstance(instDir, m_settings, this); - inst->setIntendedVersionId(version->descriptor); + inst->setIntendedVersionId(version->descriptor()); inst->setShouldUseCustomBaseJar(false); break; default: diff --git a/logic/InstanceFactory.h b/logic/InstanceFactory.h index ed54f520..1c527749 100644 --- a/logic/InstanceFactory.h +++ b/logic/InstanceFactory.h @@ -19,9 +19,9 @@ #include #include -#include "InstanceVersion.h" +#include "BaseVersion.h" -class InstVersion; +class BaseVersion; class BaseInstance; /*! @@ -61,7 +61,7 @@ public: * - InstExists if the given instance directory is already an instance. * - CantCreateDir if the given instance directory cannot be created. */ - InstCreateError createInstance(BaseInstance *&inst, InstVersionPtr version, const QString &instDir); + InstCreateError createInstance(BaseInstance *&inst, BaseVersionPtr version, const QString &instDir); /*! * \brief Loads an instance from the given directory. diff --git a/logic/InstanceVersion.h b/logic/InstanceVersion.h deleted file mode 100644 index eecd9c4e..00000000 --- a/logic/InstanceVersion.h +++ /dev/null @@ -1,68 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include - -/*! - * An abstract base class for versions. - */ -struct InstVersion -{ - /*! - * Checks if this version is less (older) than the given version. - * \param other The version to compare this one to. - * \return True if this version is older than the given version. - */ - virtual bool operator<(const InstVersion &rhs) const - { - return timestamp < rhs.timestamp; - } - - /*! - * Checks if this version is greater (newer) than the given version. - * \param other The version to compare this one to. - * \return True if this version is newer than the given version. - */ - virtual bool operator>( const InstVersion& rhs ) const - { - return timestamp > rhs.timestamp; - } - - /*! - * A string used to identify this version in config files. - * This should be unique within the version list or shenanigans will occur. - */ - QString descriptor; - /*! - * The name of this version as it is displayed to the user. - * For example: "1.5.1" - */ - QString name; - /*! - * Gets the version's timestamp. - * This is primarily used for sorting versions in a list. - */ - qint64 timestamp; - - virtual QString typeString() const - { - return "InstVersion"; - } -}; - -typedef QSharedPointer InstVersionPtr; - -Q_DECLARE_METATYPE( InstVersionPtr ) \ No newline at end of file diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index b8e179a5..25be5e7d 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -34,15 +34,15 @@ void LegacyUpdate::lwjglStart() return; } - auto &list = LWJGLVersionList::get(); - if(!list.isLoaded()) + auto list = MMC->lwjgllist(); + if(!list->isLoaded()) { emitFailed("Too soon! Let the LWJGL list load :)"); return; } setStatus("Downloading new LWJGL."); - auto version = list.getVersion(lwjglVersion); + auto version = list->getVersion(lwjglVersion); if(!version) { emitFailed("Game update failed: the selected LWJGL version is invalid."); @@ -170,7 +170,7 @@ void LegacyUpdate::extractLwjgl() if (name.contains(nativesDir)) { int lastSlash = name.lastIndexOf('/'); - int lastBackSlash = name.lastIndexOf('/'); + int lastBackSlash = name.lastIndexOf('\\'); if(lastSlash != -1) name = name.mid(lastSlash+1); else if(lastBackSlash != -1) diff --git a/logic/MinecraftVersion.h b/logic/MinecraftVersion.h index 27977262..53c2f5ef 100644 --- a/logic/MinecraftVersion.h +++ b/logic/MinecraftVersion.h @@ -15,17 +15,16 @@ #pragma once -#include "InstanceVersion.h" +#include "BaseVersion.h" #include -struct MinecraftVersion : public InstVersion +struct MinecraftVersion : public BaseVersion { - // From InstVersion: - /* - QString m_descriptor; - QString m_name; - qint64 m_timestamp; - */ + /*! + * Gets the version's timestamp. + * This is primarily used for sorting versions in a list. + */ + qint64 timestamp; /// The URL that this version will be downloaded from. maybe. QString download_url; @@ -44,6 +43,20 @@ struct MinecraftVersion : public InstVersion /// is this a snapshot? bool is_snapshot = false; + QString m_name; + + QString m_descriptor; + + virtual QString descriptor() + { + return m_descriptor; + } + + virtual QString name() + { + return m_name; + } + virtual QString typeString() const { QStringList pre_final; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index c926df60..7b038c46 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -214,6 +214,13 @@ bool OneSixInstance::shouldUpdate() const return true; } +bool OneSixInstance::versionIsCustom() +{ + QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); + QFile versionfile(verpath_custom); + return versionfile.exists(); +} + QString OneSixInstance::currentVersionId() const { return intendedVersionId(); @@ -224,6 +231,13 @@ bool OneSixInstance::reloadFullVersion() I_D(OneSixInstance); QString verpath = PathCombine(instanceRoot(), "version.json"); + { + QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); + QFile versionfile(verpath_custom); + if(versionfile.exists()) + verpath = verpath_custom; + } + QFile versionfile(verpath); if(versionfile.exists() && versionfile.open(QIODevice::ReadOnly)) { @@ -264,7 +278,12 @@ bool OneSixInstance::menuActionEnabled ( QString action_name ) const QString OneSixInstance::getStatusbarDescription() { - return "One Six : " + intendedVersionId(); + QString descr = "One Six : " + intendedVersionId(); + if(versionIsCustom()) + { + descr + " (custom)"; + } + return descr; } QString OneSixInstance::loaderModsDir() const diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index a4c67ed1..72bde630 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -41,6 +41,8 @@ public: bool reloadFullVersion(); /// get the current full version info QSharedPointer getFullVersion(); + /// is the current version original, or custom? + bool versionIsCustom(); virtual QString defaultBaseJar() const; virtual QString defaultCustomBaseJar() const; diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index a109a7f0..a45a4aec 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -1,12 +1,13 @@ #include "OneSixLibrary.h" #include "OneSixRule.h" - +#include "OpSys.h" void OneSixLibrary::finalize() { QStringList parts = m_name.split ( ':' ); QString relative = parts[0]; relative.replace ( '.','/' ); relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; + if ( !m_is_native ) relative += ".jar"; else @@ -21,9 +22,12 @@ void OneSixLibrary::finalize() relative += ".jar"; } } + + m_decentname = parts[1]; + m_decentversion = parts[2]; m_storage_path = relative; m_download_path = m_base_url + relative; - + if ( m_rules.empty() ) { m_is_active = true; @@ -42,6 +46,11 @@ void OneSixLibrary::finalize() if ( m_is_native ) { m_is_active = m_is_active && m_native_suffixes.contains ( currentSystem ); + m_decenttype = "Native"; + } + else + { + m_decenttype = "Java"; } } diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 856e409c..ac16d3d3 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -16,6 +16,12 @@ private: QList > m_rules; // derived values used for real things + /// a decent name fit for display + QString m_decentname; + /// a decent version fit for display + QString m_decentversion; + /// a decent type fit for display + QString m_decenttype; /// where to store the lib locally QString m_storage_path; /// where to download the lib from @@ -48,8 +54,24 @@ public: /// Set the library composite name void setName(QString name); + /// get a decent-looking name + QString name() + { + return m_decentname; + } + /// get a decent-looking version + QString version() + { + return m_decentversion; + } + /// what kind of library is it? (for display) + QString type() + { + return m_decenttype; + } /// Set the url base for downloads void setBaseUrl(QString base_url); + /// Call this to mark the library as 'native' (it's a zip archive with DLLs) void setIsNative(); /// Attach a name suffix to the specified OS native diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index a58a9626..67395818 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -49,7 +49,7 @@ void OneSixUpdate::executeTask() } // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = MinecraftVersionList::getMainList().findVersion(intendedVersion).dynamicCast(); + targetVersion = MMC->minecraftlist()->findVersion(intendedVersion).dynamicCast(); if(targetVersion == nullptr) { // don't do anything if it was invalid @@ -72,7 +72,7 @@ void OneSixUpdate::versionFileStart() setStatus("Getting the version files from Mojang."); QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); - urlstr += targetVersion->descriptor + "/" + targetVersion->descriptor + ".json"; + urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; auto job = new DownloadJob("Version index"); job->add(QUrl(urlstr)); specificVersionDownloadJob.reset(job); @@ -85,31 +85,46 @@ void OneSixUpdate::versionFileStart() void OneSixUpdate::versionFileFinished() { DownloadPtr DlJob = specificVersionDownloadJob->first(); + OneSixInstance * inst = (OneSixInstance *) m_inst; - QString version_id = targetVersion->descriptor; + QString version_id = targetVersion->descriptor(); QString inst_dir = m_inst->instanceRoot(); // save the version file in $instanceId/version.json { QString version1 = PathCombine(inst_dir, "/version.json"); ensureFilePathExists(version1); // FIXME: detect errors here, download to a temp file, swap - QFile vfile1 (version1); - vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly ); - vfile1.write(DlJob.dynamicCast()->m_data); - vfile1.close(); + QSaveFile vfile1 (version1); + if(!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly )) + { + emitFailed("Can't open " + version1 + " for writing."); + return; + } + auto data = DlJob.dynamicCast()->m_data; + qint64 actual = 0; + if((actual = vfile1.write(data)) != data.size()) + { + emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " + data.size() + '.'); + return; + } + if(!vfile1.commit()) + { + emitFailed("Can't commit changes to " + version1); + return; + } } // the version is downloaded safely. update is 'done' at this point m_inst->setShouldUpdate(false); - // save the version file in versions/$version/$version.json - /* - //QString version2 = QString("versions/") + version_id + "/" + version_id + ".json"; - //ensurePathExists(version2); - //QFile vfile2 (version2); - //vfile2.open(QIODevice::Truncate | QIODevice::WriteOnly ); - //vfile2.write(DlJob->m_data); - //vfile2.close(); - */ + + // delete any custom version inside the instance (it's no longer relevant, we did an update) + QString custom = PathCombine(inst_dir, "/custom.json"); + QFile finfo(custom); + if(finfo.exists()) + { + finfo.remove(); + } + inst->reloadFullVersion(); jarlibStart(); } diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 7ffe9a94..dc1b5d6f 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -28,3 +28,74 @@ QList > OneSixVersion::getActiveNativeLibs() } +QVariant OneSixVersion::data(const QModelIndex& index, int role) const +{ + if(!index.isValid()) + return QVariant(); + + int row = index.row(); + int column = index.column(); + + if(row < 0 || row >= libraries.size()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + switch(column) + { + case 0: + return libraries[row]->name(); + case 1: + return libraries[row]->type(); + case 2: + return libraries[row]->version(); + default: + return QVariant(); + } + } + return QVariant(); +} + +Qt::ItemFlags OneSixVersion::flags(const QModelIndex& index) const +{ + if(!index.isValid()) + return Qt::NoItemFlags; + int row = index.row(); + if(libraries[row]->isActive()) + { + return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; + } + else + { + return Qt::ItemNeverHasChildren; + } + //return QAbstractListModel::flags(index); +} + + +QVariant OneSixVersion::headerData ( int section, Qt::Orientation orientation, int role ) const +{ + if (role != Qt::DisplayRole || orientation != Qt::Horizontal) + return QVariant(); + switch (section) + { + case 0: + return QString("Name"); + case 1: + return QString("Type"); + case 2: + return QString("Version"); + default: + return QString(); + } +} + +int OneSixVersion::rowCount(const QModelIndex& parent) const +{ + return libraries.size(); +} + +int OneSixVersion::columnCount(const QModelIndex& parent) const +{ + return 3; +} diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 8f01f82d..6a6a5b4b 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -2,8 +2,14 @@ #include class OneSixLibrary; -class OneSixVersion +class OneSixVersion : public QAbstractListModel { +public: + virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; + virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const; + virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + virtual int columnCount ( const QModelIndex& parent ) const; + virtual Qt::ItemFlags flags(const QModelIndex& index) const; public: /// the ID - determines which jar to use! ACTUALLY IMPORTANT! QString id; @@ -58,6 +64,7 @@ public: // QList rules; public: + OneSixVersion() { minimumLauncherVersion = 0xDEADBEEF; @@ -65,4 +72,4 @@ public: QList > getActiveNormalLibs(); QList > getActiveNativeLibs(); -}; \ No newline at end of file +}; diff --git a/logic/lists/BaseVersionList.cpp b/logic/lists/BaseVersionList.cpp new file mode 100644 index 00000000..61da5eeb --- /dev/null +++ b/logic/lists/BaseVersionList.cpp @@ -0,0 +1,123 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "logic/lists/BaseVersionList.h" +#include "logic/BaseVersion.h" + +BaseVersionList::BaseVersionList(QObject *parent) : + QAbstractListModel(parent) +{ +} + +BaseVersionPtr BaseVersionList::findVersion( const QString& descriptor ) +{ + for (int i = 0; i < count(); i++) + { + if (at(i)->descriptor() == descriptor) + return at(i); + } + return BaseVersionPtr(); +} + +BaseVersionPtr BaseVersionList::getLatestStable() const +{ + if (count() <= 0) + return BaseVersionPtr(); + else + return at(0); +} + +QVariant BaseVersionList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + + BaseVersionPtr version = at(index.row()); + + switch (role) + { + case Qt::DisplayRole: + switch (index.column()) + { + case NameColumn: + return version->name(); + + case TypeColumn: + return version->typeString(); + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + return version->descriptor(); + + case VersionPointerRole: + return qVariantFromValue(version); + + default: + return QVariant(); + } +} + +QVariant BaseVersionList::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + switch (section) + { + case NameColumn: + return "Name"; + + case TypeColumn: + return "Type"; + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case NameColumn: + return "The name of the version."; + + case TypeColumn: + return "The version's type."; + + default: + return QVariant(); + } + + default: + return QVariant(); + } +} + +int BaseVersionList::rowCount(const QModelIndex &parent) const +{ + // Return count + return count(); +} + +int BaseVersionList::columnCount(const QModelIndex &parent) const +{ + return 2; +} diff --git a/logic/lists/BaseVersionList.h b/logic/lists/BaseVersionList.h new file mode 100644 index 00000000..d37431ed --- /dev/null +++ b/logic/lists/BaseVersionList.h @@ -0,0 +1,121 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include "logic/BaseVersion.h" + +class Task; + +/*! + * \brief Class that each instance type's version list derives from. + * Version lists are the lists that keep track of the available game versions + * for that instance. This list will not be loaded on startup. It will be loaded + * when the list's load function is called. Before using the version list, you + * should check to see if it has been loaded yet and if not, load the list. + * + * Note that this class also inherits from QAbstractListModel. Methods from that + * class determine how this version list shows up in a list view. Said methods + * all have a default implementation, but they can be overridden by plugins to + * change the behavior of the list. + */ +class BaseVersionList : public QAbstractListModel +{ + Q_OBJECT +public: + enum ModelRoles + { + VersionPointerRole = 0x34B1CB48 + }; + + enum VListColumns + { + // First column - Name + NameColumn = 0, + + // Second column - Type + TypeColumn, + + // Third column - Timestamp + TimeColumn + }; + + explicit BaseVersionList(QObject *parent = 0); + + /*! + * \brief Gets a task that will reload the version list. + * Simply execute the task to load the list. + * The task returned by this function should reset the model when it's done. + * \return A pointer to a task that reloads the version list. + */ + virtual Task *getLoadTask() = 0; + + //! Checks whether or not the list is loaded. If this returns false, the list should be loaded. + virtual bool isLoaded() = 0; + + //! Gets the version at the given index. + virtual const BaseVersionPtr at(int i) const = 0; + + //! Returns the number of versions in the list. + virtual int count() const = 0; + + + //////// List Model Functions //////// + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual int rowCount(const QModelIndex &parent) const; + virtual int columnCount(const QModelIndex &parent) const; + + + /*! + * \brief Finds a version by its descriptor. + * \param The descriptor of the version to find. + * \return A const pointer to the version with the given descriptor. NULL if + * one doesn't exist. + */ + virtual BaseVersionPtr findVersion(const QString &descriptor); + + /*! + * \brief Gets the latest stable version of this instance type. + * This is the version that will be selected by default. + * By default, this is simply the first version in the list. + */ + virtual BaseVersionPtr getLatestStable() const; + + /*! + * Sorts the version list. + */ + virtual void sort() = 0; + +protected slots: + /*! + * Updates this list with the given list of versions. + * This is done by copying each version in the given list and inserting it + * into this one. + * We need to do this so that we can set the parents of the versions are set to this + * version list. This can't be done in the load task, because the versions the load + * task creates are on the load task's thread and Qt won't allow their parents + * to be set to something created on another thread. + * To get around that problem, we invoke this method on the GUI thread, which + * then copies the versions and sets their parents correctly. + * \param versions List of versions whose parents should be set. + */ + virtual void updateListData(QList versions) = 0; +}; diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp new file mode 100644 index 00000000..f45b8b6b --- /dev/null +++ b/logic/lists/ForgeVersionList.cpp @@ -0,0 +1,200 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ForgeVersionList.h" +#include +#include "MultiMC.h" + +#include + +#include + +#include + +#define JSON_URL "http://files.minecraftforge.net/minecraftforge/json" + + +ForgeVersionList::ForgeVersionList(QObject* parent): BaseVersionList(parent) +{ + +} + +Task *ForgeVersionList::getLoadTask() +{ + return new ForgeListLoadTask(this); +} + +bool ForgeVersionList::isLoaded() +{ + return m_loaded; +} + +const BaseVersionPtr ForgeVersionList::at(int i) const +{ + return m_vlist.at(i); +} + +int ForgeVersionList::count() const +{ + return m_vlist.count(); +} +/* +bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) +{ + const BaseVersion & left = *first; + const BaseVersion & right = *second; + return left > right; +} + +void MinecraftVersionList::sort() +{ + beginResetModel(); + qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); + endResetModel(); +} +*/ +BaseVersionPtr ForgeVersionList::getLatestStable() const +{ + return BaseVersionPtr(); +} + +void ForgeVersionList::updateListData(QList versions) +{ + beginResetModel(); + m_vlist = versions; + m_loaded = true; + endResetModel(); + // NOW SORT!! + // sort(); +} + +void ForgeVersionList::sort() +{ + // NO-OP for now +} + + +ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList* vlist): Task() +{ + m_list = vlist; +} + + +void ForgeListLoadTask::executeTask() +{ + auto job = new DownloadJob("Version index"); + job->add(QUrl(JSON_URL)); + listJob.reset(job); + connect(listJob.data(), SIGNAL(succeeded()), SLOT(list_downloaded())); + connect(listJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); + connect(listJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + listJob->start(); +} + +void ForgeListLoadTask::list_downloaded() +{ + auto DlJob = listJob->first(); + auto data = DlJob.dynamicCast()->m_data; + + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + DlJob.reset(); + + if (jsonError.error != QJsonParseError::NoError) + { + emitFailed("Error parsing version list JSON:" + jsonError.errorString()); + return; + } + + if(!jsonDoc.isObject()) + { + emitFailed("Error parsing version list JSON: jsonDoc is not an object"); + return; + } + + QJsonObject root = jsonDoc.object(); + + // Now, get the array of versions. + if(!root.value("builds").isArray()) + { + emitFailed("Error parsing version list JSON: version list object is missing 'builds' array"); + return; + } + QJsonArray builds = root.value("builds").toArray(); + + QList tempList; + for (int i = 0; i < builds.count(); i++) + { + // Load the version info. + if(!builds[i].isObject()) + { + //FIXME: log this somewhere + continue; + } + QJsonObject obj = builds[i].toObject(); + int build_nr = obj.value("build").toDouble(0); + if(!build_nr) + continue; + QJsonArray files = root.value("files").toArray(); + QString url, jobbuildver, mcver, buildtype, filename; + QString changelog_url, installer_url; + bool valid = false; + for(int j = 0; j < files.count(); j++) + { + if(!files[j].isObject()) + continue; + QJsonObject file = files[j].toObject(); + buildtype = file.value("buildtype").toString(); + if((buildtype == "client" || buildtype == "universal") && !valid) + { + mcver = file.value("mcver").toString(); + url = file.value("url").toString(); + jobbuildver = file.value("jobbuildver").toString(); + int lastSlash = url.lastIndexOf('/'); + filename = url.mid(lastSlash+1); + valid = true; + } + else if(buildtype == "changelog") + { + QString ext = file.value("ext").toString(); + if(ext.isEmpty()) + continue; + changelog_url = file.value("url").toString(); + } + else if(buildtype == "installer") + { + installer_url = file.value("url").toString(); + } + } + if(valid) + { + // Now, we construct the version object and add it to the list. + QSharedPointer fVersion(new ForgeVersion()); + fVersion->universal_url = url; + fVersion->changelog_url = changelog_url; + fVersion->installer_url = installer_url; + fVersion->jobbuildver = jobbuildver; + fVersion->mcver = mcver; + fVersion->filename = filename; + fVersion->m_buildnr = build_nr; + tempList.append(fVersion); + } + } + m_list->updateListData(tempList); + + emitSucceeded(); + return; +} diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h new file mode 100644 index 00000000..8d4a2d46 --- /dev/null +++ b/logic/lists/ForgeVersionList.h @@ -0,0 +1,98 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include "BaseVersionList.h" +#include "logic/tasks/Task.h" +#include "logic/net/DownloadJob.h" + +class ForgeVersion; +typedef QSharedPointer PtrForgeVersion; + +struct ForgeVersion : public BaseVersion +{ + virtual QString descriptor() + { + return filename; + }; + virtual QString name() + { + return "Forge " + jobbuildver + " (" + mcver + ")"; + }; + virtual QString typeString() const + { + if(installer_url.isEmpty()) + return "Universal"; + else + return "Installer"; + }; + + int m_buildnr = 0; + QString universal_url; + QString changelog_url; + QString installer_url; + QString jobbuildver; + QString mcver; + QString filename; +}; + +class ForgeVersionList : public BaseVersionList +{ + Q_OBJECT +public: + friend class ForgeListLoadTask; + + explicit ForgeVersionList(QObject *parent = 0); + + virtual Task *getLoadTask(); + virtual bool isLoaded(); + virtual const BaseVersionPtr at(int i) const; + virtual int count() const; + virtual void sort(); + + virtual BaseVersionPtr getLatestStable() const; + +protected: + QList m_vlist; + + bool m_loaded; + +protected slots: + virtual void updateListData(QList versions); +}; + +class ForgeListLoadTask : public Task +{ + Q_OBJECT + +public: + explicit ForgeListLoadTask(ForgeVersionList *vlist); + + virtual void executeTask(); + +protected slots: + void list_downloaded(); + +protected: + DownloadJobPtr listJob; + ForgeVersionList *m_list; +}; diff --git a/logic/lists/InstVersionList.cpp b/logic/lists/InstVersionList.cpp deleted file mode 100644 index 7dc67155..00000000 --- a/logic/lists/InstVersionList.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "logic/lists/InstVersionList.h" -#include "logic/InstanceVersion.h" - -InstVersionList::InstVersionList(QObject *parent) : - QAbstractListModel(parent) -{ -} - -InstVersionPtr InstVersionList::findVersion( const QString& descriptor ) -{ - for (int i = 0; i < count(); i++) - { - if (at(i)->descriptor == descriptor) - return at(i); - } - return InstVersionPtr(); -} - -InstVersionPtr InstVersionList::getLatestStable() const -{ - if (count() <= 0) - return InstVersionPtr(); - else - return at(0); -} - -QVariant InstVersionList::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() > count()) - return QVariant(); - - - InstVersionPtr version = at(index.row()); - - switch (role) - { - case Qt::DisplayRole: - switch (index.column()) - { - case NameColumn: - return version->name; - - case TypeColumn: - return version->typeString(); - - case TimeColumn: - return version->timestamp; - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - return version->descriptor; - - case VersionPointerRole: - return qVariantFromValue(version); - - default: - return QVariant(); - } -} - -QVariant InstVersionList::headerData(int section, Qt::Orientation orientation, int role) const -{ - switch (role) - { - case Qt::DisplayRole: - switch (section) - { - case NameColumn: - return "Name"; - - case TypeColumn: - return "Type"; - - case TimeColumn: - return "Time"; - - default: - return QVariant(); - } - - case Qt::ToolTipRole: - switch (section) - { - case NameColumn: - return "The name of the version."; - - case TypeColumn: - return "The version's type."; - - default: - return QVariant(); - } - - default: - return QVariant(); - } -} - -int InstVersionList::rowCount(const QModelIndex &parent) const -{ - // Return count - return count(); -} - -int InstVersionList::columnCount(const QModelIndex &parent) const -{ - return 2; -} diff --git a/logic/lists/InstVersionList.h b/logic/lists/InstVersionList.h deleted file mode 100644 index bc6aa5d4..00000000 --- a/logic/lists/InstVersionList.h +++ /dev/null @@ -1,121 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#include "logic/InstanceVersion.h" - -class Task; - -/*! - * \brief Class that each instance type's version list derives from. - * Version lists are the lists that keep track of the available game versions - * for that instance. This list will not be loaded on startup. It will be loaded - * when the list's load function is called. Before using the version list, you - * should check to see if it has been loaded yet and if not, load the list. - * - * Note that this class also inherits from QAbstractListModel. Methods from that - * class determine how this version list shows up in a list view. Said methods - * all have a default implementation, but they can be overridden by plugins to - * change the behavior of the list. - */ -class InstVersionList : public QAbstractListModel -{ - Q_OBJECT -public: - enum ModelRoles - { - VersionPointerRole = 0x34B1CB48 - }; - - enum VListColumns - { - // First column - Name - NameColumn = 0, - - // Second column - Type - TypeColumn, - - // Third column - Timestamp - TimeColumn - }; - - explicit InstVersionList(QObject *parent = 0); - - /*! - * \brief Gets a task that will reload the version list. - * Simply execute the task to load the list. - * The task returned by this function should reset the model when it's done. - * \return A pointer to a task that reloads the version list. - */ - virtual Task *getLoadTask() = 0; - - //! Checks whether or not the list is loaded. If this returns false, the list should be loaded. - virtual bool isLoaded() = 0; - - //! Gets the version at the given index. - virtual const InstVersionPtr at(int i) const = 0; - - //! Returns the number of versions in the list. - virtual int count() const = 0; - - - //////// List Model Functions //////// - virtual QVariant data(const QModelIndex &index, int role) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; - virtual int rowCount(const QModelIndex &parent) const; - virtual int columnCount(const QModelIndex &parent) const; - - - /*! - * \brief Finds a version by its descriptor. - * \param The descriptor of the version to find. - * \return A const pointer to the version with the given descriptor. NULL if - * one doesn't exist. - */ - virtual InstVersionPtr findVersion(const QString &descriptor); - - /*! - * \brief Gets the latest stable version of this instance type. - * This is the version that will be selected by default. - * By default, this is simply the first version in the list. - */ - virtual InstVersionPtr getLatestStable() const; - - /*! - * Sorts the version list. - */ - virtual void sort() = 0; - -protected slots: - /*! - * Updates this list with the given list of versions. - * This is done by copying each version in the given list and inserting it - * into this one. - * We need to do this so that we can set the parents of the versions are set to this - * version list. This can't be done in the load task, because the versions the load - * task creates are on the load task's thread and Qt won't allow their parents - * to be set to something created on another thread. - * To get around that problem, we invoke this method on the GUI thread, which - * then copies the versions and sets their parents correctly. - * \param versions List of versions whose parents should be set. - */ - virtual void updateListData(QList versions) = 0; -}; diff --git a/logic/lists/LwjglVersionList.cpp b/logic/lists/LwjglVersionList.cpp index d7826a82..b3e2dab4 100644 --- a/logic/lists/LwjglVersionList.cpp +++ b/logic/lists/LwjglVersionList.cpp @@ -24,14 +24,6 @@ #define RSS_URL "http://sourceforge.net/api/file/index/project-id/58488/mtime/desc/rss" -LWJGLVersionList mainVersionList; - -LWJGLVersionList &LWJGLVersionList::get() -{ - return mainVersionList; -} - - LWJGLVersionList::LWJGLVersionList(QObject *parent) : QAbstractListModel(parent) { diff --git a/logic/lists/LwjglVersionList.h b/logic/lists/LwjglVersionList.h index 638a0b67..23e92a1a 100644 --- a/logic/lists/LwjglVersionList.h +++ b/logic/lists/LwjglVersionList.h @@ -53,8 +53,6 @@ class LWJGLVersionList : public QAbstractListModel public: explicit LWJGLVersionList(QObject *parent = 0); - static LWJGLVersionList &get(); - bool isLoaded() { return m_vlist.length() > 0; } const PtrLWJGLVersion getVersion(const QString &versionName); diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 42fb1b50..86ba0792 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -34,10 +34,8 @@ #define ASSETS_URLBASE "http://assets.minecraft.net/" #define MCN_URLBASE "http://sonicrules.org/mcnweb.py" -MinecraftVersionList mcVList; - MinecraftVersionList::MinecraftVersionList(QObject *parent) : - InstVersionList(parent) + BaseVersionList(parent) { } @@ -52,7 +50,7 @@ bool MinecraftVersionList::isLoaded() return m_loaded; } -const InstVersionPtr MinecraftVersionList::at(int i) const +const BaseVersionPtr MinecraftVersionList::at(int i) const { return m_vlist.at(i); } @@ -62,11 +60,11 @@ int MinecraftVersionList::count() const return m_vlist.count(); } -bool cmpVersions(InstVersionPtr first, InstVersionPtr second) +bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) { - const InstVersion & left = *first; - const InstVersion & right = *second; - return left > right; + auto left = first.dynamicCast(); + auto right = second.dynamicCast(); + return left->timestamp > right->timestamp; } void MinecraftVersionList::sort() @@ -76,7 +74,7 @@ void MinecraftVersionList::sort() endResetModel(); } -InstVersionPtr MinecraftVersionList::getLatestStable() const +BaseVersionPtr MinecraftVersionList::getLatestStable() const { for (int i = 0; i < m_vlist.length(); i++) { @@ -86,15 +84,10 @@ InstVersionPtr MinecraftVersionList::getLatestStable() const return m_vlist.at(i); } } - return InstVersionPtr(); -} - -MinecraftVersionList &MinecraftVersionList::getMainList() -{ - return mcVList; + return BaseVersionPtr(); } -void MinecraftVersionList::updateListData(QList versions) +void MinecraftVersionList::updateListData(QList versions) { beginResetModel(); m_vlist = versions; @@ -214,7 +207,7 @@ void MCVListLoadTask::list_downloaded() } QJsonArray versions = root.value("versions").toArray(); - QList tempList; + QList tempList; for (int i = 0; i < versions.count(); i++) { bool is_snapshot = false; @@ -280,7 +273,7 @@ void MCVListLoadTask::list_downloaded() // Now, we construct the version object and add it to the list. QSharedPointer mcVersion(new MinecraftVersion()); - mcVersion->name = mcVersion->descriptor = versionID; + mcVersion->m_name = mcVersion->m_descriptor = versionID; mcVersion->timestamp = versionTime.toMSecsSinceEpoch(); mcVersion->download_url = dlUrl; mcVersion->is_latest = is_latest; @@ -293,8 +286,3 @@ void MCVListLoadTask::list_downloaded() emitSucceeded(); return; } - -// FIXME: we should have a local cache of the version list and a local cache of version data -bool MCVListLoadTask::loadFromVList() -{ -} diff --git a/logic/lists/MinecraftVersionList.h b/logic/lists/MinecraftVersionList.h index 0477379f..8937ba7b 100644 --- a/logic/lists/MinecraftVersionList.h +++ b/logic/lists/MinecraftVersionList.h @@ -20,14 +20,14 @@ #include #include -#include "InstVersionList.h" +#include "BaseVersionList.h" #include "logic/tasks/Task.h" #include "logic/MinecraftVersion.h" class MCVListLoadTask; class QNetworkReply; -class MinecraftVersionList : public InstVersionList +class MinecraftVersionList : public BaseVersionList { Q_OBJECT public: @@ -37,25 +37,19 @@ public: virtual Task *getLoadTask(); virtual bool isLoaded(); - virtual const InstVersionPtr at(int i) const; + virtual const BaseVersionPtr at(int i) const; virtual int count() const; virtual void sort(); - virtual InstVersionPtr getLatestStable() const; - - /*! - * Gets the main version list instance. - */ - static MinecraftVersionList &getMainList(); - + virtual BaseVersionPtr getLatestStable() const; protected: - QList m_vlist; + QList m_vlist; bool m_loaded; protected slots: - virtual void updateListData(QList versions); + virtual void updateListData(QList versions); }; class MCVListLoadTask : public Task @@ -72,9 +66,6 @@ protected slots: void list_downloaded(); protected: - //! Loads versions from Mojang's official version list. - bool loadFromVList(); - QNetworkReply *vlistReply; MinecraftVersionList *m_list; MinecraftVersion *m_currentStable; -- cgit From b979d0ce5da515793a02802a6421ef607a498323 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 18 Sep 2013 00:00:35 +0200 Subject: Implement legacy forge button! Many refactors of the task system. Progress dialog now accepts generic ProgressProvider objects --- CMakeLists.txt | 9 ++-- MultiMC.cpp | 1 + gui/LegacyModEditDialog.cpp | 41 +++++++++++++- gui/LegacyModEditDialog.h | 2 + gui/LegacyModEditDialog.ui | 3 -- gui/OneSixModEditDialog.cpp | 22 ++++++-- gui/OneSixModEditDialog.h | 1 + gui/OneSixModEditDialog.ui | 2 +- gui/ProgressDialog.cpp | 108 +++++++++++++++++++++++++++++++++++++ gui/ProgressDialog.h | 62 +++++++++++++++++++++ gui/ProgressDialog.ui | 53 ++++++++++++++++++ gui/mainwindow.cpp | 8 +-- gui/newinstancedialog.cpp | 2 +- gui/taskdialog.cpp | 107 ------------------------------------ gui/taskdialog.h | 61 --------------------- gui/taskdialog.ui | 53 ------------------ gui/versionselectdialog.cpp | 19 +++---- gui/versionselectdialog.h | 5 +- gui/versionselectdialog.ui | 69 ------------------------ logic/BaseUpdate.cpp | 4 +- logic/InstanceLauncher.cpp | 4 +- logic/LegacyUpdate.cpp | 6 +-- logic/OneSixUpdate.cpp | 4 +- logic/lists/ForgeVersionList.cpp | 93 +++++++++++++++++++++++++++----- logic/lists/ForgeVersionList.h | 10 ++-- logic/lists/MinecraftVersionList.h | 2 +- logic/net/DownloadJob.cpp | 2 + logic/net/DownloadJob.h | 19 ++++++- logic/tasks/ProgressProvider.h | 20 +++++++ logic/tasks/Task.cpp | 48 ++++++----------- logic/tasks/Task.h | 48 ++++++----------- 31 files changed, 474 insertions(+), 414 deletions(-) create mode 100644 gui/ProgressDialog.cpp create mode 100644 gui/ProgressDialog.h create mode 100644 gui/ProgressDialog.ui delete mode 100644 gui/taskdialog.cpp delete mode 100644 gui/taskdialog.h delete mode 100644 gui/taskdialog.ui create mode 100644 logic/tasks/ProgressProvider.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index d4f0cbbf..7f09e324 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,7 +156,7 @@ gui/mainwindow.h gui/settingsdialog.h gui/newinstancedialog.h gui/logindialog.h -gui/taskdialog.h +gui/ProgressDialog.h gui/aboutdialog.h gui/consolewindow.h gui/instancedelegate.h @@ -228,6 +228,7 @@ logic/EnabledItemFilter.h # Tasks logic/tasks/Task.h logic/tasks/LoginTask.h +logic/tasks/ProgressProvider.h ) @@ -239,13 +240,14 @@ gui/mainwindow.cpp gui/settingsdialog.cpp gui/newinstancedialog.cpp gui/logindialog.cpp -gui/taskdialog.cpp gui/aboutdialog.cpp gui/consolewindow.cpp gui/instancedelegate.cpp gui/versionselectdialog.cpp gui/lwjglselectdialog.cpp gui/instancesettings.cpp + +gui/ProgressDialog.cpp gui/IconPickerDialog.cpp gui/LegacyModEditDialog.cpp gui/OneSixModEditDialog.cpp @@ -313,12 +315,13 @@ gui/mainwindow.ui gui/settingsdialog.ui gui/newinstancedialog.ui gui/logindialog.ui -gui/taskdialog.ui gui/aboutdialog.ui gui/consolewindow.ui gui/versionselectdialog.ui gui/lwjglselectdialog.ui gui/instancesettings.ui + +gui/ProgressDialog.ui gui/IconPickerDialog.ui gui/LegacyModEditDialog.ui gui/OneSixModEditDialog.ui diff --git a/MultiMC.cpp b/MultiMC.cpp index 08e5ed25..4b5b40b2 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -282,6 +282,7 @@ void MultiMC::initHttpMetaCache() m_metacache->addBase("assets", QDir("assets").absolutePath()); m_metacache->addBase("versions", QDir("versions").absolutePath()); m_metacache->addBase("libraries", QDir("libraries").absolutePath()); + m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath()); m_metacache->Load(); } diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index c336f837..ac7f7f25 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -13,10 +13,15 @@ * limitations under the License. */ +#include "MultiMC.h" #include "LegacyModEditDialog.h" #include "ModEditDialogCommon.h" +#include "versionselectdialog.h" +#include "ProgressDialog.h" #include "ui_LegacyModEditDialog.h" -#include +#include "logic/ModList.h" +#include "logic/lists/ForgeVersionList.h" + #include #include #include @@ -194,7 +199,39 @@ void LegacyModEditDialog::on_addCoreBtn_clicked() } void LegacyModEditDialog::on_addForgeBtn_clicked() { - + VersionSelectDialog vselect(MMC->forgelist(), this); + vselect.setFilter(1, m_inst->intendedVersionId()); + if (vselect.exec() && vselect.selectedVersion()) + { + ForgeVersionPtr forge = vselect.selectedVersion().dynamicCast(); + if(!forge) + return; + auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename); + if(entry->stale) + { + DownloadJob * fjob = new DownloadJob("Forge download"); + fjob->add(forge->universal_url, entry); + ProgressDialog dlg(this); + dlg.exec(fjob); + if(dlg.result() == QDialog::Accepted) + { + m_jarmods->stopWatching(); + m_jarmods->installMod(QFileInfo(entry->getFullPath())); + m_jarmods->startWatching(); + } + else + { + // failed to download forge :/ + } + } + else + { + m_jarmods->stopWatching(); + m_jarmods->installMod(QFileInfo(entry->getFullPath())); + m_jarmods->startWatching(); + } + //m_selectedInstance->setIntendedVersionId(->descriptor()); + } } void LegacyModEditDialog::on_addJarBtn_clicked() { diff --git a/gui/LegacyModEditDialog.h b/gui/LegacyModEditDialog.h index bc9ebac0..b824a86a 100644 --- a/gui/LegacyModEditDialog.h +++ b/gui/LegacyModEditDialog.h @@ -17,6 +17,7 @@ #include #include "logic/LegacyInstance.h" +#include namespace Ui { class LegacyModEditDialog; @@ -64,4 +65,5 @@ private: QSharedPointer m_jarmods; QSharedPointer m_texturepacks; LegacyInstance * m_inst; + DownloadJobPtr forgeJob; }; diff --git a/gui/LegacyModEditDialog.ui b/gui/LegacyModEditDialog.ui index bd147c85..73b767dc 100644 --- a/gui/LegacyModEditDialog.ui +++ b/gui/LegacyModEditDialog.ui @@ -52,9 +52,6 @@ - - false - MCForge diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index f778127f..fad9d2e2 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -12,18 +12,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +#include "MultiMC.h" #include "OneSixModEditDialog.h" #include "ModEditDialogCommon.h" #include "ui_OneSixModEditDialog.h" -#include +#include "logic/ModList.h" +#include "logic/OneSixVersion.h" +#include "logic/EnabledItemFilter.h" +#include "logic/lists/ForgeVersionList.h" +#include "gui/versionselectdialog.h" + #include #include #include #include #include -#include "logic/OneSixVersion.h" -#include OneSixModEditDialog::OneSixModEditDialog(OneSixInstance * inst, QWidget *parent): m_inst(inst), @@ -66,6 +69,17 @@ OneSixModEditDialog::~OneSixModEditDialog() delete ui; } +void OneSixModEditDialog::on_forgeBtn_clicked() +{ + VersionSelectDialog vselect(MMC->forgelist(), this); + vselect.setFilter(1, m_inst->currentVersionId()); + if (vselect.exec() && vselect.selectedVersion()) + { + //m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor()); + } +} + + bool OneSixModEditDialog::loaderListFilter ( QKeyEvent* keyEvent ) { switch(keyEvent->key()) diff --git a/gui/OneSixModEditDialog.h b/gui/OneSixModEditDialog.h index c637df01..d14c842c 100644 --- a/gui/OneSixModEditDialog.h +++ b/gui/OneSixModEditDialog.h @@ -40,6 +40,7 @@ private slots: void on_viewResPackBtn_clicked(); // Questionable: SettingsDialog doesn't need this for some reason? void on_buttonBox_rejected(); + void on_forgeBtn_clicked(); protected: bool eventFilter(QObject *obj, QEvent *ev); bool loaderListFilter( QKeyEvent* ev ); diff --git a/gui/OneSixModEditDialog.ui b/gui/OneSixModEditDialog.ui index bffcaed0..aadaf3ae 100644 --- a/gui/OneSixModEditDialog.ui +++ b/gui/OneSixModEditDialog.ui @@ -64,7 +64,7 @@ - + Replace any current custom version with Minecraft Forge diff --git a/gui/ProgressDialog.cpp b/gui/ProgressDialog.cpp new file mode 100644 index 00000000..154ab1c0 --- /dev/null +++ b/gui/ProgressDialog.cpp @@ -0,0 +1,108 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ProgressDialog.h" +#include "ui_ProgressDialog.h" + +#include + +#include "logic/tasks/Task.h" + +ProgressDialog::ProgressDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::ProgressDialog) +{ + ui->setupUi(this); + updateSize(); + + changeProgress(0,100); +} + +ProgressDialog::~ProgressDialog() +{ + delete ui; +} + +void ProgressDialog::updateSize() +{ + resize(QSize(480, minimumSizeHint().height())); +} + +int ProgressDialog::exec(ProgressProvider *task) +{ + this->task = task; + + // Connect signals. + connect(task, SIGNAL(started()), SLOT(onTaskStarted())); + connect(task, SIGNAL(failed(QString)), SLOT(onTaskFailed(QString))); + connect(task, SIGNAL(succeeded()), SLOT(onTaskSucceeded())); + connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString&))); + connect(task, SIGNAL(progress(qint64,qint64)), SLOT(changeProgress(qint64,qint64))); + + // this makes sure that the task is started after the dialog is created + QMetaObject::invokeMethod(task, "start", Qt::QueuedConnection); + return QDialog::exec(); +} + +ProgressProvider* ProgressDialog::getTask() +{ + return task; +} + +void ProgressDialog::onTaskStarted() +{ + +} + +void ProgressDialog::onTaskFailed(QString failure) +{ + reject(); +} + +void ProgressDialog::onTaskSucceeded() +{ + accept(); +} + +void ProgressDialog::changeStatus(const QString &status) +{ + ui->statusLabel->setText(status); + updateSize(); +} + +void ProgressDialog::changeProgress(qint64 current, qint64 total) +{ + ui->taskProgressBar->setMaximum(total); + ui->taskProgressBar->setValue(current); +} + +void ProgressDialog::keyPressEvent(QKeyEvent* e) +{ + if (e->key() == Qt::Key_Escape) + return; + QDialog::keyPressEvent(e); +} + +void ProgressDialog::closeEvent(QCloseEvent* e) +{ + if (task && task->isRunning()) + { + e->ignore(); + } + else + { + QDialog::closeEvent(e); + } +} diff --git a/gui/ProgressDialog.h b/gui/ProgressDialog.h new file mode 100644 index 00000000..ac6bb412 --- /dev/null +++ b/gui/ProgressDialog.h @@ -0,0 +1,62 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TASKDIALOG_H +#define TASKDIALOG_H + +#include + +class ProgressProvider; + +namespace Ui { +class ProgressDialog; +} + +class ProgressDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ProgressDialog(QWidget *parent = 0); + ~ProgressDialog(); + + void updateSize(); + + int exec(ProgressProvider* task); + + ProgressProvider* getTask(); + +public slots: + void onTaskStarted(); + void onTaskFailed(QString failure); + void onTaskSucceeded(); + + void changeStatus(const QString& status); + void changeProgress(qint64 current, qint64 total); + +signals: + + +protected: + virtual void keyPressEvent(QKeyEvent* e); + virtual void closeEvent(QCloseEvent* e); + +private: + Ui::ProgressDialog *ui; + + ProgressProvider* task; +}; + +#endif // TASKDIALOG_H diff --git a/gui/ProgressDialog.ui b/gui/ProgressDialog.ui new file mode 100644 index 00000000..a56d2a92 --- /dev/null +++ b/gui/ProgressDialog.ui @@ -0,0 +1,53 @@ + + + ProgressDialog + + + + 0 + 0 + 400 + 68 + + + + + 400 + 0 + + + + + 600 + 16777215 + + + + Please wait... + + + + + + Task Status... + + + true + + + + + + + 24 + + + false + + + + + + + + diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 241df383..6f707236 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -42,7 +42,7 @@ #include "gui/settingsdialog.h" #include "gui/newinstancedialog.h" #include "gui/logindialog.h" -#include "gui/taskdialog.h" +#include "gui/ProgressDialog.h" #include "gui/aboutdialog.h" #include "gui/versionselectdialog.h" #include "gui/lwjglselectdialog.h" @@ -479,7 +479,7 @@ void MainWindow::doLogin(const QString& errorMsg) { UserInfo uInfo{loginDlg->getUsername(), loginDlg->getPassword()}; - TaskDialog* tDialog = new TaskDialog(this); + ProgressDialog* tDialog = new ProgressDialog(this); LoginTask* loginTask = new LoginTask(uInfo, tDialog); connect(loginTask, SIGNAL(succeeded()),SLOT(onLoginComplete()), Qt::QueuedConnection); connect(loginTask, SIGNAL(failed(QString)), SLOT(doLogin(QString)), Qt::QueuedConnection); @@ -512,7 +512,7 @@ void MainWindow::onLoginComplete() } else { - TaskDialog *tDialog = new TaskDialog(this); + ProgressDialog *tDialog = new ProgressDialog(this); connect(updateTask, SIGNAL(succeeded()),SLOT(onGameUpdateComplete())); connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); tDialog->exec(updateTask); @@ -575,7 +575,7 @@ void MainWindow::startTask(Task *task) connect(task, SIGNAL(started()), SLOT(taskStart())); connect(task, SIGNAL(succeeded()), SLOT(taskEnd())); connect(task, SIGNAL(failed(QString)), SLOT(taskEnd())); - task->startTask(); + task->start(); } diff --git a/gui/newinstancedialog.cpp b/gui/newinstancedialog.cpp index af2b11c5..6604d03b 100644 --- a/gui/newinstancedialog.cpp +++ b/gui/newinstancedialog.cpp @@ -24,7 +24,7 @@ #include "logic/tasks/Task.h" #include "versionselectdialog.h" -#include "taskdialog.h" +#include "ProgressDialog.h" #include "IconPickerDialog.h" #include diff --git a/gui/taskdialog.cpp b/gui/taskdialog.cpp deleted file mode 100644 index 8c745b38..00000000 --- a/gui/taskdialog.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "taskdialog.h" -#include "ui_taskdialog.h" - -#include - -#include "logic/tasks/Task.h" - -TaskDialog::TaskDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::TaskDialog) -{ - ui->setupUi(this); - updateSize(); - - changeProgress(0); -} - -TaskDialog::~TaskDialog() -{ - delete ui; -} - -void TaskDialog::updateSize() -{ - resize(QSize(480, minimumSizeHint().height())); -} - -void TaskDialog::exec(Task *task) -{ - this->task = task; - - // Connect signals. - connect(task, SIGNAL(started()), SLOT(onTaskStarted())); - connect(task, SIGNAL(failed(QString)), SLOT(onTaskEnded())); - connect(task, SIGNAL(succeeded()), SLOT(onTaskEnded())); - connect(task, SIGNAL(statusChanged(const QString&)), SLOT(changeStatus(const QString&))); - connect(task, SIGNAL(progressChanged(int)), SLOT(changeProgress(int))); - - // this makes sure that the task is started after the dialog is created - QMetaObject::invokeMethod(task, "startTask", Qt::QueuedConnection); - QDialog::exec(); -} - -Task* TaskDialog::getTask() -{ - return task; -} - -void TaskDialog::onTaskStarted() -{ - -} - -void TaskDialog::onTaskEnded() -{ - close(); -} - -void TaskDialog::changeStatus(const QString &status) -{ - ui->statusLabel->setText(status); - updateSize(); -} - -void TaskDialog::changeProgress(int progress) -{ - if (progress < 0) - progress = 0; - else if (progress > 100) - progress = 100; - - ui->taskProgressBar->setValue(progress); -} - -void TaskDialog::keyPressEvent(QKeyEvent* e) -{ - if (e->key() == Qt::Key_Escape) - return; - QDialog::keyPressEvent(e); -} - -void TaskDialog::closeEvent(QCloseEvent* e) -{ - if (task && task->isRunning()) - { - e->ignore(); - } - else - { - QDialog::closeEvent(e); - } -} diff --git a/gui/taskdialog.h b/gui/taskdialog.h deleted file mode 100644 index 3d31b7be..00000000 --- a/gui/taskdialog.h +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef TASKDIALOG_H -#define TASKDIALOG_H - -#include - -class Task; - -namespace Ui { -class TaskDialog; -} - -class TaskDialog : public QDialog -{ - Q_OBJECT - -public: - explicit TaskDialog(QWidget *parent = 0); - ~TaskDialog(); - - void updateSize(); - - void exec(Task* task); - - Task* getTask(); - -public slots: - void onTaskStarted(); - void onTaskEnded(); - - void changeStatus(const QString& status); - void changeProgress(int progress); - -signals: - - -protected: - virtual void keyPressEvent(QKeyEvent* e); - virtual void closeEvent(QCloseEvent* e); - -private: - Ui::TaskDialog *ui; - - Task* task; -}; - -#endif // TASKDIALOG_H diff --git a/gui/taskdialog.ui b/gui/taskdialog.ui deleted file mode 100644 index 1cdf7978..00000000 --- a/gui/taskdialog.ui +++ /dev/null @@ -1,53 +0,0 @@ - - - TaskDialog - - - - 0 - 0 - 400 - 58 - - - - - 400 - 0 - - - - - 600 - 16777215 - - - - Please wait... - - - - - - Task Status... - - - true - - - - - - - 24 - - - false - - - - - - - - diff --git a/gui/versionselectdialog.cpp b/gui/versionselectdialog.cpp index b14956fd..1e60c7d9 100644 --- a/gui/versionselectdialog.cpp +++ b/gui/versionselectdialog.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include @@ -41,11 +41,6 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QWidget *parent ui->listView->setModel(m_proxyModel); ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui->listView->header()->setSectionResizeMode(0, QHeaderView::Stretch); - - connect(ui->filterSnapshotsCheckbox, SIGNAL(clicked()), SLOT(updateFilterState())); - connect(ui->filterMCNostalgiaCheckbox, SIGNAL(clicked()), SLOT(updateFilterState())); - - updateFilterState(); } VersionSelectDialog::~VersionSelectDialog() @@ -63,7 +58,7 @@ int VersionSelectDialog::exec() void VersionSelectDialog::loadList() { - TaskDialog *taskDlg = new TaskDialog(this); + ProgressDialog *taskDlg = new ProgressDialog(this); Task *loadTask = m_vlist->getLoadTask(); loadTask->setParent(taskDlg); taskDlg->exec(loadTask); @@ -81,10 +76,11 @@ void VersionSelectDialog::on_refreshButton_clicked() loadList(); } -void VersionSelectDialog::updateFilterState() +void VersionSelectDialog::setFilter(int column, QString filter) { - m_proxyModel->setFilterKeyColumn(BaseVersionList::TypeColumn); - + m_proxyModel->setFilterKeyColumn(column); + m_proxyModel->setFilterFixedString(filter); + /* QStringList filteredTypes; if (!ui->filterSnapshotsCheckbox->isChecked()) filteredTypes += "Snapshot"; @@ -96,6 +92,5 @@ void VersionSelectDialog::updateFilterState() regexStr = QString("^((?!%1).)*$").arg(filteredTypes.join('|')); qDebug() << "Filter:" << regexStr; - - m_proxyModel->setFilterRegExp(regexStr); + */ } diff --git a/gui/versionselectdialog.h b/gui/versionselectdialog.h index 57e4d0df..0bb1745a 100644 --- a/gui/versionselectdialog.h +++ b/gui/versionselectdialog.h @@ -43,11 +43,10 @@ public: BaseVersionPtr selectedVersion() const; + void setFilter(int column, QString filter); + private slots: void on_refreshButton_clicked(); - - void updateFilterState(); - private: Ui::VersionSelectDialog *ui; diff --git a/gui/versionselectdialog.ui b/gui/versionselectdialog.ui index 02937794..222f29cf 100644 --- a/gui/versionselectdialog.ui +++ b/gui/versionselectdialog.ui @@ -39,75 +39,6 @@ - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Show &snapshots? - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Show &Nostalgia? - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - diff --git a/logic/BaseUpdate.cpp b/logic/BaseUpdate.cpp index b086ab14..02b29d32 100644 --- a/logic/BaseUpdate.cpp +++ b/logic/BaseUpdate.cpp @@ -7,7 +7,5 @@ BaseUpdate::BaseUpdate ( BaseInstance* inst, QObject* parent ) : Task ( parent ) void BaseUpdate::updateDownloadProgress(qint64 current, qint64 total) { - // The progress on the current file is current / total - float currentDLProgress = (float) current / (float) total; - setProgress((int)(currentDLProgress * 100)); // convert to percentage + emit progress(current, total); } \ No newline at end of file diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp index a0557b37..f2f792c9 100644 --- a/logic/InstanceLauncher.cpp +++ b/logic/InstanceLauncher.cpp @@ -3,7 +3,7 @@ #include #include "gui/logindialog.h" -#include "gui/taskdialog.h" +#include "gui/ProgressDialog.h" #include "gui/consolewindow.h" #include "logic/tasks/LoginTask.h" #include "logic/MinecraftProcess.h" @@ -48,7 +48,7 @@ void InstanceLauncher::doLogin ( const QString& errorMsg ) { UserInfo uInfo {loginDlg->getUsername(), loginDlg->getPassword() }; - TaskDialog* tDialog = new TaskDialog ( nullptr ); + ProgressDialog* tDialog = new ProgressDialog ( nullptr ); LoginTask* loginTask = new LoginTask ( uInfo, tDialog ); connect ( loginTask, SIGNAL ( succeeded() ),SLOT ( onLoginComplete() ), Qt::QueuedConnection ); connect ( loginTask, SIGNAL ( failed ( QString ) ),SLOT ( doLogin ( QString ) ), Qt::QueuedConnection ); diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 25be5e7d..0f58e3e3 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -59,7 +59,7 @@ void LegacyUpdate::lwjglStart() QNetworkReply * rep = worker->get ( req ); m_reply = QSharedPointer (rep, &QObject::deleteLater); - connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); connect(worker, SIGNAL(finished(QNetworkReply*)), SLOT(lwjglFinished(QNetworkReply*))); //connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); } @@ -97,7 +97,7 @@ void LegacyUpdate::lwjglFinished(QNetworkReply* reply) req.setRawHeader("Host", hostname.toLatin1()); req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)"); QNetworkReply * rep = worker->get(req); - connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); m_reply = QSharedPointer (rep, &QObject::deleteLater); return; } @@ -232,7 +232,7 @@ void LegacyUpdate::jarStart() legacyDownloadJob.reset(dljob); connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); connect(dljob, SIGNAL(failed()), SLOT(jarFailed())); - connect(dljob, SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + connect(dljob, SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); legacyDownloadJob->start(); } diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 67395818..298ad28a 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -78,7 +78,7 @@ void OneSixUpdate::versionFileStart() specificVersionDownloadJob.reset(job); connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), SLOT(versionFileFinished())); connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); specificVersionDownloadJob->start(); } @@ -171,7 +171,7 @@ void OneSixUpdate::jarlibStart() } connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished())); connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); - connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); jarlibDownloadJob->start(); } diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index f45b8b6b..412c04fe 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -50,21 +50,90 @@ int ForgeVersionList::count() const { return m_vlist.count(); } -/* -bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) + +int ForgeVersionList::columnCount(const QModelIndex& parent) const { - const BaseVersion & left = *first; - const BaseVersion & right = *second; - return left > right; + return 3; } -void MinecraftVersionList::sort() +QVariant ForgeVersionList::data(const QModelIndex &index, int role) const { - beginResetModel(); - qSort(m_vlist.begin(), m_vlist.end(), cmpVersions); - endResetModel(); + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + auto version = m_vlist[index.row()].dynamicCast(); + switch (role) + { + case Qt::DisplayRole: + switch (index.column()) + { + case 0: + return version->name(); + + case 1: + return version->mcver; + + case 2: + return version->typeString(); + default: + return QVariant(); + } + + case Qt::ToolTipRole: + return version->descriptor(); + + case VersionPointerRole: + return qVariantFromValue(m_vlist[index.row()]); + + default: + return QVariant(); + } } -*/ + +QVariant ForgeVersionList::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + switch (section) + { + case 0: + return "Version"; + + case 1: + return "Minecraft"; + + case 2: + return "Type"; + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case 0: + return "The name of the version."; + + case 1: + return "Minecraft version"; + + case 2: + return "The version's type."; + + default: + return QVariant(); + } + + default: + return QVariant(); + } +} + BaseVersionPtr ForgeVersionList::getLatestStable() const { return BaseVersionPtr(); @@ -99,7 +168,7 @@ void ForgeListLoadTask::executeTask() listJob.reset(job); connect(listJob.data(), SIGNAL(succeeded()), SLOT(list_downloaded())); connect(listJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(listJob.data(), SIGNAL(progress(qint64,qint64)), SLOT(updateDownloadProgress(qint64,qint64))); + connect(listJob.data(), SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); listJob->start(); } @@ -148,7 +217,7 @@ void ForgeListLoadTask::list_downloaded() int build_nr = obj.value("build").toDouble(0); if(!build_nr) continue; - QJsonArray files = root.value("files").toArray(); + QJsonArray files = obj.value("files").toArray(); QString url, jobbuildver, mcver, buildtype, filename; QString changelog_url, installer_url; bool valid = false; diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h index 8d4a2d46..ca6b27bc 100644 --- a/logic/lists/ForgeVersionList.h +++ b/logic/lists/ForgeVersionList.h @@ -26,7 +26,7 @@ #include "logic/net/DownloadJob.h" class ForgeVersion; -typedef QSharedPointer PtrForgeVersion; +typedef QSharedPointer ForgeVersionPtr; struct ForgeVersion : public BaseVersion { @@ -36,7 +36,7 @@ struct ForgeVersion : public BaseVersion }; virtual QString name() { - return "Forge " + jobbuildver + " (" + mcver + ")"; + return "Forge " + jobbuildver; }; virtual QString typeString() const { @@ -71,8 +71,12 @@ public: virtual BaseVersionPtr getLatestStable() const; + virtual QVariant data(const QModelIndex& index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual int columnCount(const QModelIndex& parent) const; + protected: - QList m_vlist; + QList m_vlist; bool m_loaded; diff --git a/logic/lists/MinecraftVersionList.h b/logic/lists/MinecraftVersionList.h index 8937ba7b..fb28ddfe 100644 --- a/logic/lists/MinecraftVersionList.h +++ b/logic/lists/MinecraftVersionList.h @@ -49,7 +49,7 @@ protected: bool m_loaded; protected slots: - virtual void updateListData(QList versions); + virtual void updateListData(QList versions); }; class MCVListLoadTask : public Task diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index 9b083b6b..3acba050 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -5,6 +5,8 @@ #include "ByteArrayDownload.h" #include "CacheDownload.h" +#include + ByteArrayDownloadPtr DownloadJob::add ( QUrl url ) { ByteArrayDownloadPtr ptr (new ByteArrayDownload(url)); diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index 69a49e59..c8f6a9d7 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -5,6 +5,7 @@ #include "FileDownload.h" #include "CacheDownload.h" #include "HttpMetaCache.h" +#include "logic/tasks/ProgressProvider.h" class DownloadJob; typedef QSharedPointer DownloadJobPtr; @@ -12,12 +13,12 @@ typedef QSharedPointer DownloadJobPtr; /** * A single file for the downloader/cache to process. */ -class DownloadJob : public QObject +class DownloadJob : public ProgressProvider { Q_OBJECT public: explicit DownloadJob(QString job_name) - :QObject(), m_job_name(job_name){}; + :ProgressProvider(), m_job_name(job_name){}; ByteArrayDownloadPtr add(QUrl url); FileDownloadPtr add(QUrl url, QString rel_target_path); @@ -37,6 +38,19 @@ public: { return downloads.size(); } + virtual void getProgress(qint64& current, qint64& total) + { + current = current_progress; + total = total_progress; + }; + virtual QString getStatus() const + { + return m_job_name; + }; + virtual bool isRunning() const + { + return m_running; + }; signals: void started(); void progress(qint64 current, qint64 total); @@ -56,5 +70,6 @@ private: qint64 total_progress = 0; int num_succeeded = 0; int num_failed = 0; + bool m_running = false; }; diff --git a/logic/tasks/ProgressProvider.h b/logic/tasks/ProgressProvider.h new file mode 100644 index 00000000..e158eb54 --- /dev/null +++ b/logic/tasks/ProgressProvider.h @@ -0,0 +1,20 @@ +#pragma once +#include +class ProgressProvider : public QObject +{ + Q_OBJECT +protected: + explicit ProgressProvider(QObject* parent = 0): QObject(parent){} +signals: + void started(); + void progress(qint64 current, qint64 total); + void succeeded(); + void failed(QString reason); + void status(QString status); +public: + virtual QString getStatus() const = 0; + virtual void getProgress(qint64 ¤t, qint64 &total) = 0; + virtual bool isRunning() const = 0; +public slots: + virtual void start() = 0; +}; diff --git a/logic/tasks/Task.cpp b/logic/tasks/Task.cpp index 7c148591..c75bcb8f 100644 --- a/logic/tasks/Task.cpp +++ b/logic/tasks/Task.cpp @@ -16,70 +16,56 @@ #include "Task.h" Task::Task(QObject *parent) : - QObject(parent) + ProgressProvider(parent) { } QString Task::getStatus() const { - return status; + return m_status; } -void Task::setStatus(const QString &status) +void Task::setStatus(const QString &new_status) { - this->status = status; - emitStatusChange(status); + m_status = new_status; + emit status(new_status); } -int Task::getProgress() const +void Task::setProgress(int new_progress) { - return progress; + m_progress = new_progress; + emit progress(new_progress, 100); } -void Task::setProgress(int progress) +void Task::getProgress(qint64& current, qint64& total) { - this->progress = progress; - emitProgressChange(progress); + current = m_progress; + total = 100; } -void Task::startTask() -{ - emitStarted(); - executeTask(); -} -void Task::emitStarted() +void Task::start() { - running = true; + m_running = true; emit started(); + executeTask(); } void Task::emitFailed(QString reason) { - running = false; + m_running = false; emit failed(reason); } void Task::emitSucceeded() { - running = false; + m_running = false; emit succeeded(); } bool Task::isRunning() const { - return running; -} - - -void Task::emitStatusChange(const QString &status) -{ - emit statusChanged(status); -} - -void Task::emitProgressChange(int progress) -{ - emit progressChanged(progress); + return m_running; } diff --git a/logic/tasks/Task.h b/logic/tasks/Task.h index 91852b0f..cfe71c51 100644 --- a/logic/tasks/Task.h +++ b/logic/tasks/Task.h @@ -13,53 +13,37 @@ * limitations under the License. */ -#ifndef TASK_H -#define TASK_H +#pragma once #include #include +#include "ProgressProvider.h" -class Task : public QObject +class Task : public ProgressProvider { Q_OBJECT public: explicit Task(QObject *parent = 0); - QString getStatus() const; - int getProgress() const; - bool isRunning() const; + virtual QString getStatus() const; + virtual void getProgress(qint64& current, qint64& total); + virtual bool isRunning() const; public slots: - void startTask(); - -protected slots: - void setStatus(const QString& status); - void setProgress(int progress); - -signals: - void started(); - void failed(QString reason); - void succeeded(); - - void statusChanged(Task* task, const QString& status); - void progressChanged(Task* task, int progress); - - void statusChanged(const QString& status); - void progressChanged(int progress); + virtual void start(); protected: virtual void executeTask() = 0; - virtual void emitStarted(); - virtual void emitFailed(QString reason); virtual void emitSucceeded(); + virtual void emitFailed(QString reason); + +protected slots: + void setStatus(const QString& status); + void setProgress(int progress); - virtual void emitStatusChange(const QString &status); - virtual void emitProgressChange(int progress); - - QString status; - int progress; - bool running = false; +protected: + QString m_status; + int m_progress = 0; + bool m_running = false; }; - -#endif // TASK_H -- cgit From c2c7293083de8e8d40190992ccd6a65b613a4d06 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Fri, 20 Sep 2013 01:21:48 +0200 Subject: Things... and stuff... with 1.6 modding. Maybe. --- gui/LegacyModEditDialog.cpp | 1 - gui/OneSixModEditDialog.cpp | 85 +++++++++++++++++++++++++++++++++++++--- gui/OneSixModEditDialog.h | 5 +++ gui/OneSixModEditDialog.ui | 9 ++++- logic/OneSixInstance.cpp | 24 ++++++++++++ logic/OneSixInstance.h | 4 ++ logic/lists/ForgeVersionList.cpp | 4 ++ 7 files changed, 124 insertions(+), 8 deletions(-) (limited to 'logic') diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index ac7f7f25..1a1198b1 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -230,7 +230,6 @@ void LegacyModEditDialog::on_addForgeBtn_clicked() m_jarmods->installMod(QFileInfo(entry->getFullPath())); m_jarmods->startWatching(); } - //m_selectedInstance->setIntendedVersionId(->descriptor()); } } void LegacyModEditDialog::on_addJarBtn_clicked() diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index fad9d2e2..7cbb9cbd 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -21,9 +21,11 @@ #include "logic/EnabledItemFilter.h" #include "logic/lists/ForgeVersionList.h" #include "gui/versionselectdialog.h" +#include "ProgressDialog.h" #include #include +#include #include #include #include @@ -38,11 +40,13 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance * inst, QWidget *parent) { m_version = m_inst->getFullVersion(); - auto filter = new EnabledItemFilter(this); - filter->setActive(true); - filter->setSourceModel(m_version.data()); - ui->libraryTreeView->setModel(filter); + main_model = new EnabledItemFilter(this); + main_model->setActive(true); + main_model->setSourceModel(m_version.data()); + ui->libraryTreeView->setModel(main_model); ui->libraryTreeView->installEventFilter( this ); + ui->mainClassEdit->setText(m_version->mainClass); + updateButtons(); } // Loader mods { @@ -69,17 +73,86 @@ OneSixModEditDialog::~OneSixModEditDialog() delete ui; } +void OneSixModEditDialog::updateButtons() +{ + bool customVersion = m_inst->versionIsCustom(); + ui->customizeBtn->setEnabled(!customVersion); + ui->revertBtn->setEnabled(customVersion); +} + + +void OneSixModEditDialog::on_customizeBtn_clicked() +{ + if(m_inst->customizeVersion()) + { + m_version = m_inst->getFullVersion(); + main_model->setSourceModel(m_version.data()); + updateButtons(); + } +} + +void OneSixModEditDialog::on_revertBtn_clicked() +{ + auto reply = QMessageBox::question(this, tr("Revert?"), tr("Do you want to revert the version of this instance to its original configuration?"),QMessageBox::Yes|QMessageBox::No); + if (reply == QMessageBox::Yes) + { + if(m_inst->revertCustomVersion()) + { + m_version = m_inst->getFullVersion(); + main_model->setSourceModel(m_version.data()); + updateButtons(); + } + } +} + + void OneSixModEditDialog::on_forgeBtn_clicked() { VersionSelectDialog vselect(MMC->forgelist(), this); vselect.setFilter(1, m_inst->currentVersionId()); if (vselect.exec() && vselect.selectedVersion()) { - //m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor()); + if(m_inst->versionIsCustom()) + { + auto reply = QMessageBox::question(this, tr("Revert?"), tr("This will revert any changes you did to the version up to this point. Is that OK?"),QMessageBox::Yes|QMessageBox::No); + if (reply == QMessageBox::Yes) + { + m_inst->revertCustomVersion(); + m_inst->customizeVersion(); + { + m_version = m_inst->getFullVersion(); + main_model->setSourceModel(m_version.data()); + updateButtons(); + } + } + else return; + } + ForgeVersionPtr forge = vselect.selectedVersion().dynamicCast(); + if(!forge) + return; + auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename); + if(entry->stale) + { + DownloadJob * fjob = new DownloadJob("Forge download"); + fjob->add(forge->universal_url, entry); + ProgressDialog dlg(this); + dlg.exec(fjob); + if(dlg.result() == QDialog::Accepted) + { + // install + } + else + { + // failed to download forge :/ + } + } + else + { + // install + } } } - bool OneSixModEditDialog::loaderListFilter ( QKeyEvent* keyEvent ) { switch(keyEvent->key()) diff --git a/gui/OneSixModEditDialog.h b/gui/OneSixModEditDialog.h index d14c842c..714c911a 100644 --- a/gui/OneSixModEditDialog.h +++ b/gui/OneSixModEditDialog.h @@ -18,6 +18,7 @@ #include +class EnabledItemFilter; namespace Ui { class OneSixModEditDialog; } @@ -41,6 +42,9 @@ private slots: // Questionable: SettingsDialog doesn't need this for some reason? void on_buttonBox_rejected(); void on_forgeBtn_clicked(); + void on_customizeBtn_clicked(); + void on_revertBtn_clicked(); + void updateButtons(); protected: bool eventFilter(QObject *obj, QEvent *ev); bool loaderListFilter( QKeyEvent* ev ); @@ -50,5 +54,6 @@ private: QSharedPointer m_version; QSharedPointer m_mods; QSharedPointer m_resourcepacks; + EnabledItemFilter * main_model; OneSixInstance * m_inst; }; diff --git a/gui/OneSixModEditDialog.ui b/gui/OneSixModEditDialog.ui index aadaf3ae..527899ca 100644 --- a/gui/OneSixModEditDialog.ui +++ b/gui/OneSixModEditDialog.ui @@ -55,7 +55,11 @@ - + + + false + + @@ -85,6 +89,9 @@ + + false + Revert to original base version diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 7b038c46..8124b4a0 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -226,6 +226,30 @@ QString OneSixInstance::currentVersionId() const return intendedVersionId(); } +bool OneSixInstance::customizeVersion() +{ + if(!versionIsCustom()) + { + auto pathCustom = PathCombine(instanceRoot(), "custom.json"); + auto pathOrig = PathCombine(instanceRoot(), "version.json"); + QFile::copy(pathOrig, pathCustom); + return reloadFullVersion(); + } + else return true; +} + +bool OneSixInstance::revertCustomVersion() +{ + if(versionIsCustom()) + { + auto path = PathCombine(instanceRoot(), "custom.json"); + QFile::remove(path); + return reloadFullVersion(); + } + else return true; +} + + bool OneSixInstance::reloadFullVersion() { I_D(OneSixInstance); diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 72bde630..0139b645 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -41,6 +41,10 @@ public: bool reloadFullVersion(); /// get the current full version info QSharedPointer getFullVersion(); + /// revert the current custom version back to base + bool revertCustomVersion(); + /// customize the current base version + bool customizeVersion(); /// is the current version original, or custom? bool versionIsCustom(); diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index 412c04fe..492849ee 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -220,6 +220,7 @@ void ForgeListLoadTask::list_downloaded() QJsonArray files = obj.value("files").toArray(); QString url, jobbuildver, mcver, buildtype, filename; QString changelog_url, installer_url; + QString installer_filename; bool valid = false; for(int j = 0; j < files.count(); j++) { @@ -246,6 +247,8 @@ void ForgeListLoadTask::list_downloaded() else if(buildtype == "installer") { installer_url = file.value("url").toString(); + int lastSlash = installer_url.lastIndexOf('/'); + installer_filename = installer_url.mid(lastSlash+1); } } if(valid) @@ -258,6 +261,7 @@ void ForgeListLoadTask::list_downloaded() fVersion->jobbuildver = jobbuildver; fVersion->mcver = mcver; fVersion->filename = filename; + fVersion->filename = installer_filename; fVersion->m_buildnr = build_nr; tempList.append(fVersion); } -- cgit From ceca6959d2a7f258d62ac4f589095b65084706c3 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 22 Sep 2013 04:21:36 +0200 Subject: Working 1.6 modding (currently only forge) --- .clang-format | 24 ++++ CMakeLists.txt | 144 +++++++++------------ MultiMC.cpp | 70 ++++------- MultiMC.h | 39 +++--- gui/IconPickerDialog.cpp | 4 +- gui/LegacyModEditDialog.cpp | 2 +- gui/OneSixModEditDialog.cpp | 179 ++++++++++++++++----------- gui/OneSixModEditDialog.h | 3 +- gui/instancedelegate.h | 2 +- gui/lwjglselectdialog.cpp | 6 +- gui/mainwindow.cpp | 6 +- gui/mainwindow.ui | 2 +- gui/newinstancedialog.cpp | 2 +- gui/settingsdialog.cpp | 4 +- logic/BaseInstance.cpp | 2 +- logic/BaseInstance.h | 2 +- logic/ForgeInstaller.cpp | 125 +++++++++++++++++++ logic/ForgeInstaller.h | 25 ++++ logic/LegacyUpdate.cpp | 4 +- logic/ModList.h | 3 + logic/OneSixInstance.cpp | 114 +++++++++-------- logic/OneSixLibrary.cpp | 98 +++++++++++---- logic/OneSixLibrary.h | 26 ++-- logic/OneSixRule.cpp | 66 +++++++++- logic/OneSixRule.h | 6 +- logic/OneSixUpdate.cpp | 85 +++++++------ logic/OneSixVersion.cpp | 261 ++++++++++++++++++++++++++++++++++----- logic/OneSixVersion.h | 60 +++++---- logic/OpSys.cpp | 11 ++ logic/OpSys.h | 1 + logic/VersionFactory.cpp | 196 ----------------------------- logic/VersionFactory.h | 24 ---- logic/lists/ForgeVersionList.cpp | 11 +- logic/lists/ForgeVersionList.h | 52 ++++---- logic/tasks/LoginTask.cpp | 2 +- 35 files changed, 992 insertions(+), 669 deletions(-) create mode 100644 .clang-format create mode 100644 logic/ForgeInstaller.cpp create mode 100644 logic/ForgeInstaller.h delete mode 100644 logic/VersionFactory.cpp delete mode 100644 logic/VersionFactory.h (limited to 'logic') diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..167a8fa7 --- /dev/null +++ b/.clang-format @@ -0,0 +1,24 @@ +UseTab: true +IndentWidth: 4 +TabWidth: 4 +ConstructorInitializerIndentWidth: 4 +AccessModifierOffset: -4 +IndentCaseLabels: false +IndentFunctionDeclarationAfterType: false +NamespaceIndentation: None + +BreakBeforeBraces: Allman +AllowShortIfStatementsOnASingleLine: false +ColumnLimit: 96 +MaxEmptyLinesToKeep: 1 + +Standard: Cpp11 +Cpp11BracedListStyle: true + +SpacesInParentheses: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpaceAfterControlStatementKeyword: true + +AlignTrailingComments: true +SpacesBeforeTrailingComments: 1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f09e324..aa7a91f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,165 +147,143 @@ ADD_DEFINITIONS(-DLIBGROUPVIEW_STATIC) ################################ FILES ################################ -######## Headers ######## -SET(MULTIMC_HEADERS +######## Sources and headers ######## +SET(MULTIMC_SOURCES +# Application base MultiMC.h +MultiMC.cpp MultiMCVersion.h +# GUI gui/mainwindow.h +gui/mainwindow.cpp gui/settingsdialog.h +gui/settingsdialog.cpp gui/newinstancedialog.h +gui/newinstancedialog.cpp gui/logindialog.h +gui/logindialog.cpp gui/ProgressDialog.h +gui/ProgressDialog.cpp gui/aboutdialog.h +gui/aboutdialog.cpp gui/consolewindow.h +gui/consolewindow.cpp gui/instancedelegate.h +gui/instancedelegate.cpp gui/versionselectdialog.h +gui/versionselectdialog.cpp gui/lwjglselectdialog.h +gui/lwjglselectdialog.cpp gui/instancesettings.h +gui/instancesettings.cpp gui/IconPickerDialog.h +gui/IconPickerDialog.cpp gui/LegacyModEditDialog.h +gui/LegacyModEditDialog.cpp gui/OneSixModEditDialog.h +gui/OneSixModEditDialog.cpp gui/ModEditDialogCommon.h +gui/ModEditDialogCommon.cpp gui/ModListView.h +gui/ModListView.cpp gui/LabeledToolButton.h +gui/LabeledToolButton.cpp gui/EditNotesDialog.h +gui/EditNotesDialog.cpp # Base classes and infrastructure logic/BaseVersion.h logic/MinecraftVersion.h logic/InstanceFactory.h +logic/InstanceFactory.cpp logic/BaseUpdate.h +logic/BaseUpdate.cpp logic/BaseInstance.h +logic/BaseInstance.cpp logic/BaseInstance_p.h + logic/MinecraftProcess.h +logic/MinecraftProcess.cpp logic/Mod.h +logic/Mod.cpp logic/ModList.h +logic/ModList.cpp # Basic instance launcher for starting from terminal logic/InstanceLauncher.h +logic/InstanceLauncher.cpp # network stuffs logic/net/Download.h logic/net/FileDownload.h +logic/net/FileDownload.cpp logic/net/ByteArrayDownload.h +logic/net/ByteArrayDownload.cpp logic/net/CacheDownload.h +logic/net/CacheDownload.cpp logic/net/DownloadJob.h +logic/net/DownloadJob.cpp logic/net/HttpMetaCache.h +logic/net/HttpMetaCache.cpp # legacy instances logic/LegacyInstance.h +logic/LegacyInstance.cpp logic/LegacyInstance_p.h logic/LegacyUpdate.h +logic/LegacyUpdate.cpp logic/LegacyForge.h +logic/LegacyForge.cpp # 1.6 instances logic/OneSixAssets.h +logic/OneSixAssets.cpp logic/OneSixInstance.h +logic/OneSixInstance.cpp logic/OneSixInstance_p.h logic/OneSixUpdate.h -logic/OneSixVersion.h -logic/OneSixLibrary.h -logic/OneSixRule.h -logic/VersionFactory.h -logic/OpSys.h - - -# Nostalgia -logic/NostalgiaInstance.h - -# Lists -logic/lists/InstanceList.h -logic/lists/IconList.h -logic/lists/BaseVersionList.h -logic/lists/MinecraftVersionList.h -logic/lists/LwjglVersionList.h -logic/lists/ForgeVersionList.h - -# misc model/view -logic/EnabledItemFilter.h - -# Tasks -logic/tasks/Task.h -logic/tasks/LoginTask.h -logic/tasks/ProgressProvider.h -) - - -######## Sources ######## -SET(MULTIMC_SOURCES -MultiMC.cpp - -gui/mainwindow.cpp -gui/settingsdialog.cpp -gui/newinstancedialog.cpp -gui/logindialog.cpp -gui/aboutdialog.cpp -gui/consolewindow.cpp -gui/instancedelegate.cpp -gui/versionselectdialog.cpp -gui/lwjglselectdialog.cpp -gui/instancesettings.cpp - -gui/ProgressDialog.cpp -gui/IconPickerDialog.cpp -gui/LegacyModEditDialog.cpp -gui/OneSixModEditDialog.cpp -gui/ModEditDialogCommon.cpp -gui/ModListView.cpp -gui/LabeledToolButton.cpp -gui/EditNotesDialog.cpp - -# Base classes and infrastructure -logic/InstanceFactory.cpp -logic/BaseUpdate.cpp -logic/BaseInstance.cpp -logic/MinecraftProcess.cpp -logic/Mod.cpp -logic/ModList.cpp - -# Basic instance launcher for starting from terminal -logic/InstanceLauncher.cpp - -# network stuffs - to be moved into a depend lib ~_~ -logic/net/FileDownload.cpp -logic/net/ByteArrayDownload.cpp -logic/net/CacheDownload.cpp -logic/net/DownloadJob.cpp -logic/net/HttpMetaCache.cpp - -# legacy instances -logic/LegacyInstance.cpp -logic/LegacyUpdate.cpp -logic/LegacyForge.cpp - -# 1.6 instances -logic/OneSixAssets.cpp -logic/OneSixInstance.cpp logic/OneSixUpdate.cpp +logic/OneSixVersion.h logic/OneSixVersion.cpp +logic/OneSixLibrary.h logic/OneSixLibrary.cpp +logic/OneSixRule.h logic/OneSixRule.cpp -logic/VersionFactory.cpp +logic/OpSys.h logic/OpSys.cpp +logic/ForgeInstaller.h +logic/ForgeInstaller.cpp # Nostalgia +logic/NostalgiaInstance.h logic/NostalgiaInstance.cpp # Lists +logic/lists/InstanceList.h logic/lists/InstanceList.cpp +logic/lists/IconList.h logic/lists/IconList.cpp +logic/lists/BaseVersionList.h logic/lists/BaseVersionList.cpp +logic/lists/MinecraftVersionList.h logic/lists/MinecraftVersionList.cpp +logic/lists/LwjglVersionList.h logic/lists/LwjglVersionList.cpp +logic/lists/ForgeVersionList.h logic/lists/ForgeVersionList.cpp # misc model/view +logic/EnabledItemFilter.h logic/EnabledItemFilter.cpp # Tasks +logic/tasks/ProgressProvider.h +logic/tasks/Task.h logic/tasks/Task.cpp +logic/tasks/LoginTask.h logic/tasks/LoginTask.cpp + ) @@ -328,7 +306,7 @@ gui/OneSixModEditDialog.ui gui/EditNotesDialog.ui ) -set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${MULTIMC_SOURCES} ${MULTIMC_UIS} ${MULTIMC_HEADERS}) +set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${MULTIMC_SOURCES} ${MULTIMC_UIS}) ######## Windows resource files ######## @@ -362,7 +340,7 @@ QT5_ADD_RESOURCES(MULTIMC_QRC multimc.qrc) # Add executable ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 - ${MULTIMC_SOURCES} ${MULTIMC_HEADERS} ${MULTIMC_UI} ${MULTIMC_QRC} ${MULTIMC_RCS}) + ${MULTIMC_SOURCES} ${MULTIMC_UI} ${MULTIMC_QRC} ${MULTIMC_RCS}) # Link QT5_USE_MODULES(MultiMC Widgets Network Xml) diff --git a/MultiMC.cpp b/MultiMC.cpp index 4b5b40b2..decc22bf 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -123,7 +123,7 @@ MultiMC::MultiMC ( int& argc, char** argv ) initGlobalSettings(); // and instances - m_instances = new InstanceList(m_settings->get("InstanceDir").toString(),this); + m_instances.reset(new InstanceList(m_settings->get("InstanceDir").toString(),this)); std::cout << "Loading Instances..." << std::endl; m_instances->loadList(); @@ -131,7 +131,7 @@ MultiMC::MultiMC ( int& argc, char** argv ) initHttpMetaCache(); // create the global network manager - m_qnam = new QNetworkAccessManager(this); + m_qnam.reset(new QNetworkAccessManager(this)); // Register meta types. qRegisterMetaType("LoginResponse"); @@ -152,80 +152,59 @@ MultiMC::~MultiMC() { if(m_mmc_translator) { - removeTranslator(m_mmc_translator); - delete m_mmc_translator; - m_mmc_translator = nullptr; + removeTranslator(m_mmc_translator.data()); } if(m_qt_translator) { - removeTranslator(m_qt_translator); - delete m_qt_translator; - m_qt_translator = nullptr; + removeTranslator(m_qt_translator.data()); } - if(m_icons) - { - delete m_icons; - m_icons = nullptr; - } - if(m_lwjgllist) - { - delete m_lwjgllist; - m_lwjgllist = nullptr; - } - if(m_minecraftlist) - { - delete m_minecraftlist; - m_minecraftlist = nullptr; - } - delete m_settings; - delete m_metacache; } void MultiMC::initTranslations() { - m_qt_translator = new QTranslator(); + m_qt_translator.reset(new QTranslator()); if(m_qt_translator->load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { std::cout << "Loading Qt Language File for " << QLocale::system().name().toLocal8Bit().constData() << "..."; - if(!installTranslator(m_qt_translator)) + if(!installTranslator(m_qt_translator.data())) { std::cout << " failed."; + m_qt_translator.reset(); } std::cout << std::endl; } else { - delete m_qt_translator; - m_qt_translator = nullptr; + m_qt_translator.reset(); } - m_mmc_translator = new QTranslator(); + m_mmc_translator.reset(new QTranslator()); if(m_mmc_translator->load("mmc_" + QLocale::system().name(), QDir("translations").absolutePath())) { std::cout << "Loading MMC Language File for " << QLocale::system().name().toLocal8Bit().constData() << "..."; - if(!installTranslator(m_mmc_translator)) + if(!installTranslator(m_mmc_translator.data())) { std::cout << " failed."; + m_mmc_translator.reset(); } std::cout << std::endl; } else { - delete m_mmc_translator; - m_mmc_translator = nullptr; + m_mmc_translator.reset(); } } void MultiMC::initGlobalSettings() { - m_settings = new INISettingsObject("multimc.cfg", this); + m_settings.reset(new INISettingsObject("multimc.cfg", this)); // Updates m_settings->registerSetting(new Setting("UseDevBuilds", false)); m_settings->registerSetting(new Setting("AutoUpdate", true)); @@ -278,7 +257,7 @@ void MultiMC::initGlobalSettings() void MultiMC::initHttpMetaCache() { - m_metacache = new HttpMetaCache("metacache"); + m_metacache.reset(new HttpMetaCache("metacache")); m_metacache->addBase("assets", QDir("assets").absolutePath()); m_metacache->addBase("versions", QDir("versions").absolutePath()); m_metacache->addBase("libraries", QDir("libraries").absolutePath()); @@ -287,37 +266,38 @@ void MultiMC::initHttpMetaCache() } -IconList* MultiMC::icons() +QSharedPointer MultiMC::icons() { if ( !m_icons ) { - m_icons = new IconList; + m_icons.reset(new IconList); } return m_icons; } -LWJGLVersionList* MultiMC::lwjgllist() +QSharedPointer MultiMC::lwjgllist() { if ( !m_lwjgllist ) { - m_lwjgllist = new LWJGLVersionList(); + m_lwjgllist.reset(new LWJGLVersionList()); } return m_lwjgllist; } -ForgeVersionList* MultiMC::forgelist() + +QSharedPointer MultiMC::forgelist() { if ( !m_forgelist ) { - m_forgelist = new ForgeVersionList(); + m_forgelist.reset(new ForgeVersionList()); } return m_forgelist; } -MinecraftVersionList* MultiMC::minecraftlist() +QSharedPointer MultiMC::minecraftlist() { if ( !m_minecraftlist ) { - m_minecraftlist = new MinecraftVersionList(); + m_minecraftlist.reset(new MinecraftVersionList()); } return m_minecraftlist; } @@ -345,4 +325,6 @@ int main(int argc, char *argv[]) } } -#include "MultiMC.moc" \ No newline at end of file +#include "MultiMC.moc" + + diff --git a/MultiMC.h b/MultiMC.h index d3e92584..1c1298e2 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "MultiMCVersion.h" #include "config.h" @@ -33,17 +34,17 @@ public: MultiMC ( int& argc, char** argv ); virtual ~MultiMC(); - SettingsObject * settings() + QSharedPointer settings() { return m_settings; }; - InstanceList * instances() + QSharedPointer instances() { return m_instances; }; - IconList * icons(); + QSharedPointer icons(); Status status() { @@ -55,21 +56,21 @@ public: return m_version; } - QNetworkAccessManager * qnam() + QSharedPointer qnam() { return m_qnam; } - HttpMetaCache * metacache() + QSharedPointer metacache() { return m_metacache; } - LWJGLVersionList * lwjgllist(); + QSharedPointer lwjgllist(); - ForgeVersionList * forgelist(); + QSharedPointer forgelist(); - MinecraftVersionList * minecraftlist(); + QSharedPointer minecraftlist(); private: void initGlobalSettings(); @@ -77,17 +78,17 @@ private: void initTranslations(); private: - QTranslator * m_qt_translator = nullptr; - QTranslator * m_mmc_translator = nullptr; - SettingsObject * m_settings = nullptr; - InstanceList * m_instances = nullptr; - IconList * m_icons = nullptr; - QNetworkAccessManager * m_qnam = nullptr; - HttpMetaCache * m_metacache = nullptr; - Status m_status = MultiMC::Failed; - LWJGLVersionList * m_lwjgllist = nullptr; - ForgeVersionList * m_forgelist = nullptr; - MinecraftVersionList * m_minecraftlist = nullptr; + QSharedPointer m_qt_translator; + QSharedPointer m_mmc_translator; + QSharedPointer m_settings; + QSharedPointer m_instances; + QSharedPointer m_icons; + QSharedPointer m_qnam; + QSharedPointer m_metacache; + QSharedPointer m_lwjgllist; + QSharedPointer m_forgelist; + QSharedPointer m_minecraftlist; + Status m_status = MultiMC::Failed; MultiMCVersion m_version = {VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD}; }; \ No newline at end of file diff --git a/gui/IconPickerDialog.cpp b/gui/IconPickerDialog.cpp index f3947d21..8f5d8256 100644 --- a/gui/IconPickerDialog.cpp +++ b/gui/IconPickerDialog.cpp @@ -39,7 +39,7 @@ IconPickerDialog::IconPickerDialog(QWidget *parent) : contentsWidget->installEventFilter(this); - contentsWidget->setModel(MMC->icons()); + contentsWidget->setModel(MMC->icons().data()); auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"),QDialogButtonBox::ResetRole); auto buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"),QDialogButtonBox::ResetRole); @@ -119,7 +119,7 @@ void IconPickerDialog::selectionChanged ( QItemSelection selected, QItemSelectio int IconPickerDialog::exec ( QString selection ) { - IconList * list = MMC->icons(); + auto list = MMC->icons(); auto contentsWidget = ui->iconView; selectedIconKey = selection; diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index 1a1198b1..20296769 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -199,7 +199,7 @@ void LegacyModEditDialog::on_addCoreBtn_clicked() } void LegacyModEditDialog::on_addForgeBtn_clicked() { - VersionSelectDialog vselect(MMC->forgelist(), this); + VersionSelectDialog vselect(MMC->forgelist().data(), this); vselect.setFilter(1, m_inst->intendedVersionId()); if (vselect.exec() && vselect.selectedVersion()) { diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index 7cbb9cbd..94fea933 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -1,9 +1,9 @@ /* Copyright 2013 MultiMC Contributors - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -20,6 +20,7 @@ #include "logic/OneSixVersion.h" #include "logic/EnabledItemFilter.h" #include "logic/lists/ForgeVersionList.h" +#include #include "gui/versionselectdialog.h" #include "ProgressDialog.h" @@ -30,30 +31,33 @@ #include #include -OneSixModEditDialog::OneSixModEditDialog(OneSixInstance * inst, QWidget *parent): - m_inst(inst), - QDialog(parent), - ui(new Ui::OneSixModEditDialog) +OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) + : m_inst(inst), QDialog(parent), ui(new Ui::OneSixModEditDialog) { ui->setupUi(this); - //libraries! + // libraries! + + m_version = m_inst->getFullVersion(); + if (m_version) { - m_version = m_inst->getFullVersion(); - main_model = new EnabledItemFilter(this); main_model->setActive(true); main_model->setSourceModel(m_version.data()); ui->libraryTreeView->setModel(main_model); - ui->libraryTreeView->installEventFilter( this ); + ui->libraryTreeView->installEventFilter(this); ui->mainClassEdit->setText(m_version->mainClass); - updateButtons(); + updateVersionControls(); + } + else + { + disableVersionControls(); } // Loader mods { ensureFolderPathExists(m_inst->loaderModsDir()); m_mods = m_inst->loaderModList(); ui->loaderModTreeView->setModel(m_mods.data()); - ui->loaderModTreeView->installEventFilter( this ); + ui->loaderModTreeView->installEventFilter(this); m_mods->startWatching(); } // resource packs @@ -61,7 +65,7 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance * inst, QWidget *parent) ensureFolderPathExists(m_inst->resourcePacksDir()); m_resourcepacks = m_inst->resourcePackList(); ui->resPackTreeView->setModel(m_resourcepacks.data()); - ui->resPackTreeView->installEventFilter( this ); + ui->resPackTreeView->installEventFilter(this); m_resourcepacks->startWatching(); } } @@ -73,48 +77,62 @@ OneSixModEditDialog::~OneSixModEditDialog() delete ui; } -void OneSixModEditDialog::updateButtons() +void OneSixModEditDialog::updateVersionControls() { bool customVersion = m_inst->versionIsCustom(); ui->customizeBtn->setEnabled(!customVersion); ui->revertBtn->setEnabled(customVersion); + ui->forgeBtn->setEnabled(true); } +void OneSixModEditDialog::disableVersionControls() +{ + ui->customizeBtn->setEnabled(false); + ui->revertBtn->setEnabled(false); + ui->forgeBtn->setEnabled(false); +} void OneSixModEditDialog::on_customizeBtn_clicked() { - if(m_inst->customizeVersion()) + if (m_inst->customizeVersion()) { m_version = m_inst->getFullVersion(); main_model->setSourceModel(m_version.data()); - updateButtons(); + updateVersionControls(); } } void OneSixModEditDialog::on_revertBtn_clicked() { - auto reply = QMessageBox::question(this, tr("Revert?"), tr("Do you want to revert the version of this instance to its original configuration?"),QMessageBox::Yes|QMessageBox::No); + auto reply = QMessageBox::question( + this, tr("Revert?"), tr("Do you want to revert the " + "version of this instance to its original configuration?"), + QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { - if(m_inst->revertCustomVersion()) + if (m_inst->revertCustomVersion()) { m_version = m_inst->getFullVersion(); main_model->setSourceModel(m_version.data()); - updateButtons(); + updateVersionControls(); } } } - void OneSixModEditDialog::on_forgeBtn_clicked() { - VersionSelectDialog vselect(MMC->forgelist(), this); + VersionSelectDialog vselect(MMC->forgelist().data(), this); vselect.setFilter(1, m_inst->currentVersionId()); if (vselect.exec() && vselect.selectedVersion()) { - if(m_inst->versionIsCustom()) + if (m_inst->versionIsCustom()) { - auto reply = QMessageBox::question(this, tr("Revert?"), tr("This will revert any changes you did to the version up to this point. Is that OK?"),QMessageBox::Yes|QMessageBox::No); + auto reply = QMessageBox::question( + this, tr("Revert?"), + tr("This will revert any " + "changes you did to the version up to this point. Is that " + "OK?"), + QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { m_inst->revertCustomVersion(); @@ -122,24 +140,38 @@ void OneSixModEditDialog::on_forgeBtn_clicked() { m_version = m_inst->getFullVersion(); main_model->setSourceModel(m_version.data()); - updateButtons(); + updateVersionControls(); } } - else return; + else + return; + } + else + { + m_inst->customizeVersion(); + m_version = m_inst->getFullVersion(); + main_model->setSourceModel(m_version.data()); + updateVersionControls(); } - ForgeVersionPtr forge = vselect.selectedVersion().dynamicCast(); - if(!forge) + ForgeVersionPtr forgeVersion = vselect.selectedVersion().dynamicCast(); + if (!forgeVersion) return; - auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename); - if(entry->stale) + auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename); + if (entry->stale) { - DownloadJob * fjob = new DownloadJob("Forge download"); - fjob->add(forge->universal_url, entry); + DownloadJob *fjob = new DownloadJob("Forge download"); + fjob->add(forgeVersion->installer_url, entry); ProgressDialog dlg(this); dlg.exec(fjob); - if(dlg.result() == QDialog::Accepted) + if (dlg.result() == QDialog::Accepted) { // install + QString forgePath = entry->getFullPath(); + ForgeInstaller forge(forgePath, forgeVersion->universal_url); + if (!forge.apply(m_version)) + { + // failure notice + } } else { @@ -149,55 +181,60 @@ void OneSixModEditDialog::on_forgeBtn_clicked() else { // install + QString forgePath = entry->getFullPath(); + ForgeInstaller forge(forgePath, forgeVersion->universal_url); + if (!forge.apply(m_version)) + { + // failure notice + } } } } -bool OneSixModEditDialog::loaderListFilter ( QKeyEvent* keyEvent ) +bool OneSixModEditDialog::loaderListFilter(QKeyEvent *keyEvent) { - switch(keyEvent->key()) + switch (keyEvent->key()) { - case Qt::Key_Delete: - on_rmModBtn_clicked(); - return true; - case Qt::Key_Plus: - on_addModBtn_clicked(); - return true; - default: - break; + case Qt::Key_Delete: + on_rmModBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addModBtn_clicked(); + return true; + default: + break; } - return QDialog::eventFilter( ui->loaderModTreeView, keyEvent ); + return QDialog::eventFilter(ui->loaderModTreeView, keyEvent); } -bool OneSixModEditDialog::resourcePackListFilter ( QKeyEvent* keyEvent ) +bool OneSixModEditDialog::resourcePackListFilter(QKeyEvent *keyEvent) { - switch(keyEvent->key()) + switch (keyEvent->key()) { - case Qt::Key_Delete: - on_rmResPackBtn_clicked(); - return true; - case Qt::Key_Plus: - on_addResPackBtn_clicked(); - return true; - default: - break; + case Qt::Key_Delete: + on_rmResPackBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addResPackBtn_clicked(); + return true; + default: + break; } - return QDialog::eventFilter( ui->resPackTreeView, keyEvent ); + return QDialog::eventFilter(ui->resPackTreeView, keyEvent); } - -bool OneSixModEditDialog::eventFilter ( QObject* obj, QEvent* ev ) +bool OneSixModEditDialog::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() != QEvent::KeyPress) { - return QDialog::eventFilter( obj, ev ); + return QDialog::eventFilter(obj, ev); } - QKeyEvent *keyEvent = static_cast(ev); - if(obj == ui->loaderModTreeView) + QKeyEvent *keyEvent = static_cast(ev); + if (obj == ui->loaderModTreeView) return loaderListFilter(keyEvent); - if(obj == ui->resPackTreeView) + if (obj == ui->resPackTreeView) return resourcePackListFilter(keyEvent); - return QDialog::eventFilter( obj, ev ); + return QDialog::eventFilter(obj, ev); } void OneSixModEditDialog::on_buttonBox_rejected() @@ -207,8 +244,9 @@ void OneSixModEditDialog::on_buttonBox_rejected() void OneSixModEditDialog::on_addModBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, QApplication::translate("LegacyModEditDialog", "Select Loader Mods")); - for(auto filename:fileNames) + QStringList fileNames = QFileDialog::getOpenFileNames( + this, QApplication::translate("LegacyModEditDialog", "Select Loader Mods")); + for (auto filename : fileNames) { m_mods->stopWatching(); m_mods->installMod(QFileInfo(filename)); @@ -219,8 +257,8 @@ void OneSixModEditDialog::on_rmModBtn_clicked() { int first, last; auto list = ui->loaderModTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_mods->stopWatching(); m_mods->deleteMods(first, last); @@ -231,11 +269,11 @@ void OneSixModEditDialog::on_viewModBtn_clicked() openDirInDefaultProgram(m_inst->loaderModsDir(), true); } - void OneSixModEditDialog::on_addResPackBtn_clicked() { - QStringList fileNames = QFileDialog::getOpenFileNames(this, QApplication::translate("LegacyModEditDialog", "Select Resource Packs")); - for(auto filename:fileNames) + QStringList fileNames = QFileDialog::getOpenFileNames( + this, QApplication::translate("LegacyModEditDialog", "Select Resource Packs")); + for (auto filename : fileNames) { m_resourcepacks->stopWatching(); m_resourcepacks->installMod(QFileInfo(filename)); @@ -246,8 +284,8 @@ void OneSixModEditDialog::on_rmResPackBtn_clicked() { int first, last; auto list = ui->resPackTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_resourcepacks->stopWatching(); m_resourcepacks->deleteMods(first, last); @@ -257,4 +295,3 @@ void OneSixModEditDialog::on_viewResPackBtn_clicked() { openDirInDefaultProgram(m_inst->resourcePacksDir(), true); } - diff --git a/gui/OneSixModEditDialog.h b/gui/OneSixModEditDialog.h index 714c911a..e70bd73f 100644 --- a/gui/OneSixModEditDialog.h +++ b/gui/OneSixModEditDialog.h @@ -44,7 +44,8 @@ private slots: void on_forgeBtn_clicked(); void on_customizeBtn_clicked(); void on_revertBtn_clicked(); - void updateButtons(); + void updateVersionControls(); + void disableVersionControls(); protected: bool eventFilter(QObject *obj, QEvent *ev); bool loaderListFilter( QKeyEvent* ev ); diff --git a/gui/instancedelegate.h b/gui/instancedelegate.h index c80f95a5..56bc34ba 100644 --- a/gui/instancedelegate.h +++ b/gui/instancedelegate.h @@ -9,4 +9,4 @@ public: protected: void paint ( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; QSize sizeHint ( const QStyleOptionViewItem & option, const QModelIndex & index ) const; -}; \ No newline at end of file +}; diff --git a/gui/lwjglselectdialog.cpp b/gui/lwjglselectdialog.cpp index 7c424a6c..e48bb975 100644 --- a/gui/lwjglselectdialog.cpp +++ b/gui/lwjglselectdialog.cpp @@ -26,10 +26,10 @@ LWJGLSelectDialog::LWJGLSelectDialog(QWidget *parent) : ui->setupUi(this); ui->labelStatus->setVisible(false); auto lwjgllist = MMC->lwjgllist(); - ui->lwjglListView->setModel(lwjgllist); + ui->lwjglListView->setModel(lwjgllist.data()); - connect(lwjgllist, SIGNAL(loadingStateUpdated(bool)), SLOT(loadingStateUpdated(bool))); - connect(lwjgllist, SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString))); + connect(lwjgllist.data(), SIGNAL(loadingStateUpdated(bool)), SLOT(loadingStateUpdated(bool))); + connect(lwjgllist.data(), SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString))); loadingStateUpdated(lwjgllist->isLoading()); } diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 6f707236..d3b167fa 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -124,7 +124,7 @@ MainWindow::MainWindow ( QWidget *parent ) //proxymodel->setDynamicSortFilter ( true ); // FIXME: instList should be global-ish, or at least not tied to the main window... maybe the application itself? - proxymodel->setSourceModel ( MMC->instances() ); + proxymodel->setSourceModel ( MMC->instances().data() ); proxymodel->sort ( 0 ); view->setFrameShape ( QFrame::NoFrame ); view->setModel ( proxymodel ); @@ -149,7 +149,7 @@ MainWindow::MainWindow ( QWidget *parent ) ); // model reset -> selection is invalid. All the instance pointers are wrong. // FIXME: stop using POINTERS everywhere - connect(MMC->instances() ,SIGNAL(dataIsInvalid()),SLOT(selectionBad())); + connect(MMC->instances().data() ,SIGNAL(dataIsInvalid()),SLOT(selectionBad())); // run the things that load and download other things... FIXME: this is NOT the place // FIXME: invisible actions in the background = NOPE. @@ -601,7 +601,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() if (view->selectionModel()->selectedIndexes().count() < 1) return; - VersionSelectDialog vselect(m_selectedInstance->versionList(), this); + VersionSelectDialog vselect(m_selectedInstance->versionList().data(), this); if (vselect.exec() && vselect.selectedVersion()) { m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor()); diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 4360f5f6..1cda7a34 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -440,7 +440,7 @@ Meow - <html><head/><body><p align="center"><span style=" font-weight:600; color:#ff0004;">Catnatok!</span></p><p align="center">Or just a cat with a ball of yarn?</p><p align="center"><span style=" font-style:italic;">WHO KNOWS?!</span></p><p align="center"><img src=":/icons/instances/tnt"/></p></body></html> + <html><head/><body><p align="center"><span style=" font-weight:600; color:#ff0004;">Catnarok!</span></p><p align="center">Or just a cat with a ball of yarn?</p><p align="center"><span style=" font-style:italic;">WHO KNOWS?!</span></p><p align="center"><img src=":/icons/instances/tnt"/></p></body></html> diff --git a/gui/newinstancedialog.cpp b/gui/newinstancedialog.cpp index 6604d03b..859077d9 100644 --- a/gui/newinstancedialog.cpp +++ b/gui/newinstancedialog.cpp @@ -96,7 +96,7 @@ BaseVersionPtr NewInstanceDialog::selectedVersion() const void NewInstanceDialog::on_btnChangeVersion_clicked() { - VersionSelectDialog vselect(MMC->minecraftlist(), this); + VersionSelectDialog vselect(MMC->minecraftlist().data(), this); vselect.exec(); if (vselect.result() == QDialog::Accepted) { diff --git a/gui/settingsdialog.cpp b/gui/settingsdialog.cpp index a5283b36..9736c1c7 100644 --- a/gui/settingsdialog.cpp +++ b/gui/settingsdialog.cpp @@ -27,7 +27,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) : { ui->setupUi(this); - loadSettings(MMC->settings()); + loadSettings(MMC->settings().data()); updateCheckboxStuff(); } @@ -85,7 +85,7 @@ void SettingsDialog::on_maximizedCheckBox_clicked(bool checked) void SettingsDialog::on_buttonBox_accepted() { - applySettings(MMC->settings()); + applySettings(MMC->settings().data()); } void SettingsDialog::applySettings(SettingsObject *s) diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index 10bb4573..ec86596a 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -132,7 +132,7 @@ InstanceList *BaseInstance::instList() const return NULL; } -BaseVersionList *BaseInstance::versionList() const +QSharedPointer BaseInstance::versionList() const { return MMC->minecraftlist(); } diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index fa317ba1..374d1437 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -134,7 +134,7 @@ public: * \brief Gets a pointer to this instance's version list. * \return A pointer to the available version list for this instance. */ - virtual BaseVersionList *versionList() const; + virtual QSharedPointer versionList() const; /*! * \brief Gets this instance's settings object. diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp new file mode 100644 index 00000000..00ad8d19 --- /dev/null +++ b/logic/ForgeInstaller.cpp @@ -0,0 +1,125 @@ +#include "ForgeInstaller.h" +#include "OneSixVersion.h" +#include "OneSixLibrary.h" +#include +#include +#include +#include + +ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) +{ + QSharedPointer newVersion; + m_universal_url = universal_url; + + QuaZip zip(filename); + if (!zip.open(QuaZip::mdUnzip)) + return; + + QuaZipFile file(&zip); + + // read the install profile + if (!zip.setCurrentFile("install_profile.json")) + return; + + QJsonParseError jsonError; + if (!file.open(QIODevice::ReadOnly)) + return; + QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll(), &jsonError); + file.close(); + if (jsonError.error != QJsonParseError::NoError) + return; + + if (!jsonDoc.isObject()) + return; + + QJsonObject root = jsonDoc.object(); + + auto installVal = root.value("install"); + auto versionInfoVal = root.value("versionInfo"); + if (!installVal.isObject() || !versionInfoVal.isObject()) + return; + + // read the forge version info + { + newVersion = OneSixVersion::fromJson(versionInfoVal.toObject()); + if (!newVersion) + return; + } + + QJsonObject installObj = installVal.toObject(); + QString libraryName = installObj.value("path").toString(); + internalPath = installObj.value("filePath").toString(); + + // where do we put the library? decode the mojang path + OneSixLibrary lib(libraryName); + lib.finalize(); + finalPath = "libraries/" + lib.storagePath(); + if (!ensureFilePathExists(finalPath)) + return; + + if (!zip.setCurrentFile(internalPath)) + return; + if (!file.open(QIODevice::ReadOnly)) + return; + { + QByteArray data = file.readAll(); + // extract file + QSaveFile extraction(finalPath); + if (!extraction.open(QIODevice::WriteOnly)) + return; + if (extraction.write(data) != data.size()) + return; + if (!extraction.commit()) + return; + } + file.close(); + + m_forge_version = newVersion; + realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); +} + +bool ForgeInstaller::apply(QSharedPointer to) +{ + if (!m_forge_version) + return false; + to->externalUpdateStart(); + int sliding_insert_window = 0; + { + // for each library in the version we are adding (except for the blacklisted) + QSet blacklist{"lwjgl", "lwjgl_util", "lwjgl-platform"}; + for (auto lib : m_forge_version->libraries) + { + QString libName = lib->name(); + // if this is the actual forge lib, set an absolute url for the download + if(libName.contains("minecraftforge")) + { + lib->setAbsoluteUrl(m_universal_url); + } + if (blacklist.contains(libName)) + continue; + + // find an entry that matches this one + bool found = false; + for (auto tolib : to->libraries) + { + if (tolib->name() != libName) + continue; + found = true; + // replace lib + tolib = lib; + break; + } + if (!found) + { + // add lib + to->libraries.insert(sliding_insert_window, lib); + sliding_insert_window++; + } + } + to->mainClass = m_forge_version->mainClass; + to->minecraftArguments = m_forge_version->minecraftArguments; + to->processArguments = m_forge_version->processArguments; + } + to->externalUpdateFinish(); + return to->toOriginalFile(); +} diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h new file mode 100644 index 00000000..f4ceaaef --- /dev/null +++ b/logic/ForgeInstaller.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +class OneSixVersion; + +class ForgeInstaller +{ +public: + ForgeInstaller(QString filename, QString universal_url); + + bool apply(QSharedPointer to); + +private: + // the version, read from the installer + QSharedPointer m_forge_version; + QString internalPath; + QString finalPath; + QString realVersionId; + QString m_universal_url; +}; + + + + diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 0f58e3e3..84d3d830 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -60,7 +60,7 @@ void LegacyUpdate::lwjglStart() m_reply = QSharedPointer (rep, &QObject::deleteLater); connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); - connect(worker, SIGNAL(finished(QNetworkReply*)), SLOT(lwjglFinished(QNetworkReply*))); + connect(worker.data(), SIGNAL(finished(QNetworkReply*)), SLOT(lwjglFinished(QNetworkReply*))); //connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); } @@ -77,7 +77,7 @@ void LegacyUpdate::lwjglFinished(QNetworkReply* reply) "\nSometimes you have to wait a bit if you download many LWJGL versions in a row. YMMV"); return; } - auto *worker = MMC->qnam(); + auto worker = MMC->qnam(); //Here i check if there is a cookie for me in the reply and extract it QList cookies = qvariant_cast>(reply->header(QNetworkRequest::SetCookieHeader)); if(cookies.count() != 0) diff --git a/logic/ModList.h b/logic/ModList.h index 5395e9ae..e99b6c82 100644 --- a/logic/ModList.h +++ b/logic/ModList.h @@ -112,3 +112,6 @@ protected: QString m_list_id; QList mods; }; + + + diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 8124b4a0..9262b155 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -2,7 +2,7 @@ #include "OneSixInstance_p.h" #include "OneSixUpdate.h" #include "MinecraftProcess.h" -#include "VersionFactory.h" +#include "OneSixVersion.h" #include #include @@ -10,8 +10,9 @@ #include #include -OneSixInstance::OneSixInstance ( const QString& rootDir, SettingsObject* setting_obj, QObject* parent ) -: BaseInstance ( new OneSixInstancePrivate(), rootDir, setting_obj, parent ) +OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj, + QObject *parent) + : BaseInstance(new OneSixInstancePrivate(), rootDir, setting_obj, parent) { I_D(OneSixInstance); d->m_settings->registerSetting(new Setting("IntendedVersion", "")); @@ -19,7 +20,7 @@ OneSixInstance::OneSixInstance ( const QString& rootDir, SettingsObject* setting reloadFullVersion(); } -BaseUpdate* OneSixInstance::doUpdate() +BaseUpdate *OneSixInstance::doUpdate() { return new OneSixUpdate(this); } @@ -34,10 +35,10 @@ QString replaceTokensIn(QString text, QMap with) int head = 0; while ((head = token_regexp.indexIn(text, head)) != -1) { - result.append(text.mid(tail, head-tail)); + result.append(text.mid(tail, head - tail)); QString key = token_regexp.cap(1); auto iter = with.find(key); - if(iter != with.end()) + if (iter != with.end()) { result.append(*iter); } @@ -48,26 +49,26 @@ QString replaceTokensIn(QString text, QMap with) return result; } -QStringList OneSixInstance::processMinecraftArgs( QString user, QString session ) +QStringList OneSixInstance::processMinecraftArgs(QString user, QString session) { I_D(OneSixInstance); auto version = d->version; QString args_pattern = version->minecraftArguments; - + QMap token_mapping; token_mapping["auth_username"] = user; token_mapping["auth_session"] = session; - //FIXME: user and player name are DIFFERENT! + // FIXME: user and player name are DIFFERENT! token_mapping["auth_player_name"] = user; - //FIXME: WTF is this. I just plugged in a random UUID here. + // FIXME: WTF is this. I just plugged in a random UUID here. token_mapping["auth_uuid"] = "7d4bacf0-fd62-11e2-b778-0800200c9a66"; // obviously fake. - + // this is for offline: /* map["auth_player_name"] = "Player"; map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; */ - + token_mapping["profile_name"] = name(); token_mapping["version_name"] = version->id; @@ -75,8 +76,8 @@ QStringList OneSixInstance::processMinecraftArgs( QString user, QString session token_mapping["game_directory"] = absRootDir; QString absAssetsDir = QDir("assets/").absolutePath(); token_mapping["game_assets"] = absAssetsDir; - - QStringList parts = args_pattern.split(' ',QString::SkipEmptyParts); + + QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); for (int i = 0; i < parts.length(); i++) { parts[i] = replaceTokensIn(parts[i], token_mapping); @@ -84,27 +85,28 @@ QStringList OneSixInstance::processMinecraftArgs( QString user, QString session return parts; } -MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString session ) +MinecraftProcess *OneSixInstance::prepareForLaunch(QString user, QString session) { I_D(OneSixInstance); cleanupAfterRun(); auto version = d->version; - if(!version) + if (!version) return nullptr; auto libs_to_extract = version->getActiveNativeLibs(); QString natives_dir_raw = PathCombine(instanceRoot(), "natives/"); bool success = ensureFolderPathExists(natives_dir_raw); - if(!success) + if (!success) { // FIXME: handle errors return nullptr; } - - for(auto lib: libs_to_extract) + + for (auto lib : libs_to_extract) { QString path = "libraries/" + lib->storagePath(); qDebug() << "Will extract " << path.toLocal8Bit(); - if(JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes).isEmpty()) + if (JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes) + .isEmpty()) { return nullptr; } @@ -116,11 +118,11 @@ MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString sessi args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt()); args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt()); QDir natives_dir(natives_dir_raw); - args << QString("-Djava.library.path=%1").arg( natives_dir.absolutePath() ); + args << QString("-Djava.library.path=%1").arg(natives_dir.absolutePath()); QString classPath; { auto libs = version->getActiveNormalLibs(); - for (auto lib: libs) + for (auto lib : libs) { QFileInfo fi(QString("libraries/") + lib->storagePath()); classPath.append(fi.absoluteFilePath()); @@ -134,16 +136,16 @@ MinecraftProcess* OneSixInstance::prepareForLaunch ( QString user, QString sessi QFileInfo fi(targetstr); classPath.append(fi.absoluteFilePath()); } - if(classPath.size()) + if (classPath.size()) { args << "-cp"; args << classPath; } args << version->mainClass; args.append(processMinecraftArgs(user, session)); - + // create the process and set its parameters - MinecraftProcess * proc = new MinecraftProcess(this); + MinecraftProcess *proc = new MinecraftProcess(this); proc->setMinecraftArguments(args); proc->setMinecraftWorkdir(minecraftRoot()); return proc; @@ -156,10 +158,10 @@ void OneSixInstance::cleanupAfterRun() dir.removeRecursively(); } -QSharedPointer< ModList > OneSixInstance::loaderModList() +QSharedPointer OneSixInstance::loaderModList() { I_D(OneSixInstance); - if(!d->loader_mod_list) + if (!d->loader_mod_list) { d->loader_mod_list.reset(new ModList(loaderModsDir())); } @@ -168,10 +170,10 @@ QSharedPointer< ModList > OneSixInstance::loaderModList() return d->loader_mod_list; } -QSharedPointer< ModList > OneSixInstance::resourcePackList() +QSharedPointer OneSixInstance::resourcePackList() { I_D(OneSixInstance); - if(!d->resource_pack_list) + if (!d->resource_pack_list) { d->resource_pack_list.reset(new ModList(resourcePacksDir())); } @@ -180,13 +182,12 @@ QSharedPointer< ModList > OneSixInstance::resourcePackList() return d->resource_pack_list; } - -QDialog * OneSixInstance::createModEditDialog ( QWidget* parent ) +QDialog *OneSixInstance::createModEditDialog(QWidget *parent) { return new OneSixModEditDialog(this, parent); } -bool OneSixInstance::setIntendedVersionId ( QString version ) +bool OneSixInstance::setIntendedVersionId(QString version) { settings().set("IntendedVersion", version); setShouldUpdate(true); @@ -198,16 +199,16 @@ QString OneSixInstance::intendedVersionId() const return settings().get("IntendedVersion").toString(); } -void OneSixInstance::setShouldUpdate ( bool val ) +void OneSixInstance::setShouldUpdate(bool val) { - settings().set ( "ShouldUpdate", val ); + settings().set("ShouldUpdate", val); } bool OneSixInstance::shouldUpdate() const { I_D(OneSixInstance); - QVariant var = settings().get ( "ShouldUpdate" ); - if ( !var.isValid() || var.toBool() == false ) + QVariant var = settings().get("ShouldUpdate"); + if (!var.isValid() || var.toBool() == false) { return intendedVersionId() != currentVersionId(); } @@ -228,56 +229,51 @@ QString OneSixInstance::currentVersionId() const bool OneSixInstance::customizeVersion() { - if(!versionIsCustom()) + if (!versionIsCustom()) { auto pathCustom = PathCombine(instanceRoot(), "custom.json"); auto pathOrig = PathCombine(instanceRoot(), "version.json"); QFile::copy(pathOrig, pathCustom); return reloadFullVersion(); } - else return true; + else + return true; } bool OneSixInstance::revertCustomVersion() { - if(versionIsCustom()) + if (versionIsCustom()) { auto path = PathCombine(instanceRoot(), "custom.json"); QFile::remove(path); return reloadFullVersion(); } - else return true; + else + return true; } - bool OneSixInstance::reloadFullVersion() { I_D(OneSixInstance); - + QString verpath = PathCombine(instanceRoot(), "version.json"); { QString verpath_custom = PathCombine(instanceRoot(), "custom.json"); QFile versionfile(verpath_custom); - if(versionfile.exists()) + if (versionfile.exists()) verpath = verpath_custom; } - - QFile versionfile(verpath); - if(versionfile.exists() && versionfile.open(QIODevice::ReadOnly)) + + auto version = OneSixVersion::fromFile(verpath); + if (version) { - FullVersionFactory fvf; - auto version = fvf.parse(versionfile.readAll()); - versionfile.close(); - if(version) - { - d->version = version; - return true; - } - }; + d->version = version; + return true; + } return false; } -QSharedPointer< OneSixVersion > OneSixInstance::getFullVersion() +QSharedPointer OneSixInstance::getFullVersion() { I_D(OneSixInstance); return d->version; @@ -293,9 +289,9 @@ QString OneSixInstance::defaultCustomBaseJar() const return PathCombine(instanceRoot(), "custom.jar"); } -bool OneSixInstance::menuActionEnabled ( QString action_name ) const +bool OneSixInstance::menuActionEnabled(QString action_name) const { - if(action_name == "actionChangeInstLWJGLVersion") + if (action_name == "actionChangeInstLWJGLVersion") return false; return true; } @@ -303,7 +299,7 @@ bool OneSixInstance::menuActionEnabled ( QString action_name ) const QString OneSixInstance::getStatusbarDescription() { QString descr = "One Six : " + intendedVersionId(); - if(versionIsCustom()) + if (versionIsCustom()) { descr + " (custom)"; } diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index a45a4aec..8da1fde7 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -1,18 +1,19 @@ #include "OneSixLibrary.h" #include "OneSixRule.h" #include "OpSys.h" +#include void OneSixLibrary::finalize() { - QStringList parts = m_name.split ( ':' ); + QStringList parts = m_name.split(':'); QString relative = parts[0]; - relative.replace ( '.','/' ); + relative.replace('.', '/'); relative += '/' + parts[1] + '/' + parts[2] + '/' + parts[1] + '-' + parts[2]; - - if ( !m_is_native ) + + if (!m_is_native) relative += ".jar"; else { - if ( m_native_suffixes.contains ( currentSystem ) ) + if (m_native_suffixes.contains(currentSystem)) { relative += "-" + m_native_suffixes[currentSystem] + ".jar"; } @@ -22,30 +23,30 @@ void OneSixLibrary::finalize() relative += ".jar"; } } - + m_decentname = parts[1]; m_decentversion = parts[2]; m_storage_path = relative; - m_download_path = m_base_url + relative; - - if ( m_rules.empty() ) + m_download_url = m_base_url + relative; + + if (m_rules.empty()) { m_is_active = true; } else { RuleAction result = Disallow; - for ( auto rule: m_rules ) + for (auto rule : m_rules) { - RuleAction temp = rule->apply ( this ); - if ( temp != Defer ) + RuleAction temp = rule->apply(this); + if (temp != Defer) result = temp; } - m_is_active = ( result == Allow ); + m_is_active = (result == Allow); } - if ( m_is_native ) + if (m_is_native) { - m_is_active = m_is_active && m_native_suffixes.contains ( currentSystem ); + m_is_active = m_is_active && m_native_suffixes.contains(currentSystem); m_decenttype = "Native"; } else @@ -54,11 +55,11 @@ void OneSixLibrary::finalize() } } -void OneSixLibrary::setName ( QString name ) +void OneSixLibrary::setName(QString name) { m_name = name; } -void OneSixLibrary::setBaseUrl ( QString base_url ) +void OneSixLibrary::setBaseUrl(QString base_url) { m_base_url = base_url; } @@ -66,12 +67,12 @@ void OneSixLibrary::setIsNative() { m_is_native = true; } -void OneSixLibrary::addNative ( OpSys os, QString suffix ) +void OneSixLibrary::addNative(OpSys os, QString suffix) { m_is_native = true; m_native_suffixes[os] = suffix; } -void OneSixLibrary::setRules ( QList< QSharedPointer< Rule > > rules ) +void OneSixLibrary::setRules(QList> rules) { m_rules = rules; } @@ -83,11 +84,66 @@ bool OneSixLibrary::isNative() { return m_is_native; } -QString OneSixLibrary::downloadPath() +QString OneSixLibrary::downloadUrl() { - return m_download_path; + if(m_absolute_url.size()) + return m_absolute_url; + return m_download_url; } QString OneSixLibrary::storagePath() { return m_storage_path; } + +void OneSixLibrary::setAbsoluteUrl(QString absolute_url) +{ + m_absolute_url = absolute_url; +} + +QString OneSixLibrary::absoluteUrl() +{ + return m_absolute_url; +} + +QJsonObject OneSixLibrary::toJson() +{ + QJsonObject libRoot; + libRoot.insert("name", m_name); + if(m_absolute_url.size()) + libRoot.insert("MMC-absulute_url", m_absolute_url); + if(m_base_url != "https://s3.amazonaws.com/Minecraft.Download/libraries/") + libRoot.insert("url", m_base_url); + if (isNative() && m_native_suffixes.size()) + { + QJsonObject nativeList; + auto iter = m_native_suffixes.begin(); + while (iter != m_native_suffixes.end()) + { + nativeList.insert(OpSys_toString(iter.key()), iter.value()); + iter++; + } + libRoot.insert("natives", nativeList); + } + if (isNative() && extract_excludes.size()) + { + QJsonArray excludes; + QJsonObject extract; + for (auto exclude : extract_excludes) + { + excludes.append(exclude); + } + extract.insert("exclude", excludes); + libRoot.insert("extract", extract); + } + if (m_rules.size()) + { + QJsonArray allRules; + for (auto &rule : m_rules) + { + QJsonObject ruleObj = rule->toJson(); + allRules.append(ruleObj); + } + libRoot.insert("rules", allRules); + } + return libRoot; +} diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index ac16d3d3..f3106483 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "OpSys.h" class Rule; @@ -12,9 +13,13 @@ class OneSixLibrary private: // basic values used internally (so far) QString m_name; - QString m_base_url; + QString m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; QList > m_rules; - + + // custom values + /// absolute URL. takes precedence over m_download_path, if defined + QString m_absolute_url; + // derived values used for real things /// a decent name fit for display QString m_decentname; @@ -25,11 +30,11 @@ private: /// where to store the lib locally QString m_storage_path; /// where to download the lib from - QString m_download_path; + QString m_download_url; /// is this lib actually active on the current OS? - bool m_is_active; + bool m_is_active = false; /// is the library a native? - bool m_is_native; + bool m_is_native = false; /// native suffixes per OS QMap m_native_suffixes; public: @@ -39,12 +44,11 @@ public: /// Constructor OneSixLibrary(QString name) { - m_is_native = false; - m_is_active = false; m_name = name; - m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; } + QJsonObject toJson(); + /** * finalize the library, processing the input values into derived values and state * @@ -84,7 +88,11 @@ public: /// Returns true if the library is native bool isNative(); /// Get the URL to download the library from - QString downloadPath(); + QString downloadUrl(); /// Get the relative path where the library should be saved QString storagePath(); + + /// set an absolute URL for the library. This is an MMC extension. + void setAbsoluteUrl(QString absolute_url); + QString absoluteUrl(); }; diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp index 85f7d434..545cd641 100644 --- a/logic/OneSixRule.cpp +++ b/logic/OneSixRule.cpp @@ -1,10 +1,72 @@ #include "OneSixRule.h" +#include +#include + +QList> rulesFromJsonV4(QJsonObject &objectWithRules) +{ + QList> rules; + auto rulesVal = objectWithRules.value("rules"); + if (!rulesVal.isArray()) + return rules; + + QJsonArray ruleList = rulesVal.toArray(); + for (auto ruleVal : ruleList) + { + QSharedPointer rule; + if (!ruleVal.isObject()) + continue; + auto ruleObj = ruleVal.toObject(); + auto actionVal = ruleObj.value("action"); + if (!actionVal.isString()) + continue; + auto action = RuleAction_fromString(actionVal.toString()); + if (action == Defer) + continue; + + auto osVal = ruleObj.value("os"); + if (!osVal.isObject()) + { + // add a new implicit action rule + rules.append(ImplicitRule::create(action)); + continue; + } + + auto osObj = osVal.toObject(); + auto osNameVal = osObj.value("name"); + if (!osNameVal.isString()) + continue; + OpSys requiredOs = OpSys_fromString(osNameVal.toString()); + QString versionRegex = osObj.value("version").toString(); + // add a new OS rule + rules.append(OsRule::create(action, requiredOs, versionRegex)); + } +} + +QJsonObject ImplicitRule::toJson() +{ + QJsonObject ruleObj; + ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); + return ruleObj; +} + +QJsonObject OsRule::toJson() +{ + QJsonObject ruleObj; + ruleObj.insert("action", m_result == Allow ? QString("allow") : QString("disallow")); + QJsonObject osObj; + { + osObj.insert("name", OpSys_toString(m_system)); + osObj.insert("version", m_version_regexp); + } + ruleObj.insert("os", osObj); + return ruleObj; +} RuleAction RuleAction_fromString(QString name) { - if(name == "allow") + if (name == "allow") return Allow; - if(name == "disallow") + if (name == "disallow") return Disallow; return Defer; } \ No newline at end of file diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h index 465c963f..23d20ff4 100644 --- a/logic/OneSixRule.h +++ b/logic/OneSixRule.h @@ -1,8 +1,6 @@ #pragma once #include #include - -class OneSixLibrary; #include "OneSixLibrary.h" enum RuleAction @@ -13,6 +11,7 @@ enum RuleAction }; RuleAction RuleAction_fromString(QString); +QList> rulesFromJsonV4(QJsonObject &objectWithRules); class Rule { @@ -23,6 +22,7 @@ public: Rule(RuleAction result) :m_result(result) {} virtual ~Rule(){}; + virtual QJsonObject toJson() = 0; RuleAction apply(OneSixLibrary * parent) { if(applies(parent)) @@ -47,6 +47,7 @@ protected: OsRule(RuleAction result, OpSys system, QString version_regexp) : Rule(result), m_system(system), m_version_regexp(version_regexp) {} public: + virtual QJsonObject toJson(); static QSharedPointer create(RuleAction result, OpSys system, QString version_regexp) { return QSharedPointer (new OsRule(result, system, version_regexp)); @@ -63,6 +64,7 @@ protected: ImplicitRule(RuleAction result) : Rule(result) {} public: + virtual QJsonObject toJson(); static QSharedPointer create(RuleAction result) { return QSharedPointer (new ImplicitRule(result)); diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 298ad28a..d0af8b93 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -26,20 +26,20 @@ #include "BaseInstance.h" #include "lists/MinecraftVersionList.h" -#include "VersionFactory.h" #include "OneSixVersion.h" #include "OneSixLibrary.h" #include "OneSixInstance.h" #include "pathutils.h" - -OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent):BaseUpdate(inst, parent){} +OneSixUpdate::OneSixUpdate(BaseInstance *inst, QObject *parent) : BaseUpdate(inst, parent) +{ +} void OneSixUpdate::executeTask() { QString intendedVersion = m_inst->intendedVersionId(); - + // Make directories QDir mcDir(m_inst->minecraftRoot()); if (!mcDir.exists() && !mcDir.mkpath(".")) @@ -47,17 +47,18 @@ void OneSixUpdate::executeTask() emitFailed("Failed to create bin folder."); return; } - + // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = MMC->minecraftlist()->findVersion(intendedVersion).dynamicCast(); - if(targetVersion == nullptr) + targetVersion = + MMC->minecraftlist()->findVersion(intendedVersion).dynamicCast(); + if (targetVersion == nullptr) { // don't do anything if it was invalid emitSucceeded(); return; } - - if(m_inst->shouldUpdate()) + + if (m_inst->shouldUpdate()) { versionFileStart(); } @@ -70,62 +71,65 @@ void OneSixUpdate::executeTask() void OneSixUpdate::versionFileStart() { setStatus("Getting the version files from Mojang."); - + QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; auto job = new DownloadJob("Version index"); job->add(QUrl(urlstr)); specificVersionDownloadJob.reset(job); - connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), SLOT(versionFileFinished())); + connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), + SLOT(versionFileFinished())); connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); + connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); specificVersionDownloadJob->start(); } void OneSixUpdate::versionFileFinished() { DownloadPtr DlJob = specificVersionDownloadJob->first(); - OneSixInstance * inst = (OneSixInstance *) m_inst; - + OneSixInstance *inst = (OneSixInstance *)m_inst; + QString version_id = targetVersion->descriptor(); QString inst_dir = m_inst->instanceRoot(); // save the version file in $instanceId/version.json { - QString version1 = PathCombine(inst_dir, "/version.json"); + QString version1 = PathCombine(inst_dir, "/version.json"); ensureFilePathExists(version1); // FIXME: detect errors here, download to a temp file, swap - QSaveFile vfile1 (version1); - if(!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly )) + QSaveFile vfile1(version1); + if (!vfile1.open(QIODevice::Truncate | QIODevice::WriteOnly)) { emitFailed("Can't open " + version1 + " for writing."); return; } auto data = DlJob.dynamicCast()->m_data; qint64 actual = 0; - if((actual = vfile1.write(data)) != data.size()) + if ((actual = vfile1.write(data)) != data.size()) { - emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " + data.size() + '.'); + emitFailed("Failed to write into " + version1 + ". Written " + actual + " out of " + + data.size() + '.'); return; } - if(!vfile1.commit()) + if (!vfile1.commit()) { emitFailed("Can't commit changes to " + version1); return; } } - + // the version is downloaded safely. update is 'done' at this point m_inst->setShouldUpdate(false); - + // delete any custom version inside the instance (it's no longer relevant, we did an update) - QString custom = PathCombine(inst_dir, "/custom.json"); + QString custom = PathCombine(inst_dir, "/custom.json"); QFile finfo(custom); - if(finfo.exists()) + if (finfo.exists()) { finfo.remove(); } inst->reloadFullVersion(); - + jarlibStart(); } @@ -136,42 +140,44 @@ void OneSixUpdate::versionFileFailed() void OneSixUpdate::jarlibStart() { - OneSixInstance * inst = (OneSixInstance *) m_inst; + OneSixInstance *inst = (OneSixInstance *)m_inst; bool successful = inst->reloadFullVersion(); - if(!successful) + if (!successful) { - emitFailed("Failed to load the version description file (version.json). It might be corrupted, missing or simply too new."); + emitFailed("Failed to load the version description file (version.json). It might be " + "corrupted, missing or simply too new."); return; } - + QSharedPointer version = inst->getFullVersion(); - + // download the right jar, save it in versions/$version/$version.jar QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); urlstr += version->id + "/" + version->id + ".jar"; - QString targetstr ("versions/"); + QString targetstr("versions/"); targetstr += version->id + "/" + version->id + ".jar"; - + auto job = new DownloadJob("Libraries for instance " + inst->name()); job->add(QUrl(urlstr), targetstr); jarlibDownloadJob.reset(job); - + auto libs = version->getActiveNativeLibs(); libs.append(version->getActiveNormalLibs()); - + auto metacache = MMC->metacache(); - for(auto lib: libs) + for (auto lib : libs) { - QString download_path = lib->downloadPath(); + QString download_path = lib->downloadUrl(); auto entry = metacache->resolveEntry("libraries", lib->storagePath()); - if(entry->stale) + if (entry->stale) { jarlibDownloadJob->add(download_path, entry); } } connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished())); connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); - connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); + connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); jarlibDownloadJob->start(); } @@ -185,4 +191,3 @@ void OneSixUpdate::jarlibFailed() { emitFailed("Failed to download the binary garbage. Try again. Maybe. IF YOU DARE"); } - diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index dc1b5d6f..663d903a 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -1,10 +1,201 @@ #include "OneSixVersion.h" #include "OneSixLibrary.h" +#include "OneSixRule.h" -QList > OneSixVersion::getActiveNormalLibs() +QSharedPointer fromJsonV4(QJsonObject root, + QSharedPointer fullVersion) { - QList > output; - for ( auto lib: libraries ) + fullVersion->id = root.value("id").toString(); + + fullVersion->mainClass = root.value("mainClass").toString(); + auto procArgsValue = root.value("processArguments"); + if (procArgsValue.isString()) + { + fullVersion->processArguments = procArgsValue.toString(); + QString toCompare = fullVersion->processArguments.toLower(); + if (toCompare == "legacy") + { + fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}"; + } + else if (toCompare == "username_session") + { + fullVersion->minecraftArguments = + "--username ${auth_player_name} --session ${auth_session}"; + } + else if (toCompare == "username_session_version") + { + fullVersion->minecraftArguments = "--username ${auth_player_name} " + "--session ${auth_session} " + "--version ${profile_name}"; + } + } + + auto minecraftArgsValue = root.value("minecraftArguments"); + if (minecraftArgsValue.isString()) + { + fullVersion->minecraftArguments = minecraftArgsValue.toString(); + } + + auto minecraftTypeValue = root.value("type"); + if (minecraftTypeValue.isString()) + { + fullVersion->type = minecraftTypeValue.toString(); + } + + fullVersion->releaseTime = root.value("releaseTime").toString(); + fullVersion->time = root.value("time").toString(); + + // Iterate through the list, if it's a list. + auto librariesValue = root.value("libraries"); + if (!librariesValue.isArray()) + return fullVersion; + + QJsonArray libList = root.value("libraries").toArray(); + for (auto libVal : libList) + { + if (!libVal.isObject()) + { + continue; + } + + QJsonObject libObj = libVal.toObject(); + + // Library name + auto nameVal = libObj.value("name"); + if (!nameVal.isString()) + continue; + QSharedPointer library(new OneSixLibrary(nameVal.toString())); + + auto urlVal = libObj.value("url"); + if (urlVal.isString()) + { + library->setBaseUrl(urlVal.toString()); + } + auto urlAbsVal = libObj.value("MMC-absulute_url"); + if (urlAbsVal.isString()) + { + library->setAbsoluteUrl(urlAbsVal.toString()); + } + // Extract excludes (if any) + auto extractVal = libObj.value("extract"); + if (extractVal.isObject()) + { + QStringList excludes; + auto extractObj = extractVal.toObject(); + auto excludesVal = extractObj.value("exclude"); + if (excludesVal.isArray()) + { + auto excludesList = excludesVal.toArray(); + for (auto excludeVal : excludesList) + { + if (excludeVal.isString()) + excludes.append(excludeVal.toString()); + } + library->extract_excludes = excludes; + } + } + + auto nativesVal = libObj.value("natives"); + if (nativesVal.isObject()) + { + library->setIsNative(); + auto nativesObj = nativesVal.toObject(); + auto iter = nativesObj.begin(); + while (iter != nativesObj.end()) + { + auto osType = OpSys_fromString(iter.key()); + if (osType == Os_Other) + continue; + if (!iter.value().isString()) + continue; + library->addNative(osType, iter.value().toString()); + iter++; + } + } + library->setRules(rulesFromJsonV4(libObj)); + library->finalize(); + fullVersion->libraries.append(library); + } + return fullVersion; +} + +QSharedPointer OneSixVersion::fromJson(QJsonObject root) +{ + QSharedPointer readVersion(new OneSixVersion()); + int launcher_ver = readVersion->minimumLauncherVersion = + root.value("minimumLauncherVersion").toDouble(); + + // ADD MORE HERE :D + if (launcher_ver > 0 && launcher_ver <= 7) + return fromJsonV4(root, readVersion); + else + { + return QSharedPointer(); + } +} + +QSharedPointer OneSixVersion::fromFile(QString filepath) +{ + QFile file(filepath); + if (!file.open(QIODevice::ReadOnly)) + { + return QSharedPointer(); + } + + auto data = file.readAll(); + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + + if (jsonError.error != QJsonParseError::NoError) + { + return QSharedPointer(); + } + + if (!jsonDoc.isObject()) + { + return QSharedPointer(); + } + QJsonObject root = jsonDoc.object(); + auto version = fromJson(root); + version->original_file = filepath; + return version; +} + +bool OneSixVersion::toOriginalFile() +{ + if (original_file.isEmpty()) + return false; + QSaveFile file(original_file); + if (!file.open(QIODevice::WriteOnly)) + { + return false; + } + // serialize base attributes (those we care about anyway) + QJsonObject root; + root.insert("minecraftArguments", minecraftArguments); + root.insert("mainClass", mainClass); + root.insert("minimumLauncherVersion", minimumLauncherVersion); + root.insert("time", time); + root.insert("id", id); + root.insert("type", type); + // screw processArguments + root.insert("releaseTime", releaseTime); + QJsonArray libarray; + for(const auto & lib: libraries) + { + libarray.append(lib->toJson()); + } + if(libarray.count()) + root.insert("libraries", libarray); + QJsonDocument doc(root); + file.write(doc.toJson()); + return file.commit(); +} + +QList> OneSixVersion::getActiveNormalLibs() +{ + QList> output; + for (auto lib : libraries) { if (lib->isActive() && !lib->isNative()) { @@ -14,10 +205,10 @@ QList > OneSixVersion::getActiveNormalLibs() return output; } -QList > OneSixVersion::getActiveNativeLibs() +QList> OneSixVersion::getActiveNativeLibs() { - QList > output; - for ( auto lib: libraries ) + QList> output; + for (auto lib : libraries) { if (lib->isActive() && lib->isNative()) { @@ -27,41 +218,50 @@ QList > OneSixVersion::getActiveNativeLibs() return output; } +void OneSixVersion::externalUpdateStart() +{ + beginResetModel(); +} + +void OneSixVersion::externalUpdateFinish() +{ + endResetModel(); +} -QVariant OneSixVersion::data(const QModelIndex& index, int role) const +QVariant OneSixVersion::data(const QModelIndex &index, int role) const { - if(!index.isValid()) + if (!index.isValid()) return QVariant(); - + int row = index.row(); int column = index.column(); - - if(row < 0 || row >= libraries.size()) + + if (row < 0 || row >= libraries.size()) return QVariant(); - - if(role == Qt::DisplayRole) + + if (role == Qt::DisplayRole) { - switch(column) + switch (column) { - case 0: - return libraries[row]->name(); - case 1: - return libraries[row]->type(); - case 2: - return libraries[row]->version(); - default: - return QVariant(); + case 0: + return libraries[row]->name(); + case 1: + return libraries[row]->type(); + case 2: + return libraries[row]->version(); + default: + return QVariant(); } } return QVariant(); } -Qt::ItemFlags OneSixVersion::flags(const QModelIndex& index) const +Qt::ItemFlags OneSixVersion::flags(const QModelIndex &index) const { - if(!index.isValid()) + if (!index.isValid()) return Qt::NoItemFlags; int row = index.row(); - if(libraries[row]->isActive()) + if (libraries[row]->isActive()) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; } @@ -69,11 +269,10 @@ Qt::ItemFlags OneSixVersion::flags(const QModelIndex& index) const { return Qt::ItemNeverHasChildren; } - //return QAbstractListModel::flags(index); + // return QAbstractListModel::flags(index); } - -QVariant OneSixVersion::headerData ( int section, Qt::Orientation orientation, int role ) const +QVariant OneSixVersion::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); @@ -90,12 +289,12 @@ QVariant OneSixVersion::headerData ( int section, Qt::Orientation orientation, i } } -int OneSixVersion::rowCount(const QModelIndex& parent) const +int OneSixVersion::rowCount(const QModelIndex &parent) const { return libraries.size(); } -int OneSixVersion::columnCount(const QModelIndex& parent) const +int OneSixVersion::columnCount(const QModelIndex &parent) const { - return 3; + return 3; } diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 6a6a5b4b..69268ecf 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -4,13 +4,33 @@ class OneSixLibrary; class OneSixVersion : public QAbstractListModel { + // Things required to implement the Qt list model public: - virtual QVariant data ( const QModelIndex& index, int role = Qt::DisplayRole ) const; - virtual int rowCount ( const QModelIndex& parent = QModelIndex() ) const; - virtual QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; - virtual int columnCount ( const QModelIndex& parent ) const; - virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role = Qt::DisplayRole) const; + virtual int columnCount(const QModelIndex &parent) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + + // serialization/deserialization +public: + bool toOriginalFile(); + static QSharedPointer fromJson(QJsonObject root); + static QSharedPointer fromFile(QString filepath); + public: + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); + // called when something starts/stops messing with the object + // FIXME: these are ugly in every possible way. + void externalUpdateStart(); + void externalUpdateFinish(); + + // data members +public: + /// file this was read from. blank, if none + QString original_file; /// the ID - determines which jar to use! ACTUALLY IMPORTANT! QString id; /// Last updated time - as a string @@ -23,26 +43,27 @@ public: * DEPRECATED: Old versions of the new vanilla launcher used this * ex: "username_session_version" */ - QString processArguments; + QString processArguments; /** * arguments that should be used for launching minecraft - * + * * ex: "--username ${auth_player_name} --session ${auth_session} * --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}" */ QString minecraftArguments; /** - * the minimum launcher version required by this version ... current is 4 (at point of writing) + * the minimum launcher version required by this version ... current is 4 (at point of + * writing) */ - int minimumLauncherVersion; + int minimumLauncherVersion = 0xDEADBEEF; /** * The main class to load first */ QString mainClass; - + /// the list of libs - both active and inactive, native and java - QList > libraries; - + QList> libraries; + /* FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. @@ -58,18 +79,11 @@ public: } } ], - "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" + "incompatibilityReason": "There is a bug in LWJGL which makes it incompatible with OSX + 10.5.8. Please go to New Profile and use 1.5.2 for now. Sorry!" } */ // QList rules; - -public: - - OneSixVersion() - { - minimumLauncherVersion = 0xDEADBEEF; - } - - QList > getActiveNormalLibs(); - QList > getActiveNativeLibs(); + + }; diff --git a/logic/OpSys.cpp b/logic/OpSys.cpp index 559479fd..f101fd08 100644 --- a/logic/OpSys.cpp +++ b/logic/OpSys.cpp @@ -9,4 +9,15 @@ OpSys OpSys_fromString(QString name) if(name == "osx") return Os_OSX; return Os_Other; +} + +QString OpSys_toString(OpSys name) +{ + switch(name) + { + case Os_Linux: return "linux"; + case Os_OSX: return "osx"; + case Os_Windows: return "windows"; + default: return "other"; + } } \ No newline at end of file diff --git a/logic/OpSys.h b/logic/OpSys.h index c68c437a..aaa2eb65 100644 --- a/logic/OpSys.h +++ b/logic/OpSys.h @@ -9,6 +9,7 @@ enum OpSys }; OpSys OpSys_fromString(QString); +QString OpSys_toString(OpSys); #ifdef Q_OS_WIN32 #define currentSystem Os_Windows diff --git a/logic/VersionFactory.cpp b/logic/VersionFactory.cpp deleted file mode 100644 index 4fa5ad3f..00000000 --- a/logic/VersionFactory.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "VersionFactory.h" -#include "OneSixVersion.h" -#include "OneSixRule.h" - -// Library rules (if any) -QList > FullVersionFactory::parse4rules(QJsonObject & baseObj) -{ - QList > rules; - auto rulesVal = baseObj.value("rules"); - if(rulesVal.isArray()) - { - QJsonArray ruleList = rulesVal.toArray(); - for(auto ruleVal : ruleList) - { - QSharedPointer rule; - if(!ruleVal.isObject()) - continue; - auto ruleObj = ruleVal.toObject(); - auto actionVal = ruleObj.value("action"); - if(!actionVal.isString()) - continue; - auto action = RuleAction_fromString(actionVal.toString()); - if(action == Defer) - continue; - - auto osVal = ruleObj.value("os"); - if(!osVal.isObject()) - { - // add a new implicit action rule - rules.append(ImplicitRule::create(action)); - } - else - { - auto osObj = osVal.toObject(); - auto osNameVal = osObj.value("name"); - if(!osNameVal.isString()) - continue; - OpSys requiredOs = OpSys_fromString(osNameVal.toString()); - QString versionRegex = osObj.value("version").toString(); - // add a new OS rule - rules.append(OsRule::create(action, requiredOs, versionRegex)); - } - } - } - return rules; -} - - -QSharedPointer FullVersionFactory::parse4(QJsonObject root, QSharedPointer fullVersion) -{ - fullVersion->id = root.value("id").toString(); - - fullVersion->mainClass = root.value("mainClass").toString(); - auto procArgsValue = root.value("processArguments"); - if(procArgsValue.isString()) - { - fullVersion->processArguments = procArgsValue.toString(); - QString toCompare = fullVersion->processArguments.toLower(); - if(toCompare == "legacy") - { - fullVersion->minecraftArguments = " ${auth_player_name} ${auth_session}"; - } - else if(toCompare == "username_session") - { - fullVersion->minecraftArguments = "--username ${auth_player_name} --session ${auth_session}"; - } - else if(toCompare == "username_session_version") - { - fullVersion->minecraftArguments = "--username ${auth_player_name} --session ${auth_session} --version ${profile_name}"; - } - } - - auto minecraftArgsValue = root.value("minecraftArguments"); - if(minecraftArgsValue.isString()) - { - fullVersion->minecraftArguments = minecraftArgsValue.toString(); - } - - auto minecraftTypeValue = root.value("type"); - if(minecraftTypeValue.isString()) - { - fullVersion->type = minecraftTypeValue.toString(); - } - - fullVersion->releaseTime = root.value("releaseTime").toString(); - fullVersion->time = root.value("time").toString(); - - // Iterate through the list, if it's a list. - auto librariesValue = root.value("libraries"); - if(!librariesValue.isArray()) - return fullVersion; - - QJsonArray libList = root.value("libraries").toArray(); - for (auto libVal : libList) - { - if (!libVal.isObject()) - { - continue; - } - - QJsonObject libObj = libVal.toObject(); - - // Library name - auto nameVal = libObj.value("name"); - if(!nameVal.isString()) - continue; - QSharedPointer library(new OneSixLibrary(nameVal.toString())); - - auto urlVal = libObj.value("url"); - if(urlVal.isString()) - { - library->setBaseUrl(urlVal.toString()); - } - - // Extract excludes (if any) - auto extractVal = libObj.value("extract"); - if(extractVal.isObject()) - { - QStringList excludes; - auto extractObj = extractVal.toObject(); - auto excludesVal = extractObj.value("exclude"); - if(!excludesVal.isArray()) - goto SKIP_EXTRACTS; - auto excludesList = excludesVal.toArray(); - for(auto excludeVal : excludesList) - { - if(excludeVal.isString()) - excludes.append(excludeVal.toString()); - } - library->extract_excludes = excludes; - } - SKIP_EXTRACTS: - - auto nativesVal = libObj.value("natives"); - if(nativesVal.isObject()) - { - library->setIsNative(); - auto nativesObj = nativesVal.toObject(); - auto iter = nativesObj.begin(); - while(iter != nativesObj.end()) - { - auto osType = OpSys_fromString(iter.key()); - if(osType == Os_Other) - continue; - if(!iter.value().isString()) - continue; - library->addNative(osType, iter.value().toString()); - iter++; - } - } - library->setRules(parse4rules(libObj)); - library->finalize(); - fullVersion->libraries.append(library); - } - return fullVersion; -} - -QSharedPointer FullVersionFactory::parse(QByteArray data) -{ - QSharedPointer readVersion(new OneSixVersion()); - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - - if (jsonError.error != QJsonParseError::NoError) - { - error_string = QString( "Error reading version file :") + " " + jsonError.errorString(); - m_error = FullVersionFactory::ParseError; - return QSharedPointer(); - } - - if(!jsonDoc.isObject()) - { - error_string = "Error reading version file."; - m_error = FullVersionFactory::ParseError; - return QSharedPointer(); - } - QJsonObject root = jsonDoc.object(); - - int launcher_ver = readVersion->minimumLauncherVersion = root.value("minimumLauncherVersion").toDouble(); - // ADD MORE HERE :D - if(launcher_ver > 0 && launcher_ver <= 7) - return parse4(root, readVersion); - else - { - error_string = "Version file was for an unrecognized launcher version. RIP"; - m_error = FullVersionFactory::UnsupportedVersion; - return QSharedPointer(); - } -} - - -FullVersionFactory::FullVersionFactory() -{ - m_error = FullVersionFactory::AllOK; -} diff --git a/logic/VersionFactory.h b/logic/VersionFactory.h deleted file mode 100644 index 0c0ee2d4..00000000 --- a/logic/VersionFactory.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include - -struct OneSixVersion; -class Rule; - -class FullVersionFactory -{ -public: - enum Error - { - AllOK, // all parsed OK - ParseError, // the file was corrupted somehow - UnsupportedVersion // the file was meant for a launcher version we don't support (yet) - } m_error; - QString error_string; - -public: - FullVersionFactory(); - QSharedPointer parse(QByteArray data); -private: - QSharedPointer parse4(QJsonObject root, QSharedPointer product); - QList > parse4rules(QJsonObject & baseObj); -}; \ No newline at end of file diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index 492849ee..9205e70f 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -260,8 +260,10 @@ void ForgeListLoadTask::list_downloaded() fVersion->installer_url = installer_url; fVersion->jobbuildver = jobbuildver; fVersion->mcver = mcver; - fVersion->filename = filename; - fVersion->filename = installer_filename; + if(installer_filename.isEmpty()) + fVersion->filename = filename; + else + fVersion->filename = installer_filename; fVersion->m_buildnr = build_nr; tempList.append(fVersion); } @@ -271,3 +273,8 @@ void ForgeListLoadTask::list_downloaded() emitSucceeded(); return; } + + + + + diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h index ca6b27bc..613de8a6 100644 --- a/logic/lists/ForgeVersionList.h +++ b/logic/lists/ForgeVersionList.h @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -33,19 +33,22 @@ struct ForgeVersion : public BaseVersion virtual QString descriptor() { return filename; - }; + } + ; virtual QString name() { return "Forge " + jobbuildver; - }; + } + ; virtual QString typeString() const { - if(installer_url.isEmpty()) + if (installer_url.isEmpty()) return "Universal"; else return "Installer"; - }; - + } + ; + int m_buildnr = 0; QString universal_url; QString changelog_url; @@ -60,42 +63,45 @@ class ForgeVersionList : public BaseVersionList Q_OBJECT public: friend class ForgeListLoadTask; - + explicit ForgeVersionList(QObject *parent = 0); - + virtual Task *getLoadTask(); virtual bool isLoaded(); virtual const BaseVersionPtr at(int i) const; virtual int count() const; virtual void sort(); - + virtual BaseVersionPtr getLatestStable() const; - - virtual QVariant data(const QModelIndex& index, int role) const; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; - virtual int columnCount(const QModelIndex& parent) const; - + + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role) const; + virtual int columnCount(const QModelIndex &parent) const; + protected: QList m_vlist; - + bool m_loaded; - -protected slots: - virtual void updateListData(QList versions); + +protected +slots: + virtual void updateListData(QList versions); }; class ForgeListLoadTask : public Task { Q_OBJECT - + public: explicit ForgeListLoadTask(ForgeVersionList *vlist); - + virtual void executeTask(); - -protected slots: + +protected +slots: void list_downloaded(); - + protected: DownloadJobPtr listJob; ForgeVersionList *m_list; diff --git a/logic/tasks/LoginTask.cpp b/logic/tasks/LoginTask.cpp index 859827bc..222af618 100644 --- a/logic/tasks/LoginTask.cpp +++ b/logic/tasks/LoginTask.cpp @@ -30,7 +30,7 @@ void LoginTask::executeTask() { setStatus(tr("Logging in...")); auto worker = MMC->qnam(); - connect(worker, SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*))); + connect(worker.data(), SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*))); QUrl loginURL("https://login.minecraft.net/"); QNetworkRequest netRequest(loginURL); -- cgit From 9d03a9c1e3b9c24a4146adedb2971591d23b037a Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 22 Sep 2013 14:00:37 +0200 Subject: Cache forge version list (it's huge) --- logic/ForgeInstaller.cpp | 12 +++- logic/NostalgiaInstance.h | 1 + logic/OneSixInstance.cpp | 1 + logic/lists/ForgeVersionList.cpp | 119 ++++++++++++++++++++------------------- logic/net/CacheDownload.cpp | 63 +++++++++++---------- logic/net/HttpMetaCache.h | 19 ++++--- 6 files changed, 118 insertions(+), 97 deletions(-) (limited to 'logic') diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 00ad8d19..bcba00e9 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -1,10 +1,12 @@ #include "ForgeInstaller.h" #include "OneSixVersion.h" #include "OneSixLibrary.h" +#include "net/HttpMetaCache.h" #include #include #include #include +#include "MultiMC.h" ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { @@ -53,6 +55,8 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) // where do we put the library? decode the mojang path OneSixLibrary lib(libraryName); lib.finalize(); + + auto cacheentry = MMC->metacache()->resolveEntry("libraries", lib.storagePath()); finalPath = "libraries/" + lib.storagePath(); if (!ensureFilePathExists(finalPath)) return; @@ -71,6 +75,12 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) return; if (!extraction.commit()) return; + QCryptographicHash md5sum(QCryptographicHash::Md5); + md5sum.addData(data); + + cacheentry->stale = false; + cacheentry->md5sum = md5sum.result().toHex().constData(); + MMC->metacache()->updateEntry(cacheentry); } file.close(); @@ -91,7 +101,7 @@ bool ForgeInstaller::apply(QSharedPointer to) { QString libName = lib->name(); // if this is the actual forge lib, set an absolute url for the download - if(libName.contains("minecraftforge")) + if (libName.contains("minecraftforge")) { lib->setAbsoluteUrl(m_universal_url); } diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h index f2df1828..1436e48d 100644 --- a/logic/NostalgiaInstance.h +++ b/logic/NostalgiaInstance.h @@ -9,3 +9,4 @@ public: explicit NostalgiaInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0); virtual QString getStatusbarDescription(); }; + diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 9262b155..e22a8890 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -320,3 +320,4 @@ QString OneSixInstance::instanceConfigFolder() const { return PathCombine(minecraftRoot(), "config"); } + diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index 9205e70f..721f2c0a 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -25,10 +25,8 @@ #define JSON_URL "http://files.minecraftforge.net/minecraftforge/json" - -ForgeVersionList::ForgeVersionList(QObject* parent): BaseVersionList(parent) +ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent) { - } Task *ForgeVersionList::getLoadTask() @@ -51,19 +49,19 @@ int ForgeVersionList::count() const return m_vlist.count(); } -int ForgeVersionList::columnCount(const QModelIndex& parent) const +int ForgeVersionList::columnCount(const QModelIndex &parent) const { - return 3; + return 3; } QVariant ForgeVersionList::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - + if (index.row() > count()) return QVariant(); - + auto version = m_vlist[index.row()].dynamicCast(); switch (role) { @@ -72,22 +70,22 @@ QVariant ForgeVersionList::data(const QModelIndex &index, int role) const { case 0: return version->name(); - + case 1: return version->mcver; - + case 2: return version->typeString(); default: return QVariant(); } - + case Qt::ToolTipRole: return version->descriptor(); - + case VersionPointerRole: return qVariantFromValue(m_vlist[index.row()]); - + default: return QVariant(); } @@ -102,33 +100,33 @@ QVariant ForgeVersionList::headerData(int section, Qt::Orientation orientation, { case 0: return "Version"; - + case 1: return "Minecraft"; - + case 2: return "Type"; - + default: return QVariant(); } - + case Qt::ToolTipRole: switch (section) { case 0: return "The name of the version."; - + case 1: return "Minecraft version"; - + case 2: return "The version's type."; - + default: return QVariant(); } - + default: return QVariant(); } @@ -139,7 +137,7 @@ BaseVersionPtr ForgeVersionList::getLatestStable() const return BaseVersionPtr(); } -void ForgeVersionList::updateListData(QList versions) +void ForgeVersionList::updateListData(QList versions) { beginResetModel(); m_vlist = versions; @@ -154,104 +152,112 @@ void ForgeVersionList::sort() // NO-OP for now } - -ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList* vlist): Task() +ForgeListLoadTask::ForgeListLoadTask(ForgeVersionList *vlist) : Task() { m_list = vlist; } - void ForgeListLoadTask::executeTask() { auto job = new DownloadJob("Version index"); - job->add(QUrl(JSON_URL)); + // we do not care if the version is stale or not. + auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); + job->add(QUrl(JSON_URL), forgeListEntry); listJob.reset(job); connect(listJob.data(), SIGNAL(succeeded()), SLOT(list_downloaded())); connect(listJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(listJob.data(), SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); + connect(listJob.data(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); listJob->start(); } void ForgeListLoadTask::list_downloaded() { - auto DlJob = listJob->first(); - auto data = DlJob.dynamicCast()->m_data; - - + QByteArray data; + { + auto DlJob = listJob->first(); + auto filename = DlJob.dynamicCast()->m_target_path; + QFile listFile(filename); + if(!listFile.open(QIODevice::ReadOnly)) + return; + data = listFile.readAll(); + DlJob.reset(); + } + QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - DlJob.reset(); - + + if (jsonError.error != QJsonParseError::NoError) { emitFailed("Error parsing version list JSON:" + jsonError.errorString()); return; } - if(!jsonDoc.isObject()) + if (!jsonDoc.isObject()) { emitFailed("Error parsing version list JSON: jsonDoc is not an object"); return; } - + QJsonObject root = jsonDoc.object(); - + // Now, get the array of versions. - if(!root.value("builds").isArray()) + if (!root.value("builds").isArray()) { - emitFailed("Error parsing version list JSON: version list object is missing 'builds' array"); + emitFailed( + "Error parsing version list JSON: version list object is missing 'builds' array"); return; } QJsonArray builds = root.value("builds").toArray(); - - QList tempList; + + QList tempList; for (int i = 0; i < builds.count(); i++) { // Load the version info. - if(!builds[i].isObject()) + if (!builds[i].isObject()) { - //FIXME: log this somewhere + // FIXME: log this somewhere continue; } QJsonObject obj = builds[i].toObject(); int build_nr = obj.value("build").toDouble(0); - if(!build_nr) + if (!build_nr) continue; QJsonArray files = obj.value("files").toArray(); QString url, jobbuildver, mcver, buildtype, filename; QString changelog_url, installer_url; QString installer_filename; bool valid = false; - for(int j = 0; j < files.count(); j++) + for (int j = 0; j < files.count(); j++) { - if(!files[j].isObject()) + if (!files[j].isObject()) continue; QJsonObject file = files[j].toObject(); buildtype = file.value("buildtype").toString(); - if((buildtype == "client" || buildtype == "universal") && !valid) + if ((buildtype == "client" || buildtype == "universal") && !valid) { mcver = file.value("mcver").toString(); url = file.value("url").toString(); jobbuildver = file.value("jobbuildver").toString(); int lastSlash = url.lastIndexOf('/'); - filename = url.mid(lastSlash+1); + filename = url.mid(lastSlash + 1); valid = true; } - else if(buildtype == "changelog") + else if (buildtype == "changelog") { QString ext = file.value("ext").toString(); - if(ext.isEmpty()) + if (ext.isEmpty()) continue; changelog_url = file.value("url").toString(); } - else if(buildtype == "installer") + else if (buildtype == "installer") { installer_url = file.value("url").toString(); int lastSlash = installer_url.lastIndexOf('/'); - installer_filename = installer_url.mid(lastSlash+1); + installer_filename = installer_url.mid(lastSlash + 1); } } - if(valid) + if (valid) { // Now, we construct the version object and add it to the list. QSharedPointer fVersion(new ForgeVersion()); @@ -260,7 +266,7 @@ void ForgeListLoadTask::list_downloaded() fVersion->installer_url = installer_url; fVersion->jobbuildver = jobbuildver; fVersion->mcver = mcver; - if(installer_filename.isEmpty()) + if (installer_filename.isEmpty()) fVersion->filename = filename; else fVersion->filename = installer_filename; @@ -269,12 +275,7 @@ void ForgeListLoadTask::list_downloaded() } } m_list->updateListData(tempList); - + emitSucceeded(); return; } - - - - - diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index c0074574..dc2e0448 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -7,8 +7,8 @@ #include #include -CacheDownload::CacheDownload (QUrl url, MetaEntryPtr entry ) - :Download(), md5sum(QCryptographicHash::Md5) +CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry) + : Download(), md5sum(QCryptographicHash::Md5) { m_url = url; m_entry = entry; @@ -19,38 +19,40 @@ CacheDownload::CacheDownload (QUrl url, MetaEntryPtr entry ) void CacheDownload::start() { - if(!m_entry->stale) + if (!m_entry->stale) { emit succeeded(index_within_job); return; } m_output_file.setFileName(m_target_path); // if there already is a file and md5 checking is in effect and it can be opened - if(!ensureFilePathExists(m_target_path)) + if (!ensureFilePathExists(m_target_path)) { emit failed(index_within_job); return; } qDebug() << "Downloading " << m_url.toString(); - QNetworkRequest request ( m_url ); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); - + QNetworkRequest request(m_url); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + auto worker = MMC->qnam(); - QNetworkReply * rep = worker->get ( request ); - - m_reply = QSharedPointer ( rep, &QObject::deleteLater ); - connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); - connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) ); - connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) ); - connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) ); + QNetworkReply *rep = worker->get(request); + + m_reply = QSharedPointer(rep, &QObject::deleteLater); + connect(rep, SIGNAL(downloadProgress(qint64, qint64)), + SLOT(downloadProgress(qint64, qint64))); + connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); + connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), + SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); } -void CacheDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +void CacheDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - emit progress (index_within_job, bytesReceived, bytesTotal ); + emit progress(index_within_job, bytesReceived, bytesTotal); } -void CacheDownload::downloadError ( QNetworkReply::NetworkError error ) +void CacheDownload::downloadError(QNetworkReply::NetworkError error) { // error happened during download. // TODO: log the reason why @@ -59,12 +61,12 @@ void CacheDownload::downloadError ( QNetworkReply::NetworkError error ) void CacheDownload::downloadFinished() { // if the download succeeded - if ( m_status != Job_Failed ) + if (m_status != Job_Failed) { - + // nothing went wrong... m_status = Job_Finished; - if(m_opened_for_saving) + if (m_opened_for_saving) { // save the data to the downloadable if we aren't saving to file m_output_file.close(); @@ -72,20 +74,23 @@ void CacheDownload::downloadFinished() } else { - if ( m_output_file.open ( QIODevice::ReadOnly ) ) + if (m_output_file.open(QIODevice::ReadOnly)) { - m_entry->md5sum = QCryptographicHash::hash ( m_output_file.readAll(), QCryptographicHash::Md5 ).toHex().constData(); + m_entry->md5sum = + QCryptographicHash::hash(m_output_file.readAll(), QCryptographicHash::Md5) + .toHex() + .constData(); m_output_file.close(); } } QFileInfo output_file_info(m_target_path); - - + m_entry->etag = m_reply->rawHeader("ETag").constData(); - m_entry->last_changed_timestamp = output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); + m_entry->last_changed_timestamp = + output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); m_entry->stale = false; MMC->metacache()->updateEntry(m_entry); - + m_reply.clear(); emit succeeded(index_within_job); return; @@ -103,9 +108,9 @@ void CacheDownload::downloadFinished() void CacheDownload::downloadReadyRead() { - if(!m_opened_for_saving) + if (!m_opened_for_saving) { - if ( !m_output_file.open ( QIODevice::WriteOnly ) ) + if (!m_output_file.open(QIODevice::WriteOnly)) { /* * Can't open the file... the job failed @@ -118,5 +123,5 @@ void CacheDownload::downloadReadyRead() } QByteArray ba = m_reply->readAll(); md5sum.addData(ba); - m_output_file.write ( ba ); + m_output_file.write(ba); } diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index fac6bec3..daf6c43f 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -24,25 +24,28 @@ public: // supply path to the cache index file HttpMetaCache(QString path); ~HttpMetaCache(); - + // get the entry solely from the cache // you probably don't want this, unless you have some specific caching needs. MetaEntryPtr getEntry(QString base, QString resource_path); - + // get the entry from cache and verify that it isn't stale (within reason) - MetaEntryPtr resolveEntry(QString base, QString resource_path, QString expected_etag = QString()); - + MetaEntryPtr resolveEntry(QString base, QString resource_path, + QString expected_etag = QString()); + // add a previously resolved stale entry bool updateEntry(MetaEntryPtr stale_entry); - + void addBase(QString base, QString base_root); - + // (re)start a timer that calls SaveNow later. void SaveEventually(); void Load(); - QString getBasePath ( QString base ); -public slots: + QString getBasePath(QString base); +public +slots: void SaveNow(); + private: // create a new stale entry, given the parameters MetaEntryPtr staleEntry(QString base, QString resource_path); -- cgit From 984c36e571aae45cdd55da2fb689538198aadd3c Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 23 Sep 2013 00:23:50 +0200 Subject: Implement basic yggdrasil auth. No fancy login token saving involved. --- CMakeLists.txt | 5 +- MultiMC.cpp | 158 +++++++++++++------------- MultiMC.h | 1 + gui/mainwindow.cpp | 8 +- gui/mainwindow.h | 2 +- logic/BaseInstance.h | 3 +- logic/InstanceLauncher.cpp | 4 +- logic/LegacyInstance.cpp | 6 +- logic/LegacyInstance.h | 2 +- logic/MinecraftProcess.cpp | 83 +++++++------- logic/OneSixInstance.cpp | 20 ++-- logic/OneSixInstance.h | 4 +- logic/net/LoginTask.cpp | 269 +++++++++++++++++++++++++++++++++++++++++++++ logic/net/LoginTask.h | 62 +++++++++++ logic/tasks/LoginTask.cpp | 111 ------------------- logic/tasks/LoginTask.h | 58 ---------- 16 files changed, 482 insertions(+), 314 deletions(-) create mode 100644 logic/net/LoginTask.cpp create mode 100644 logic/net/LoginTask.h delete mode 100644 logic/tasks/LoginTask.cpp delete mode 100644 logic/tasks/LoginTask.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index aa7a91f6..04886184 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -226,6 +226,8 @@ logic/net/DownloadJob.h logic/net/DownloadJob.cpp logic/net/HttpMetaCache.h logic/net/HttpMetaCache.cpp +logic/net/LoginTask.h +logic/net/LoginTask.cpp # legacy instances logic/LegacyInstance.h @@ -281,8 +283,7 @@ logic/EnabledItemFilter.cpp logic/tasks/ProgressProvider.h logic/tasks/Task.h logic/tasks/Task.cpp -logic/tasks/LoginTask.h -logic/tasks/LoginTask.cpp + ) diff --git a/MultiMC.cpp b/MultiMC.cpp index decc22bf..ee9a9bf8 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -16,7 +16,6 @@ #include "logic/InstanceLauncher.h" #include "logic/net/HttpMetaCache.h" - #include "pathutils.h" #include "cmdutils.h" #include @@ -25,23 +24,22 @@ #include "config.h" using namespace Util::Commandline; -MultiMC::MultiMC ( int& argc, char** argv ) - :QApplication ( argc, argv ) +MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) { setOrganizationName("Forkk"); setApplicationName("MultiMC 5"); - + initTranslations(); - + // Print app header std::cout << "MultiMC 5" << std::endl; std::cout << "(c) 2013 MultiMC Contributors" << std::endl << std::endl; - + // Commandline parsing QHash args; { Parser parser(FlagStyle::GNU, ArgumentStyle::SpaceAndEquals); - + // --help parser.addSwitch("help"); parser.addShortOpt("help", 'h'); @@ -53,33 +51,37 @@ MultiMC::MultiMC ( int& argc, char** argv ) // --dir parser.addOption("dir", applicationDirPath()); parser.addShortOpt("dir", 'd'); - parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of the binary location (use '.' for current)"); + parser.addDocumentation("dir", "use the supplied directory as MultiMC root instead of " + "the binary location (use '.' for current)"); // --update parser.addOption("update"); parser.addShortOpt("update", 'u'); - parser.addDocumentation("update", "replaces the given file with the running executable", ""); + parser.addDocumentation("update", "replaces the given file with the running executable", + ""); // --quietupdate parser.addSwitch("quietupdate"); parser.addShortOpt("quietupdate", 'U'); - parser.addDocumentation("quietupdate", "doesn't restart MultiMC after installing updates"); + parser.addDocumentation("quietupdate", + "doesn't restart MultiMC after installing updates"); // --launch parser.addOption("launch"); parser.addShortOpt("launch", 'l'); parser.addDocumentation("launch", "tries to launch the given instance", ""); - + // parse the arguments try { args = parser.parse(arguments()); } - catch(ParsingError e) + catch (ParsingError e) { std::cerr << "CommandLineError: " << e.what() << std::endl; - std::cerr << "Try '%1 -h' to get help on MultiMC's command line parameters." << std::endl; + std::cerr << "Try '%1 -h' to get help on MultiMC's command line parameters." + << std::endl; m_status = MultiMC::Failed; return; } - + // display help and exit if (args["help"].toBool()) { @@ -87,27 +89,29 @@ MultiMC::MultiMC ( int& argc, char** argv ) m_status = MultiMC::Succeeded; return; } - + // display version and exit if (args["version"].toBool()) { std::cout << "Version " << VERSION_STR << std::endl; std::cout << "Git " << GIT_COMMIT << std::endl; - std::cout << "Tag: " << JENKINS_BUILD_TAG << " " << (ARCH==x64?"x86_64":"x86") << std::endl; + std::cout << "Tag: " << JENKINS_BUILD_TAG << " " << (ARCH == x64 ? "x86_64" : "x86") + << std::endl; m_status = MultiMC::Succeeded; return; } - + // update // Note: cwd is always the current executable path! if (!args["update"].isNull()) { - std::cout << "Performing MultiMC update: " << qPrintable(args["update"].toString()) << std::endl; + std::cout << "Performing MultiMC update: " << qPrintable(args["update"].toString()) + << std::endl; QString cwd = QDir::currentPath(); QDir::setCurrent(applicationDirPath()); QFile file(applicationFilePath()); file.copy(args["update"].toString()); - if(args["quietupdate"].toBool()) + if (args["quietupdate"].toBool()) { m_status = MultiMC::Succeeded; return; @@ -115,31 +119,31 @@ MultiMC::MultiMC ( int& argc, char** argv ) QDir::setCurrent(cwd); } } - + // change directory QDir::setCurrent(args["dir"].toString()); - + // load settings initGlobalSettings(); - + // and instances - m_instances.reset(new InstanceList(m_settings->get("InstanceDir").toString(),this)); + m_instances.reset(new InstanceList(m_settings->get("InstanceDir").toString(), this)); std::cout << "Loading Instances..." << std::endl; m_instances->loadList(); - + // init the http meta cache initHttpMetaCache(); - + // create the global network manager m_qnam.reset(new QNetworkAccessManager(this)); - + // Register meta types. qRegisterMetaType("LoginResponse"); - + // launch instance, if that's what should be done if (!args["launch"].isNull()) { - if(InstanceLauncher(args["launch"].toString()).launch()) + if (InstanceLauncher(args["launch"].toString()).launch()) m_status = MultiMC::Succeeded; else m_status = MultiMC::Failed; @@ -150,11 +154,11 @@ MultiMC::MultiMC ( int& argc, char** argv ) MultiMC::~MultiMC() { - if(m_mmc_translator) + if (m_mmc_translator) { removeTranslator(m_mmc_translator.data()); } - if(m_qt_translator) + if (m_qt_translator) { removeTranslator(m_qt_translator.data()); } @@ -163,13 +167,12 @@ MultiMC::~MultiMC() void MultiMC::initTranslations() { m_qt_translator.reset(new QTranslator()); - if(m_qt_translator->load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + if (m_qt_translator->load("qt_" + QLocale::system().name(), + QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { - std::cout - << "Loading Qt Language File for " - << QLocale::system().name().toLocal8Bit().constData() - << "..."; - if(!installTranslator(m_qt_translator.data())) + std::cout << "Loading Qt Language File for " + << QLocale::system().name().toLocal8Bit().constData() << "..."; + if (!installTranslator(m_qt_translator.data())) { std::cout << " failed."; m_qt_translator.reset(); @@ -182,13 +185,12 @@ void MultiMC::initTranslations() } m_mmc_translator.reset(new QTranslator()); - if(m_mmc_translator->load("mmc_" + QLocale::system().name(), QDir("translations").absolutePath())) + if (m_mmc_translator->load("mmc_" + QLocale::system().name(), + QDir("translations").absolutePath())) { - std::cout - << "Loading MMC Language File for " - << QLocale::system().name().toLocal8Bit().constData() - << "..."; - if(!installTranslator(m_mmc_translator.data())) + std::cout << "Loading MMC Language File for " + << QLocale::system().name().toLocal8Bit().constData() << "..."; + if (!installTranslator(m_mmc_translator.data())) { std::cout << " failed."; m_mmc_translator.reset(); @@ -201,58 +203,66 @@ void MultiMC::initTranslations() } } - void MultiMC::initGlobalSettings() { m_settings.reset(new INISettingsObject("multimc.cfg", this)); - // Updates + // Updates m_settings->registerSetting(new Setting("UseDevBuilds", false)); m_settings->registerSetting(new Setting("AutoUpdate", true)); - + // Folders m_settings->registerSetting(new Setting("InstanceDir", "instances")); m_settings->registerSetting(new Setting("CentralModsDir", "mods")); m_settings->registerSetting(new Setting("LWJGLDir", "lwjgl")); - + // Console m_settings->registerSetting(new Setting("ShowConsole", true)); m_settings->registerSetting(new Setting("AutoCloseConsole", true)); - + // Toolbar settings m_settings->registerSetting(new Setting("InstanceToolbarVisible", true)); m_settings->registerSetting(new Setting("InstanceToolbarPosition", QPoint())); - + // Console Colors -// m_settings->registerSetting(new Setting("SysMessageColor", QColor(Qt::blue))); -// m_settings->registerSetting(new Setting("StdOutColor", QColor(Qt::black))); -// m_settings->registerSetting(new Setting("StdErrColor", QColor(Qt::red))); - + // m_settings->registerSetting(new Setting("SysMessageColor", QColor(Qt::blue))); + // m_settings->registerSetting(new Setting("StdOutColor", QColor(Qt::black))); + // m_settings->registerSetting(new Setting("StdErrColor", QColor(Qt::red))); + // Window Size m_settings->registerSetting(new Setting("LaunchMaximized", false)); m_settings->registerSetting(new Setting("MinecraftWinWidth", 854)); m_settings->registerSetting(new Setting("MinecraftWinHeight", 480)); - + // Auto login m_settings->registerSetting(new Setting("AutoLogin", false)); - + // Memory m_settings->registerSetting(new Setting("MinMemAlloc", 512)); m_settings->registerSetting(new Setting("MaxMemAlloc", 1024)); m_settings->registerSetting(new Setting("PermGen", 64)); - + // Java Settings m_settings->registerSetting(new Setting("JavaPath", "java")); m_settings->registerSetting(new Setting("JvmArgs", "")); - + // Custom Commands m_settings->registerSetting(new Setting("PreLaunchCommand", "")); m_settings->registerSetting(new Setting("PostExitCommand", "")); - + // The cat m_settings->registerSetting(new Setting("TheCat", false)); - + // Shall the main window hide on instance launch m_settings->registerSetting(new Setting("NoHide", false)); + + // Persistent value for the client ID + m_settings->registerSetting(new Setting("YggdrasilClientToken", "")); + QString currentYggID = m_settings->get("YggdrasilClientToken").toString(); + if (currentYggID.isEmpty()) + { + QUuid uuid = QUuid::createUuid(); + m_settings->set("YggdrasilClientToken", uuid.toString()); + } } void MultiMC::initHttpMetaCache() @@ -265,10 +275,9 @@ void MultiMC::initHttpMetaCache() m_metacache->Load(); } - QSharedPointer MultiMC::icons() { - if ( !m_icons ) + if (!m_icons) { m_icons.reset(new IconList); } @@ -277,7 +286,7 @@ QSharedPointer MultiMC::icons() QSharedPointer MultiMC::lwjgllist() { - if ( !m_lwjgllist ) + if (!m_lwjgllist) { m_lwjgllist.reset(new LWJGLVersionList()); } @@ -286,7 +295,7 @@ QSharedPointer MultiMC::lwjgllist() QSharedPointer MultiMC::forgelist() { - if ( !m_forgelist ) + if (!m_forgelist) { m_forgelist.reset(new ForgeVersionList()); } @@ -295,36 +304,31 @@ QSharedPointer MultiMC::forgelist() QSharedPointer MultiMC::minecraftlist() { - if ( !m_minecraftlist ) + if (!m_minecraftlist) { m_minecraftlist.reset(new MinecraftVersionList()); } return m_minecraftlist; } - int main(int argc, char *argv[]) { // initialize Qt MultiMC app(argc, argv); - + // show main window MainWindow mainWin; mainWin.show(); - - - - switch(app.status()) + + switch (app.status()) { - case MultiMC::Initialized: - return app.exec(); - case MultiMC::Failed: - return 1; - case MultiMC::Succeeded: - return 0; + case MultiMC::Initialized: + return app.exec(); + case MultiMC::Failed: + return 1; + case MultiMC::Succeeded: + return 0; } } #include "MultiMC.moc" - - diff --git a/MultiMC.h b/MultiMC.h index 1c1298e2..c634dd33 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -71,6 +71,7 @@ public: QSharedPointer forgelist(); QSharedPointer minecraftlist(); + private: void initGlobalSettings(); diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index d3b167fa..d7b77c8b 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -54,7 +54,7 @@ #include "logic/lists/LwjglVersionList.h" #include "logic/lists/IconList.h" -#include "logic/tasks/LoginTask.h" +#include "logic/net/LoginTask.h" #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logic/MinecraftProcess.h" @@ -491,7 +491,7 @@ void MainWindow::doLogin(const QString& errorMsg) QString user = loginDlg->getUsername(); if (user.length() == 0) user = QString("Offline"); - m_activeLogin = {user, QString("Offline"), qint64(-1)}; + m_activeLogin = {user, QString("Offline"), QString(), QString()}; m_activeInst = m_selectedInstance; launchInstance(m_activeInst, m_activeLogin); } @@ -533,7 +533,7 @@ void MainWindow::launchInstance(BaseInstance *instance, LoginResponse response) { Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); - proc = instance->prepareForLaunch(response.username, response.sessionID); + proc = instance->prepareForLaunch(response); if(!proc) return; @@ -552,7 +552,7 @@ void MainWindow::launchInstance(BaseInstance *instance, LoginResponse response) connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, SLOT(write(QString, MessageLevel::Enum))); connect(proc, SIGNAL(ended()), this, SLOT(instanceEnded())); - proc->setLogin(m_activeLogin.username, m_activeLogin.sessionID); + proc->setLogin(response.username, response.session_id); proc->launch(); } diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 2b0e1d34..cc9b0b7b 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -19,7 +19,7 @@ #include #include "logic/lists/InstanceList.h" -#include "logic/tasks/LoginTask.h" +#include "logic/net/LoginTask.h" #include "logic/BaseInstance.h" class LabeledToolButton; diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index 374d1437..0056327a 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -22,6 +22,7 @@ #include "inifile.h" #include "lists/BaseVersionList.h" +#include "net/LoginTask.h" class QDialog; class BaseUpdate; @@ -147,7 +148,7 @@ public: virtual BaseUpdate* doUpdate() = 0; /// returns a valid minecraft process, ready for launch - virtual MinecraftProcess* prepareForLaunch(QString user, QString session) = 0; + virtual MinecraftProcess* prepareForLaunch(LoginResponse response) = 0; /// do any necessary cleanups after the instance finishes. also runs before 'prepareForLaunch' virtual void cleanupAfterRun() = 0; diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp index f2f792c9..93b87f23 100644 --- a/logic/InstanceLauncher.cpp +++ b/logic/InstanceLauncher.cpp @@ -5,7 +5,7 @@ #include "gui/logindialog.h" #include "gui/ProgressDialog.h" #include "gui/consolewindow.h" -#include "logic/tasks/LoginTask.h" +#include "logic/net/LoginTask.h" #include "logic/MinecraftProcess.h" #include "lists/InstanceList.h" @@ -25,7 +25,7 @@ void InstanceLauncher::onLoginComplete() LoginTask * task = ( LoginTask * ) QObject::sender(); auto result = task->getResult(); auto instance = MMC->instances()->getInstanceById(instId); - proc = instance->prepareForLaunch ( result.username, result.sessionID ); + proc = instance->prepareForLaunch ( result ); if ( !proc ) { //FIXME: report error diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 0672d2c8..4f367980 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -29,7 +29,7 @@ BaseUpdate* LegacyInstance::doUpdate() return new LegacyUpdate(this, this); } -MinecraftProcess* LegacyInstance::prepareForLaunch(QString user, QString session) +MinecraftProcess* LegacyInstance::prepareForLaunch(LoginResponse response) { MinecraftProcess * proc = new MinecraftProcess(this); @@ -73,8 +73,8 @@ MinecraftProcess* LegacyInstance::prepareForLaunch(QString user, QString session args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt()); args << QString("-XX:PermSize=%1m").arg(settings().get("PermGen").toInt()); args << "-jar" << LAUNCHER_FILE; - args << user; - args << session; + args << response.player_name; + args << response.session_id; args << windowTitle; args << windowSize; args << lwjgl; diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index b36026fc..2eab9035 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -57,7 +57,7 @@ public: virtual void setShouldUpdate(bool val); virtual BaseUpdate* doUpdate(); - virtual MinecraftProcess* prepareForLaunch( QString user, QString session ); + virtual MinecraftProcess* prepareForLaunch(LoginResponse response); virtual void cleanupAfterRun(); virtual QDialog * createModEditDialog ( QWidget* parent ); diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index 299f00be..06b7a1f1 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -32,46 +32,45 @@ #define IBUS "@im=ibus" // constructor -MinecraftProcess::MinecraftProcess( BaseInstance* inst ) : - m_instance(inst) +MinecraftProcess::MinecraftProcess(BaseInstance *inst) : m_instance(inst) { - connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(finish(int, QProcess::ExitStatus))); - + connect(this, SIGNAL(finished(int, QProcess::ExitStatus)), + SLOT(finish(int, QProcess::ExitStatus))); + // prepare the process environment QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - + #ifdef LINUX // Strip IBus if (env.value("XMODIFIERS").contains(IBUS)) env.insert("XMODIFIERS", env.value("XMODIFIERS").replace(IBUS, "")); #endif - + // export some infos env.insert("INST_NAME", inst->name()); env.insert("INST_ID", inst->id()); env.insert("INST_DIR", QDir(inst->instanceRoot()).absolutePath()); - + this->setProcessEnvironment(env); m_prepostlaunchprocess.setProcessEnvironment(env); - + // std channels connect(this, SIGNAL(readyReadStandardError()), SLOT(on_stdErr())); connect(this, SIGNAL(readyReadStandardOutput()), SLOT(on_stdOut())); } -void MinecraftProcess::setMinecraftArguments ( QStringList args ) +void MinecraftProcess::setMinecraftArguments(QStringList args) { m_args = args; } -void MinecraftProcess::setMinecraftWorkdir ( QString path ) +void MinecraftProcess::setMinecraftWorkdir(QString path) { QDir mcDir(path); this->setWorkingDirectory(mcDir.absolutePath()); m_prepostlaunchprocess.setWorkingDirectory(mcDir.absolutePath()); } - // console window void MinecraftProcess::on_stdErr() { @@ -80,18 +79,17 @@ void MinecraftProcess::on_stdErr() m_err_leftover.clear(); QStringList lines = str.split("\n"); bool complete = str.endsWith("\n"); - - for(int i = 0; i < lines.size() - 1; i++) + + for (int i = 0; i < lines.size() - 1; i++) { - QString & line = lines[i]; - emit log(line.replace(username, "").replace(sessionID, "").toLocal8Bit(), getLevel(line, MessageLevel::Error)); + QString &line = lines[i]; + emit log(line /*.replace(username, "").replace(sessionID, "")*/, + getLevel(line, MessageLevel::Error)); } - if(!complete) + if (!complete) m_err_leftover = lines.last(); } - - void MinecraftProcess::on_stdOut() { QByteArray data = readAllStandardOutput(); @@ -99,13 +97,14 @@ void MinecraftProcess::on_stdOut() m_out_leftover.clear(); QStringList lines = str.split("\n"); bool complete = str.endsWith("\n"); - - for(int i = 0; i < lines.size() - 1; i++) + + for (int i = 0; i < lines.size() - 1; i++) { - QString & line = lines[i]; - emit log(line.replace(username, "").replace(sessionID, "").toLocal8Bit(), getLevel(line, MessageLevel::Message)); + QString &line = lines[i]; + emit log(line /*.replace(username, "").replace(sessionID, "")*/, + getLevel(line, MessageLevel::Message)); } - if(!complete) + if (!complete) m_out_leftover = lines.last(); } @@ -114,20 +113,20 @@ void MinecraftProcess::finish(int code, ExitStatus status) { if (status != NormalExit) { - //TODO: error handling + // TODO: error handling } - + // TODO: Localization - + if (!killed) //: Message displayed on instance exit emit log(tr("Minecraft exited with exitcode %1.").arg(status)); else //: Message displayed after the instance exits due to kill request emit log(tr("Minecraft was killed by user."), MessageLevel::Error); - + m_prepostlaunchprocess.processEnvironment().insert("INST_EXITCODE", QString(code)); - + // run post-exit if (!m_instance->settings().get("PostExitCommand").toString().isEmpty()) { @@ -135,7 +134,7 @@ void MinecraftProcess::finish(int code, ExitStatus status) m_prepostlaunchprocess.waitForFinished(); if (m_prepostlaunchprocess.exitStatus() != NormalExit) { - //TODO: error handling + // TODO: error handling } } m_instance->cleanupAfterRun(); @@ -156,40 +155,42 @@ void MinecraftProcess::launch() m_prepostlaunchprocess.waitForFinished(); if (m_prepostlaunchprocess.exitStatus() != NormalExit) { - //TODO: error handling + // TODO: error handling return; } } - + m_instance->setLastLaunch(); - + emit log(QString("Minecraft folder is: '%1'").arg(workingDirectory())); QString JavaPath = m_instance->settings().get("JavaPath").toString(); emit log(QString("Java path: '%1'").arg(JavaPath)); - emit log(QString("Arguments: '%1'").arg(m_args.join("' '").replace(username, "").replace(sessionID, ""))); + emit log(QString("Arguments: '%1'").arg( + m_args.join("' '") /*.replace(username, "").replace(sessionID, "")*/)); start(JavaPath, m_args); if (!waitForStarted()) { //: Error message displayed if instace can't start emit log(tr("Could not launch minecraft!")); return; - //TODO: error handling + // TODO: error handling } } MessageLevel::Enum MinecraftProcess::getLevel(const QString &line, MessageLevel::Enum level) { - - if(line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || line.contains("[FINER]") || line.contains("[FINEST]") ) + + if (line.contains("[INFO]") || line.contains("[CONFIG]") || line.contains("[FINE]") || + line.contains("[FINER]") || line.contains("[FINEST]")) level = MessageLevel::Message; - if(line.contains("[SEVERE]") || line.contains("[STDERR]")) + if (line.contains("[SEVERE]") || line.contains("[STDERR]")) level = MessageLevel::Error; - if(line.contains("[WARNING]")) + if (line.contains("[WARNING]")) level = MessageLevel::Warning; - if(line.contains("Exception in thread") || line.contains(" at ")) + if (line.contains("Exception in thread") || line.contains(" at ")) level = MessageLevel::Fatal; - if(line.contains("[DEBUG]")) + if (line.contains("[DEBUG]")) level = MessageLevel::Debug; return level; - } \ No newline at end of file diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index e22a8890..6e39b5b5 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -49,21 +49,19 @@ QString replaceTokensIn(QString text, QMap with) return result; } -QStringList OneSixInstance::processMinecraftArgs(QString user, QString session) +QStringList OneSixInstance::processMinecraftArgs(LoginResponse response) { I_D(OneSixInstance); auto version = d->version; QString args_pattern = version->minecraftArguments; QMap token_mapping; - token_mapping["auth_username"] = user; - token_mapping["auth_session"] = session; - // FIXME: user and player name are DIFFERENT! - token_mapping["auth_player_name"] = user; - // FIXME: WTF is this. I just plugged in a random UUID here. - token_mapping["auth_uuid"] = "7d4bacf0-fd62-11e2-b778-0800200c9a66"; // obviously fake. - - // this is for offline: + token_mapping["auth_username"] = response.username; + token_mapping["auth_session"] = response.session_id; + token_mapping["auth_player_name"] = response.player_name; + token_mapping["auth_uuid"] = response.player_id; + + // this is for offline?: /* map["auth_player_name"] = "Player"; map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; @@ -85,7 +83,7 @@ QStringList OneSixInstance::processMinecraftArgs(QString user, QString session) return parts; } -MinecraftProcess *OneSixInstance::prepareForLaunch(QString user, QString session) +MinecraftProcess *OneSixInstance::prepareForLaunch(LoginResponse response) { I_D(OneSixInstance); cleanupAfterRun(); @@ -142,7 +140,7 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(QString user, QString session args << classPath; } args << version->mainClass; - args.append(processMinecraftArgs(user, session)); + args.append(processMinecraftArgs(response)); // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 0139b645..33091188 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -23,7 +23,7 @@ public: virtual QString instanceConfigFolder() const; virtual BaseUpdate* doUpdate(); - virtual MinecraftProcess* prepareForLaunch ( QString user, QString session ); + virtual MinecraftProcess* prepareForLaunch ( LoginResponse response ); virtual void cleanupAfterRun(); virtual QString intendedVersionId() const; @@ -54,5 +54,5 @@ public: virtual bool menuActionEnabled ( QString action_name ) const; virtual QString getStatusbarDescription(); private: - QStringList processMinecraftArgs( QString user, QString session ); + QStringList processMinecraftArgs( LoginResponse response ); }; \ No newline at end of file diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp new file mode 100644 index 00000000..2a45400e --- /dev/null +++ b/logic/net/LoginTask.cpp @@ -0,0 +1,269 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "LoginTask.h" +#include "MultiMC.h" +#include + +#include + +#include +#include + +#include +#include +#include +#include + +LoginTask::LoginTask(const UserInfo &uInfo, QObject *parent) : Task(parent), uInfo(uInfo) +{ +} + +void LoginTask::executeTask() +{ + yggdrasilLogin(); +} + +void LoginTask::legacyLogin() +{ + setStatus(tr("Logging in...")); + auto worker = MMC->qnam(); + connect(worker.data(), SIGNAL(finished(QNetworkReply *)), this, + SLOT(processLegacyReply(QNetworkReply *))); + + QUrl loginURL("https://login.minecraft.net/"); + QNetworkRequest netRequest(loginURL); + netRequest.setHeader(QNetworkRequest::ContentTypeHeader, + "application/x-www-form-urlencoded"); + + QUrlQuery params; + params.addQueryItem("user", uInfo.username); + params.addQueryItem("password", uInfo.password); + params.addQueryItem("version", "13"); + + netReply = worker->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8()); +} + +void LoginTask::processLegacyReply(QNetworkReply *reply) +{ + if (netReply != reply) + return; + // Check for errors. + switch (reply->error()) + { + case QNetworkReply::NoError: + { + // Check the response code. + int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (responseCode == 200) + { + parseLegacyReply(reply->readAll()); + } + else if (responseCode == 503) + { + emitFailed(tr("The login servers are currently unavailable. Check " + "http://help.mojang.com/ for more info.")); + } + else + { + emitFailed(tr("Login failed: Unknown HTTP error %1 occurred.") + .arg(QString::number(responseCode))); + } + break; + } + + case QNetworkReply::OperationCanceledError: + emitFailed(tr("Login canceled.")); + break; + + default: + emitFailed(tr("Login failed: %1").arg(reply->errorString())); + break; + } +} + +void LoginTask::parseLegacyReply(QByteArray data) +{ + QString responseStr = QString::fromUtf8(data); + + QStringList strings = responseStr.split(":"); + if (strings.count() >= 4) + { + // strings[1] is the download ticket. It isn't used anymore. + QString username = strings[2]; + QString sessionID = strings[3]; + /* + struct LoginResponse + { + QString username; + QString session_id; + QString player_name; + QString player_id; + QString client_id; + }; + */ + result = {username, sessionID, username, QString()}; + emitSucceeded(); + } + else + { + if (responseStr.toLower() == "bad login") + emitFailed(tr("Invalid username or password.")); + else if (responseStr.toLower() == "old version") + emitFailed(tr("Launcher outdated, please update.")); + else + emitFailed(tr("Login failed: %1").arg(responseStr)); + } +} + + +void LoginTask::yggdrasilLogin() +{ + setStatus(tr("Logging in...")); + auto worker = MMC->qnam(); + connect(worker.data(), SIGNAL(finished(QNetworkReply *)), this, + SLOT(processYggdrasilReply(QNetworkReply *))); + + /* + { + // agent def. version might be incremented at some point + "agent":{"name":"Minecraft","version":1}, + "username": "mojang account name", + "password": "mojang account password", + // client token is optional. but we supply one anyway + "clientToken": "client identifier" + } + */ + + QUrl loginURL("https://authserver.mojang.com/authenticate"); + QNetworkRequest netRequest(loginURL); + netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + auto settings = MMC->settings(); + QString clientToken = settings->get("YggdrasilClientToken").toString(); + // escape the {} + clientToken.remove('{'); + clientToken.remove('}'); + // create the request + QString requestConstent; + requestConstent += "{"; + requestConstent += " \"agent\":{\"name\":\"Minecraft\",\"version\":1},\n"; + requestConstent += " \"username\":\"" + uInfo.username + "\",\n"; + requestConstent += " \"password\":\"" + uInfo.password + "\",\n"; + requestConstent += " \"clientToken\":\"" + clientToken + "\"\n"; + requestConstent += "}"; + netReply = worker->post(netRequest, requestConstent.toUtf8()); +} + +void LoginTask::processYggdrasilReply(QNetworkReply *reply) +{ + if (netReply != reply) + return; + // Check for errors. + switch (reply->error()) + { + case QNetworkReply::NoError: + { + // Check the response code. + int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (responseCode == 200) + { + parseYggdrasilReply(reply->readAll()); + } + else if (responseCode == 503) + { + emitFailed(tr("The login servers are currently unavailable. Check " + "http://help.mojang.com/ for more info.")); + } + else + { + emitFailed(tr("Login failed: Unknown HTTP error %1 occurred.") + .arg(QString::number(responseCode))); + } + break; + } + + case QNetworkReply::OperationCanceledError: + emitFailed(tr("Login canceled.")); + break; + + default: + emitFailed(tr("Login failed: %1").arg(reply->errorString())); + break; + } +} + +/* +{ + "accessToken": "random access token", // hexadecimal + "clientToken": "client identifier", // identical to the one received + "availableProfiles": [ // only present if the agent field was received + { + "id": "profile identifier", // hexadecimal + "name": "player name" + } + ], + "selectedProfile": { // only present if the agent field was received + "id": "profile identifier", + "name": "player name" + } +} +*/ +void LoginTask::parseYggdrasilReply(QByteArray data) +{ + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + if (jsonError.error != QJsonParseError::NoError) + { + emitFailed(tr("Login failed: %1").arg(jsonError.errorString())); + return; + } + + if (!jsonDoc.isObject()) + { + emitFailed(tr("Login failed: BAD FORMAT #1")); + return; + } + + QJsonObject root = jsonDoc.object(); + + QString accessToken = root.value("accessToken").toString(); + QString clientToken = root.value("clientToken").toString(); + QString playerID; + QString playerName; + auto selectedProfile = root.value("selectedProfile"); + if(selectedProfile.isObject()) + { + auto selectedProfileO = selectedProfile.toObject(); + playerID = selectedProfileO.value("id").toString(); + playerName = selectedProfileO.value("name").toString(); + } + QString sessionID = "token:" + accessToken + ":" + playerID; + /* + struct LoginResponse + { + QString username; + QString session_id; + QString player_name; + QString player_id; + QString client_id; + }; + */ + + result = {uInfo.username, sessionID, playerName, playerID}; + emitSucceeded(); +} diff --git a/logic/net/LoginTask.h b/logic/net/LoginTask.h new file mode 100644 index 00000000..ba87142d --- /dev/null +++ b/logic/net/LoginTask.h @@ -0,0 +1,62 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "logic/tasks/Task.h" +#include + +struct UserInfo +{ + QString username; + QString password; +}; + +struct LoginResponse +{ + QString username; + QString session_id; + QString player_name; + QString player_id; +}; + +class QNetworkReply; + +class LoginTask : public Task +{ + Q_OBJECT +public: + explicit LoginTask(const UserInfo &uInfo, QObject *parent = 0); + LoginResponse getResult() + { + return result; + } + +protected slots: + void legacyLogin(); + void processLegacyReply(QNetworkReply *reply); + void parseLegacyReply(QByteArray data); + + void yggdrasilLogin(); + void processYggdrasilReply(QNetworkReply *reply); + void parseYggdrasilReply(QByteArray data); + +protected: + void executeTask(); + + LoginResponse result; + QNetworkReply *netReply; + UserInfo uInfo; +}; diff --git a/logic/tasks/LoginTask.cpp b/logic/tasks/LoginTask.cpp deleted file mode 100644 index 222af618..00000000 --- a/logic/tasks/LoginTask.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "LoginTask.h" -#include "MultiMC.h" - -#include - -#include -#include - -#include -#include - -LoginTask::LoginTask( const UserInfo& uInfo, QObject* parent ) : Task(parent), uInfo(uInfo){} - -void LoginTask::executeTask() -{ - setStatus(tr("Logging in...")); - auto worker = MMC->qnam(); - connect(worker.data(), SIGNAL(finished(QNetworkReply*)), this, SLOT(processNetReply(QNetworkReply*))); - - QUrl loginURL("https://login.minecraft.net/"); - QNetworkRequest netRequest(loginURL); - netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - - QUrlQuery params; - params.addQueryItem("user", uInfo.username); - params.addQueryItem("password", uInfo.password); - params.addQueryItem("version", "13"); - - netReply = worker->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8()); -} - -void LoginTask::processNetReply(QNetworkReply *reply) -{ - if(netReply != reply) - return; - // Check for errors. - switch (reply->error()) - { - case QNetworkReply::NoError: - { - // Check the response code. - int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) - { - QString responseStr(reply->readAll()); - - QStringList strings = responseStr.split(":"); - if (strings.count() >= 4) - { - bool parseSuccess; - qint64 latestVersion = strings[0].toLongLong(&parseSuccess); - if (parseSuccess) - { - // strings[1] is the download ticket. It isn't used anymore. - QString username = strings[2]; - QString sessionID = strings[3]; - - result = {username, sessionID, latestVersion}; - emitSucceeded(); - } - else - { - emitFailed(tr("Failed to parse Minecraft version string.")); - } - } - else - { - if (responseStr.toLower() == "bad login") - emitFailed(tr("Invalid username or password.")); - else if (responseStr.toLower() == "old version") - emitFailed(tr("Launcher outdated, please update.")); - else - emitFailed(tr("Login failed: %1").arg(responseStr)); - } - } - else if (responseCode == 503) - { - emitFailed(tr("The login servers are currently unavailable. Check http://help.mojang.com/ for more info.")); - } - else - { - emitFailed(tr("Login failed: Unknown HTTP error %1 occurred.").arg(QString::number(responseCode))); - } - break; - } - - case QNetworkReply::OperationCanceledError: - emitFailed(tr("Login canceled.")); - break; - - default: - emitFailed(tr("Login failed: %1").arg(reply->errorString())); - break; - } -} diff --git a/logic/tasks/LoginTask.h b/logic/tasks/LoginTask.h deleted file mode 100644 index bde672b8..00000000 --- a/logic/tasks/LoginTask.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef LOGINTASK_H -#define LOGINTASK_H - -#include "Task.h" -#include - -struct UserInfo -{ - QString username; - QString password; -}; - -struct LoginResponse -{ - QString username; - QString sessionID; - qint64 latestVersion; -}; - -class QNetworkReply; - -class LoginTask : public Task -{ - Q_OBJECT -public: - explicit LoginTask(const UserInfo& uInfo, QObject *parent = 0); - LoginResponse getResult() - { - return result; - }; - -protected slots: - void processNetReply(QNetworkReply* reply); - -protected: - void executeTask(); - - LoginResponse result; - QNetworkReply* netReply; - UserInfo uInfo; -}; - -#endif // LOGINTASK_H -- cgit From 2c8dc0b855c38c5204d398ad306fa9cf43be1ada Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Thu, 26 Sep 2013 02:58:09 +0200 Subject: Compression algo dependencies, still need hackery... --- CMakeLists.txt | 11 +- depends/lzma/CMakeLists.txt | 54 + depends/lzma/LICENSE.txt | 9 + depends/lzma/easylzma_test.c | 282 ++ depends/lzma/elzma.c | 557 ++++ depends/lzma/include/common.h | 118 + depends/lzma/include/compress.h | 77 + depends/lzma/include/decompress.h | 58 + depends/lzma/include/simple.h | 37 + depends/lzma/pavlov/7zCrc.c | 35 + depends/lzma/pavlov/7zCrc.h | 24 + depends/lzma/pavlov/LzFind.c | 779 +++++ depends/lzma/pavlov/LzFind.h | 107 + depends/lzma/pavlov/LzHash.h | 62 + depends/lzma/pavlov/LzmaDec.c | 1076 +++++++ depends/lzma/pavlov/LzmaDec.h | 220 ++ depends/lzma/pavlov/LzmaEnc.c | 2349 +++++++++++++++ depends/lzma/pavlov/LzmaEnc.h | 71 + depends/lzma/pavlov/LzmaLib.c | 41 + depends/lzma/pavlov/LzmaLib.h | 137 + depends/lzma/pavlov/Types.h | 87 + depends/lzma/wrapper/common_internal.c | 46 + depends/lzma/wrapper/common_internal.h | 60 + depends/lzma/wrapper/compress.c | 297 ++ depends/lzma/wrapper/decompress.c | 263 ++ depends/lzma/wrapper/lzip_header.c | 96 + depends/lzma/wrapper/lzip_header.h | 11 + depends/lzma/wrapper/lzma_header.c | 134 + depends/lzma/wrapper/lzma_header.h | 10 + depends/lzma/wrapper/simple.c | 139 + depends/pack200/CMakeLists.txt | 43 + depends/pack200/include/unpack200.h | 1 + depends/pack200/src/bands.cpp | 451 +++ depends/pack200/src/bands.h | 492 +++ depends/pack200/src/bytes.cpp | 217 ++ depends/pack200/src/bytes.h | 284 ++ depends/pack200/src/coding.cpp | 1049 +++++++ depends/pack200/src/coding.h | 270 ++ depends/pack200/src/constants.h | 442 +++ depends/pack200/src/defines.h | 136 + depends/pack200/src/main.cpp | 489 +++ depends/pack200/src/unpack.cpp | 5105 ++++++++++++++++++++++++++++++++ depends/pack200/src/unpack.h | 585 ++++ depends/pack200/src/utils.cpp | 91 + depends/pack200/src/utils.h | 54 + depends/pack200/src/zip.cpp | 610 ++++ depends/pack200/src/zip.h | 130 + logic/OneSixUpdate.cpp | 5 +- logic/lists/MinecraftVersionList.cpp | 2 +- logic/net/ByteArrayDownload.cpp | 2 +- logic/net/DownloadJob.cpp | 95 +- logic/net/DownloadJob.h | 9 +- 52 files changed, 17769 insertions(+), 40 deletions(-) create mode 100644 depends/lzma/CMakeLists.txt create mode 100644 depends/lzma/LICENSE.txt create mode 100644 depends/lzma/easylzma_test.c create mode 100644 depends/lzma/elzma.c create mode 100644 depends/lzma/include/common.h create mode 100644 depends/lzma/include/compress.h create mode 100644 depends/lzma/include/decompress.h create mode 100644 depends/lzma/include/simple.h create mode 100755 depends/lzma/pavlov/7zCrc.c create mode 100755 depends/lzma/pavlov/7zCrc.h create mode 100755 depends/lzma/pavlov/LzFind.c create mode 100755 depends/lzma/pavlov/LzFind.h create mode 100755 depends/lzma/pavlov/LzHash.h create mode 100755 depends/lzma/pavlov/LzmaDec.c create mode 100755 depends/lzma/pavlov/LzmaDec.h create mode 100755 depends/lzma/pavlov/LzmaEnc.c create mode 100755 depends/lzma/pavlov/LzmaEnc.h create mode 100755 depends/lzma/pavlov/LzmaLib.c create mode 100755 depends/lzma/pavlov/LzmaLib.h create mode 100755 depends/lzma/pavlov/Types.h create mode 100644 depends/lzma/wrapper/common_internal.c create mode 100644 depends/lzma/wrapper/common_internal.h create mode 100644 depends/lzma/wrapper/compress.c create mode 100644 depends/lzma/wrapper/decompress.c create mode 100644 depends/lzma/wrapper/lzip_header.c create mode 100644 depends/lzma/wrapper/lzip_header.h create mode 100644 depends/lzma/wrapper/lzma_header.c create mode 100644 depends/lzma/wrapper/lzma_header.h create mode 100644 depends/lzma/wrapper/simple.c create mode 100644 depends/pack200/CMakeLists.txt create mode 100644 depends/pack200/include/unpack200.h create mode 100644 depends/pack200/src/bands.cpp create mode 100644 depends/pack200/src/bands.h create mode 100644 depends/pack200/src/bytes.cpp create mode 100644 depends/pack200/src/bytes.h create mode 100644 depends/pack200/src/coding.cpp create mode 100644 depends/pack200/src/coding.h create mode 100644 depends/pack200/src/constants.h create mode 100644 depends/pack200/src/defines.h create mode 100644 depends/pack200/src/main.cpp create mode 100644 depends/pack200/src/unpack.cpp create mode 100644 depends/pack200/src/unpack.h create mode 100644 depends/pack200/src/utils.cpp create mode 100644 depends/pack200/src/utils.h create mode 100644 depends/pack200/src/zip.cpp create mode 100644 depends/pack200/src/zip.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index 04886184..f16a5620 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,10 @@ include_directories(${Qt5Widgets_INCLUDE_DIRS}) add_subdirectory(depends/quazip) include_directories(depends/quazip) +# Add lzma +add_subdirectory(depends/lzma) +include_directories(depends/lzma/include) + # Add the java launcher add_subdirectory(depends/launcher) @@ -60,6 +64,8 @@ include_directories(${LIBSETTINGS_INCLUDE_DIR}) add_subdirectory(depends/groupview) include_directories(${LIBGROUPVIEW_INCLUDE_DIR}) +#pack 200 +add_subdirectory(depends/pack200) ################################ SET UP BUILD OPTIONS ################################ @@ -345,8 +351,9 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 # Link QT5_USE_MODULES(MultiMC Widgets Network Xml) -TARGET_LINK_LIBRARIES(MultiMC quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS}) -ADD_DEPENDENCIES(MultiMC MultiMCLauncher libUtil libSettings libGroupView) +TARGET_LINK_LIBRARIES(MultiMC quazip lzma libUtil libSettings libGroupView +${MultiMC_LINK_ADDITIONAL_LIBS}) +#ADD_DEPENDENCIES(MultiMC MultiMCLauncher libUtil libSettings libGroupView) option(BUILD_KEYRING_TEST "Build the simple keyring test binary" OFF) diff --git a/depends/lzma/CMakeLists.txt b/depends/lzma/CMakeLists.txt new file mode 100644 index 00000000..4df2b762 --- /dev/null +++ b/depends/lzma/CMakeLists.txt @@ -0,0 +1,54 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +PROJECT(lzma) + +IF (WIN32) + ADD_DEFINITIONS(-DWIN32) +ENDIF (WIN32) + +SET(SRCS +# original code by Igor Pavlov +# Lzma version 4.63 +# Minified ~_~ +pavlov/7zCrc.c +pavlov/7zCrc.h +pavlov/LzFind.c +pavlov/LzFind.h +pavlov/LzHash.h +pavlov/LzmaDec.c +pavlov/LzmaDec.h +pavlov/LzmaEnc.c +pavlov/LzmaEnc.h +pavlov/LzmaLib.c +pavlov/LzmaLib.h +pavlov/Types.h + +# Public headers +include/common.h +include/compress.h +include/decompress.h +include/simple.h + +# Wrapper by Lloyd Hilaiel (lloyd@hilaiel.com) +wrapper/common_internal.c +wrapper/common_internal.h +wrapper/compress.c +wrapper/decompress.c +wrapper/simple.c +wrapper/lzip_header.c +wrapper/lzip_header.h +wrapper/lzma_header.c +wrapper/lzma_header.h +) + +# an include directory to allow easylzma implementation to find public +# headers +INCLUDE_DIRECTORIES(include) +ADD_LIBRARY(lzma STATIC ${SRCS}) + +# lzma compress/decompress tool +ADD_EXECUTABLE(elzma elzma.c) +TARGET_LINK_LIBRARIES(elzma lzma) +# a simple test... +ADD_EXECUTABLE(easylzma_test easylzma_test.c) +TARGET_LINK_LIBRARIES(easylzma_test lzma) diff --git a/depends/lzma/LICENSE.txt b/depends/lzma/LICENSE.txt new file mode 100644 index 00000000..a8a34e6a --- /dev/null +++ b/depends/lzma/LICENSE.txt @@ -0,0 +1,9 @@ +# Written in 2009 by Lloyd Hilaiel +# Butchered in 2013 by Petr Mrazek +# +# License +# +# All the cruft you find here is public domain. You don't have to credit +# anyone to use this code, but my personal request is that you mention +# Igor Pavlov for his hard, high quality work. +# diff --git a/depends/lzma/easylzma_test.c b/depends/lzma/easylzma_test.c new file mode 100644 index 00000000..69858728 --- /dev/null +++ b/depends/lzma/easylzma_test.c @@ -0,0 +1,282 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + * + * Various compiled-in tests for the easylzma library which excercise + * API correctness and handling of corrupt data. + */ + +#include "simple.h" + +#include +#include + +static const char *sampleData = + "Overview\n" + "\n" + "Easylzma is a C library and command line tools for LZMA compression and \n" + "decompression. It uses a Igor Pavlov's reference implementation and SDK\n" + "written in C.\n" + "\n" + "License\n" + "\n" + "All the cruft you find here is public domain. You don't have to credit\n" + "anyone to use this code, but my personal request is that you mention\n" + "Igor Pavlov for his hard, high quality work.\n" + "\n" + "Project Goals\n" + "\n" + "1. A tiny C wrapper and portable build system around a subset of\n" + " Igor Pavlov's public domain LZMA compression and decompression\n" + " implementation.\n" + "2. A tiny and straighforward API\n" + "3. Support for multiple different prominent LZMA file formats (see section on\n" + " file formats below)\n" + "4. easy to build and use everywhere (doze and nix alike)\n" + "5. public domain licensing through and through. (hats off to Igor)\n" + "\n" + "Current State:\n" + "\n" + "THIS IS A WORK IN PROGRESS. The code here should be considered pre-alpha,\n" + "and this should only be used by tinkerers or hackers at this point. Once\n" + "feature completion is attained this message will be updated. See the\n" + "TODO file distributed with the source for remaining work to be done.\n" + "\n" + "Platforms Supported\n" + "\n" + "0.0.2 has been successfully compiled and run basic round trip testing\n" + "on the following platforms & compilers:\n" + "\n" + " * win32 - visual studio 2005\n" + " * osx - 10.4 & 10.5 (intel)\n" + " * netbsd ppc - 4.0.1 with gcc 4.1.2\n" + " (NOTE: memory allocation errors when dict size is default)\n" + " * freebsd 6.1 - amd64 gcc 3.4.4\n" + "\n" + "Features\n" + "\n" + "XXX: write me (and the code)\n" + "\n" + "Usage\n" + "\n" + "XXX: write me (and the code)\n" + "\n" + "The Saga of LZMA File Formats, and a couple cents.\n" + "\n" + "As far as I can tell, there are at least four different ways to put LZMA\n" + "compressed data in a stream:\n" + "\n" + "1. The LZMA-Alone format, which consists of a 13 byte header including\n" + " compression properties, dictionary size, and the uncompressed size of\n" + " the file, followed by compressed data. This format has some support\n" + " in Igor Pavlov's reference implementation and is in widespread use, as\n" + " it's supported by lzmautils: http://tukaani.org/lzma/\n" + "\n" + " The canonical (afaict) implementation of this format (lzmautis) is\n" + " BSD licensed.\n" + "\n" + "2. The lzip format (http://www.nongnu.org/lzip/lzip.html) - which\n" + " includes a CRC footer and leading \"magic number\". The former\n" + " affords data integrity gaurantees, while the latter simplifies\n" + " heuristic determination of file format. This format looks to have\n" + " reasonably widespread usage, though not quite as significant as\n" + " LZMA-Alone.\n" + "\n" + " The only implementation of this format I can find (lzip) is GPL licensed.\n" + "\n" + "3. the xz format ( http://tukaani.org/xz/xz-file-format.txt ) which is\n" + " a more complex representation that includes CRC support and a magic\n" + " number. This format is to be supported by the next iteration of\n" + " XZ Utils which is currently in beta. The source may be obtained\n" + " here: git://ctrl.tukaani.org/xz.git\n" + "\n" + " This format will address some criticisms to the LZMA-Alone format and\n" + " was developed collaboratively by Lasse Collin (the current maintainer\n" + " of XZ utils) and Igor Pavlov (the author of 7zip and the refrence\n" + " implementation of LZMA).\n" + "\n" + " The xz format will employ LZMA2 which consists of extensions on top\n" + " of LZMA, in the xz utils maintainer's words:\n" + "\n" + " \"The primary compression algorithm in .xz is currently LZMA2, which\n" + " is an extension on top of the orignal LZMA to fix a few practical\n" + " issues, like adding support for flushing the encoder (equivalent\n" + " to zlib's Z_SYNC_FLUSH), which isn't possible with the original\n" + " LZMA.\"\n" + "\n" + " Again, maintainers words, regarding licensing:\n" + "\n" + " \"XZ Utils currently contains a zlib-like compression library and a \n" + " gzip-like command line tool. It's currently under LGPLv2.1+ but I will \n" + " put it into the public domain before the first stable release.\"\n" + "\n" + "4. The 7zip disk format which can contain multiple files possibly stored in\n" + " LZMA compressed format.\n" + "\n" + "Given the state of things, the goal of this project is to develop something\n" + "based on the existing formats, and quickly leverage code generated by the XZ\n" + "Utils project, or simply kill this thing if that project produces something\n" + "that's easy to embed and has a clean API at a similar level of abstraction\n" + "as easylzma.\n" + "\n" + "lloyd - sometime in oh nine.\n"; + +/* a test that we can round trip compress/decompress data using LZMA or LZIP + * formats */ +static int roundTripTest(elzma_file_format format) +{ + int rc; + unsigned char *compressed; + unsigned char *decompressed; + size_t sz; + + rc = simpleCompress(format, (unsigned char *)sampleData, strlen(sampleData), &compressed, + &sz); + + if (rc != ELZMA_E_OK) + return rc; + + /* gross assurance that compression is actually compressing */ + if (sz > strlen(sampleData)) + { + free(compressed); + return 1; + } + + rc = simpleDecompress(format, compressed, sz, &decompressed, &sz); + + free(compressed); + + if (rc != ELZMA_E_OK) + return rc; + + if (sz != strlen(sampleData) || 0 != memcmp(decompressed, sampleData, sz)) + { + free(decompressed); + return 1; + } + + return ELZMA_E_OK; +} + +/* "correct" lzip generated from the lzip program */ +/*|LZIP...3.?..????|*/ +/*|....?e2~........|*/ +static unsigned char correctLzip[] = { + 0x4c, 0x5a, 0x49, 0x50, 0x01, 0x0c, 0x00, 0x33, 0x1b, 0xec, 0x15, 0x07, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* "correct" lzip generated from lzma utils */ +static unsigned char correctLzma[] = {0x5d, 0x00, 0x00, 0x80, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x1b, 0xec, 0x14, 0x00, 0x00, 0x00}; + +/* lzip with a bad CRC */ +static unsigned char corruptCRC[] = { + 0x4c, 0x5a, 0x49, 0x50, 0x01, 0x0c, 0x00, 0x33, 0x1b, 0xec, 0x15, 0x07, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xa8, 0x65, 0x31, 0x7e, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* lzip with a bad uncompressed size */ +static unsigned char corruptSize[] = { + 0x4c, 0x5a, 0x49, 0x50, 0x01, 0x0c, 0x00, 0x33, 0x1b, 0xec, 0x15, 0x07, 0xff, 0xff, + 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0xa8, 0x65, 0x32, 0x7e, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* lzma with a bad uncompressed size */ +static unsigned char corruptSizeLzma[] = {0x5d, 0x00, 0x00, 0x80, 0x00, 0x04, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x1b, 0xec, 0x14, 0x00, 0x00, 0x00}; + +/* lzma with a bad uncompressed size */ +static unsigned char corruptSizeLzma2[] = {0x5d, 0x00, 0x00, 0x80, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x1b, 0xec, 0x14, 0x00, 0x00, 0x00}; + +/* tests */ +static struct +{ + const char *testName; /* the name of the test */ + int expectedCode; /* the expected output of the test */ + elzma_file_format format; + unsigned char *data; /* input data */ + unsigned int dataSize; +} tests[] = { + {"correct lzip", ELZMA_E_OK, ELZMA_lzip, correctLzip, sizeof(correctLzip)}, + {"lzip as lzma", ELZMA_E_DECOMPRESS_ERROR, ELZMA_lzma, correctLzip, sizeof(correctLzip)}, + {"correct lzma", ELZMA_E_OK, ELZMA_lzma, correctLzma, sizeof(correctLzma)}, + {"lzma as lzip", ELZMA_E_CORRUPT_HEADER, ELZMA_lzip, correctLzma, sizeof(correctLzma)}, + {"corrupt crc", ELZMA_E_CRC32_MISMATCH, ELZMA_lzip, corruptCRC, sizeof(corruptCRC)}, + {"bad lzip size", ELZMA_E_SIZE_MISMATCH, ELZMA_lzip, corruptSize, sizeof(corruptSize)}, + {"bad lzma size", ELZMA_E_INSUFFICIENT_INPUT, ELZMA_lzma, + corruptSizeLzma, sizeof(corruptSizeLzma)}, + {"bad lzma size 2", ELZMA_E_SIZE_MISMATCH, ELZMA_lzma, + corruptSizeLzma2, sizeof(corruptSizeLzma2)}}; + +int main(void) +{ + unsigned int i; + unsigned int testsPassed = 0; + unsigned int testsRun = 0; + + int rc = 0; + + printf("round trip lzma test: "); + fflush(stdout); + testsRun++; + if (ELZMA_E_OK != (rc = roundTripTest(ELZMA_lzma))) + { + printf("fail! (%d)\n", rc); + } + else + { + testsPassed++; + printf("ok\n"); + } + + printf("round trip lzip test: "); + fflush(stdout); + testsRun++; + if (ELZMA_E_OK != (rc = roundTripTest(ELZMA_lzip))) + { + printf("fail (%d)!\n", rc); + } + else + { + testsPassed++; + printf("ok\n"); + } + + /* now run through the tests table */ + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + { + unsigned char *decompressed = NULL; + size_t sz = 0; + + printf("%s test: ", tests[i].testName); + rc = simpleDecompress(tests[i].format, tests[i].data, tests[i].dataSize, &decompressed, + &sz); + + testsRun++; + if (rc != tests[i].expectedCode) + { + printf("fail - got %d - expected %d\n", rc, tests[i].expectedCode); + } + else + { + testsPassed++; + printf("ok\n"); + free(decompressed); + } + } + + printf("\n%d/%d tests passed\n", testsPassed, testsRun); + + return (testsPassed == testsRun) ? 0 : 1; +} diff --git a/depends/lzma/elzma.c b/depends/lzma/elzma.c new file mode 100644 index 00000000..f715a7b2 --- /dev/null +++ b/depends/lzma/elzma.c @@ -0,0 +1,557 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + * + * command line elzma tool for lzma compression + * + * At time of writing, the primary purpose of this tool is to test the + * easylzma library. + * + * TODO: + * - stdin/stdout support + * - multiple file support + * - much more + */ + +#include "include/compress.h" +#include "include/decompress.h" + +#include +#include +#include + +#ifdef WIN32 +#include +#define unlink _unlink +#else +#include +#endif + +int deleteFile(const char *path) +{ + return unlink(path); +} + +/* a utility to open a pair of files */ +/* XXX: respect overwrite flag */ +static int openFiles(const char *ifname, FILE **inFile, const char *ofname, FILE **outFile, + int overwrite) +{ + *inFile = fopen(ifname, "rb"); + if (*inFile == NULL) + { + fprintf(stderr, "couldn't open '%s' for reading\n", ifname); + return 1; + } + + *outFile = fopen(ofname, "wb"); + if (*outFile == NULL) + { + fprintf(stderr, "couldn't open '%s' for writing\n", ofname); + return 1; + } + + return 0; +} + +#define ELZMA_COMPRESS_USAGE \ + "Compress files using the LZMA algorithm (in place by default).\n" \ + "\n" \ + "Usage: elzma [options] [file]\n" \ + " -1 .. -9 compression level, -1 is fast, -9 is best (default 5)\n" \ + " -f, --force overwrite output files if they exist\n" \ + " -h, --help output this message and exit\n" \ + " -k, --keep don't delete input files\n" \ + " --lzip compress to lzip disk format (.lz extension)\n" \ + " --lzma compress to LZMA-Alone disk format (.lzma extension)\n" \ + " -v, --verbose output verbose status information while compressing\n" \ + " -z, --compress compress files (default when invoking elzma program)\n" \ + " -d, --decompress decompress files (default when invoking unelzma program)\n" \ + "\n" \ + "Advanced Options:\n" \ + " -s --set-max-dict (advanced) specify maximum dictionary size in bytes\n" + +/* parse arguments populating output parameters, return nonzero on failure */ +static int parseCompressArgs(int argc, char **argv, unsigned char *level, char **fname, + unsigned int *maxDictSize, unsigned int *verbose, + unsigned int *keep, unsigned int *overwrite, + elzma_file_format *format) +{ + int i; + + if (argc < 2) + return 1; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + char *val = NULL; + char *arg = &(argv[i][1]); + if (arg[0] == '-') + arg++; + + /* now see what argument this is */ + if (!strcmp(arg, "h") || !strcmp(arg, "help")) + { + return 1; + } + else if (!strcmp(arg, "s") || !strcmp(arg, "set-max-dict")) + { + unsigned int j = 0; + val = argv[++i]; + + /* validate argument is numeric */ + for (j = 0; j < strlen(val); j++) + { + if (val[j] < '0' || val[j] > '9') + return 1; + } + + *maxDictSize = strtoul(val, (char **)NULL, 10); + + /* don't allow dictionary sizes less than 8k */ + if (*maxDictSize < (1 < 13)) + *maxDictSize = 1 < 13; + else + { + /* make sure dict size is compatible with lzip, + * this will effectively collapse it to a close power + * of 2 */ + *maxDictSize = elzma_get_dict_size(*maxDictSize); + } + } + else if (!strcmp(arg, "v") || !strcmp(arg, "verbose")) + { + *verbose = 1; + } + else if (!strcmp(arg, "f") || !strcmp(arg, "force")) + { + *overwrite = 1; + } + else if (!strcmp(arg, "k") || !strcmp(arg, "keep")) + { + *keep = 1; + } + else if (strlen(arg) == 1 && arg[0] >= '1' && arg[0] <= '9') + { + *level = arg[0] - '0'; + } + else if (!strcmp(arg, "lzma")) + { + *format = ELZMA_lzma; + } + else if (!strcmp(arg, "lzip")) + { + *format = ELZMA_lzip; + } + else if (!strcmp(arg, "z") || !strcmp(arg, "d") || !strcmp(arg, "compress") || + !strcmp(arg, "decompress")) + { + /* noop */ + } + else + { + return 1; + } + } + else + { + *fname = argv[i]; + break; + } + } + + /* proper number of arguments? */ + if (i != argc - 1 || *fname == NULL) + return 1; + + return 0; +} + +/* callbacks for streamed input and output */ +static size_t elzmaWriteFunc(void *ctx, const void *buf, size_t size) +{ + size_t wt; + FILE *f = (FILE *)ctx; + assert(f != NULL); + + wt = fwrite(buf, 1, size, f); + + return wt; +} + +static int elzmaReadFunc(void *ctx, void *buf, size_t *size) +{ + FILE *f = (FILE *)ctx; + assert(f != NULL); + *size = fread(buf, 1, *size, f); + + return 0; +} + +static void printProgressHeader(void) +{ + printf("|0%% 50%% 100%%|\n"); +} + +static void endProgress(int pCtx) +{ + while (pCtx++ < 64) + { + printf("."); + } + printf("|\n"); +} + +static void elzmaProgressFunc(void *ctx, size_t complete, size_t total) +{ + int *dots = (int *)ctx; + int wantDots = (int)(64 * (double)complete / (double)total); + if (*dots == 0) + { + printf("|"); + (*dots)++; + } + while (wantDots > *dots) + { + printf("."); + (*dots)++; + } + fflush(stdout); +} + +static int doCompress(int argc, char **argv) +{ + /* default compression parameters, some of which may be overridded by + * command line arguments */ + unsigned char level = 5; + unsigned char lc = ELZMA_LC_DEFAULT; + unsigned char lp = ELZMA_LP_DEFAULT; + unsigned char pb = ELZMA_PB_DEFAULT; + unsigned int maxDictSize = ELZMA_DICT_SIZE_DEFAULT_MAX; + unsigned int dictSize = 0; + elzma_file_format format = ELZMA_lzma; + char *ext = ".lzma"; + char *ifname = NULL; + char *ofname = NULL; + unsigned int verbose = 0; + FILE *inFile = NULL; + FILE *outFile = NULL; + elzma_compress_handle hand = NULL; + /* XXX: large file support */ + unsigned int uncompressedSize = 0; + unsigned int keep = 0; + unsigned int overwrite = 0; + + if (0 != parseCompressArgs(argc, argv, &level, &ifname, &maxDictSize, &verbose, &keep, + &overwrite, &format)) + { + fprintf(stderr, ELZMA_COMPRESS_USAGE); + return 1; + } + + /* extension switching based on compression type*/ + if (format == ELZMA_lzip) + ext = ".lz"; + + /* generate output file name */ + { + ofname = malloc(strlen(ifname) + strlen(ext) + 1); + ofname[0] = 0; + strcat(ofname, ifname); + strcat(ofname, ext); + } + + /* now attempt to open input and ouput files */ + /* XXX: stdin/stdout support */ + if (0 != openFiles(ifname, &inFile, ofname, &outFile, overwrite)) + { + return 1; + } + + /* set uncompressed size */ + if (0 != fseek(inFile, 0, SEEK_END) || 0 == (uncompressedSize = ftell(inFile)) || + 0 != fseek(inFile, 0, SEEK_SET)) + { + fprintf(stderr, "error seeking input file (%s) - zero length?\n", ifname); + deleteFile(ofname); + return 1; + } + + /* determine a reasonable dictionary size given input size */ + dictSize = elzma_get_dict_size(uncompressedSize); + if (dictSize > maxDictSize) + dictSize = maxDictSize; + + if (verbose) + { + printf("compressing '%s' to '%s'\n", ifname, ofname); + printf("lc/lp/pb = %u/%u/%u | dictionary size = %u bytes\n", lc, lp, pb, dictSize); + printf("input file is %u bytes\n", uncompressedSize); + } + + /* allocate a compression handle */ + hand = elzma_compress_alloc(); + if (hand == NULL) + { + fprintf(stderr, "couldn't allocate compression object\n"); + deleteFile(ofname); + return 1; + } + + if (ELZMA_E_OK != + elzma_compress_config(hand, lc, lp, pb, level, dictSize, format, uncompressedSize)) + { + fprintf(stderr, "couldn't configure compression with " + "provided parameters\n"); + deleteFile(ofname); + return 1; + } + + { + int rv; + int pCtx = 0; + + if (verbose) + printProgressHeader(); + + rv = elzma_compress_run(hand, elzmaReadFunc, (void *)inFile, elzmaWriteFunc, + (void *)outFile, (verbose ? elzmaProgressFunc : NULL), &pCtx); + + if (verbose) + endProgress(pCtx); + + if (ELZMA_E_OK != rv) + { + fprintf(stderr, "error compressing\n"); + deleteFile(ofname); + return 1; + } + } + + /* clean up */ + elzma_compress_free(&hand); + fclose(inFile); + fclose(outFile); + free(ofname); + + if (!keep) + deleteFile(ifname); + + return 0; +} + +#define ELZMA_DECOMPRESS_USAGE \ + "Decompress files compressed using the LZMA algorithm (in place by default).\n" \ + "\n" \ + "Usage: unelzma [options] [file]\n" \ + " -f, --force overwrite output files if they exist\n" \ + " -h, --help output this message and exit\n" \ + " -k, --keep don't delete input files\n" \ + " -v, --verbose output verbose status information while decompressing\n" \ + " -z, --compress compress files (default when invoking elzma program)\n" \ + " -d, --decompress decompress files (default when invoking unelzma program)\n" \ + "\n" +/* parse arguments populating output parameters, return nonzero on failure */ +static int parseDecompressArgs(int argc, char **argv, char **fname, unsigned int *verbose, + unsigned int *keep, unsigned int *overwrite) +{ + int i; + + if (argc < 2) + return 1; + + for (i = 1; i < argc; i++) + { + if (argv[i][0] == '-') + { + char *arg = &(argv[i][1]); + if (arg[0] == '-') + arg++; + + /* now see what argument this is */ + if (!strcmp(arg, "h") || !strcmp(arg, "help")) + { + return 1; + } + else if (!strcmp(arg, "v") || !strcmp(arg, "verbose")) + { + *verbose = 1; + } + else if (!strcmp(arg, "k") || !strcmp(arg, "keep")) + { + *keep = 1; + } + else if (!strcmp(arg, "f") || !strcmp(arg, "force")) + { + *overwrite = 1; + } + else if (!strcmp(arg, "z") || !strcmp(arg, "d") || !strcmp(arg, "compress") || + !strcmp(arg, "decompress")) + { + /* noop */ + } + else + { + return 1; + } + } + else + { + *fname = argv[i]; + break; + } + } + + /* proper number of arguments? */ + if (i != argc - 1 || *fname == NULL) + return 1; + + return 0; +} + +static int doDecompress(int argc, char **argv) +{ + char *ifname = NULL; + char *ofname = NULL; + unsigned int verbose = 0; + FILE *inFile = NULL; + FILE *outFile = NULL; + elzma_decompress_handle hand = NULL; + unsigned int overwrite = 0; + unsigned int keep = 0; + elzma_file_format format; + const char *lzmaExt = ".lzma"; + const char *lzipExt = ".lz"; + const char *ext = ".lz"; + + if (0 != parseDecompressArgs(argc, argv, &ifname, &verbose, &keep, &overwrite)) + { + fprintf(stderr, ELZMA_DECOMPRESS_USAGE); + return 1; + } + + /* generate output file name */ + if (strlen(ifname) > strlen(lzmaExt) && + 0 == strcmp(lzmaExt, ifname + strlen(ifname) - strlen(lzmaExt))) + { + format = ELZMA_lzma; + ext = lzmaExt; + } + else if (strlen(ifname) > strlen(lzipExt) && + 0 == strcmp(lzipExt, ifname + strlen(ifname) - strlen(lzipExt))) + { + format = ELZMA_lzip; + ext = lzipExt; + } + else + { + fprintf(stderr, "input file extension not recognized (expected either " + "%s or %s)", + lzmaExt, lzipExt); + return 1; + } + + ofname = malloc(strlen(ifname) - strlen(ext)); + ofname[0] = 0; + strncat(ofname, ifname, strlen(ifname) - strlen(ext)); + + /* now attempt to open input and ouput files */ + /* XXX: stdin/stdout support */ + if (0 != openFiles(ifname, &inFile, ofname, &outFile, overwrite)) + { + return 1; + } + + hand = elzma_decompress_alloc(); + if (hand == NULL) + { + fprintf(stderr, "couldn't allocate decompression object\n"); + deleteFile(ofname); + return 1; + } + + if (ELZMA_E_OK != elzma_decompress_run(hand, elzmaReadFunc, (void *)inFile, elzmaWriteFunc, + (void *)outFile, format)) + { + fprintf(stderr, "error decompressing\n"); + deleteFile(ofname); + return 1; + } + + elzma_decompress_free(&hand); + + if (!keep) + deleteFile(ifname); + + return 0; +} + +int main(int argc, char **argv) +{ + const char *unelzma = "unelzma"; + const char *unelzmaLose = "unelzma.exe"; + const char *elzma = "elzma"; + const char *elzmaLose = "elzma.exe"; + + enum + { + RM_NONE, + RM_COMPRESS, + RM_DECOMPRESS + } runmode = RM_NONE; + + /* first we'll determine the mode we're running in, indicated by + * the binary name (argv[0]) or by the presence of a flag: + * one of -z, -d, -compress, --decompress */ + if ((strlen(argv[0]) >= strlen(unelzma) && + !strcmp((argv[0] + strlen(argv[0]) - strlen(unelzma)), unelzma)) || + (strlen(argv[0]) >= strlen(unelzmaLose) && + !strcmp((argv[0] + strlen(argv[0]) - strlen(unelzmaLose)), unelzmaLose))) + { + runmode = RM_DECOMPRESS; + } + else if ((strlen(argv[0]) >= strlen(elzma) && + !strcmp((argv[0] + strlen(argv[0]) - strlen(elzma)), elzma)) || + (strlen(argv[0]) >= strlen(elzmaLose) && + !strcmp((argv[0] + strlen(argv[0]) - strlen(elzmaLose)), elzmaLose))) + { + runmode = RM_COMPRESS; + } + + /* allow runmode to be overridded by a command line flag, first flag + * wins */ + { + int i; + for (i = 1; i < argc; i++) + { + if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--decompress")) + { + runmode = RM_DECOMPRESS; + break; + } + else if (!strcmp(argv[i], "-z") || !strcmp(argv[i], "--compress")) + { + runmode = RM_COMPRESS; + break; + } + } + } + + if (runmode != RM_COMPRESS && runmode != RM_DECOMPRESS) + { + fprintf(stderr, "couldn't determine whether " + "you want to compress or decompress\n"); + return 1; + } + + if (runmode == RM_COMPRESS) + return doCompress(argc, argv); + return doDecompress(argc, argv); +} diff --git a/depends/lzma/include/common.h b/depends/lzma/include/common.h new file mode 100644 index 00000000..f02bdb4d --- /dev/null +++ b/depends/lzma/include/common.h @@ -0,0 +1,118 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + * + * easylzma/common.h - definitions common to both compression and + * decompression + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* msft dll export gunk. To build a DLL on windows, you + * must define WIN32, EASYLZMA_SHARED, and EASYLZMA_BUILD. To use a + * DLL, you must define EASYLZMA_SHARED and WIN32 */ +#if defined(WIN32) && defined(EASYLZMA_SHARED) +#ifdef EASYLZMA_BUILD +#define EASYLZMA_API __declspec(dllexport) +#else +#define EASYLZMA_API __declspec(dllimport) +#endif +#else +#define EASYLZMA_API +#endif + +/** error codes */ + +/** no error */ +#define ELZMA_E_OK 0 +/** bad parameters passed to an ELZMA function */ +#define ELZMA_E_BAD_PARAMS 10 +/** could not initialize the encode with configured parameters. */ +#define ELZMA_E_ENCODING_PROPERTIES_ERROR 11 +/** an error occured during compression (XXX: be more specific) */ +#define ELZMA_E_COMPRESS_ERROR 12 +/** currently unsupported lzma file format was specified*/ +#define ELZMA_E_UNSUPPORTED_FORMAT 13 +/** an error occured when reading input */ +#define ELZMA_E_INPUT_ERROR 14 +/** an error occured when writing output */ +#define ELZMA_E_OUTPUT_ERROR 15 +/** LZMA header couldn't be parsed */ +#define ELZMA_E_CORRUPT_HEADER 16 +/** an error occured during decompression (XXX: be more specific) */ +#define ELZMA_E_DECOMPRESS_ERROR 17 +/** the input stream returns EOF before the decompression could complete */ +#define ELZMA_E_INSUFFICIENT_INPUT 18 +/** for formats which have an emebedded crc, this error would indicated that + * what came out was not what went in, i.e. data corruption */ +#define ELZMA_E_CRC32_MISMATCH 19 +/** for formats which have an emebedded uncompressed content length, + * this error indicates that the amount we read was not what we expected */ +#define ELZMA_E_SIZE_MISMATCH 20 + +/** Supported file formats */ +typedef enum +{ + ELZMA_lzip, /**< the lzip format which includes a magic number and + * CRC check */ + ELZMA_lzma /**< the LZMA-Alone format, originally designed by + * Igor Pavlov and in widespread use due to lzmautils, + * lacking both aforementioned features of lzip */ + /* XXX: future, potentially , + ELZMA_xz + */ +} elzma_file_format; + +/** + * A callback invoked during elzma_[de]compress_run when the [de]compression + * process has generated [de]compressed output. + * + * the size parameter indicates how much data is in buf to be written. + * it is required that the write callback consume all data, and a return + * value not equal to input size indicates and error. + */ +typedef size_t (*elzma_write_callback)(void *ctx, const void *buf, size_t size); + +/** + * A callback invoked during elzma_[de]compress_run when the [de]compression + * process requires more [un]compressed input. + * + * the size parameter is an in/out argument. on input it indicates + * the buffer size. on output it indicates the amount of data read into + * buf. when *size is zero on output it indicates EOF. + * + * \returns the read callback should return nonzero on failure. + */ +typedef int (*elzma_read_callback)(void *ctx, void *buf, size_t *size); + +/** + * A callback invoked during elzma_[de]compress_run to report progress + * on the [de]compression. + * + * \returns the read callback should return nonzero on failure. + */ +typedef void (*elzma_progress_callback)(void *ctx, size_t complete, size_t total); + +/** pointer to a malloc function, supporting client overriding memory + * allocation routines */ +typedef void *(*elzma_malloc)(void *ctx, unsigned int sz); + +/** pointer to a free function, supporting client overriding memory + * allocation routines */ +typedef void (*elzma_free)(void *ctx, void *ptr); + +#ifdef __cplusplus +} +; +#endif diff --git a/depends/lzma/include/compress.h b/depends/lzma/include/compress.h new file mode 100644 index 00000000..46c81d75 --- /dev/null +++ b/depends/lzma/include/compress.h @@ -0,0 +1,77 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + * + * compress.h - the API for LZMA compression using easylzma + */ + +#pragma once + +#include "common.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** suggested default values */ +#define ELZMA_LC_DEFAULT 3 +#define ELZMA_LP_DEFAULT 0 +#define ELZMA_PB_DEFAULT 2 +#define ELZMA_DICT_SIZE_DEFAULT_MAX (1 << 24) + +/** an opaque handle to an lzma compressor */ +typedef struct _elzma_compress_handle *elzma_compress_handle; + +/** + * Allocate a handle to an LZMA compressor object. + */ +elzma_compress_handle EASYLZMA_API elzma_compress_alloc(); + +/** + * set allocation routines (optional, if not called malloc & free will + * be used) + */ +void EASYLZMA_API +elzma_compress_set_allocation_callbacks(elzma_compress_handle hand, elzma_malloc mallocFunc, + void *mallocFuncContext, elzma_free freeFunc, + void *freeFuncContext); + +/** + * Free all data associated with an LZMA compressor object. + */ +void EASYLZMA_API elzma_compress_free(elzma_compress_handle *hand); + +/** + * Set configuration paramters for a compression run. If not called, + * reasonable defaults will be used. + */ +int EASYLZMA_API elzma_compress_config(elzma_compress_handle hand, unsigned char lc, + unsigned char lp, unsigned char pb, unsigned char level, + unsigned int dictionarySize, elzma_file_format format, + unsigned long long uncompressedSize); + +/** + * Run compression + */ +int EASYLZMA_API +elzma_compress_run(elzma_compress_handle hand, elzma_read_callback inputStream, + void *inputContext, elzma_write_callback outputStream, void *outputContext, + elzma_progress_callback progressCallback, void *progressContext); + +/** + * a heuristic utility routine to guess a dictionary size that gets near + * optimal compression while reducing memory usage. + * accepts a size in bytes, returns a proposed dictionary size + */ +unsigned int EASYLZMA_API elzma_get_dict_size(unsigned long long size); + +#ifdef __cplusplus +} +; +#endif diff --git a/depends/lzma/include/decompress.h b/depends/lzma/include/decompress.h new file mode 100644 index 00000000..cb10b2ba --- /dev/null +++ b/depends/lzma/include/decompress.h @@ -0,0 +1,58 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + * + * easylzma/decompress.h - The API for LZMA decompression using easylzma + */ + +#pragma once + +#include "include/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** an opaque handle to an lzma decompressor */ +typedef struct _elzma_decompress_handle *elzma_decompress_handle; + +/** + * Allocate a handle to an LZMA decompressor object. + */ +elzma_decompress_handle EASYLZMA_API elzma_decompress_alloc(); + +/** + * set allocation routines (optional, if not called malloc & free will + * be used) + */ +void EASYLZMA_API +elzma_decompress_set_allocation_callbacks(elzma_decompress_handle hand, elzma_malloc mallocFunc, + void *mallocFuncContext, elzma_free freeFunc, + void *freeFuncContext); + +/** + * Free all data associated with an LZMA decompressor object. + */ +void EASYLZMA_API elzma_decompress_free(elzma_decompress_handle *hand); + +/** + * Perform decompression + * + * XXX: should the library automatically detect format by reading stream? + * currently it's based on data external to stream (such as extension + * or convention) + */ +int EASYLZMA_API elzma_decompress_run(elzma_decompress_handle hand, + elzma_read_callback inputStream, void *inputContext, + elzma_write_callback outputStream, void *outputContext, + elzma_file_format format); + +#ifdef __cplusplus +} +; +#endif diff --git a/depends/lzma/include/simple.h b/depends/lzma/include/simple.h new file mode 100644 index 00000000..83f7b2d2 --- /dev/null +++ b/depends/lzma/include/simple.h @@ -0,0 +1,37 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + * + * simple.h - a wrapper around easylzma to compress/decompress to memory + */ + +#pragma once + +#include "include/common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "include/compress.h" +#include "include/decompress.h" + +/* compress a chunk of memory and return a dynamically allocated buffer + * if successful. return value is an easylzma error code */ +int EASYLZMA_API simpleCompress(elzma_file_format format, const unsigned char *inData, + size_t inLen, unsigned char **outData, size_t *outLen); + +/* decompress a chunk of memory and return a dynamically allocated buffer + * if successful. return value is an easylzma error code */ +int EASYLZMA_API simpleDecompress(elzma_file_format format, const unsigned char *inData, + size_t inLen, unsigned char **outData, size_t *outLen); + +#ifdef __cplusplus +} +; +#endif \ No newline at end of file diff --git a/depends/lzma/pavlov/7zCrc.c b/depends/lzma/pavlov/7zCrc.c new file mode 100755 index 00000000..c1598ce2 --- /dev/null +++ b/depends/lzma/pavlov/7zCrc.c @@ -0,0 +1,35 @@ +/* 7zCrc.c -- CRC32 calculation +2008-08-05 +Igor Pavlov +Public domain */ + +#include "7zCrc.h" + +#define kCrcPoly 0xEDB88320 +uint32_t g_CrcTable[256]; + +void MY_FAST_CALL CrcGenerateTable(void) +{ + uint32_t i; + for (i = 0; i < 256; i++) + { + uint32_t r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } +} + +uint32_t MY_FAST_CALL CrcUpdate(uint32_t v, const void *data, size_t size) +{ + const uint8_t *p = (const uint8_t *)data; + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE(v, *p); + return v; +} + +uint32_t MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return CrcUpdate(CRC_INIT_VAL, data, size) ^ 0xFFFFFFFF; +} diff --git a/depends/lzma/pavlov/7zCrc.h b/depends/lzma/pavlov/7zCrc.h new file mode 100755 index 00000000..0609cb87 --- /dev/null +++ b/depends/lzma/pavlov/7zCrc.h @@ -0,0 +1,24 @@ +/* 7zCrc.h -- CRC32 calculation +2008-03-13 +Igor Pavlov +Public domain */ + +#ifndef __7Z_CRC_H +#define __7Z_CRC_H + +#include + +#include "Types.h" + +extern uint32_t g_CrcTable[]; + +void MY_FAST_CALL CrcGenerateTable(void); + +#define CRC_INIT_VAL 0xFFFFFFFF +#define CRC_GET_DIGEST(crc) ((crc) ^ 0xFFFFFFFF) +#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +uint32_t MY_FAST_CALL CrcUpdate(uint32_t crc, const void *data, size_t size); +uint32_t MY_FAST_CALL CrcCalc(const void *data, size_t size); + +#endif diff --git a/depends/lzma/pavlov/LzFind.c b/depends/lzma/pavlov/LzFind.c new file mode 100755 index 00000000..75003ac1 --- /dev/null +++ b/depends/lzma/pavlov/LzFind.c @@ -0,0 +1,779 @@ +/* LzFind.c -- Match finder for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#include +#include + +#include "LzFind.h" +#include "LzHash.h" + +#define kEmptyHashValue 0 +#define kMaxValForNormalize ((uint32_t)0xFFFFFFFF) +#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */ +#define kNormalizeMask (~(kNormalizeStepMin - 1)) +#define kMaxHistorySize ((uint32_t)3 << 30) + +#define kStartMaxLen 3 + +static void LzInWindow_Free(CMatchFinder *p) +{ + if (!p->directInput) + { + free(p->bufferBase); + p->bufferBase = 0; + } +} + +/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ + +static int LzInWindow_Create(CMatchFinder *p, uint32_t keepSizeReserv) +{ + uint32_t blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; + if (p->directInput) + { + p->blockSize = blockSize; + return 1; + } + if (p->bufferBase == 0 || p->blockSize != blockSize) + { + LzInWindow_Free(p); + p->blockSize = blockSize; + p->bufferBase = (uint8_t *)malloc((size_t)blockSize); + } + return (p->bufferBase != 0); +} + +uint8_t *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) +{ + return p->buffer; +} +uint8_t MatchFinder_GetIndexByte(CMatchFinder *p, int32_t index) +{ + return p->buffer[index]; +} + +uint32_t MatchFinder_GetNumAvailableBytes(CMatchFinder *p) +{ + return p->streamPos - p->pos; +} + +void MatchFinder_ReduceOffsets(CMatchFinder *p, uint32_t subValue) +{ + p->posLimit -= subValue; + p->pos -= subValue; + p->streamPos -= subValue; +} + +static void MatchFinder_ReadBlock(CMatchFinder *p) +{ + if (p->streamEndWasReached || p->result != SZ_OK) + return; + for (;;) + { + uint8_t *dest = p->buffer + (p->streamPos - p->pos); + size_t size = (p->bufferBase + p->blockSize - dest); + if (size == 0) + return; + p->result = p->stream->Read(p->stream, dest, &size); + if (p->result != SZ_OK) + return; + if (size == 0) + { + p->streamEndWasReached = 1; + return; + } + p->streamPos += (uint32_t)size; + if (p->streamPos - p->pos > p->keepSizeAfter) + return; + } +} + +void MatchFinder_MoveBlock(CMatchFinder *p) +{ + memmove(p->bufferBase, p->buffer - p->keepSizeBefore, + (size_t)(p->streamPos - p->pos + p->keepSizeBefore)); + p->buffer = p->bufferBase + p->keepSizeBefore; +} + +int MatchFinder_NeedMove(CMatchFinder *p) +{ + /* if (p->streamEndWasReached) return 0; */ + return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter); +} + +void MatchFinder_ReadIfRequired(CMatchFinder *p) +{ + if (p->streamEndWasReached) + return; + if (p->keepSizeAfter >= p->streamPos - p->pos) + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p) +{ + if (MatchFinder_NeedMove(p)) + MatchFinder_MoveBlock(p); + MatchFinder_ReadBlock(p); +} + +static void MatchFinder_SetDefaultSettings(CMatchFinder *p) +{ + p->cutValue = 32; + p->btMode = 1; + p->numHashBytes = 4; + /* p->skipModeBits = 0; */ + p->directInput = 0; + p->bigHash = 0; +} + +#define kCrcPoly 0xEDB88320 + +void MatchFinder_Construct(CMatchFinder *p) +{ + uint32_t i; + p->bufferBase = 0; + p->directInput = 0; + p->hash = 0; + MatchFinder_SetDefaultSettings(p); + + for (i = 0; i < 256; i++) + { + uint32_t r = i; + int j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + p->crc[i] = r; + } +} + +static void MatchFinder_FreeThisClassMemory(CMatchFinder *p) +{ + free(p->hash); + p->hash = 0; +} + +void MatchFinder_Free(CMatchFinder *p) +{ + MatchFinder_FreeThisClassMemory(p); + LzInWindow_Free(p); +} + +static CLzRef *AllocRefs(uint32_t num) +{ + size_t sizeInBytes = (size_t)num * sizeof(CLzRef); + if (sizeInBytes / sizeof(CLzRef) != num) + return 0; + return (CLzRef *)malloc(sizeInBytes); +} + +int MatchFinder_Create(CMatchFinder *p, uint32_t historySize, uint32_t keepAddBufferBefore, + uint32_t matchMaxLen, uint32_t keepAddBufferAfter) +{ + uint32_t sizeReserv; + if (historySize > kMaxHistorySize) + { + MatchFinder_Free(p); + return 0; + } + sizeReserv = historySize >> 1; + if (historySize > ((uint32_t)2 << 30)) + sizeReserv = historySize >> 2; + sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19); + + p->keepSizeBefore = historySize + keepAddBufferBefore + 1; + p->keepSizeAfter = matchMaxLen + keepAddBufferAfter; + /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary + * using */ + if (LzInWindow_Create(p, sizeReserv)) + { + uint32_t newCyclicBufferSize = (historySize /* >> p->skipModeBits */) + 1; + uint32_t hs; + p->matchMaxLen = matchMaxLen; + { + p->fixedHashSize = 0; + if (p->numHashBytes == 2) + hs = (1 << 16) - 1; + else + { + hs = historySize - 1; + hs |= (hs >> 1); + hs |= (hs >> 2); + hs |= (hs >> 4); + hs |= (hs >> 8); + hs >>= 1; + /* hs >>= p->skipModeBits; */ + hs |= 0xFFFF; /* don't change it! It's required for Deflate */ + if (hs > (1 << 24)) + { + if (p->numHashBytes == 3) + hs = (1 << 24) - 1; + else + hs >>= 1; + } + } + p->hashMask = hs; + hs++; + if (p->numHashBytes > 2) + p->fixedHashSize += kHash2Size; + if (p->numHashBytes > 3) + p->fixedHashSize += kHash3Size; + if (p->numHashBytes > 4) + p->fixedHashSize += kHash4Size; + hs += p->fixedHashSize; + } + + { + uint32_t prevSize = p->hashSizeSum + p->numSons; + uint32_t newSize; + p->historySize = historySize; + p->hashSizeSum = hs; + p->cyclicBufferSize = newCyclicBufferSize; + p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize); + newSize = p->hashSizeSum + p->numSons; + if (p->hash != 0 && prevSize == newSize) + return 1; + MatchFinder_FreeThisClassMemory(p); + p->hash = AllocRefs(newSize); + if (p->hash != 0) + { + p->son = p->hash + p->hashSizeSum; + return 1; + } + } + } + MatchFinder_Free(p); + return 0; +} + +static void MatchFinder_SetLimits(CMatchFinder *p) +{ + uint32_t limit = kMaxValForNormalize - p->pos; + uint32_t limit2 = p->cyclicBufferSize - p->cyclicBufferPos; + if (limit2 < limit) + limit = limit2; + limit2 = p->streamPos - p->pos; + if (limit2 <= p->keepSizeAfter) + { + if (limit2 > 0) + limit2 = 1; + } + else + limit2 -= p->keepSizeAfter; + if (limit2 < limit) + limit = limit2; + { + uint32_t lenLimit = p->streamPos - p->pos; + if (lenLimit > p->matchMaxLen) + lenLimit = p->matchMaxLen; + p->lenLimit = lenLimit; + } + p->posLimit = p->pos + limit; +} + +void MatchFinder_Init(CMatchFinder *p) +{ + uint32_t i; + for (i = 0; i < p->hashSizeSum; i++) + p->hash[i] = kEmptyHashValue; + p->cyclicBufferPos = 0; + p->buffer = p->bufferBase; + p->pos = p->streamPos = p->cyclicBufferSize; + p->result = SZ_OK; + p->streamEndWasReached = 0; + MatchFinder_ReadBlock(p); + MatchFinder_SetLimits(p); +} + +static uint32_t MatchFinder_GetSubValue(CMatchFinder *p) +{ + return (p->pos - p->historySize - 1) & kNormalizeMask; +} + +void MatchFinder_Normalize3(uint32_t subValue, CLzRef *items, uint32_t numItems) +{ + uint32_t i; + for (i = 0; i < numItems; i++) + { + uint32_t value = items[i]; + if (value <= subValue) + value = kEmptyHashValue; + else + value -= subValue; + items[i] = value; + } +} + +static void MatchFinder_Normalize(CMatchFinder *p) +{ + uint32_t subValue = MatchFinder_GetSubValue(p); + MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons); + MatchFinder_ReduceOffsets(p, subValue); +} + +static void MatchFinder_CheckLimits(CMatchFinder *p) +{ + if (p->pos == kMaxValForNormalize) + MatchFinder_Normalize(p); + if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos) + MatchFinder_CheckAndMoveAndRead(p); + if (p->cyclicBufferPos == p->cyclicBufferSize) + p->cyclicBufferPos = 0; + MatchFinder_SetLimits(p); +} + +static uint32_t *Hc_GetMatchesSpec(uint32_t lenLimit, uint32_t curMatch, uint32_t pos, + const uint8_t *cur, CLzRef *son, uint32_t _cyclicBufferPos, + uint32_t _cyclicBufferSize, uint32_t cutValue, + uint32_t *distances, uint32_t maxLen) +{ + son[_cyclicBufferPos] = curMatch; + for (;;) + { + uint32_t delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + return distances; + { + const uint8_t *pb = cur - delta; + curMatch = son[_cyclicBufferPos - delta + + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)]; + if (pb[maxLen] == cur[maxLen] && *pb == *cur) + { + uint32_t len = 0; + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + return distances; + } + } + } + } +} + +uint32_t *GetMatchesSpec1(uint32_t lenLimit, uint32_t curMatch, uint32_t pos, + const uint8_t *cur, CLzRef *son, uint32_t _cyclicBufferPos, + uint32_t _cyclicBufferSize, uint32_t cutValue, uint32_t *distances, + uint32_t maxLen) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + uint32_t len0 = 0, len1 = 0; + for (;;) + { + uint32_t delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return distances; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) + << 1); + const uint8_t *pb = cur - delta; + uint32_t len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + if (++len != lenLimit && pb[len] == cur[len]) + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + if (maxLen < len) + { + *distances++ = maxLen = len; + *distances++ = delta - 1; + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return distances; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +static void SkipMatchesSpec(uint32_t lenLimit, uint32_t curMatch, uint32_t pos, + const uint8_t *cur, CLzRef *son, uint32_t _cyclicBufferPos, + uint32_t _cyclicBufferSize, uint32_t cutValue) +{ + CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1; + CLzRef *ptr1 = son + (_cyclicBufferPos << 1); + uint32_t len0 = 0, len1 = 0; + for (;;) + { + uint32_t delta = pos - curMatch; + if (cutValue-- == 0 || delta >= _cyclicBufferSize) + { + *ptr0 = *ptr1 = kEmptyHashValue; + return; + } + { + CLzRef *pair = son + ((_cyclicBufferPos - delta + + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) + << 1); + const uint8_t *pb = cur - delta; + uint32_t len = (len0 < len1 ? len0 : len1); + if (pb[len] == cur[len]) + { + while (++len != lenLimit) + if (pb[len] != cur[len]) + break; + { + if (len == lenLimit) + { + *ptr1 = pair[0]; + *ptr0 = pair[1]; + return; + } + } + } + if (pb[len] < cur[len]) + { + *ptr1 = curMatch; + ptr1 = pair + 1; + curMatch = *ptr1; + len1 = len; + } + else + { + *ptr0 = curMatch; + ptr0 = pair; + curMatch = *ptr0; + len0 = len; + } + } + } +} + +#define MOVE_POS \ + ++p->cyclicBufferPos; \ + p->buffer++; \ + if (++p->pos == p->posLimit) \ + MatchFinder_CheckLimits(p); + +#define MOVE_POS_RET MOVE_POS return offset; + +static void MatchFinder_MovePos(CMatchFinder *p) +{ + MOVE_POS; +} + +#define GET_MATCHES_HEADER2(minLen, ret_op) \ + uint32_t lenLimit; \ + uint32_t hashValue; \ + const uint8_t *cur; \ + uint32_t curMatch; \ + lenLimit = p->lenLimit; \ + { \ + if (lenLimit < minLen) \ + { \ + MatchFinder_MovePos(p); \ + ret_op; \ + } \ + } \ + cur = p->buffer; + +#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0) +#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue) + +#define MF_PARAMS(p) \ + p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue + +#define GET_MATCHES_FOOTER(offset, maxLen) \ + offset = (uint32_t)( \ + GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), distances + offset, maxLen) - \ + distances); \ + MOVE_POS_RET; + +#define SKIP_FOOTER \ + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); \ + MOVE_POS; + +static uint32_t Bt2_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) +{ + uint32_t offset; + GET_MATCHES_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 1) +} + +uint32_t Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) +{ + uint32_t offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = 0; + GET_MATCHES_FOOTER(offset, 2) +} + +static uint32_t Bt3_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) +{ + uint32_t hash2Value, delta2, maxLen, offset; + GET_MATCHES_HEADER(3) + + HASH3_CALC; + + delta2 = p->pos - p->hash[hash2Value]; + curMatch = p->hash[kFix3HashSize + hashValue]; + + p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; + + maxLen = 2; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[0] = maxLen; + distances[1] = delta2 - 1; + offset = 2; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + GET_MATCHES_FOOTER(offset, maxLen) +} + +static uint32_t Bt4_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) +{ + uint32_t hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + GET_MATCHES_FOOTER(offset, maxLen) +} + +static uint32_t Hc4_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) +{ + uint32_t hash2Value, hash3Value, delta2, delta3, maxLen, offset; + GET_MATCHES_HEADER(4) + + HASH4_CALC; + + delta2 = p->pos - p->hash[hash2Value]; + delta3 = p->pos - p->hash[kFix3HashSize + hash3Value]; + curMatch = p->hash[kFix4HashSize + hashValue]; + + p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + + maxLen = 1; + offset = 0; + if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur) + { + distances[0] = maxLen = 2; + distances[1] = delta2 - 1; + offset = 2; + } + if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur) + { + maxLen = 3; + distances[offset + 1] = delta3 - 1; + offset += 2; + delta2 = delta3; + } + if (offset != 0) + { + for (; maxLen != lenLimit; maxLen++) + if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen]) + break; + distances[offset - 2] = maxLen; + if (maxLen == lenLimit) + { + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS_RET; + } + } + if (maxLen < 3) + maxLen = 3; + offset = (uint32_t)( + Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances + offset, maxLen) - + (distances)); + MOVE_POS_RET +} + +uint32_t Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances) +{ + uint32_t offset; + GET_MATCHES_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + offset = (uint32_t)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p), distances, 2) - + (distances)); + MOVE_POS_RET +} + +static void Bt2_MatchFinder_Skip(CMatchFinder *p, uint32_t num) +{ + do + { + SKIP_HEADER(2) + HASH2_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } while (--num != 0); +} + +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, uint32_t num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + SKIP_FOOTER + } while (--num != 0); +} + +static void Bt3_MatchFinder_Skip(CMatchFinder *p, uint32_t num) +{ + do + { + uint32_t hash2Value; + SKIP_HEADER(3) + HASH3_CALC; + curMatch = p->hash[kFix3HashSize + hashValue]; + p->hash[hash2Value] = p->hash[kFix3HashSize + hashValue] = p->pos; + SKIP_FOOTER + } while (--num != 0); +} + +static void Bt4_MatchFinder_Skip(CMatchFinder *p, uint32_t num) +{ + do + { + uint32_t hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = p->pos; + p->hash[kFix4HashSize + hashValue] = p->pos; + SKIP_FOOTER + } while (--num != 0); +} + +static void Hc4_MatchFinder_Skip(CMatchFinder *p, uint32_t num) +{ + do + { + uint32_t hash2Value, hash3Value; + SKIP_HEADER(4) + HASH4_CALC; + curMatch = p->hash[kFix4HashSize + hashValue]; + p->hash[hash2Value] = p->hash[kFix3HashSize + hash3Value] = + p->hash[kFix4HashSize + hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } while (--num != 0); +} + +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, uint32_t num) +{ + do + { + SKIP_HEADER(3) + HASH_ZIP_CALC; + curMatch = p->hash[hashValue]; + p->hash[hashValue] = p->pos; + p->son[p->cyclicBufferPos] = curMatch; + MOVE_POS + } while (--num != 0); +} + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable) +{ + vTable->Init = (Mf_Init_Func)MatchFinder_Init; + vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte; + vTable->GetNumAvailableBytes = + (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes; + vTable->GetPointerToCurrentPos = + (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos; + if (!p->btMode) + { + vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip; + } + else if (p->numHashBytes == 2) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip; + } + else if (p->numHashBytes == 3) + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip; + } + else + { + vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches; + vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip; + } +} diff --git a/depends/lzma/pavlov/LzFind.h b/depends/lzma/pavlov/LzFind.h new file mode 100755 index 00000000..12d89aac --- /dev/null +++ b/depends/lzma/pavlov/LzFind.h @@ -0,0 +1,107 @@ +/* LzFind.h -- Match finder for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZFIND_H +#define __LZFIND_H + +#include "Types.h" + +typedef uint32_t CLzRef; + +typedef struct _CMatchFinder +{ + uint8_t *buffer; + uint32_t pos; + uint32_t posLimit; + uint32_t streamPos; + uint32_t lenLimit; + + uint32_t cyclicBufferPos; + uint32_t cyclicBufferSize; /* it must be = (historySize + 1) */ + + uint32_t matchMaxLen; + CLzRef *hash; + CLzRef *son; + uint32_t hashMask; + uint32_t cutValue; + + uint8_t *bufferBase; + ISeqInStream *stream; + int streamEndWasReached; + + uint32_t blockSize; + uint32_t keepSizeBefore; + uint32_t keepSizeAfter; + + uint32_t numHashBytes; + int directInput; + int btMode; + /* int skipModeBits; */ + int bigHash; + uint32_t historySize; + uint32_t fixedHashSize; + uint32_t hashSizeSum; + uint32_t numSons; + SRes result; + uint32_t crc[256]; +} CMatchFinder; + +#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) +#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)]) + +#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos) + +int MatchFinder_NeedMove(CMatchFinder *p); +uint8_t *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p); +void MatchFinder_MoveBlock(CMatchFinder *p); +void MatchFinder_ReadIfRequired(CMatchFinder *p); + +void MatchFinder_Construct(CMatchFinder *p); + +/* Conditions: + historySize <= 3 GB + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB +*/ +int MatchFinder_Create(CMatchFinder *p, uint32_t historySize, uint32_t keepAddBufferBefore, + uint32_t matchMaxLen, uint32_t keepAddBufferAfter); +void MatchFinder_Free(CMatchFinder *p); +void MatchFinder_Normalize3(uint32_t subValue, CLzRef *items, uint32_t numItems); +void MatchFinder_ReduceOffsets(CMatchFinder *p, uint32_t subValue); + +uint32_t *GetMatchesSpec1(uint32_t lenLimit, uint32_t curMatch, uint32_t pos, + const uint8_t *buffer, CLzRef *son, uint32_t _cyclicBufferPos, + uint32_t _cyclicBufferSize, uint32_t _cutValue, uint32_t *distances, + uint32_t maxLen); + +/* +Conditions: + Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func. + Mf_GetPointerToCurrentPos_Func's result must be used only before any other function +*/ + +typedef void (*Mf_Init_Func)(void *object); +typedef uint8_t (*Mf_GetIndexByte_Func)(void *object, int32_t index); +typedef uint32_t (*Mf_GetNumAvailableBytes_Func)(void *object); +typedef const uint8_t *(*Mf_GetPointerToCurrentPos_Func)(void *object); +typedef uint32_t (*Mf_GetMatches_Func)(void *object, uint32_t *distances); +typedef void (*Mf_Skip_Func)(void *object, uint32_t); + +typedef struct _IMatchFinder +{ + Mf_Init_Func Init; + Mf_GetIndexByte_Func GetIndexByte; + Mf_GetNumAvailableBytes_Func GetNumAvailableBytes; + Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos; + Mf_GetMatches_Func GetMatches; + Mf_Skip_Func Skip; +} IMatchFinder; + +void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); + +void MatchFinder_Init(CMatchFinder *p); +uint32_t Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances); +uint32_t Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, uint32_t *distances); +void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, uint32_t num); +void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, uint32_t num); + +#endif diff --git a/depends/lzma/pavlov/LzHash.h b/depends/lzma/pavlov/LzHash.h new file mode 100755 index 00000000..22cb0430 --- /dev/null +++ b/depends/lzma/pavlov/LzHash.h @@ -0,0 +1,62 @@ +/* LzHash.h -- HASH functions for LZ algorithms +2008-10-04 : Igor Pavlov : Public domain */ + +#pragma once + +#define kHash2Size (1 << 10) +#define kHash3Size (1 << 16) +#define kHash4Size (1 << 20) + +#define kFix3HashSize (kHash2Size) +#define kFix4HashSize (kHash2Size + kHash3Size) +#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size) + +#define HASH2_CALC hashValue = cur[0] | ((uint32_t)cur[1] << 8); + +#define HASH3_CALC \ + { \ + uint32_t temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hashValue = (temp ^ ((uint32_t)cur[2] << 8)) & p->hashMask; \ + } + +#define HASH4_CALC \ + { \ + uint32_t temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((uint32_t)cur[2] << 8)) & (kHash3Size - 1); \ + hashValue = (temp ^ ((uint32_t)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; \ + } + +#define HASH5_CALC \ + { \ + uint32_t temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((uint32_t)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = (temp ^ ((uint32_t)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \ + hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \ + hash4Value &= (kHash4Size - 1); \ + } + +/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((uint32_t)cur[1] << 8)) ^ p->crc[cur[2]]) & + * 0xFFFF; */ +#define HASH_ZIP_CALC \ + hashValue = ((cur[2] | ((uint32_t)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF; + +#define MT_HASH2_CALC hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1); + +#define MT_HASH3_CALC \ + { \ + uint32_t temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((uint32_t)cur[2] << 8)) & (kHash3Size - 1); \ + } + +#define MT_HASH4_CALC \ + { \ + uint32_t temp = p->crc[cur[0]] ^ cur[1]; \ + hash2Value = temp & (kHash2Size - 1); \ + hash3Value = (temp ^ ((uint32_t)cur[2] << 8)) & (kHash3Size - 1); \ + hash4Value = \ + (temp ^ ((uint32_t)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); \ + } diff --git a/depends/lzma/pavlov/LzmaDec.c b/depends/lzma/pavlov/LzmaDec.c new file mode 100755 index 00000000..1a44dd00 --- /dev/null +++ b/depends/lzma/pavlov/LzmaDec.c @@ -0,0 +1,1076 @@ +/* LzmaDec.c -- LZMA Decoder +2008-11-06 : Igor Pavlov : Public domain */ + +#include "LzmaDec.h" + +#include +#include + +#define kNumTopBits 24 +#define kTopValue ((uint32_t)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE \ + if (range < kTopValue) \ + { \ + range <<= 8; \ + code = (code << 8) | (*buf++); \ + } + +#define IF_BIT_0(p) \ + ttt = *(p); \ + NORMALIZE; \ + bound = (range >> kNumBitModelTotalBits) * ttt; \ + if (code < bound) +#define UPDATE_0(p) \ + range = bound; \ + *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) \ + range -= bound; \ + code -= bound; \ + *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) \ + IF_BIT_0(p) \ + { \ + UPDATE_0(p); \ + i = (i + i); \ + A0; \ + } \ + else \ + { \ + UPDATE_1(p); \ + i = (i + i) + 1; \ + A1; \ + } +#define GET_BIT(p, i) GET_BIT2(p, i, ;, ;) + +#define TREE_GET_BIT(probs, i) \ + { \ + GET_BIT((probs + i), i); \ + } +#define TREE_DECODE(probs, limit, i) \ + { \ + i = 1; \ + do \ + { \ + TREE_GET_BIT(probs, i); \ + } while (i < limit); \ + i -= limit; \ + } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { \ + i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; \ + } +#endif + +#define NORMALIZE_CHECK \ + if (range < kTopValue) \ + { \ + if (buf >= bufLimit) \ + return DUMMY_ERROR; \ + range <<= 8; \ + code = (code << 8) | (*buf++); \ + } + +#define IF_BIT_0_CHECK(p) \ + ttt = *(p); \ + NORMALIZE_CHECK; \ + bound = (range >> kNumBitModelTotalBits) * ttt; \ + if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK \ + range -= bound; \ + code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) \ + IF_BIT_0_CHECK(p) \ + { \ + UPDATE_0_CHECK; \ + i = (i + i); \ + A0; \ + } \ + else \ + { \ + UPDATE_1_CHECK; \ + i = (i + i) + 1; \ + A1; \ + } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ;, ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { \ + i = 1; \ + do \ + { \ + GET_BIT_CHECK(probs + i, i) \ + } while (i < limit); \ + i -= limit; \ + } + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart \ + (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LzmaProps_GetNumProbs(p) \ + ((uint32_t)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + static const uint8_t kLiteralNextStates[kNumStates * 2] = { + 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5, 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker + = kMatchSpecLenStart + 2 : State Init Marker +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, size_t limit, const uint8_t *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + uint32_t rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + uint8_t *dic = p->dic; + size_t dicBufSize = p->dicBufSize; + size_t dicPos = p->dicPos; + + uint32_t processedPos = p->processedPos; + uint32_t checkDicSize = p->checkDicSize; + unsigned len = 0; + + const uint8_t *buf = p->buf; + uint32_t range = p->range; + uint32_t code = p->code; + + do + { + CLzmaProb *prob; + uint32_t bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (checkDicSize != 0 || processedPos != 0) + prob += (LZMA_LIT_SIZE * + (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + + if (state < kNumLitStates) + { + symbol = 1; + do + { + GET_BIT(prob + symbol, symbol) + } while (symbol < 0x100); + } + else + { + unsigned matchByte = + p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + unsigned offs = 0x100; + symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + } while (symbol < 0x100); + } + dic[dicPos++] = (uint8_t)symbol; + processedPos++; + + state = kLiteralNextStates[state]; + /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */ + continue; + } + else + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + uint32_t distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, limit, len); + len += offset; + } + + if (state >= kNumStates) + { + uint32_t distance; + prob = + probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) + << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + int numDirectBits = (int)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + uint32_t mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ;, distance |= mask); + mask <<= 1; + } while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + uint32_t t; + code -= range; + t = (0 - + ((uint32_t)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ;, distance |= 1); + GET_BIT2(prob + i, i, ;, distance |= 2); + GET_BIT2(prob + i, i, ;, distance |= 4); + GET_BIT2(prob + i, i, ;, distance |= 8); + } + if (distance == (uint32_t)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + return SZ_ERROR_DATA; + } + else if (distance >= checkDicSize) + return SZ_ERROR_DATA; + state = + (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + /* state = kLiteralNextStates[state]; */ + } + + len += kMatchMinLen; + + if (limit == dicPos) + return SZ_ERROR_DATA; + { + size_t rem = limit - dicPos; + unsigned curLen = ((rem < len) ? (unsigned)rem : len); + size_t pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (pos + curLen <= dicBufSize) + { + uint8_t *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const uint8_t *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (uint8_t) * (dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } while (--curLen != 0); + } + } + } + } while (dicPos < limit && buf < bufLimit); + NORMALIZE; + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, size_t limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + uint8_t *dic = p->dic; + size_t dicPos = p->dicPos; + size_t dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + uint32_t rep0 = p->reps[0]; + if (limit - dicPos < len) + len = (unsigned)(limit - dicPos); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len-- != 0) + { + dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, size_t limit, const uint8_t *bufLimit) +{ + do + { + size_t limit2 = limit; + if (p->checkDicSize == 0) + { + uint32_t rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + if (p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + LzmaDec_WriteRem(p, limit); + } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + { + p->remainLen = kMatchSpecLenStart; + } + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const uint8_t *buf, size_t inSize) +{ + uint32_t range = p->range; + uint32_t code = p->code; + const uint8_t *bufLimit = buf + inSize; + CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + CLzmaProb *prob; + uint32_t bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += (LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> + (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do + { + GET_BIT_CHECK(prob + symbol, symbol) + } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = + probs + PosSlot + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) + << kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - + posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + +static void LzmaDec_InitRc(CLzmaDec *p, const uint8_t *data) +{ + p->code = ((uint32_t)data[1] << 24) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 8) | + ((uint32_t)data[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; +} + +void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + uint32_t numProbs = Literal + ((uint32_t)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp)); + uint32_t i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, size_t dicLimit, const uint8_t *src, size_t *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + size_t inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush != 0) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + + LzmaDec_InitRc(p, p->tempBuf); + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + size_t processed; + const uint8_t *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (size_t)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf)); + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, uint8_t *dest, size_t *destLen, const uint8_t *src, + size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + size_t outSize = *destLen; + size_t inSize = *srcLen; + *srcLen = *destLen = 0; + for (;;) + { + size_t inSizeCur = inSize, outSizeCur, dicPos; + ELzmaFinishMode curFinishMode; + SRes res; + if (p->dicPos == p->dicBufSize) + p->dicPos = 0; + dicPos = p->dicPos; + if (outSize > p->dicBufSize - dicPos) + { + outSizeCur = p->dicBufSize; + curFinishMode = LZMA_FINISH_ANY; + } + else + { + outSizeCur = dicPos + outSize; + curFinishMode = finishMode; + } + + res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status); + src += inSizeCur; + inSize -= inSizeCur; + *srcLen += inSizeCur; + outSizeCur = p->dicPos - dicPos; + memcpy(dest, p->dic + dicPos, outSizeCur); + dest += outSizeCur; + outSize -= outSizeCur; + *destLen += outSizeCur; + if (res != 0) + return res; + if (outSizeCur == 0 || outSize == 0) + return SZ_OK; + } +} + +void LzmaDec_FreeProbs(CLzmaDec *p) +{ + free(p->probs); + p->probs = 0; +} + +static void LzmaDec_FreeDict(CLzmaDec *p) +{ + free(p->dic); + p->dic = 0; +} + +void LzmaDec_Free(CLzmaDec *p) +{ + LzmaDec_FreeProbs(p); + LzmaDec_FreeDict(p); +} + +SRes LzmaProps_Decode(CLzmaProps *p, const uint8_t *data, unsigned size) +{ + uint32_t dicSize; + uint8_t d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((uint32_t)data[2] << 8) | ((uint32_t)data[3] << 16) | + ((uint32_t)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew) +{ + uint32_t numProbs = LzmaProps_GetNumProbs(propNew); + if (p->probs == 0 || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p); + p->probs = (CLzmaProb *)malloc(numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (p->probs == 0) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const uint8_t *props, unsigned propsSize) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew)); + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDec_Allocate(CLzmaDec *p, const uint8_t *props, unsigned propsSize) +{ + CLzmaProps propNew; + size_t dicBufSize; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew)); + dicBufSize = propNew.dicSize; + if (p->dic == 0 || dicBufSize != p->dicBufSize) + { + LzmaDec_FreeDict(p); + p->dic = (uint8_t *)malloc(dicBufSize); + if (p->dic == 0) + { + LzmaDec_FreeProbs(p); + return SZ_ERROR_MEM; + } + } + p->dicBufSize = dicBufSize; + p->prop = propNew; + return SZ_OK; +} + +SRes LzmaDecode(uint8_t *dest, size_t *destLen, const uint8_t *src, size_t *srcLen, + const uint8_t *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status) +{ + CLzmaDec p; + SRes res; + size_t inSize = *srcLen; + size_t outSize = *destLen; + *srcLen = *destLen = 0; + if (inSize < RC_INIT_SIZE) + return SZ_ERROR_INPUT_EOF; + + LzmaDec_Construct(&p); + res = LzmaDec_AllocateProbs(&p, propData, propSize); + if (res != 0) + return res; + p.dic = dest; + p.dicBufSize = outSize; + + LzmaDec_Init(&p); + + *srcLen = inSize; + res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status); + + if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT) + res = SZ_ERROR_INPUT_EOF; + + (*destLen) = p.dicPos; + LzmaDec_FreeProbs(&p); + return res; +} diff --git a/depends/lzma/pavlov/LzmaDec.h b/depends/lzma/pavlov/LzmaDec.h new file mode 100755 index 00000000..25cb7e94 --- /dev/null +++ b/depends/lzma/pavlov/LzmaDec.h @@ -0,0 +1,220 @@ +/* LzmaDec.h -- LZMA Decoder +2008-10-04 : Igor Pavlov : Public domain */ + +#pragma once + +#include "Types.h" + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb uint16_t +#endif + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + uint32_t dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaProps_Decode(CLzmaProps *p, const uint8_t *data, unsigned size); + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + uint8_t *dic; + const uint8_t *buf; + uint32_t range, code; + size_t dicPos; + size_t dicBufSize; + uint32_t processedPos; + uint32_t checkDicSize; + unsigned state; + uint32_t reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + uint32_t numProbs; + unsigned tempBufSize; + uint8_t tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) \ + { \ + (p)->dic = 0; \ + (p)->probs = 0; \ + } + +void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such + stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished + without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const uint8_t *props, unsigned propsSize); +void LzmaDec_FreeProbs(CLzmaDec *p); + +SRes LzmaDec_Allocate(CLzmaDec *state, const uint8_t *prop, unsigned propsSize); +void LzmaDec_Free(CLzmaDec *state); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +SRes LzmaDec_DecodeToDic(CLzmaDec *p, size_t dicLimit, const uint8_t *src, size_t *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status); + +/* ---------- Buffer Interface ---------- */ + +/* It's zlib-like interface. + See LzmaDec_DecodeToDic description for information about STEPS and return results, + but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need + to work with CLzmaDec variables manually. + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). +*/ + +SRes LzmaDec_DecodeToBuf(CLzmaDec *p, uint8_t *dest, size_t *destLen, const uint8_t *src, + size_t *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + +/* ---------- One Call Interface ---------- */ + +/* LzmaDecode + +finishMode: + It has meaning only if the decoding reaches output limit (*destLen). + LZMA_FINISH_ANY - Decode just destLen bytes. + LZMA_FINISH_END - Stream must be finished after (*destLen). + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src). +*/ + +SRes LzmaDecode(uint8_t *dest, size_t *destLen, const uint8_t *src, size_t *srcLen, + const uint8_t *propData, unsigned propSize, ELzmaFinishMode finishMode, + ELzmaStatus *status); diff --git a/depends/lzma/pavlov/LzmaEnc.c b/depends/lzma/pavlov/LzmaEnc.c new file mode 100755 index 00000000..ac34eb45 --- /dev/null +++ b/depends/lzma/pavlov/LzmaEnc.c @@ -0,0 +1,2349 @@ +/* LzmaEnc.c -- LZMA Encoder +2008-10-04 : Igor Pavlov : Public domain */ + +#include +#include + +/* #define SHOW_STAT */ +/* #define SHOW_STAT2 */ + +#if defined(SHOW_STAT) || defined(SHOW_STAT2) +#include +#endif + +#include "LzmaEnc.h" + +#include "LzFind.h" +#ifdef COMPRESS_MF_MT +#include "LzFindMt.h" +#endif + +#ifdef SHOW_STAT +static int ttt = 0; +#endif + +#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) + +#define kBlockSize (9 << 10) +#define kUnpackBlockSize (1 << 18) +#define kMatchArraySize (1 << 21) +#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) + +#define kNumMaxDirectBits (31) + +#define kNumTopBits 24 +#define kTopValue ((uint32_t)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 +#define kProbInitValue (kBitModelTotal >> 1) + +#define kNumMoveReducingBits 4 +#define kNumBitPriceShiftBits 4 +#define kBitPrice (1 << kNumBitPriceShiftBits) + +void LzmaEncProps_Init(CLzmaEncProps *p) +{ + p->level = 5; + p->dictSize = p->mc = 0; + p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1; + p->writeEndMark = 0; +} + +void LzmaEncProps_Normalize(CLzmaEncProps *p) +{ + int level = p->level; + if (level < 0) + level = 5; + p->level = level; + if (p->dictSize == 0) + p->dictSize = + (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); + if (p->lc < 0) + p->lc = 3; + if (p->lp < 0) + p->lp = 0; + if (p->pb < 0) + p->pb = 2; + if (p->algo < 0) + p->algo = (level < 5 ? 0 : 1); + if (p->fb < 0) + p->fb = (level < 7 ? 32 : 64); + if (p->btMode < 0) + p->btMode = (p->algo == 0 ? 0 : 1); + if (p->numHashBytes < 0) + p->numHashBytes = 4; + if (p->mc == 0) + p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1); + if (p->numThreads < 0) + p->numThreads = ((p->btMode && p->algo) ? 2 : 1); +} + +uint32_t LzmaEncProps_GetDictSize(const CLzmaEncProps *props2) +{ + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + return props.dictSize; +} + +/* #define LZMA_LOG_BSR */ +/* Define it for Intel's CPU */ + +#ifdef LZMA_LOG_BSR + +#define kDicLogSizeMaxCompress 30 + +#define BSR2_RET(pos, res) \ + { \ + unsigned long i; \ + _BitScanReverse(&i, (pos)); \ + res = (i + i) + ((pos >> (i - 1)) & 1); \ + } + +uint32_t GetPosSlot1(uint32_t pos) +{ + uint32_t res; + BSR2_RET(pos, res); + return res; +} +#define GetPosSlot2(pos, res) \ + { \ + BSR2_RET(pos, res); \ + } +#define GetPosSlot(pos, res) \ + { \ + if (pos < 2) \ + res = pos; \ + else \ + BSR2_RET(pos, res); \ + } + +#else + +#define kNumLogBits (9 + (int)sizeof(size_t) / 2) +#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7) + +void LzmaEnc_FastPosInit(uint8_t *g_FastPos) +{ + int c = 2, slotFast; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + + for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++) + { + uint32_t k = (1 << ((slotFast >> 1) - 1)); + uint32_t j; + for (j = 0; j < k; j++, c++) + g_FastPos[c] = (uint8_t)slotFast; + } +} + +#define BSR2_RET(pos, res) \ + { \ + uint32_t i = 6 + ((kNumLogBits - 1) & \ + (0 - (((((uint32_t)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ + res = p->g_FastPos[pos >> i] + (i * 2); \ + } +/* +#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ + p->g_FastPos[pos >> 6] + 12 : \ + p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; } +*/ + +#define GetPosSlot1(pos) p->g_FastPos[pos] +#define GetPosSlot2(pos, res) \ + { \ + BSR2_RET(pos, res); \ + } +#define GetPosSlot(pos, res) \ + { \ + if (pos < kNumFullDistances) \ + res = p->g_FastPos[pos]; \ + else \ + BSR2_RET(pos, res); \ + } + +#endif + +#define LZMA_NUM_REPS 4 + +typedef unsigned CState; + +typedef struct _COptimal +{ + uint32_t price; + + CState state; + int prev1IsChar; + int prev2; + + uint32_t posPrev2; + uint32_t backPrev2; + + uint32_t posPrev; + uint32_t backPrev; + uint32_t backs[LZMA_NUM_REPS]; +} COptimal; + +#define kNumOpts (1 << 12) + +#define kNumLenToPosStates 4 +#define kNumPosSlotBits 6 +#define kDicLogSizeMin 0 +#define kDicLogSizeMax 32 +#define kDistTableSizeMax (kDicLogSizeMax * 2) + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) +#define kAlignMask (kAlignTableSize - 1) + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) + +#define kNumFullDistances (1 << (kEndPosModelIndex / 2)) + +#ifdef _LZMA_PROB32 +#define CLzmaProb uint32_t +#else +#define CLzmaProb uint16_t +#endif + +#define LZMA_PB_MAX 4 +#define LZMA_LC_MAX 8 +#define LZMA_LP_MAX 4 + +#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define LZMA_MATCH_LEN_MIN 2 +#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) + +#define kNumStates 12 + +typedef struct +{ + CLzmaProb choice; + CLzmaProb choice2; + CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; + CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; + CLzmaProb high[kLenNumHighSymbols]; +} CLenEnc; + +typedef struct +{ + CLenEnc p; + uint32_t prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; + uint32_t tableSize; + uint32_t counters[LZMA_NUM_PB_STATES_MAX]; +} CLenPriceEnc; + +typedef struct _CRangeEnc +{ + uint32_t range; + uint8_t cache; + uint64_t low; + uint64_t cacheSize; + uint8_t *buf; + uint8_t *bufLim; + uint8_t *bufBase; + ISeqOutStream *outStream; + uint64_t processed; + SRes res; +} CRangeEnc; + +typedef struct _CSeqInStreamBuf +{ + ISeqInStream funcTable; + const uint8_t *data; + size_t rem; +} CSeqInStreamBuf; + +static SRes MyRead(void *pp, void *data, size_t *size) +{ + size_t curSize = *size; + CSeqInStreamBuf *p = (CSeqInStreamBuf *)pp; + if (p->rem < curSize) + curSize = p->rem; + memcpy(data, p->data, curSize); + p->rem -= curSize; + p->data += curSize; + *size = curSize; + return SZ_OK; +} + +typedef struct +{ + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + uint32_t reps[LZMA_NUM_REPS]; + uint32_t state; +} CSaveState; + +typedef struct _CLzmaEnc +{ + IMatchFinder matchFinder; + void *matchFinderObj; + +#ifdef COMPRESS_MF_MT + Bool mtMode; + CMatchFinderMt matchFinderMt; +#endif + + CMatchFinder matchFinderBase; + +#ifdef COMPRESS_MF_MT + Byte pad[128]; +#endif + + uint32_t optimumEndIndex; + uint32_t optimumCurrentIndex; + + uint32_t longestMatchLength; + uint32_t numPairs; + uint32_t numAvail; + COptimal opt[kNumOpts]; + +#ifndef LZMA_LOG_BSR + uint8_t g_FastPos[1 << kNumLogBits]; +#endif + + uint32_t ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; + uint32_t matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + uint32_t numFastBytes; + uint32_t additionalOffset; + uint32_t reps[LZMA_NUM_REPS]; + uint32_t state; + + uint32_t posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; + uint32_t distancesPrices[kNumLenToPosStates][kNumFullDistances]; + uint32_t alignPrices[kAlignTableSize]; + uint32_t alignPriceCount; + + uint32_t distTableSize; + + unsigned lc, lp, pb; + unsigned lpMask, pbMask; + + CLzmaProb *litProbs; + + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb isRep[kNumStates]; + CLzmaProb isRepG0[kNumStates]; + CLzmaProb isRepG1[kNumStates]; + CLzmaProb isRepG2[kNumStates]; + CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; + + CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; + CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + + CLenPriceEnc lenEnc; + CLenPriceEnc repLenEnc; + + unsigned lclp; + + Bool fastMode; + + CRangeEnc rc; + + Bool writeEndMark; + uint64_t nowPos64; + uint32_t matchPriceCount; + Bool finished; + Bool multiThread; + + SRes result; + uint32_t dictSize; + uint32_t matchFinderCycles; + + ISeqInStream *inStream; + CSeqInStreamBuf seqBufInStream; + + CSaveState saveState; +} CLzmaEnc; + +void LzmaEnc_SaveState(CLzmaEncHandle pp) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CSaveState *dest = &p->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb)); +} + +void LzmaEnc_RestoreState(CLzmaEncHandle pp) +{ + CLzmaEnc *dest = (CLzmaEnc *)pp; + const CSaveState *p = &dest->saveState; + int i; + dest->lenEnc = p->lenEnc; + dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + for (i = 0; i < kNumStates; i++) + { + memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); + memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); + } + for (i = 0; i < kNumLenToPosStates; i++) + memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); + memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); + memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); + memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); + memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); + memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); + memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); + memcpy(dest->reps, p->reps, sizeof(p->reps)); + memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb)); +} + +SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + CLzmaEncProps props = *props2; + LzmaEncProps_Normalize(&props); + + if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || + props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30)) + return SZ_ERROR_PARAM; + p->dictSize = props.dictSize; + p->matchFinderCycles = props.mc; + { + unsigned fb = props.fb; + if (fb < 5) + fb = 5; + if (fb > LZMA_MATCH_LEN_MAX) + fb = LZMA_MATCH_LEN_MAX; + p->numFastBytes = fb; + } + p->lc = props.lc; + p->lp = props.lp; + p->pb = props.pb; + p->fastMode = (props.algo == 0); + p->matchFinderBase.btMode = props.btMode; + { + uint32_t numHashBytes = 4; + if (props.btMode) + { + if (props.numHashBytes < 2) + numHashBytes = 2; + else if (props.numHashBytes < 4) + numHashBytes = props.numHashBytes; + } + p->matchFinderBase.numHashBytes = numHashBytes; + } + + p->matchFinderBase.cutValue = props.mc; + + p->writeEndMark = props.writeEndMark; + +#ifdef COMPRESS_MF_MT + /* + if (newMultiThread != _multiThread) + { + ReleaseMatchFinder(); + _multiThread = newMultiThread; + } + */ + p->multiThread = (props.numThreads > 1); +#endif + + return SZ_OK; +} + +static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const int kShortRepNextStates[kNumStates] = {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; + +#define IsCharState(s) ((s) < 7) + +#define GetLenToPosState(len) \ + (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) + +#define kInfinityPrice (1 << 30) + +static void RangeEnc_Construct(CRangeEnc *p) +{ + p->outStream = 0; + p->bufBase = 0; +} + +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) + +#define RC_BUF_SIZE (1 << 16) +static int RangeEnc_Alloc(CRangeEnc *p) +{ + if (p->bufBase == 0) + { + p->bufBase = malloc(RC_BUF_SIZE); + if (p->bufBase == 0) + return 0; + p->bufLim = p->bufBase + RC_BUF_SIZE; + } + return 1; +} + +static void RangeEnc_Free(CRangeEnc *p) +{ + free(p->bufBase); + p->bufBase = 0; +} + +static void RangeEnc_Init(CRangeEnc *p) +{ + /* Stream.Init(); */ + p->low = 0; + p->range = 0xFFFFFFFF; + p->cacheSize = 1; + p->cache = 0; + + p->buf = p->bufBase; + + p->processed = 0; + p->res = SZ_OK; +} + +static void RangeEnc_FlushStream(CRangeEnc *p) +{ + size_t num; + if (p->res != SZ_OK) + return; + num = p->buf - p->bufBase; + if (num != p->outStream->Write(p->outStream, p->bufBase, num)) + p->res = SZ_ERROR_WRITE; + p->processed += num; + p->buf = p->bufBase; +} + +static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +{ + if ((uint32_t)p->low < (uint32_t)0xFF000000 || (int)(p->low >> 32) != 0) + { + uint8_t temp = p->cache; + do + { + uint8_t *buf = p->buf; + *buf++ = (uint8_t)(temp + (uint8_t)(p->low >> 32)); + p->buf = buf; + if (buf == p->bufLim) + RangeEnc_FlushStream(p); + temp = 0xFF; + } while (--p->cacheSize != 0); + p->cache = (uint8_t)((uint32_t)p->low >> 24); + } + p->cacheSize++; + p->low = (uint32_t)p->low << 8; +} + +static void RangeEnc_FlushData(CRangeEnc *p) +{ + int i; + for (i = 0; i < 5; i++) + RangeEnc_ShiftLow(p); +} + +static void RangeEnc_EncodeDirectBits(CRangeEnc *p, uint32_t value, int numBits) +{ + do + { + p->range >>= 1; + p->low += p->range & (0 - ((value >> --numBits) & 1)); + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } + } while (numBits != 0); +} + +static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, uint32_t symbol) +{ + uint32_t ttt = *prob; + uint32_t newBound = (p->range >> kNumBitModelTotalBits) * ttt; + if (symbol == 0) + { + p->range = newBound; + ttt += (kBitModelTotal - ttt) >> kNumMoveBits; + } + else + { + p->low += newBound; + p->range -= newBound; + ttt -= ttt >> kNumMoveBits; + } + *prob = (CLzmaProb)ttt; + if (p->range < kTopValue) + { + p->range <<= 8; + RangeEnc_ShiftLow(p); + } +} + +static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, uint32_t symbol) +{ + symbol |= 0x100; + do + { + RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + symbol <<= 1; + } while (symbol < 0x10000); +} + +static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, uint32_t symbol, + uint32_t matchByte) +{ + uint32_t offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), + (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } while (symbol < 0x10000); +} + +void LzmaEnc_InitPriceTables(uint32_t *ProbPrices) +{ + uint32_t i; + for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; + i += (1 << kNumMoveReducingBits)) + { + const int kCyclesBits = kNumBitPriceShiftBits; + uint32_t w = i; + uint32_t bitCount = 0; + int j; + for (j = 0; j < kCyclesBits; j++) + { + w = w * w; + bitCount <<= 1; + while (w >= ((uint32_t)1 << 16)) + { + w >>= 1; + bitCount++; + } + } + ProbPrices[i >> kNumMoveReducingBits] = + ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + } +} + +#define GET_PRICE(prob, symbol) \ + p->ProbPrices \ + [((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICEa(prob, symbol) \ + ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + +#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + +static uint32_t LitEnc_GetPrice(const CLzmaProb *probs, uint32_t symbol, uint32_t *ProbPrices) +{ + uint32_t price = 0; + symbol |= 0x100; + do + { + price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); + symbol <<= 1; + } while (symbol < 0x10000); + return price; +} + +static uint32_t LitEnc_GetPriceMatched(const CLzmaProb *probs, uint32_t symbol, + uint32_t matchByte, uint32_t *ProbPrices) +{ + uint32_t price = 0; + uint32_t offs = 0x100; + symbol |= 0x100; + do + { + matchByte <<= 1; + price += + GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1); + symbol <<= 1; + offs &= ~(matchByte ^ symbol); + } while (symbol < 0x10000); + return price; +} + +static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, uint32_t symbol) +{ + uint32_t m = 1; + int i; + for (i = numBitLevels; i != 0;) + { + uint32_t bit; + i--; + bit = (symbol >> i) & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + } +} + +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, + uint32_t symbol) +{ + uint32_t m = 1; + int i; + for (i = 0; i < numBitLevels; i++) + { + uint32_t bit = symbol & 1; + RangeEnc_EncodeBit(rc, probs + m, bit); + m = (m << 1) | bit; + symbol >>= 1; + } +} + +static uint32_t RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, uint32_t symbol, + uint32_t *ProbPrices) +{ + uint32_t price = 0; + symbol |= (1 << numBitLevels); + while (symbol != 1) + { + price += GET_PRICEa(probs[symbol >> 1], symbol & 1); + symbol >>= 1; + } + return price; +} + +static uint32_t RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, + uint32_t symbol, uint32_t *ProbPrices) +{ + uint32_t price = 0; + uint32_t m = 1; + int i; + for (i = numBitLevels; i != 0; i--) + { + uint32_t bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[m], bit); + m = (m << 1) | bit; + } + return price; +} + +static void LenEnc_Init(CLenEnc *p) +{ + unsigned i; + p->choice = p->choice2 = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) + p->low[i] = kProbInitValue; + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) + p->mid[i] = kProbInitValue; + for (i = 0; i < kLenNumHighSymbols; i++) + p->high[i] = kProbInitValue; +} + +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, uint32_t symbol, uint32_t posState) +{ + if (symbol < kLenNumLowSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice, 0); + RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice, 1); + if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) + { + RangeEnc_EncodeBit(rc, &p->choice2, 0); + RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, + symbol - kLenNumLowSymbols); + } + else + { + RangeEnc_EncodeBit(rc, &p->choice2, 1); + RcTree_Encode(rc, p->high, kLenNumHighBits, + symbol - kLenNumLowSymbols - kLenNumMidSymbols); + } + } +} + +static void LenEnc_SetPrices(CLenEnc *p, uint32_t posState, uint32_t numSymbols, + uint32_t *prices, uint32_t *ProbPrices) +{ + uint32_t a0 = GET_PRICE_0a(p->choice); + uint32_t a1 = GET_PRICE_1a(p->choice); + uint32_t b0 = a1 + GET_PRICE_0a(p->choice2); + uint32_t b1 = a1 + GET_PRICE_1a(p->choice2); + uint32_t i = 0; + for (i = 0; i < kLenNumLowSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, + i, ProbPrices); + } + for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) + { + if (i >= numSymbols) + return; + prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, + i - kLenNumLowSymbols, ProbPrices); + } + for (; i < numSymbols; i++) + prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, + i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); +} + +static void MY_FAST_CALL +LenPriceEnc_UpdateTable(CLenPriceEnc *p, uint32_t posState, uint32_t *ProbPrices) +{ + LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); + p->counters[posState] = p->tableSize; +} + +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, uint32_t numPosStates, + uint32_t *ProbPrices) +{ + uint32_t posState; + for (posState = 0; posState < numPosStates; posState++) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, uint32_t symbol, uint32_t posState, + Bool updatePrice, uint32_t *ProbPrices) +{ + LenEnc_Encode(&p->p, rc, symbol, posState); + if (updatePrice) + if (--p->counters[posState] == 0) + LenPriceEnc_UpdateTable(p, posState, ProbPrices); +} + +static void MovePos(CLzmaEnc *p, uint32_t num) +{ +#ifdef SHOW_STAT + ttt += num; + printf("\n MovePos %d", num); +#endif + if (num != 0) + { + p->additionalOffset += num; + p->matchFinder.Skip(p->matchFinderObj, num); + } +} + +static uint32_t ReadMatchDistances(CLzmaEnc *p, uint32_t *numDistancePairsRes) +{ + uint32_t lenRes = 0, numPairs; + p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); + numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); +#ifdef SHOW_STAT + printf("\n i = %d numPairs = %d ", ttt, numPairs / 2); + ttt++; + { + uint32_t i; + for (i = 0; i < numPairs; i += 2) + printf("%2d %6d | ", p->matches[i], p->matches[i + 1]); + } +#endif + if (numPairs > 0) + { + lenRes = p->matches[numPairs - 2]; + if (lenRes == p->numFastBytes) + { + const uint8_t *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + uint32_t distance = p->matches[numPairs - 1] + 1; + uint32_t numAvail = p->numAvail; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + { + const uint8_t *pby2 = pby - distance; + for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++) + ; + } + } + } + p->additionalOffset++; + *numDistancePairsRes = numPairs; + return lenRes; +} + +#define MakeAsChar(p) \ + (p)->backPrev = (uint32_t)(-1); \ + (p)->prev1IsChar = False; +#define MakeAsShortRep(p) \ + (p)->backPrev = 0; \ + (p)->prev1IsChar = False; +#define IsShortRep(p) ((p)->backPrev == 0) + +static uint32_t GetRepLen1Price(CLzmaEnc *p, uint32_t state, uint32_t posState) +{ + return GET_PRICE_0(p->isRepG0[state]) + GET_PRICE_0(p->isRep0Long[state][posState]); +} + +static uint32_t GetPureRepPrice(CLzmaEnc *p, uint32_t repIndex, uint32_t state, + uint32_t posState) +{ + uint32_t price; + if (repIndex == 0) + { + price = GET_PRICE_0(p->isRepG0[state]); + price += GET_PRICE_1(p->isRep0Long[state][posState]); + } + else + { + price = GET_PRICE_1(p->isRepG0[state]); + if (repIndex == 1) + price += GET_PRICE_0(p->isRepG1[state]); + else + { + price += GET_PRICE_1(p->isRepG1[state]); + price += GET_PRICE(p->isRepG2[state], repIndex - 2); + } + } + return price; +} + +static uint32_t GetRepPrice(CLzmaEnc *p, uint32_t repIndex, uint32_t len, uint32_t state, + uint32_t posState) +{ + return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + + GetPureRepPrice(p, repIndex, state, posState); +} + +static uint32_t Backward(CLzmaEnc *p, uint32_t *backRes, uint32_t cur) +{ + uint32_t posMem = p->opt[cur].posPrev; + uint32_t backMem = p->opt[cur].backPrev; + p->optimumEndIndex = cur; + do + { + if (p->opt[cur].prev1IsChar) + { + MakeAsChar(&p->opt[posMem]) + p->opt[posMem].posPrev = posMem - 1; + if (p->opt[cur].prev2) + { + p->opt[posMem - 1].prev1IsChar = False; + p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; + p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; + } + } + { + uint32_t posPrev = posMem; + uint32_t backCur = backMem; + + backMem = p->opt[posPrev].backPrev; + posMem = p->opt[posPrev].posPrev; + + p->opt[posPrev].backPrev = backCur; + p->opt[posPrev].posPrev = cur; + cur = posPrev; + } + } while (cur != 0); + *backRes = p->opt[0].backPrev; + p->optimumCurrentIndex = p->opt[0].posPrev; + return p->optimumCurrentIndex; +} + +#define LIT_PROBS(pos, prevByte) \ + (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300) + +static uint32_t GetOptimum(CLzmaEnc *p, uint32_t position, uint32_t *backRes) +{ + uint32_t numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; + uint32_t matchPrice, repMatchPrice, normalMatchPrice; + uint32_t reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; + uint32_t *matches; + const uint8_t *data; + uint8_t curByte, matchByte; + if (p->optimumEndIndex != p->optimumCurrentIndex) + { + const COptimal *opt = &p->opt[p->optimumCurrentIndex]; + uint32_t lenRes = opt->posPrev - p->optimumCurrentIndex; + *backRes = opt->backPrev; + p->optimumCurrentIndex = opt->posPrev; + return lenRes; + } + p->optimumCurrentIndex = p->optimumEndIndex = 0; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + if (numAvail < 2) + { + *backRes = (uint32_t)(-1); + return 1; + } + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + repMaxIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + uint32_t lenTest; + const uint8_t *data2; + reps[i] = p->reps[i]; + data2 = data - (reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + { + repLens[i] = 0; + continue; + } + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++) + ; + repLens[i] = lenTest; + if (lenTest > repLens[repMaxIndex]) + repMaxIndex = i; + } + if (repLens[repMaxIndex] >= p->numFastBytes) + { + uint32_t lenRes; + *backRes = repMaxIndex; + lenRes = repLens[repMaxIndex]; + MovePos(p, lenRes - 1); + return lenRes; + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) + { + *backRes = (uint32_t) - 1; + return 1; + } + + p->opt[0].state = (CState)p->state; + + posState = (position & p->pbMask); + + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + p->opt[1].price = + GET_PRICE_0(p->isMatch[p->state][posState]) + + (!IsCharState(p->state) + ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) + : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + MakeAsChar(&p->opt[1]); + + matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + + if (matchByte == curByte) + { + uint32_t shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); + if (shortRepPrice < p->opt[1].price) + { + p->opt[1].price = shortRepPrice; + MakeAsShortRep(&p->opt[1]); + } + } + lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); + + if (lenEnd < 2) + { + *backRes = p->opt[1].backPrev; + return 1; + } + + p->opt[1].posPrev = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + p->opt[0].backs[i] = reps[i]; + + len = lenEnd; + do + p->opt[len--].price = kInfinityPrice; + while (len >= 2); + + for (i = 0; i < LZMA_NUM_REPS; i++) + { + uint32_t repLen = repLens[i]; + uint32_t price; + if (repLen < 2) + continue; + price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); + do + { + uint32_t curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; + COptimal *opt = &p->opt[repLen]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = i; + opt->prev1IsChar = False; + } + } while (--repLen >= 2); + } + + normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + + len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= mainLen) + { + uint32_t offs = 0; + while (len > matches[offs]) + offs += 2; + for (;; len++) + { + COptimal *opt; + uint32_t distance = matches[offs + 1]; + + uint32_t curAndLenPrice = + normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; + uint32_t lenToPosState = GetLenToPosState(len); + if (distance < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][distance]; + else + { + uint32_t slot; + GetPosSlot2(distance, slot); + curAndLenPrice += p->alignPrices[distance & kAlignMask] + + p->posSlotPrices[lenToPosState][slot]; + } + opt = &p->opt[len]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = 0; + opt->backPrev = distance + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + if (len == matches[offs]) + { + offs += 2; + if (offs == numPairs) + break; + } + } + } + + cur = 0; + +#ifdef SHOW_STAT2 + if (position >= 0) + { + unsigned i; + printf("\n pos = %4X", position); + for (i = cur; i <= lenEnd; i++) + printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price); + } +#endif + + for (;;) + { + uint32_t numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; + uint32_t curPrice, curAnd1Price, matchPrice, repMatchPrice; + Bool nextIsChar; + uint8_t curByte, matchByte; + const uint8_t *data; + COptimal *curOpt; + COptimal *nextOpt; + + cur++; + if (cur == lenEnd) + return Backward(p, backRes, cur); + + newLen = ReadMatchDistances(p, &numPairs); + if (newLen >= p->numFastBytes) + { + p->numPairs = numPairs; + p->longestMatchLength = newLen; + return Backward(p, backRes, cur); + } + position++; + curOpt = &p->opt[cur]; + posPrev = curOpt->posPrev; + if (curOpt->prev1IsChar) + { + posPrev--; + if (curOpt->prev2) + { + state = p->opt[curOpt->posPrev2].state; + if (curOpt->backPrev2 < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + else + state = p->opt[posPrev].state; + state = kLiteralNextStates[state]; + } + else + state = p->opt[posPrev].state; + if (posPrev == cur - 1) + { + if (IsShortRep(curOpt)) + state = kShortRepNextStates[state]; + else + state = kLiteralNextStates[state]; + } + else + { + uint32_t pos; + const COptimal *prevOpt; + if (curOpt->prev1IsChar && curOpt->prev2) + { + posPrev = curOpt->posPrev2; + pos = curOpt->backPrev2; + state = kRepNextStates[state]; + } + else + { + pos = curOpt->backPrev; + if (pos < LZMA_NUM_REPS) + state = kRepNextStates[state]; + else + state = kMatchNextStates[state]; + } + prevOpt = &p->opt[posPrev]; + if (pos < LZMA_NUM_REPS) + { + uint32_t i; + reps[0] = prevOpt->backs[pos]; + for (i = 1; i <= pos; i++) + reps[i] = prevOpt->backs[i - 1]; + for (; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i]; + } + else + { + uint32_t i; + reps[0] = (pos - LZMA_NUM_REPS); + for (i = 1; i < LZMA_NUM_REPS; i++) + reps[i] = prevOpt->backs[i - 1]; + } + } + curOpt->state = (CState)state; + + curOpt->backs[0] = reps[0]; + curOpt->backs[1] = reps[1]; + curOpt->backs[2] = reps[2]; + curOpt->backs[3] = reps[3]; + + curPrice = curOpt->price; + nextIsChar = False; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + curByte = *data; + matchByte = *(data - (reps[0] + 1)); + + posState = (position & p->pbMask); + + curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + curAnd1Price += + (!IsCharState(state) + ? LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) + : LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + nextOpt = &p->opt[cur + 1]; + + if (curAnd1Price < nextOpt->price) + { + nextOpt->price = curAnd1Price; + nextOpt->posPrev = cur; + MakeAsChar(nextOpt); + nextIsChar = True; + } + + matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); + + if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) + { + uint32_t shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); + if (shortRepPrice <= nextOpt->price) + { + nextOpt->price = shortRepPrice; + nextOpt->posPrev = cur; + MakeAsShortRep(nextOpt); + nextIsChar = True; + } + } + numAvailFull = p->numAvail; + { + uint32_t temp = kNumOpts - 1 - cur; + if (temp < numAvailFull) + numAvailFull = temp; + } + + if (numAvailFull < 2) + continue; + numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); + + if (!nextIsChar && matchByte != curByte) /* speed optimization */ + { + /* try Literal + rep0 */ + uint32_t temp; + uint32_t lenTest2; + const uint8_t *data2 = data - (reps[0] + 1); + uint32_t limit = p->numFastBytes + 1; + if (limit > numAvailFull) + limit = numAvailFull; + + for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++) + ; + lenTest2 = temp - 1; + if (lenTest2 >= 2) + { + uint32_t state2 = kLiteralNextStates[state]; + uint32_t posStateNext = (position + 1) & p->pbMask; + uint32_t nextRepMatchPrice = curAnd1Price + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + /* for (; lenTest2 >= 2; lenTest2--) */ + { + uint32_t curAndLenPrice; + COptimal *opt; + uint32_t offset = cur + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = + nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = False; + } + } + } + } + + startLen = 2; /* speed optimization */ + { + uint32_t repIndex; + for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) + { + uint32_t lenTest; + uint32_t lenTestTemp; + uint32_t price; + const uint8_t *data2 = data - (reps[repIndex] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; + lenTest++) + ; + while (lenEnd < cur + lenTest) + p->opt[++lenEnd].price = kInfinityPrice; + lenTestTemp = lenTest; + price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); + do + { + uint32_t curAndLenPrice = + price + p->repLenEnc.prices[posState][lenTest - 2]; + COptimal *opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = repIndex; + opt->prev1IsChar = False; + } + } while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex == 0) + startLen = lenTest + 1; + + /* if (_maxMode) */ + { + uint32_t lenTest2 = lenTest + 1; + uint32_t limit = lenTest2 + p->numFastBytes; + uint32_t nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++) + ; + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + uint32_t state2 = kRepNextStates[state]; + uint32_t posStateNext = (position + lenTest) & p->pbMask; + uint32_t curAndLenCharPrice = + price + p->repLenEnc.prices[posState][lenTest - 2] + + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched( + LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], + data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (position + lenTest + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + uint32_t curAndLenPrice; + COptimal *opt; + uint32_t offset = cur + lenTest + 1 + lenTest2; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = repIndex; + } + } + } + } + } + } + /* for (uint32_t lenTest = 2; lenTest <= newLen; lenTest++) */ + if (newLen > numAvail) + { + newLen = numAvail; + for (numPairs = 0; newLen > matches[numPairs]; numPairs += 2) + ; + matches[numPairs] = newLen; + numPairs += 2; + } + if (newLen >= startLen) + { + uint32_t normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); + uint32_t offs, curBack, posSlot; + uint32_t lenTest; + while (lenEnd < cur + newLen) + p->opt[++lenEnd].price = kInfinityPrice; + + offs = 0; + while (startLen > matches[offs]) + offs += 2; + curBack = matches[offs + 1]; + GetPosSlot2(curBack, posSlot); + for (lenTest = /*2*/ startLen;; lenTest++) + { + uint32_t curAndLenPrice = + normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; + uint32_t lenToPosState = GetLenToPosState(lenTest); + COptimal *opt; + if (curBack < kNumFullDistances) + curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; + else + curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + + p->alignPrices[curBack & kAlignMask]; + + opt = &p->opt[cur + lenTest]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur; + opt->backPrev = curBack + LZMA_NUM_REPS; + opt->prev1IsChar = False; + } + + if (/*_maxMode && */ lenTest == matches[offs]) + { + /* Try Match + Literal + Rep0 */ + const uint8_t *data2 = data - (curBack + 1); + uint32_t lenTest2 = lenTest + 1; + uint32_t limit = lenTest2 + p->numFastBytes; + uint32_t nextRepMatchPrice; + if (limit > numAvailFull) + limit = numAvailFull; + for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++) + ; + lenTest2 -= lenTest + 1; + if (lenTest2 >= 2) + { + uint32_t state2 = kMatchNextStates[state]; + uint32_t posStateNext = (position + lenTest) & p->pbMask; + uint32_t curAndLenCharPrice = + curAndLenPrice + GET_PRICE_0(p->isMatch[state2][posStateNext]) + + LitEnc_GetPriceMatched( + LIT_PROBS(position + lenTest, data[lenTest - 1]), data[lenTest], + data2[lenTest], p->ProbPrices); + state2 = kLiteralNextStates[state2]; + posStateNext = (posStateNext + 1) & p->pbMask; + nextRepMatchPrice = curAndLenCharPrice + + GET_PRICE_1(p->isMatch[state2][posStateNext]) + + GET_PRICE_1(p->isRep[state2]); + + /* for (; lenTest2 >= 2; lenTest2--) */ + { + uint32_t offset = cur + lenTest + 1 + lenTest2; + uint32_t curAndLenPrice; + COptimal *opt; + while (lenEnd < offset) + p->opt[++lenEnd].price = kInfinityPrice; + curAndLenPrice = nextRepMatchPrice + + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + opt = &p->opt[offset]; + if (curAndLenPrice < opt->price) + { + opt->price = curAndLenPrice; + opt->posPrev = cur + lenTest + 1; + opt->backPrev = 0; + opt->prev1IsChar = True; + opt->prev2 = True; + opt->posPrev2 = cur; + opt->backPrev2 = curBack + LZMA_NUM_REPS; + } + } + } + offs += 2; + if (offs == numPairs) + break; + curBack = matches[offs + 1]; + if (curBack >= kNumFullDistances) + GetPosSlot2(curBack, posSlot); + } + } + } + } +} + +#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) + +static uint32_t GetOptimumFast(CLzmaEnc *p, uint32_t *backRes) +{ + uint32_t numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; + const uint8_t *data; + const uint32_t *matches; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else + { + mainLen = p->longestMatchLength; + numPairs = p->numPairs; + } + + numAvail = p->numAvail; + *backRes = (uint32_t) - 1; + if (numAvail < 2) + return 1; + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + + repLen = repIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + uint32_t len; + const uint8_t *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + for (len = 2; len < numAvail && data[len] == data2[len]; len++) + ; + if (len >= p->numFastBytes) + { + *backRes = i; + MovePos(p, len - 1); + return len; + } + if (len > repLen) + { + repIndex = i; + repLen = len; + } + } + + matches = p->matches; + if (mainLen >= p->numFastBytes) + { + *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; + MovePos(p, mainLen - 1); + return mainLen; + } + + mainDist = 0; /* for GCC */ + if (mainLen >= 2) + { + mainDist = matches[numPairs - 1]; + while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) + { + if (!ChangePair(matches[numPairs - 3], mainDist)) + break; + numPairs -= 2; + mainLen = matches[numPairs - 2]; + mainDist = matches[numPairs - 1]; + } + if (mainLen == 2 && mainDist >= 0x80) + mainLen = 1; + } + + if (repLen >= 2 && + ((repLen + 1 >= mainLen) || (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || + (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) + { + *backRes = repIndex; + MovePos(p, repLen - 1); + return repLen; + } + + if (mainLen < 2 || numAvail <= 2) + return 1; + + p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); + if (p->longestMatchLength >= 2) + { + uint32_t newDistance = matches[p->numPairs - 1]; + if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || + (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || + (p->longestMatchLength > mainLen + 1) || + (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && + ChangePair(newDistance, mainDist))) + return 1; + } + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + for (i = 0; i < LZMA_NUM_REPS; i++) + { + uint32_t len, limit; + const uint8_t *data2 = data - (p->reps[i] + 1); + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + limit = mainLen - 1; + for (len = 2; len < limit && data[len] == data2[len]; len++) + ; + if (len >= limit) + return 1; + } + *backRes = mainDist + LZMA_NUM_REPS; + MovePos(p, mainLen - 2); + return mainLen; +} + +static void WriteEndMarker(CLzmaEnc *p, uint32_t posState) +{ + uint32_t len; + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + len = LZMA_MATCH_LEN_MIN; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, + p->ProbPrices); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, + (1 << kNumPosSlotBits) - 1); + RangeEnc_EncodeDirectBits(&p->rc, (((uint32_t)1 << 30) - 1) >> kNumAlignBits, + 30 - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); +} + +static SRes CheckErrors(CLzmaEnc *p) +{ + if (p->result != SZ_OK) + return p->result; + if (p->rc.res != SZ_OK) + p->result = SZ_ERROR_WRITE; + if (p->matchFinderBase.result != SZ_OK) + p->result = SZ_ERROR_READ; + if (p->result != SZ_OK) + p->finished = True; + return p->result; +} + +static SRes Flush(CLzmaEnc *p, uint32_t nowPos) +{ + /* ReleaseMFStream(); */ + p->finished = True; + if (p->writeEndMark) + WriteEndMarker(p, nowPos & p->pbMask); + RangeEnc_FlushData(&p->rc); + RangeEnc_FlushStream(&p->rc); + return CheckErrors(p); +} + +static void FillAlignPrices(CLzmaEnc *p) +{ + uint32_t i; + for (i = 0; i < kAlignTableSize; i++) + p->alignPrices[i] = + RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + p->alignPriceCount = 0; +} + +static void FillDistancesPrices(CLzmaEnc *p) +{ + uint32_t tempPrices[kNumFullDistances]; + uint32_t i, lenToPosState; + for (i = kStartPosModelIndex; i < kNumFullDistances; i++) + { + uint32_t posSlot = GetPosSlot1(i); + uint32_t footerBits = ((posSlot >> 1) - 1); + uint32_t base = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, + i - base, p->ProbPrices); + } + + for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) + { + uint32_t posSlot; + const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; + uint32_t *posSlotPrices = p->posSlotPrices[lenToPosState]; + for (posSlot = 0; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] = + RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); + for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) + posSlotPrices[posSlot] += + ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + + { + uint32_t *distancesPrices = p->distancesPrices[lenToPosState]; + uint32_t i; + for (i = 0; i < kStartPosModelIndex; i++) + distancesPrices[i] = posSlotPrices[i]; + for (; i < kNumFullDistances; i++) + distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; + } + } + p->matchPriceCount = 0; +} + +void LzmaEnc_Construct(CLzmaEnc *p) +{ + RangeEnc_Construct(&p->rc); + MatchFinder_Construct(&p->matchFinderBase); +#ifdef COMPRESS_MF_MT + MatchFinderMt_Construct(&p->matchFinderMt); + p->matchFinderMt.MatchFinder = &p->matchFinderBase; +#endif + + { + CLzmaEncProps props; + LzmaEncProps_Init(&props); + LzmaEnc_SetProps(p, &props); + } + +#ifndef LZMA_LOG_BSR + LzmaEnc_FastPosInit(p->g_FastPos); +#endif + + LzmaEnc_InitPriceTables(p->ProbPrices); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +CLzmaEncHandle LzmaEnc_Create() +{ + void *p; + p = malloc(sizeof(CLzmaEnc)); + if (p != 0) + LzmaEnc_Construct((CLzmaEnc *)p); + return p; +} + +void LzmaEnc_FreeLits(CLzmaEnc *p) +{ + free(p->litProbs); + free(p->saveState.litProbs); + p->litProbs = 0; + p->saveState.litProbs = 0; +} + +void LzmaEnc_Destruct(CLzmaEnc *p) +{ +#ifdef COMPRESS_MF_MT + MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); +#endif + MatchFinder_Free(&p->matchFinderBase); + LzmaEnc_FreeLits(p); + RangeEnc_Free(&p->rc); +} + +void LzmaEnc_Destroy(CLzmaEncHandle p) +{ + LzmaEnc_Destruct((CLzmaEnc *)p); + free(p); +} + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, uint32_t maxPackSize, + uint32_t maxUnpackSize) +{ + uint32_t nowPos32, startPos32; + if (p->inStream != 0) + { + p->matchFinderBase.stream = p->inStream; + p->matchFinder.Init(p->matchFinderObj); + p->inStream = 0; + } + + if (p->finished) + return p->result; + RINOK(CheckErrors(p)); + + nowPos32 = (uint32_t)p->nowPos64; + startPos32 = nowPos32; + + if (p->nowPos64 == 0) + { + uint32_t numPairs; + uint8_t curByte; + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + return Flush(p, nowPos32); + ReadMatchDistances(p, &numPairs); + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); + p->state = kLiteralNextStates[p->state]; + curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset); + LitEnc_Encode(&p->rc, p->litProbs, curByte); + p->additionalOffset--; + nowPos32++; + } + + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) + for (;;) + { + uint32_t pos, len, posState; + + if (p->fastMode) + len = GetOptimumFast(p, &pos); + else + len = GetOptimum(p, nowPos32, &pos); + +#ifdef SHOW_STAT2 + printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos); +#endif + + posState = nowPos32 & p->pbMask; + if (len == 1 && pos == (uint32_t) - 1) + { + uint8_t curByte; + CLzmaProb *probs; + const uint8_t *data; + + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - + p->additionalOffset; + curByte = *data; + probs = LIT_PROBS(nowPos32, *(data - 1)); + if (IsCharState(p->state)) + LitEnc_Encode(&p->rc, probs, curByte); + else + LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); + p->state = kLiteralNextStates[p->state]; + } + else + { + RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); + if (pos < LZMA_NUM_REPS) + { + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); + if (pos == 0) + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); + RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], + ((len == 1) ? 0 : 1)); + } + else + { + uint32_t distance = p->reps[pos]; + RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); + if (pos == 1) + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); + else + { + RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); + RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); + if (pos == 3) + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + } + p->reps[1] = p->reps[0]; + p->reps[0] = distance; + } + if (len == 1) + p->state = kShortRepNextStates[p->state]; + else + { + LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, + posState, !p->fastMode, p->ProbPrices); + p->state = kRepNextStates[p->state]; + } + } + else + { + uint32_t posSlot; + RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + p->state = kMatchNextStates[p->state]; + LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, + !p->fastMode, p->ProbPrices); + pos -= LZMA_NUM_REPS; + GetPosSlot(pos, posSlot); + RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], + kNumPosSlotBits, posSlot); + + if (posSlot >= kStartPosModelIndex) + { + uint32_t footerBits = ((posSlot >> 1) - 1); + uint32_t base = ((2 | (posSlot & 1)) << footerBits); + uint32_t posReduced = pos - base; + + if (posSlot < kEndPosModelIndex) + RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, + footerBits, posReduced); + else + { + RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, + footerBits - kNumAlignBits); + RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, + posReduced & kAlignMask); + p->alignPriceCount++; + } + } + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + p->reps[1] = p->reps[0]; + p->reps[0] = pos; + p->matchPriceCount++; + } + } + p->additionalOffset -= len; + nowPos32 += len; + if (p->additionalOffset == 0) + { + uint32_t processed; + if (!p->fastMode) + { + if (p->matchPriceCount >= (1 << 7)) + FillDistancesPrices(p); + if (p->alignPriceCount >= kAlignTableSize) + FillAlignPrices(p); + } + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) + break; + processed = nowPos32 - startPos32; + if (useLimits) + { + if (processed + kNumOpts + 300 >= maxUnpackSize || + RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) + break; + } + else if (processed >= (1 << 15)) + { + p->nowPos64 += nowPos32 - startPos32; + return CheckErrors(p); + } + } + } + p->nowPos64 += nowPos32 - startPos32; + return Flush(p, nowPos32); +} + +#define kBigHashDicLimit ((uint32_t)1 << 24) + +static SRes LzmaEnc_Alloc(CLzmaEnc *p, uint32_t keepWindowSize) +{ + uint32_t beforeSize = kNumOpts; + Bool btMode; + if (!RangeEnc_Alloc(&p->rc)) + return SZ_ERROR_MEM; + btMode = (p->matchFinderBase.btMode != 0); +#ifdef COMPRESS_MF_MT + p->mtMode = (p->multiThread && !p->fastMode && btMode); +#endif + + { + unsigned lclp = p->lc + p->lp; + if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp) + { + LzmaEnc_FreeLits(p); + p->litProbs = (CLzmaProb *)malloc((0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)malloc((0x300 << lclp) * sizeof(CLzmaProb)); + if (p->litProbs == 0 || p->saveState.litProbs == 0) + { + LzmaEnc_FreeLits(p); + return SZ_ERROR_MEM; + } + p->lclp = lclp; + } + } + + p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit); + + if (beforeSize + p->dictSize < keepWindowSize) + beforeSize = keepWindowSize - p->dictSize; + +#ifdef COMPRESS_MF_MT + if (p->mtMode) + { + RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, + LZMA_MATCH_LEN_MAX)); + p->matchFinderObj = &p->matchFinderMt; + MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); + } + else +#endif + { + if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, + LZMA_MATCH_LEN_MAX)) + return SZ_ERROR_MEM; + p->matchFinderObj = &p->matchFinderBase; + MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder); + } + return SZ_OK; +} + +void LzmaEnc_Init(CLzmaEnc *p) +{ + uint32_t i; + p->state = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) + p->reps[i] = 0; + + RangeEnc_Init(&p->rc); + + for (i = 0; i < kNumStates; i++) + { + uint32_t j; + for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) + { + p->isMatch[i][j] = kProbInitValue; + p->isRep0Long[i][j] = kProbInitValue; + } + p->isRep[i] = kProbInitValue; + p->isRepG0[i] = kProbInitValue; + p->isRepG1[i] = kProbInitValue; + p->isRepG2[i] = kProbInitValue; + } + + { + uint32_t num = 0x300 << (p->lp + p->lc); + for (i = 0; i < num; i++) + p->litProbs[i] = kProbInitValue; + } + + { + for (i = 0; i < kNumLenToPosStates; i++) + { + CLzmaProb *probs = p->posSlotEncoder[i]; + uint32_t j; + for (j = 0; j < (1 << kNumPosSlotBits); j++) + probs[j] = kProbInitValue; + } + } + { + for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + p->posEncoders[i] = kProbInitValue; + } + + LenEnc_Init(&p->lenEnc.p); + LenEnc_Init(&p->repLenEnc.p); + + for (i = 0; i < (1 << kNumAlignBits); i++) + p->posAlignEncoder[i] = kProbInitValue; + + p->optimumEndIndex = 0; + p->optimumCurrentIndex = 0; + p->additionalOffset = 0; + + p->pbMask = (1 << p->pb) - 1; + p->lpMask = (1 << p->lp) - 1; +} + +void LzmaEnc_InitPrices(CLzmaEnc *p) +{ + if (!p->fastMode) + { + FillDistancesPrices(p); + FillAlignPrices(p); + } + + p->lenEnc.tableSize = p->repLenEnc.tableSize = p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; + LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); +} + +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, uint32_t keepWindowSize) +{ + uint32_t i; + for (i = 0; i < (uint32_t)kDicLogSizeMaxCompress; i++) + if (p->dictSize <= ((uint32_t)1 << i)) + break; + p->distTableSize = i * 2; + + p->finished = False; + p->result = SZ_OK; + RINOK(LzmaEnc_Alloc(p, keepWindowSize)); + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + p->nowPos64 = 0; + return SZ_OK; +} + +static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutStream *outStream) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + p->rc.outStream = outStream; + return LzmaEnc_AllocAndInit(p, 0); +} + +SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, uint32_t keepWindowSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->inStream = inStream; + return LzmaEnc_AllocAndInit(p, keepWindowSize); +} + +static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const uint8_t *src, size_t srcLen) +{ + p->seqBufInStream.funcTable.Read = MyRead; + p->seqBufInStream.data = src; + p->seqBufInStream.rem = srcLen; +} + +SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const uint8_t *src, size_t srcLen, + uint32_t keepWindowSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + LzmaEnc_SetInputBuf(p, src, srcLen); + p->inStream = &p->seqBufInStream.funcTable; + return LzmaEnc_AllocAndInit(p, keepWindowSize); +} + +void LzmaEnc_Finish(CLzmaEncHandle pp) +{ +#ifdef COMPRESS_MF_MT + CLzmaEnc *p = (CLzmaEnc *)pp; + if (p->mtMode) + MatchFinderMt_ReleaseStream(&p->matchFinderMt); +#else + pp = pp; +#endif +} + +typedef struct _CSeqOutStreamBuf +{ + ISeqOutStream funcTable; + uint8_t *data; + size_t rem; + Bool overflow; +} CSeqOutStreamBuf; + +static size_t MyWrite(void *pp, const void *data, size_t size) +{ + CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; + if (p->rem < size) + { + size = p->rem; + p->overflow = True; + } + memcpy(p->data, data, size); + p->rem -= size; + p->data += size; + return size; +} + +uint32_t LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); +} + +const uint8_t *LzmaEnc_GetCurBuf(CLzmaEncHandle pp) +{ + const CLzmaEnc *p = (CLzmaEnc *)pp; + return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; +} + +SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit, uint8_t *dest, size_t *destLen, + uint32_t desiredPackSize, uint32_t *unpackSize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + uint64_t nowPos64; + SRes res; + CSeqOutStreamBuf outStream; + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = False; + p->finished = False; + p->result = SZ_OK; + + if (reInit) + LzmaEnc_Init(p); + LzmaEnc_InitPrices(p); + nowPos64 = p->nowPos64; + RangeEnc_Init(&p->rc); + p->rc.outStream = &outStream.funcTable; + + res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); + + *unpackSize = (uint32_t)(p->nowPos64 - nowPos64); + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + + return res; +} + +SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, + ICompressProgress *progress) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + SRes res = SZ_OK; + +#ifdef COMPRESS_MF_MT + Byte allocaDummy[0x300]; + int i = 0; + for (i = 0; i < 16; i++) + allocaDummy[i] = (Byte)i; +#endif + + RINOK(LzmaEnc_Prepare(pp, inStream, outStream)); + + for (;;) + { + res = LzmaEnc_CodeOneBlock(p, False, 0, 0); + if (res != SZ_OK || p->finished != 0) + break; + if (progress != 0) + { + res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); + if (res != SZ_OK) + { + res = SZ_ERROR_PROGRESS; + break; + } + } + } + LzmaEnc_Finish(pp); + return res; +} + +SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, uint8_t *props, size_t *size) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + int i; + uint32_t dictSize = p->dictSize; + if (*size < LZMA_PROPS_SIZE) + return SZ_ERROR_PARAM; + *size = LZMA_PROPS_SIZE; + props[0] = (uint8_t)((p->pb * 5 + p->lp) * 9 + p->lc); + + for (i = 11; i <= 30; i++) + { + if (dictSize <= ((uint32_t)2 << i)) + { + dictSize = (2 << i); + break; + } + if (dictSize <= ((uint32_t)3 << i)) + { + dictSize = (3 << i); + break; + } + } + + for (i = 0; i < 4; i++) + props[1 + i] = (uint8_t)(dictSize >> (8 * i)); + return SZ_OK; +} + +SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, uint8_t *dest, size_t *destLen, const uint8_t *src, + size_t srcLen, int writeEndMark, ICompressProgress *progress) +{ + SRes res; + CLzmaEnc *p = (CLzmaEnc *)pp; + + CSeqOutStreamBuf outStream; + + LzmaEnc_SetInputBuf(p, src, srcLen); + + outStream.funcTable.Write = MyWrite; + outStream.data = dest; + outStream.rem = *destLen; + outStream.overflow = False; + + p->writeEndMark = writeEndMark; + res = LzmaEnc_Encode(pp, &outStream.funcTable, &p->seqBufInStream.funcTable, progress); + + *destLen -= outStream.rem; + if (outStream.overflow) + return SZ_ERROR_OUTPUT_EOF; + return res; +} + +SRes LzmaEncode(uint8_t *dest, size_t *destLen, const uint8_t *src, size_t srcLen, + const CLzmaEncProps *props, uint8_t *propsEncoded, size_t *propsSize, + int writeEndMark, ICompressProgress *progress) +{ + CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(); + SRes res; + if (p == 0) + return SZ_ERROR_MEM; + + res = LzmaEnc_SetProps(p, props); + if (res == SZ_OK) + { + res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize); + if (res == SZ_OK) + res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen, writeEndMark, progress); + } + + LzmaEnc_Destroy(p); + return res; +} diff --git a/depends/lzma/pavlov/LzmaEnc.h b/depends/lzma/pavlov/LzmaEnc.h new file mode 100755 index 00000000..961436e4 --- /dev/null +++ b/depends/lzma/pavlov/LzmaEnc.h @@ -0,0 +1,71 @@ +/* LzmaEnc.h -- LZMA Encoder +2008-10-04 : Igor Pavlov : Public domain */ + +#ifndef __LZMAENC_H +#define __LZMAENC_H + +#include "Types.h" + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaEncProps +{ + int level; /* 0 <= level <= 9 */ + uint32_t dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version + (1 << 12) <= dictSize <= (1 << 30) for 64-bit version + default = (1 << 24) */ + int lc; /* 0 <= lc <= 8, default = 3 */ + int lp; /* 0 <= lp <= 4, default = 0 */ + int pb; /* 0 <= pb <= 4, default = 2 */ + int algo; /* 0 - fast, 1 - normal, default = 1 */ + int fb; /* 5 <= fb <= 273, default = 32 */ + int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ + int numHashBytes; /* 2, 3 or 4, default = 4 */ + uint32_t mc; /* 1 <= mc <= (1 << 30), default = 32 */ + unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ + int numThreads; /* 1 or 2, default = 2 */ +} CLzmaEncProps; + +void LzmaEncProps_Init(CLzmaEncProps *p); +void LzmaEncProps_Normalize(CLzmaEncProps *p); +uint32_t LzmaEncProps_GetDictSize(const CLzmaEncProps *props2); + +/* ---------- CLzmaEncHandle Interface ---------- */ + +/* LzmaEnc_* functions can return the following exit codes: +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater in props + SZ_ERROR_WRITE - Write callback error. + SZ_ERROR_PROGRESS - some break from progress callback + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +typedef void *CLzmaEncHandle; + +CLzmaEncHandle LzmaEnc_Create(); +void LzmaEnc_Destroy(CLzmaEncHandle p); +SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, uint8_t *properties, size_t *size); +SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, + ICompressProgress *progress); +SRes LzmaEnc_MemEncode(CLzmaEncHandle p, uint8_t *dest, size_t *destLen, const uint8_t *src, + size_t srcLen, int writeEndMark, ICompressProgress *progress); + +/* ---------- One Call Interface ---------- */ + +/* LzmaEncode +Return code: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +SRes LzmaEncode(uint8_t *dest, size_t *destLen, const uint8_t *src, size_t srcLen, + const CLzmaEncProps *props, uint8_t *propsEncoded, size_t *propsSize, + int writeEndMark, ICompressProgress *progress); + +#endif diff --git a/depends/lzma/pavlov/LzmaLib.c b/depends/lzma/pavlov/LzmaLib.c new file mode 100755 index 00000000..6759d69b --- /dev/null +++ b/depends/lzma/pavlov/LzmaLib.c @@ -0,0 +1,41 @@ +/* LzmaLib.c -- LZMA library wrapper +2008-08-05 +Igor Pavlov +Public domain */ + +#include "LzmaEnc.h" +#include "LzmaDec.h" +#include "LzmaLib.h" + +MY_STDAPI +LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, + unsigned char *outProps, size_t *outPropsSize, + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* use (1 << N) or (3 << N). 4 KB < dictSize <= 128 MB */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ + ) +{ + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = level; + props.dictSize = dictSize; + props.lc = lc; + props.lp = lp; + props.pb = pb; + props.fb = fb; + props.numThreads = numThreads; + + return LzmaEncode(dest, destLen, src, srcLen, &props, outProps, outPropsSize, 0, NULL); +} + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, + size_t *srcLen, const unsigned char *props, size_t propsSize) +{ + ELzmaStatus status; + return LzmaDecode(dest, destLen, src, srcLen, props, (unsigned)propsSize, LZMA_FINISH_ANY, + &status); +} diff --git a/depends/lzma/pavlov/LzmaLib.h b/depends/lzma/pavlov/LzmaLib.h new file mode 100755 index 00000000..804329d1 --- /dev/null +++ b/depends/lzma/pavlov/LzmaLib.h @@ -0,0 +1,137 @@ +/* LzmaLib.h -- LZMA library interface +2008-08-05 +Igor Pavlov +Public domain */ + +#ifndef __LZMALIB_H +#define __LZMALIB_H + +#include "Types.h" + +#ifdef __cplusplus +#define MY_EXTERN_C extern "C" +#else +#define MY_EXTERN_C extern +#endif + +#define MY_STDAPI MY_EXTERN_C int MY_STD_CALL + +#define LZMA_PROPS_SIZE 5 + +/* +RAM requirements for LZMA: + for compression: (dictSize * 11.5 + 6 MB) + state_size + for decompression: dictSize + state_size + state_size = (4 + (1.5 << (lc + lp))) KB + by default (lc=3, lp=0), state_size = 16 KB. + +LZMA properties (5 bytes) format + Offset Size Description + 0 1 lc, lp and pb in encoded form. + 1 4 dictSize (little endian). +*/ + +/* +LzmaCompress +------------ + +outPropsSize - + In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. + Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = +LZMA_PROPS_SIZE = 5. + + LZMA Encoder will use defult values for any parameter, if it is + -1 for any from: level, loc, lp, pb, fb, numThreads + 0 for dictSize + +level - compression level: 0 <= level <= 9; + + level dictSize algo fb + 0: 16 KB 0 32 + 1: 64 KB 0 32 + 2: 256 KB 0 32 + 3: 1 MB 0 32 + 4: 4 MB 0 32 + 5: 16 MB 1 32 + 6: 32 MB 1 32 + 7+: 64 MB 1 64 + + The default value for "level" is 5. + + algo = 0 means fast method + algo = 1 means normal method + +dictSize - The dictionary size in bytes. The maximum value is + 128 MB = (1 << 27) bytes for 32-bit version + 1 GB = (1 << 30) bytes for 64-bit version + The default value is 16 MB = (1 << 24) bytes. + It's recommended to use the dictionary that is larger than 4 KB and + that can be calculated as (1 << N) or (3 << N) sizes. + +lc - The number of literal context bits (high bits of previous literal). + It can be in the range from 0 to 8. The default value is 3. + Sometimes lc=4 gives the gain for big files. + +lp - The number of literal pos bits (low bits of current position for literals). + It can be in the range from 0 to 4. The default value is 0. + The lp switch is intended for periodical data when the period is equal to 2^lp. + For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's + better to set lc=0, if you change lp switch. + +pb - The number of pos bits (low bits of current position). + It can be in the range from 0 to 4. The default value is 2. + The pb switch is intended for periodical data when the period is equal 2^pb. + +fb - Word size (the number of fast bytes). + It can be in the range from 5 to 273. The default value is 32. + Usually, a big number gives a little bit better compression ratio and + slower compression process. + +numThreads - The number of thereads. 1 or 2. The default value is 2. + Fast mode (algo = 0) can use only 1 thread. + +Out: + destLen - processed output size +Returns: + SZ_OK - OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_PARAM - Incorrect paramater + SZ_ERROR_OUTPUT_EOF - output buffer overflow + SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) +*/ + +MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, + size_t srcLen, unsigned char *outProps, + size_t *outPropsSize, /* *outPropsSize must be = 5 */ + int level, /* 0 <= level <= 9, default = 5 */ + unsigned dictSize, /* default = (1 << 24) */ + int lc, /* 0 <= lc <= 8, default = 3 */ + int lp, /* 0 <= lp <= 4, default = 0 */ + int pb, /* 0 <= pb <= 4, default = 2 */ + int fb, /* 5 <= fb <= 273, default = 32 */ + int numThreads /* 1 or 2, default = 2 */ + ); + +/* +LzmaUncompress +-------------- +In: + dest - output data + destLen - output data size + src - input data + srcLen - input data size +Out: + destLen - processed output size + srcLen - processed input size +Returns: + SZ_OK - OK + SZ_ERROR_DATA - Data error + SZ_ERROR_MEM - Memory allocation arror + SZ_ERROR_UNSUPPORTED - Unsupported properties + SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) +*/ + +MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, + size_t *srcLen, const unsigned char *props, size_t propsSize); + +#endif diff --git a/depends/lzma/pavlov/Types.h b/depends/lzma/pavlov/Types.h new file mode 100755 index 00000000..e75bcb4a --- /dev/null +++ b/depends/lzma/pavlov/Types.h @@ -0,0 +1,87 @@ +/* Types.h -- Basic types +2008-11-23 : Igor Pavlov : Public domain */ + +#pragma once + +#include +#include + +#ifdef _WIN32 +#include +#endif + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifndef RINOK +#define RINOK(x) \ + { \ + int __result__ = (x); \ + if (__result__ != 0) \ + return __result__; \ + } +#endif + +typedef int Bool; +#define True 1 +#define False 0 + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_STD_CALL __stdcall +#define MY_FAST_CALL MY_NO_INLINE __fastcall + +#else + +#define MY_CDECL +#define MY_STD_CALL +#define MY_FAST_CALL + +#endif + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef struct +{ + SRes (*Progress)(void *p, uint64_t inSize, uint64_t outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (uint64_t)(int64_t)-1 for size means unknown value. */ +} ICompressProgress; diff --git a/depends/lzma/wrapper/common_internal.c b/depends/lzma/wrapper/common_internal.c new file mode 100644 index 00000000..c9213ef4 --- /dev/null +++ b/depends/lzma/wrapper/common_internal.c @@ -0,0 +1,46 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + */ + +#include "common_internal.h" + +static void *elzmaAlloc(void *p, size_t size) +{ + struct elzma_alloc_struct *as = (struct elzma_alloc_struct *)p; + if (as->clientMallocFunc) + { + return as->clientMallocFunc(as->clientMallocContext, size); + } + return malloc(size); +} + +static void elzmaFree(void *p, void *address) +{ + struct elzma_alloc_struct *as = (struct elzma_alloc_struct *)p; + if (as->clientFreeFunc) + { + as->clientFreeFunc(as->clientMallocContext, address); + } + else + { + free(address); + } +} + +void init_alloc_struct(struct elzma_alloc_struct *as, elzma_malloc clientMallocFunc, + void *clientMallocContext, elzma_free clientFreeFunc, + void *clientFreeContext) +{ + as->Alloc = elzmaAlloc; + as->Free = elzmaFree; + as->clientMallocFunc = clientMallocFunc; + as->clientMallocContext = clientMallocContext; + as->clientFreeFunc = clientFreeFunc; + as->clientFreeContext = clientFreeContext; +} diff --git a/depends/lzma/wrapper/common_internal.h b/depends/lzma/wrapper/common_internal.h new file mode 100644 index 00000000..2c46fadf --- /dev/null +++ b/depends/lzma/wrapper/common_internal.h @@ -0,0 +1,60 @@ +#ifndef __ELZMA_COMMON_INTERNAL_H__ +#define __ELZMA_COMMON_INTERNAL_H__ + +#include "common.h" + +/** a structure which may be cast and passed into Igor's allocate + * routines */ +struct elzma_alloc_struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ + + elzma_malloc clientMallocFunc; + void *clientMallocContext; + + elzma_free clientFreeFunc; + void *clientFreeContext; +}; + +/* initialize an allocation structure, may be called safely multiple + * times */ +void init_alloc_struct(struct elzma_alloc_struct *allocStruct, elzma_malloc clientMallocFunc, + void *clientMallocContext, elzma_free clientFreeFunc, + void *clientFreeContext); + +/** superset representation of a compressed file header */ +struct elzma_file_header +{ + unsigned char pb; + unsigned char lp; + unsigned char lc; + unsigned char isStreamed; + long long unsigned int uncompressedSize; + unsigned int dictSize; +}; + +/** superset representation of a compressed file footer */ +struct elzma_file_footer +{ + unsigned int crc32; + long long unsigned int uncompressedSize; +}; + +/** a structure which encapsulates information about the particular + * file header and footer in use (lzip vs lzma vs (eventually) xz. + * The intention of this structure is to simplify compression and + * decompression logic by abstracting the file format details a bit. */ +struct elzma_format_handler +{ + unsigned int header_size; + void (*init_header)(struct elzma_file_header *hdr); + int (*parse_header)(const unsigned char *hdrBuf, struct elzma_file_header *hdr); + int (*serialize_header)(unsigned char *hdrBuf, const struct elzma_file_header *hdr); + + unsigned int footer_size; + int (*serialize_footer)(struct elzma_file_footer *ftr, unsigned char *ftrBuf); + int (*parse_footer)(const unsigned char *ftrBuf, struct elzma_file_footer *ftr); +}; + +#endif diff --git a/depends/lzma/wrapper/compress.c b/depends/lzma/wrapper/compress.c new file mode 100644 index 00000000..38ca0a68 --- /dev/null +++ b/depends/lzma/wrapper/compress.c @@ -0,0 +1,297 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + */ + +#include "compress.h" +#include "lzma_header.h" +#include "lzip_header.h" +#include "common_internal.h" + +#include "pavlov/Types.h" +#include "pavlov/LzmaEnc.h" +#include "pavlov/7zCrc.h" + +#include + +struct _elzma_compress_handle +{ + CLzmaEncProps props; + CLzmaEncHandle encHand; + unsigned long long uncompressedSize; + elzma_file_format format; + struct elzma_alloc_struct allocStruct; + struct elzma_format_handler formatHandler; +}; + +elzma_compress_handle elzma_compress_alloc() +{ + elzma_compress_handle hand = malloc(sizeof(struct _elzma_compress_handle)); + memset((void *)hand, 0, sizeof(struct _elzma_compress_handle)); + + /* "reasonable" defaults for props */ + LzmaEncProps_Init(&(hand->props)); + hand->props.lc = 3; + hand->props.lp = 0; + hand->props.pb = 2; + hand->props.level = 5; + hand->props.algo = 1; + hand->props.fb = 32; + hand->props.dictSize = 1 << 24; + hand->props.btMode = 1; + hand->props.numHashBytes = 4; + hand->props.mc = 32; + hand->props.numThreads = 1; + hand->props.writeEndMark = 1; + + init_alloc_struct(&(hand->allocStruct), NULL, NULL, NULL, NULL); + + /* default format is LZMA-Alone */ + initializeLZMAFormatHandler(&(hand->formatHandler)); + + return hand; +} + +void elzma_compress_free(elzma_compress_handle *hand) +{ + if (hand && *hand) + { + if ((*hand)->encHand) + { + LzmaEnc_Destroy((*hand)->encHand); + } + } + *hand = NULL; +} + +int elzma_compress_config(elzma_compress_handle hand, unsigned char lc, unsigned char lp, + unsigned char pb, unsigned char level, unsigned int dictionarySize, + elzma_file_format format, unsigned long long uncompressedSize) +{ + /* XXX: validate arguments are in valid ranges */ + + hand->props.lc = lc; + hand->props.lp = lp; + hand->props.pb = pb; + hand->props.level = level; + hand->props.dictSize = dictionarySize; + hand->uncompressedSize = uncompressedSize; + hand->format = format; + + /* default of LZMA-Alone is set at alloc time, and there are only + * two possible formats */ + if (format == ELZMA_lzip) + { + initializeLZIPFormatHandler(&(hand->formatHandler)); + } + + return ELZMA_E_OK; +} + +/* use Igor's stream hooks for compression. */ +struct elzmaInStream +{ + SRes (*ReadPtr)(void *p, void *buf, size_t *size); + elzma_read_callback inputStream; + void *inputContext; + unsigned int crc32; + unsigned int crc32a; + unsigned int crc32b; + unsigned int crc32c; + int calculateCRC; +}; + +static SRes elzmaReadFunc(void *p, void *buf, size_t *size) +{ + int rv; + struct elzmaInStream *is = (struct elzmaInStream *)p; + rv = is->inputStream(is->inputContext, buf, size); + if (rv == 0 && *size > 0 && is->calculateCRC) + { + is->crc32 = CrcUpdate(is->crc32, buf, *size); + } + return rv; +} + +struct elzmaOutStream +{ + size_t (*WritePtr)(void *p, const void *buf, size_t size); + elzma_write_callback outputStream; + void *outputContext; +}; + +static size_t elzmaWriteFunc(void *p, const void *buf, size_t size) +{ + struct elzmaOutStream *os = (struct elzmaOutStream *)p; + return os->outputStream(os->outputContext, buf, size); +} + +/* use Igor's stream hooks for compression. */ +struct elzmaProgressStruct +{ + SRes (*Progress)(void *p, uint64_t inSize, uint64_t outSize); + long long unsigned int uncompressedSize; + elzma_progress_callback progressCallback; + void *progressContext; +}; + +#include +static SRes elzmaProgress(void *p, uint64_t inSize, uint64_t outSize) +{ + struct elzmaProgressStruct *ps = (struct elzmaProgressStruct *)p; + if (ps->progressCallback) + { + ps->progressCallback(ps->progressContext, inSize, ps->uncompressedSize); + } + return SZ_OK; +} + +void elzma_compress_set_allocation_callbacks(elzma_compress_handle hand, + elzma_malloc mallocFunc, void *mallocFuncContext, + elzma_free freeFunc, void *freeFuncContext) +{ + if (hand) + { + init_alloc_struct(&(hand->allocStruct), mallocFunc, mallocFuncContext, freeFunc, + freeFuncContext); + } +} + +int elzma_compress_run(elzma_compress_handle hand, elzma_read_callback inputStream, + void *inputContext, elzma_write_callback outputStream, + void *outputContext, elzma_progress_callback progressCallback, + void *progressContext) +{ + struct elzmaInStream inStreamStruct; + struct elzmaOutStream outStreamStruct; + struct elzmaProgressStruct progressStruct; + SRes r; + + CrcGenerateTable(); + + if (hand == NULL || inputStream == NULL) + return ELZMA_E_BAD_PARAMS; + + /* initialize stream structrures */ + inStreamStruct.ReadPtr = elzmaReadFunc; + inStreamStruct.inputStream = inputStream; + inStreamStruct.inputContext = inputContext; + inStreamStruct.crc32 = CRC_INIT_VAL; + inStreamStruct.calculateCRC = (hand->formatHandler.serialize_footer != NULL); + + outStreamStruct.WritePtr = elzmaWriteFunc; + outStreamStruct.outputStream = outputStream; + outStreamStruct.outputContext = outputContext; + + progressStruct.Progress = elzmaProgress; + progressStruct.uncompressedSize = hand->uncompressedSize; + progressStruct.progressCallback = progressCallback; + progressStruct.progressContext = progressContext; + + /* create an encoding object */ + hand->encHand = LzmaEnc_Create(); + + if (hand->encHand == NULL) + { + return ELZMA_E_COMPRESS_ERROR; + } + + /* inintialize with compression parameters */ + if (SZ_OK != LzmaEnc_SetProps(hand->encHand, &(hand->props))) + { + return ELZMA_E_BAD_PARAMS; + } + + /* verify format is sane */ + if (ELZMA_lzma != hand->format && ELZMA_lzip != hand->format) + { + return ELZMA_E_UNSUPPORTED_FORMAT; + } + + /* now write the compression header header */ + { + unsigned char *hdr = + hand->allocStruct.Alloc(&(hand->allocStruct), hand->formatHandler.header_size); + + struct elzma_file_header h; + size_t wt; + + hand->formatHandler.init_header(&h); + h.pb = (unsigned char)hand->props.pb; + h.lp = (unsigned char)hand->props.lp; + h.lc = (unsigned char)hand->props.lc; + h.dictSize = hand->props.dictSize; + h.isStreamed = (unsigned char)(hand->uncompressedSize == 0); + h.uncompressedSize = hand->uncompressedSize; + + hand->formatHandler.serialize_header(hdr, &h); + + wt = outputStream(outputContext, (void *)hdr, hand->formatHandler.header_size); + + hand->allocStruct.Free(&(hand->allocStruct), hdr); + + if (wt != hand->formatHandler.header_size) + { + return ELZMA_E_OUTPUT_ERROR; + } + } + + /* begin LZMA encoding */ + /* XXX: expose encoding progress */ + r = LzmaEnc_Encode(hand->encHand, (ISeqOutStream *)&outStreamStruct, + (ISeqInStream *)&inStreamStruct, (ICompressProgress *)&progressStruct); + + if (r != SZ_OK) + return ELZMA_E_COMPRESS_ERROR; + + /* support a footer! (lzip) */ + if (hand->formatHandler.serialize_footer != NULL && hand->formatHandler.footer_size > 0) + { + size_t wt; + unsigned char *ftrBuf = + hand->allocStruct.Alloc(&(hand->allocStruct), hand->formatHandler.footer_size); + struct elzma_file_footer ftr; + ftr.crc32 = inStreamStruct.crc32 ^ 0xFFFFFFFF; + ftr.uncompressedSize = hand->uncompressedSize; + + hand->formatHandler.serialize_footer(&ftr, ftrBuf); + + wt = outputStream(outputContext, (void *)ftrBuf, hand->formatHandler.footer_size); + + hand->allocStruct.Free(&(hand->allocStruct), ftrBuf); + + if (wt != hand->formatHandler.footer_size) + { + return ELZMA_E_OUTPUT_ERROR; + } + } + + return ELZMA_E_OK; +} + +unsigned int elzma_get_dict_size(unsigned long long size) +{ + int i = 13; /* 16k dict is minimum */ + + /* now we'll find the closes power of two with a max at 16< * + * if the size is greater than 8m, we'll divide by two, all of this + * is based on a quick set of emperical tests on hopefully + * representative sample data */ + if (size > (1 << 23)) + size >>= 1; + + while (size >> i) + i++; + + if (i > 23) + return 1 << 23; + + /* now 1 << i is greater than size, let's return either 1< (size - (1 << (i - 1)))) ? i - 1 : i); +} diff --git a/depends/lzma/wrapper/decompress.c b/depends/lzma/wrapper/decompress.c new file mode 100644 index 00000000..65ff9119 --- /dev/null +++ b/depends/lzma/wrapper/decompress.c @@ -0,0 +1,263 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + */ + +#include "include/decompress.h" +#include "pavlov/LzmaDec.h" +#include "pavlov/7zCrc.h" +#include "common_internal.h" +#include "lzma_header.h" +#include "lzip_header.h" + +#include +#include + +#define ELZMA_DECOMPRESS_INPUT_BUFSIZE (1024 * 64) +#define ELZMA_DECOMPRESS_OUTPUT_BUFSIZE (1024 * 256) + +/** an opaque handle to an lzma decompressor */ +struct _elzma_decompress_handle +{ + char inbuf[ELZMA_DECOMPRESS_INPUT_BUFSIZE]; + char outbuf[ELZMA_DECOMPRESS_OUTPUT_BUFSIZE]; + struct elzma_alloc_struct allocStruct; +}; + +elzma_decompress_handle elzma_decompress_alloc() +{ + elzma_decompress_handle hand = malloc(sizeof(struct _elzma_decompress_handle)); + memset((void *)hand, 0, sizeof(struct _elzma_decompress_handle)); + init_alloc_struct(&(hand->allocStruct), NULL, NULL, NULL, NULL); + return hand; +} + +void elzma_decompress_set_allocation_callbacks(elzma_decompress_handle hand, + elzma_malloc mallocFunc, void *mallocFuncContext, + elzma_free freeFunc, void *freeFuncContext) +{ + if (hand) + { + init_alloc_struct(&(hand->allocStruct), mallocFunc, mallocFuncContext, freeFunc, + freeFuncContext); + } +} + +void elzma_decompress_free(elzma_decompress_handle *hand) +{ + if (*hand) + free(*hand); + *hand = NULL; +} + +int elzma_decompress_run(elzma_decompress_handle hand, elzma_read_callback inputStream, + void *inputContext, elzma_write_callback outputStream, + void *outputContext, elzma_file_format format) +{ + unsigned long long int totalRead = 0; /* total amount read from stream */ + unsigned int crc32 = CRC_INIT_VAL; /* running crc32 (lzip case) */ + CLzmaDec dec; + unsigned int errorCode = ELZMA_E_OK; + struct elzma_format_handler formatHandler; + struct elzma_file_header h; + struct elzma_file_footer f; + + /* switch between supported formats */ + if (format == ELZMA_lzma) + { + initializeLZMAFormatHandler(&formatHandler); + } + else if (format == ELZMA_lzip) + { + CrcGenerateTable(); + initializeLZIPFormatHandler(&formatHandler); + } + else + { + return ELZMA_E_BAD_PARAMS; + } + + /* initialize footer */ + f.crc32 = 0; + f.uncompressedSize = 0; + + /* initialize decoder memory */ + memset((void *)&dec, 0, sizeof(dec)); + LzmaDec_Init(&dec); + + /* decode the header. */ + { + unsigned char *hdr = + hand->allocStruct.Alloc(&(hand->allocStruct), formatHandler.header_size); + + size_t sz = formatHandler.header_size; + + formatHandler.init_header(&h); + + if (inputStream(inputContext, hdr, &sz) != 0 || sz != formatHandler.header_size) + { + hand->allocStruct.Free(&(hand->allocStruct), hdr); + return ELZMA_E_INPUT_ERROR; + } + + if (0 != formatHandler.parse_header(hdr, &h)) + { + hand->allocStruct.Free(&(hand->allocStruct), hdr); + return ELZMA_E_CORRUPT_HEADER; + } + + /* the LzmaDec_Allocate call requires 5 bytes which have + * compression properties encoded in them. In the case of + * lzip, the header format does not already contain what + * LzmaDec_Allocate expects, so we must craft it, silly */ + { + unsigned char propsBuf[13]; + const unsigned char *propsPtr = hdr; + + if (format == ELZMA_lzip) + { + struct elzma_format_handler lzmaHand; + initializeLZMAFormatHandler(&lzmaHand); + lzmaHand.serialize_header(propsBuf, &h); + propsPtr = propsBuf; + } + + /* now we're ready to allocate the decoder */ + LzmaDec_Allocate(&dec, propsPtr, 5); + } + + hand->allocStruct.Free(&(hand->allocStruct), hdr); + } + + /* perform the decoding */ + for (;;) + { + size_t dstLen = ELZMA_DECOMPRESS_OUTPUT_BUFSIZE; + size_t srcLen = ELZMA_DECOMPRESS_INPUT_BUFSIZE; + size_t amt = 0; + size_t bufOff = 0; + ELzmaStatus stat; + + if (0 != inputStream(inputContext, hand->inbuf, &srcLen)) + { + errorCode = ELZMA_E_INPUT_ERROR; + goto decompressEnd; + } + + /* handle the case where the input prematurely finishes */ + if (srcLen == 0) + { + errorCode = ELZMA_E_INSUFFICIENT_INPUT; + goto decompressEnd; + } + + amt = srcLen; + + /* handle the case where a single read buffer of compressed bytes + * will translate into multiple buffers of uncompressed bytes, + * with this inner loop */ + stat = LZMA_STATUS_NOT_SPECIFIED; + + while (bufOff < srcLen) + { + SRes r = LzmaDec_DecodeToBuf(&dec, (uint8_t *)hand->outbuf, &dstLen, + ((uint8_t *)hand->inbuf + bufOff), &amt, + LZMA_FINISH_ANY, &stat); + + /* XXX deal with result code more granularly*/ + if (r != SZ_OK) + { + errorCode = ELZMA_E_DECOMPRESS_ERROR; + goto decompressEnd; + } + + /* write what we've read */ + { + size_t wt; + + /* if decoding lzip, update our crc32 value */ + if (format == ELZMA_lzip && dstLen > 0) + { + crc32 = CrcUpdate(crc32, hand->outbuf, dstLen); + } + totalRead += dstLen; + + wt = outputStream(outputContext, hand->outbuf, dstLen); + if (wt != dstLen) + { + errorCode = ELZMA_E_OUTPUT_ERROR; + goto decompressEnd; + } + } + + /* do we have more data on the input buffer? */ + bufOff += amt; + assert(bufOff <= srcLen); + if (bufOff >= srcLen) + break; + amt = srcLen - bufOff; + + /* with lzip, we will have the footer left on the buffer! */ + if (stat == LZMA_STATUS_FINISHED_WITH_MARK) + { + break; + } + } + + /* now check status */ + if (stat == LZMA_STATUS_FINISHED_WITH_MARK) + { + /* read a footer if one is expected and + * present */ + if (formatHandler.footer_size > 0 && amt >= formatHandler.footer_size && + formatHandler.parse_footer != NULL) + { + formatHandler.parse_footer((unsigned char *)hand->inbuf + bufOff, &f); + } + + break; + } + /* for LZMA utils, we don't always have a finished mark */ + if (!h.isStreamed && totalRead >= h.uncompressedSize) + { + break; + } + } + + /* finish the calculated crc32 */ + crc32 ^= 0xFFFFFFFF; + + /* if we have a footer, check that the calculated crc32 matches + * the encoded crc32, and that the sizes match */ + if (formatHandler.footer_size) + { + if (f.crc32 != crc32) + { + errorCode = ELZMA_E_CRC32_MISMATCH; + } + else if (f.uncompressedSize != totalRead) + { + errorCode = ELZMA_E_SIZE_MISMATCH; + } + } + else if (!h.isStreamed) + { + /* if the format does not support a footer and has an uncompressed + * size in the header, let's compare that with how much we actually + * read */ + if (h.uncompressedSize != totalRead) + { + errorCode = ELZMA_E_SIZE_MISMATCH; + } + } + +decompressEnd: + LzmaDec_Free(&dec); + + return errorCode; +} diff --git a/depends/lzma/wrapper/lzip_header.c b/depends/lzma/wrapper/lzip_header.c new file mode 100644 index 00000000..39872813 --- /dev/null +++ b/depends/lzma/wrapper/lzip_header.c @@ -0,0 +1,96 @@ +#include "lzip_header.h" + +#include + +#define ELZMA_LZIP_HEADER_SIZE 6 +#define ELZMA_LZIP_FOOTER_SIZE 12 + +static void initLzipHeader(struct elzma_file_header *hdr) +{ + memset((void *)hdr, 0, sizeof(struct elzma_file_header)); +} + +static int parseLzipHeader(const unsigned char *hdrBuf, struct elzma_file_header *hdr) +{ + if (0 != strncmp("LZIP", (char *)hdrBuf, 4)) + return 1; + /* XXX: ignore version for now */ + hdr->pb = 2; + hdr->lp = 0; + hdr->lc = 3; + /* unknown at this point */ + hdr->isStreamed = 1; + hdr->uncompressedSize = 0; + hdr->dictSize = 1 << (hdrBuf[5] & 0x1F); + return 0; +} + +static int serializeLzipHeader(unsigned char *hdrBuf, const struct elzma_file_header *hdr) +{ + hdrBuf[0] = 'L'; + hdrBuf[1] = 'Z'; + hdrBuf[2] = 'I'; + hdrBuf[3] = 'P'; + hdrBuf[4] = 0; + { + int r = 0; + while ((hdr->dictSize >> r) != 0) + r++; + hdrBuf[5] = (unsigned char)(r - 1) & 0x1F; + } + return 0; +} + +static int serializeLzipFooter(struct elzma_file_footer *ftr, unsigned char *ftrBuf) +{ + unsigned int i = 0; + + /* first crc32 */ + for (i = 0; i < 4; i++) + { + *(ftrBuf++) = (unsigned char)(ftr->crc32 >> (i * 8)); + } + + /* next data size */ + for (i = 0; i < 8; i++) + { + *(ftrBuf++) = (unsigned char)(ftr->uncompressedSize >> (i * 8)); + } + + /* write version 0 files, omit member length for now*/ + + return 0; +} + +static int parseLzipFooter(const unsigned char *ftrBuf, struct elzma_file_footer *ftr) +{ + unsigned int i = 0; + ftr->crc32 = 0; + ftr->uncompressedSize = 0; + + /* first crc32 */ + for (i = 0; i < 4; i++) + { + ftr->crc32 += ((unsigned int)*(ftrBuf++) << (i * 8)); + } + + /* next data size */ + for (i = 0; i < 8; i++) + { + ftr->uncompressedSize += (unsigned long long)*(ftrBuf++) << (i * 8); + } + /* read version 0 files, omit member length for now*/ + + return 0; +} + +void initializeLZIPFormatHandler(struct elzma_format_handler *hand) +{ + hand->header_size = ELZMA_LZIP_HEADER_SIZE; + hand->init_header = initLzipHeader; + hand->parse_header = parseLzipHeader; + hand->serialize_header = serializeLzipHeader; + hand->footer_size = ELZMA_LZIP_FOOTER_SIZE; + hand->serialize_footer = serializeLzipFooter; + hand->parse_footer = parseLzipFooter; +} diff --git a/depends/lzma/wrapper/lzip_header.h b/depends/lzma/wrapper/lzip_header.h new file mode 100644 index 00000000..138afa60 --- /dev/null +++ b/depends/lzma/wrapper/lzip_header.h @@ -0,0 +1,11 @@ +#ifndef __EASYLZMA_LZIP_HEADER__ +#define __EASYLZMA_LZIP_HEADER__ + +#include "common_internal.h" + +/* lzip file format documented here: + * http://download.savannah.gnu.org/releases-noredirect/lzip/manual/ */ + +void initializeLZIPFormatHandler(struct elzma_format_handler *hand); + +#endif diff --git a/depends/lzma/wrapper/lzma_header.c b/depends/lzma/wrapper/lzma_header.c new file mode 100644 index 00000000..ab32549f --- /dev/null +++ b/depends/lzma/wrapper/lzma_header.c @@ -0,0 +1,134 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + */ + +/* XXX: clean this up, it's mostly lifted from pavel */ + +#include "lzma_header.h" + +#include +#include + +#define ELZMA_LZMA_HEADER_SIZE 13 +#define ELZMA_LZMA_PROPSBUF_SIZE 5 + +/**************** + Header parsing + ****************/ + +#ifndef UINT64_MAX +#define UINT64_MAX ((unsigned long long)-1) +#endif + +/* Parse the properties byte */ +static char lzmadec_header_properties(unsigned char *pb, unsigned char *lp, unsigned char *lc, + const unsigned char c) +{ + /* pb, lp and lc are encoded into a single byte. */ + if (c > (9 * 5 * 5)) + return -1; + *pb = c / (9 * 5); /* 0 <= pb <= 4 */ + *lp = (c % (9 * 5)) / 9; /* 0 <= lp <= 4 */ + *lc = c % 9; /* 0 <= lc <= 8 */ + + assert(*pb < 5 && *lp < 5 && *lc < 9); + return 0; +} + +/* Parse the dictionary size (4 bytes, little endian) */ +static char lzmadec_header_dictionary(unsigned int *size, const unsigned char *buffer) +{ + unsigned int i; + *size = 0; + for (i = 0; i < 4; i++) + *size += (unsigned int)(*buffer++) << (i * 8); + /* The dictionary size is limited to 256 MiB (checked from + * LZMA SDK 4.30) */ + if (*size > (1 << 28)) + return -1; + return 0; +} + +/* Parse the uncompressed size field (8 bytes, little endian) */ +static void lzmadec_header_uncompressed(unsigned long long *size, unsigned char *is_streamed, + const unsigned char *buffer) +{ + unsigned int i; + + /* Streamed files have all 64 bits set in the size field. + * We don't know the uncompressed size beforehand. */ + *is_streamed = 1; /* Assume streamed. */ + *size = 0; + for (i = 0; i < 8; i++) + { + *size += (unsigned long long)buffer[i] << (i * 8); + if (buffer[i] != 255) + *is_streamed = 0; + } + assert((*is_streamed == 1 && *size == UINT64_MAX) || + (*is_streamed == 0 && *size < UINT64_MAX)); +} + +static void initLzmaHeader(struct elzma_file_header *hdr) +{ + memset((void *)hdr, 0, sizeof(struct elzma_file_header)); +} + +static int parseLzmaHeader(const unsigned char *hdrBuf, struct elzma_file_header *hdr) +{ + if (lzmadec_header_properties(&(hdr->pb), &(hdr->lp), &(hdr->lc), *hdrBuf) || + lzmadec_header_dictionary(&(hdr->dictSize), hdrBuf + 1)) + { + return 1; + } + lzmadec_header_uncompressed(&(hdr->uncompressedSize), &(hdr->isStreamed), hdrBuf + 5); + + return 0; +} + +static int serializeLzmaHeader(unsigned char *hdrBuf, const struct elzma_file_header *hdr) +{ + unsigned int i; + + memset((void *)hdrBuf, 0, ELZMA_LZMA_HEADER_SIZE); + + /* encode lc, pb, and lp */ + *hdrBuf++ = hdr->lc + (hdr->pb * 45) + (hdr->lp * 45 * 9); + + /* encode dictionary size */ + for (i = 0; i < 4; i++) + { + *(hdrBuf++) = (unsigned char)(hdr->dictSize >> (i * 8)); + } + + /* encode uncompressed size */ + for (i = 0; i < 8; i++) + { + if (hdr->isStreamed) + { + *(hdrBuf++) = 0xff; + } + else + { + *(hdrBuf++) = (unsigned char)(hdr->uncompressedSize >> (i * 8)); + } + } + + return 0; +} + +void initializeLZMAFormatHandler(struct elzma_format_handler *hand) +{ + hand->header_size = ELZMA_LZMA_HEADER_SIZE; + hand->init_header = initLzmaHeader; + hand->parse_header = parseLzmaHeader; + hand->serialize_header = serializeLzmaHeader; + hand->footer_size = 0; + hand->serialize_footer = NULL; +} diff --git a/depends/lzma/wrapper/lzma_header.h b/depends/lzma/wrapper/lzma_header.h new file mode 100644 index 00000000..6a5d7a9c --- /dev/null +++ b/depends/lzma/wrapper/lzma_header.h @@ -0,0 +1,10 @@ +#ifndef __EASYLZMA_LZMA_HEADER__ +#define __EASYLZMA_LZMA_HEADER__ + +#include "common_internal.h" + +/* LZMA-Alone header format gleaned from reading Igor's code */ + +void initializeLZMAFormatHandler(struct elzma_format_handler *hand); + +#endif diff --git a/depends/lzma/wrapper/simple.c b/depends/lzma/wrapper/simple.c new file mode 100644 index 00000000..98d3c285 --- /dev/null +++ b/depends/lzma/wrapper/simple.c @@ -0,0 +1,139 @@ +/* + * Written in 2009 by Lloyd Hilaiel + * + * License + * + * All the cruft you find here is public domain. You don't have to credit + * anyone to use this code, but my personal request is that you mention + * Igor Pavlov for his hard, high quality work. + * + * simple.c - a wrapper around easylzma to compress/decompress to memory + */ + +#include "simple.h" + +#include +#include + +struct dataStream +{ + const unsigned char *inData; + size_t inLen; + + unsigned char *outData; + size_t outLen; +}; + +static int inputCallback(void *ctx, void *buf, size_t *size) +{ + size_t rd = 0; + struct dataStream *ds = (struct dataStream *)ctx; + assert(ds != NULL); + + rd = (ds->inLen < *size) ? ds->inLen : *size; + + if (rd > 0) + { + memcpy(buf, (void *)ds->inData, rd); + ds->inData += rd; + ds->inLen -= rd; + } + + *size = rd; + + return 0; +} + +static size_t outputCallback(void *ctx, const void *buf, size_t size) +{ + struct dataStream *ds = (struct dataStream *)ctx; + assert(ds != NULL); + + if (size > 0) + { + ds->outData = realloc(ds->outData, ds->outLen + size); + memcpy((void *)(ds->outData + ds->outLen), buf, size); + ds->outLen += size; + } + + return size; +} + +int simpleCompress(elzma_file_format format, const unsigned char *inData, size_t inLen, + unsigned char **outData, size_t *outLen) +{ + int rc; + elzma_compress_handle hand; + + /* allocate compression handle */ + hand = elzma_compress_alloc(); + assert(hand != NULL); + + rc = elzma_compress_config(hand, ELZMA_LC_DEFAULT, ELZMA_LP_DEFAULT, ELZMA_PB_DEFAULT, 5, + (1 << 20) /* 1mb */, format, inLen); + + if (rc != ELZMA_E_OK) + { + elzma_compress_free(&hand); + return rc; + } + + /* now run the compression */ + { + struct dataStream ds; + ds.inData = inData; + ds.inLen = inLen; + ds.outData = NULL; + ds.outLen = 0; + + rc = elzma_compress_run(hand, inputCallback, (void *)&ds, outputCallback, (void *)&ds, + NULL, NULL); + + if (rc != ELZMA_E_OK) + { + if (ds.outData != NULL) + free(ds.outData); + elzma_compress_free(&hand); + return rc; + } + + *outData = ds.outData; + *outLen = ds.outLen; + } + + return rc; +} + +int simpleDecompress(elzma_file_format format, const unsigned char *inData, size_t inLen, + unsigned char **outData, size_t *outLen) +{ + int rc; + elzma_decompress_handle hand; + + hand = elzma_decompress_alloc(); + + /* now run the compression */ + { + struct dataStream ds; + ds.inData = inData; + ds.inLen = inLen; + ds.outData = NULL; + ds.outLen = 0; + + rc = elzma_decompress_run(hand, inputCallback, (void *)&ds, outputCallback, (void *)&ds, + format); + + if (rc != ELZMA_E_OK) + { + if (ds.outData != NULL) + free(ds.outData); + elzma_decompress_free(&hand); + return rc; + } + + *outData = ds.outData; + *outLen = ds.outLen; + } + + return rc; +} diff --git a/depends/pack200/CMakeLists.txt b/depends/pack200/CMakeLists.txt new file mode 100644 index 00000000..1befa352 --- /dev/null +++ b/depends/pack200/CMakeLists.txt @@ -0,0 +1,43 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +project(unpack200) + +# Find ZLIB for quazip +# Use system zlib on unix and Qt ZLIB on Windows +IF(UNIX) + find_package(ZLIB REQUIRED) +ELSE(UNIX) + get_filename_component (ZLIB_FOUND_DIR "${Qt5Core_DIR}/../../../include/QtZlib" ABSOLUTE) + SET(ZLIB_INCLUDE_DIRS ${ZLIB_FOUND_DIR} CACHE PATH "Path to ZLIB headers of Qt") + SET(ZLIB_LIBRARIES "") + IF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h") + MESSAGE("Please specify a valid zlib include dir") + ENDIF(NOT EXISTS "${ZLIB_INCLUDE_DIRS}/zlib.h") +ENDIF(UNIX) + +SET(PACK200_SRC +src/bands.cpp +src/bands.h +src/bytes.cpp +src/bytes.h +src/coding.cpp +src/coding.h +src/constants.h +src/defines.h +src/main.cpp +src/unpack.cpp +src/unpack.h +src/utils.cpp +src/utils.h +src/zip.cpp +src/zip.h +) + +add_executable(unpack200 ${PACK200_SRC}) + +IF(UNIX) + target_link_libraries(unpack200 ${ZLIB_LIBRARIES}) +ELSE() + # zlib is part of Qt on windows. use it. + QT5_USE_MODULES(unpack200 Core) +ENDIF() diff --git a/depends/pack200/include/unpack200.h b/depends/pack200/include/unpack200.h new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/depends/pack200/include/unpack200.h @@ -0,0 +1 @@ + diff --git a/depends/pack200/src/bands.cpp b/depends/pack200/src/bands.cpp new file mode 100644 index 00000000..41547ad1 --- /dev/null +++ b/depends/pack200/src/bands.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// -*- C++ -*- +// Small program for unpacking specially compressed Java packages. +// John R. Rose + +#include + +#include +#include +#include +#include +#include + +#include "defines.h" +#include "bytes.h" +#include "utils.h" +#include "coding.h" +#include "bands.h" + +#include "constants.h" +#include "unpack.h" + +inline void band::abort(const char *msg) +{ + u->abort(msg); +} +inline bool band::aborting() +{ + return u->aborting(); +} + +void band::readData(int expectedLength) +{ + CHECK; + assert(expectedLength >= 0); + assert(vs[0].cmk == cmk_ERROR); + if (expectedLength != 0) + { + assert(length == 0); + length = expectedLength; + } + if (length == 0) + { + assert((rplimit = cm.vs0.rp = u->rp) != nullptr); + return; + } + assert(length > 0); + + bool is_BYTE1 = (defc->spec == BYTE1_spec); + + if (is_BYTE1) + { + // No possibility of coding change. Sizing is exact. + u->ensure_input(length); + } + else + { + // Make a conservatively generous estimate of band size in bytes. + // Assume B == 5 everywhere. + // Assume awkward pop with all {U} values (2*5 per value) + jlong generous = (jlong)length * (B_MAX * 3 + 1) + C_SLOP; + u->ensure_input(generous); + } + + // Read one value to see what it might be. + int XB = _meta_default; + int cp1 = 0, cp2 = 0; + if (!is_BYTE1) + { + // must be a variable-length coding + assert(defc->B() > 1 && defc->L() > 0); + // must have already read from previous band: + assert(bn >= BAND_LIMIT || bn <= 0 || bn == e_cp_Utf8_big_chars || + endsWith(name, "_lo") // preceded by _hi conditional band + || + bn == e_file_options // preceded by conditional band + || + u->rp == u->all_bands[bn - 1].maxRP() || u->all_bands[bn - 1].defc == nullptr); + + value_stream xvs; + coding *valc = defc; + if (valc->D() != 0) + { + valc = coding::findBySpec(defc->B(), defc->H(), defc->S()); + assert(!valc->isMalloc); + } + xvs.init(u->rp, u->rplimit, valc); + CHECK; + int X = xvs.getInt(); + if (valc->S() != 0) + { + assert(valc->min <= -256); + XB = -1 - X; + } + else + { + int L = valc->L(); + assert(valc->max >= L + 255); + XB = X - L; + } + if (0 <= XB && XB < 256) + { + // Skip over the escape value. + u->rp = xvs.rp; + cp1 = 1; + } + else + { + // No, it's still default. + XB = _meta_default; + } + } + + if (XB <= _meta_canon_max) + { + byte XB_byte = (byte)XB; + byte *XB_ptr = &XB_byte; + cm.init(u->rp, u->rplimit, XB_ptr, 0, defc, length, nullptr); + CHECK; + } + else + { + assert(u->meta_rp != nullptr); + // Scribble the initial byte onto the band. + byte *save_meta_rp = --u->meta_rp; + byte save_meta_xb = (*save_meta_rp); + (*save_meta_rp) = (byte)XB; + cm.init(u->rp, u->rplimit, u->meta_rp, 0, defc, length, nullptr); + (*save_meta_rp) = save_meta_xb; // put it back, just to be tidy + } + rplimit = u->rp; + + rewind(); +} + +void band::setIndex(cpindex *ix_) +{ + assert(ix_ == nullptr || ixTag == ix_->ixTag); + ix = ix_; +} +void band::setIndexByTag(byte tag) +{ + setIndex(u->cp.getIndex(tag)); +} + +entry *band::getRefCommon(cpindex *ix_, bool nullOKwithCaller) +{ + CHECK_0; + assert(ix_->ixTag == ixTag || + (ixTag == CONSTANT_Literal && ix_->ixTag >= CONSTANT_Integer && + ix_->ixTag <= CONSTANT_String)); + int n = vs[0].getInt() - nullOK; + // Note: band-local nullOK means nullptr encodes as 0. + // But nullOKwithCaller means caller is willing to tolerate a nullptr. + entry *ref = ix_->get(n); + if (ref == nullptr && !(nullOKwithCaller && n == -1)) + abort(n == -1 ? "nullptr ref" : "bad ref"); + return ref; +} + +jlong band::getLong(band &lo_band, bool have_hi) +{ + band &hi_band = (*this); + assert(lo_band.bn == hi_band.bn + 1); + uint lo = lo_band.getInt(); + if (!have_hi) + { + assert(hi_band.length == 0); + return makeLong(0, lo); + } + uint hi = hi_band.getInt(); + return makeLong(hi, lo); +} + +int band::getIntTotal() +{ + CHECK_0; + if (length == 0) + return 0; + if (total_memo > 0) + return total_memo - 1; + int total = getInt(); + // overflow checks require that none of the addends are <0, + // and that the partial sums never overflow (wrap negative) + if (total < 0) + { + abort("overflow detected"); + return 0; + } + for (int k = length - 1; k > 0; k--) + { + int prev_total = total; + total += vs[0].getInt(); + if (total < prev_total) + { + abort("overflow detected"); + return 0; + } + } + rewind(); + total_memo = total + 1; + return total; +} + +int band::getIntCount(int tag) +{ + CHECK_0; + if (length == 0) + return 0; + if (tag >= HIST0_MIN && tag <= HIST0_MAX) + { + if (hist0 == nullptr) + { + // Lazily calculate an approximate histogram. + hist0 = U_NEW(int, (HIST0_MAX - HIST0_MIN) + 1); + CHECK_0; + for (int k = length; k > 0; k--) + { + int x = vs[0].getInt(); + if (x >= HIST0_MIN && x <= HIST0_MAX) + hist0[x - HIST0_MIN] += 1; + } + rewind(); + } + return hist0[tag - HIST0_MIN]; + } + int total = 0; + for (int k = length; k > 0; k--) + { + total += (vs[0].getInt() == tag) ? 1 : 0; + } + rewind(); + return total; +} + +#define INDEX_INIT(tag, nullOK, subindex) ((tag) + (subindex) * SUBINDEX_BIT + (nullOK) * 256) + +#define INDEX(tag) INDEX_INIT(tag, 0, 0) +#define NULL_OR_INDEX(tag) INDEX_INIT(tag, 1, 0) +#define SUB_INDEX(tag) INDEX_INIT(tag, 0, 1) +#define NO_INDEX 0 + +struct band_init +{ + int defc; + int index; +}; + +#define BAND_INIT(name, cspec, ix) \ + { \ + cspec, ix \ + } + +const band_init all_band_inits[] = + { + // BAND_INIT(archive_magic, BYTE1_spec, 0), + // BAND_INIT(archive_header, UNSIGNED5_spec, 0), + // BAND_INIT(band_headers, BYTE1_spec, 0), + BAND_INIT(cp_Utf8_prefix, DELTA5_spec, 0), BAND_INIT(cp_Utf8_suffix, UNSIGNED5_spec, 0), + BAND_INIT(cp_Utf8_chars, CHAR3_spec, 0), BAND_INIT(cp_Utf8_big_suffix, DELTA5_spec, 0), + BAND_INIT(cp_Utf8_big_chars, DELTA5_spec, 0), BAND_INIT(cp_Int, UDELTA5_spec, 0), + BAND_INIT(cp_Float, UDELTA5_spec, 0), BAND_INIT(cp_Long_hi, UDELTA5_spec, 0), + BAND_INIT(cp_Long_lo, DELTA5_spec, 0), BAND_INIT(cp_Double_hi, UDELTA5_spec, 0), + BAND_INIT(cp_Double_lo, DELTA5_spec, 0), + BAND_INIT(cp_String, UDELTA5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(cp_Class, UDELTA5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(cp_Signature_form, DELTA5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(cp_Signature_classes, UDELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(cp_Descr_name, DELTA5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(cp_Descr_type, UDELTA5_spec, INDEX(CONSTANT_Signature)), + BAND_INIT(cp_Field_class, DELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(cp_Field_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), + BAND_INIT(cp_Method_class, DELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(cp_Method_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), + BAND_INIT(cp_Imethod_class, DELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(cp_Imethod_desc, UDELTA5_spec, INDEX(CONSTANT_NameandType)), + BAND_INIT(attr_definition_headers, BYTE1_spec, 0), + BAND_INIT(attr_definition_name, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(attr_definition_layout, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(ic_this_class, UDELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(ic_flags, UNSIGNED5_spec, 0), + BAND_INIT(ic_outer_class, DELTA5_spec, NULL_OR_INDEX(CONSTANT_Class)), + BAND_INIT(ic_name, DELTA5_spec, NULL_OR_INDEX(CONSTANT_Utf8)), + BAND_INIT(class_this, DELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(class_super, DELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(class_interface_count, DELTA5_spec, 0), + BAND_INIT(class_interface, DELTA5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(class_field_count, DELTA5_spec, 0), + BAND_INIT(class_method_count, DELTA5_spec, 0), + BAND_INIT(field_descr, DELTA5_spec, INDEX(CONSTANT_NameandType)), + BAND_INIT(field_flags_hi, UNSIGNED5_spec, 0), + BAND_INIT(field_flags_lo, UNSIGNED5_spec, 0), + BAND_INIT(field_attr_count, UNSIGNED5_spec, 0), + BAND_INIT(field_attr_indexes, UNSIGNED5_spec, 0), + BAND_INIT(field_attr_calls, UNSIGNED5_spec, 0), + BAND_INIT(field_ConstantValue_KQ, UNSIGNED5_spec, INDEX(CONSTANT_Literal)), + BAND_INIT(field_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)), + BAND_INIT(field_metadata_bands, -1, -1), BAND_INIT(field_attr_bands, -1, -1), + BAND_INIT(method_descr, MDELTA5_spec, INDEX(CONSTANT_NameandType)), + BAND_INIT(method_flags_hi, UNSIGNED5_spec, 0), + BAND_INIT(method_flags_lo, UNSIGNED5_spec, 0), + BAND_INIT(method_attr_count, UNSIGNED5_spec, 0), + BAND_INIT(method_attr_indexes, UNSIGNED5_spec, 0), + BAND_INIT(method_attr_calls, UNSIGNED5_spec, 0), + BAND_INIT(method_Exceptions_N, UNSIGNED5_spec, 0), + BAND_INIT(method_Exceptions_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(method_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)), + BAND_INIT(method_metadata_bands, -1, -1), BAND_INIT(method_attr_bands, -1, -1), + BAND_INIT(class_flags_hi, UNSIGNED5_spec, 0), + BAND_INIT(class_flags_lo, UNSIGNED5_spec, 0), + BAND_INIT(class_attr_count, UNSIGNED5_spec, 0), + BAND_INIT(class_attr_indexes, UNSIGNED5_spec, 0), + BAND_INIT(class_attr_calls, UNSIGNED5_spec, 0), + BAND_INIT(class_SourceFile_RUN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Utf8)), + BAND_INIT(class_EnclosingMethod_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(class_EnclosingMethod_RDN, UNSIGNED5_spec, + NULL_OR_INDEX(CONSTANT_NameandType)), + BAND_INIT(class_Signature_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)), + BAND_INIT(class_metadata_bands, -1, -1), + BAND_INIT(class_InnerClasses_N, UNSIGNED5_spec, 0), + BAND_INIT(class_InnerClasses_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(class_InnerClasses_F, UNSIGNED5_spec, 0), + BAND_INIT(class_InnerClasses_outer_RCN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)), + BAND_INIT(class_InnerClasses_name_RUN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Utf8)), + BAND_INIT(class_ClassFile_version_minor_H, UNSIGNED5_spec, 0), + BAND_INIT(class_ClassFile_version_major_H, UNSIGNED5_spec, 0), + BAND_INIT(class_attr_bands, -1, -1), BAND_INIT(code_headers, BYTE1_spec, 0), + BAND_INIT(code_max_stack, UNSIGNED5_spec, 0), + BAND_INIT(code_max_na_locals, UNSIGNED5_spec, 0), + BAND_INIT(code_handler_count, UNSIGNED5_spec, 0), + BAND_INIT(code_handler_start_P, BCI5_spec, 0), + BAND_INIT(code_handler_end_PO, BRANCH5_spec, 0), + BAND_INIT(code_handler_catch_PO, BRANCH5_spec, 0), + BAND_INIT(code_handler_class_RCN, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)), + BAND_INIT(code_flags_hi, UNSIGNED5_spec, 0), + BAND_INIT(code_flags_lo, UNSIGNED5_spec, 0), + BAND_INIT(code_attr_count, UNSIGNED5_spec, 0), + BAND_INIT(code_attr_indexes, UNSIGNED5_spec, 0), + BAND_INIT(code_attr_calls, UNSIGNED5_spec, 0), + BAND_INIT(code_StackMapTable_N, UNSIGNED5_spec, 0), + BAND_INIT(code_StackMapTable_frame_T, BYTE1_spec, 0), + BAND_INIT(code_StackMapTable_local_N, UNSIGNED5_spec, 0), + BAND_INIT(code_StackMapTable_stack_N, UNSIGNED5_spec, 0), + BAND_INIT(code_StackMapTable_offset, UNSIGNED5_spec, 0), + BAND_INIT(code_StackMapTable_T, BYTE1_spec, 0), + BAND_INIT(code_StackMapTable_RC, UNSIGNED5_spec, INDEX(CONSTANT_Class)), + BAND_INIT(code_StackMapTable_P, BCI5_spec, 0), + BAND_INIT(code_LineNumberTable_N, UNSIGNED5_spec, 0), + BAND_INIT(code_LineNumberTable_bci_P, BCI5_spec, 0), + BAND_INIT(code_LineNumberTable_line, UNSIGNED5_spec, 0), + BAND_INIT(code_LocalVariableTable_N, UNSIGNED5_spec, 0), + BAND_INIT(code_LocalVariableTable_bci_P, BCI5_spec, 0), + BAND_INIT(code_LocalVariableTable_span_O, BRANCH5_spec, 0), + BAND_INIT(code_LocalVariableTable_name_RU, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(code_LocalVariableTable_type_RS, UNSIGNED5_spec, INDEX(CONSTANT_Signature)), + BAND_INIT(code_LocalVariableTable_slot, UNSIGNED5_spec, 0), + BAND_INIT(code_LocalVariableTypeTable_N, UNSIGNED5_spec, 0), + BAND_INIT(code_LocalVariableTypeTable_bci_P, BCI5_spec, 0), + BAND_INIT(code_LocalVariableTypeTable_span_O, BRANCH5_spec, 0), + BAND_INIT(code_LocalVariableTypeTable_name_RU, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(code_LocalVariableTypeTable_type_RS, UNSIGNED5_spec, + INDEX(CONSTANT_Signature)), + BAND_INIT(code_LocalVariableTypeTable_slot, UNSIGNED5_spec, 0), + BAND_INIT(code_attr_bands, -1, -1), BAND_INIT(bc_codes, BYTE1_spec, 0), + BAND_INIT(bc_case_count, UNSIGNED5_spec, 0), BAND_INIT(bc_case_value, DELTA5_spec, 0), + BAND_INIT(bc_byte, BYTE1_spec, 0), BAND_INIT(bc_short, DELTA5_spec, 0), + BAND_INIT(bc_local, UNSIGNED5_spec, 0), BAND_INIT(bc_label, BRANCH5_spec, 0), + BAND_INIT(bc_intref, DELTA5_spec, INDEX(CONSTANT_Integer)), + BAND_INIT(bc_floatref, DELTA5_spec, INDEX(CONSTANT_Float)), + BAND_INIT(bc_longref, DELTA5_spec, INDEX(CONSTANT_Long)), + BAND_INIT(bc_doubleref, DELTA5_spec, INDEX(CONSTANT_Double)), + BAND_INIT(bc_stringref, DELTA5_spec, INDEX(CONSTANT_String)), + BAND_INIT(bc_classref, UNSIGNED5_spec, NULL_OR_INDEX(CONSTANT_Class)), + BAND_INIT(bc_fieldref, DELTA5_spec, INDEX(CONSTANT_Fieldref)), + BAND_INIT(bc_methodref, UNSIGNED5_spec, INDEX(CONSTANT_Methodref)), + BAND_INIT(bc_imethodref, DELTA5_spec, INDEX(CONSTANT_InterfaceMethodref)), + BAND_INIT(bc_thisfield, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Fieldref)), + BAND_INIT(bc_superfield, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Fieldref)), + BAND_INIT(bc_thismethod, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)), + BAND_INIT(bc_supermethod, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)), + BAND_INIT(bc_initref, UNSIGNED5_spec, SUB_INDEX(CONSTANT_Methodref)), + BAND_INIT(bc_escref, UNSIGNED5_spec, INDEX(CONSTANT_All)), + BAND_INIT(bc_escrefsize, UNSIGNED5_spec, 0), BAND_INIT(bc_escsize, UNSIGNED5_spec, 0), + BAND_INIT(bc_escbyte, BYTE1_spec, 0), + BAND_INIT(file_name, UNSIGNED5_spec, INDEX(CONSTANT_Utf8)), + BAND_INIT(file_size_hi, UNSIGNED5_spec, 0), BAND_INIT(file_size_lo, UNSIGNED5_spec, 0), + BAND_INIT(file_modtime, DELTA5_spec, 0), BAND_INIT(file_options, UNSIGNED5_spec, 0), + // BAND_INIT(file_bits, BYTE1_spec, 0), + {0, 0}}; +#define NUM_BAND_INITS (sizeof(all_band_inits) / sizeof(all_band_inits[0])) + +band *band::makeBands(unpacker *u) +{ + band *tmp_all_bands = U_NEW(band, BAND_LIMIT); + for (int i = 0; i < BAND_LIMIT; i++) + { + assert((byte *)&all_band_inits[i + 1] < + (byte *)all_band_inits + sizeof(all_band_inits)); + const band_init &bi = all_band_inits[i]; + band &b = tmp_all_bands[i]; + coding *defc = coding::findBySpec(bi.defc); + assert((defc == nullptr) == (bi.defc == -1)); // no garbage, please + assert(defc == nullptr || !defc->isMalloc); + assert(bi.bn == i); // band array consistent w/ band enum + b.init(u, i, defc); + if (bi.index > 0) + { + b.nullOK = ((bi.index >> 8) & 1); + b.ixTag = (bi.index & 0xFF); + } + } + return tmp_all_bands; +} + +void band::initIndexes(unpacker *u) +{ + band *tmp_all_bands = u->all_bands; + for (int i = 0; i < BAND_LIMIT; i++) + { + band *scan = &tmp_all_bands[i]; + uint tag = scan->ixTag; // Cf. #define INDEX(tag) above + if (tag != 0 && tag != CONSTANT_Literal && (tag & SUBINDEX_BIT) == 0) + { + scan->setIndex(u->cp.getIndex(tag)); + } + } +} diff --git a/depends/pack200/src/bands.h b/depends/pack200/src/bands.h new file mode 100644 index 00000000..3f944481 --- /dev/null +++ b/depends/pack200/src/bands.h @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2002, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// -*- C++ -*- +struct entry; +struct cpindex; +struct unpacker; + +struct band +{ + int bn; // band_number of this band + coding *defc; // default coding method + cpindex *ix; // CP entry mapping, if CPRefBand + byte ixTag; // 0 or 1; nullptr is coded as (nullOK?0:-1) + byte nullOK; // 0 or 1; nullptr is coded as (nullOK?0:-1) + int length; // expected # values + unpacker *u; // back pointer + + value_stream vs[2]; // source of values + coding_method cm; // method used for initial state of vs[0] + byte *rplimit; // end of band (encoded, transmitted) + + int total_memo; // cached value of getIntTotal, or -1 + int *hist0; // approximate. histogram + enum + { + HIST0_MIN = 0, + HIST0_MAX = 255 + }; // catches the usual cases + + // properties for attribute layout elements: + byte le_kind; // EK_XXX + byte le_bci; // 0,EK_BCI,EK_BCD,EK_BCO + byte le_back; // ==EF_BACK + byte le_len; // 0,1,2,4 (size in classfile), or call addr + band **le_body; // body of repl, union, call (nullptr-terminated) +// Note: EK_CASE elements use hist0 to record union tags. +#define le_casetags hist0 + + band &nextBand() + { + return this[1]; + } + band &prevBand() + { + return this[-1]; + } + + void init(unpacker *u_, int bn_, coding *defc_) + { + u = u_; + cm.u = u_; + bn = bn_; + defc = defc_; + } + void init(unpacker *u_, int bn_, int defcSpec) + { + init(u_, bn_, coding::findBySpec(defcSpec)); + } + void initRef(int ixTag_ = 0, bool nullOK_ = false) + { + ixTag = ixTag_; + nullOK = nullOK_; + setIndexByTag(ixTag); + } + + void expectMoreLength(int l) + { + assert(length >= 0); // able to accept a length + assert((int)l >= 0); // no overflow + assert(rplimit == nullptr); // readData not yet called + length += l; + assert(length >= l); // no overflow + } + + void setIndex(cpindex *ix_); + void setIndexByTag(byte tag); + + // Parse the band and its meta-coding header. + void readData(int expectedLength = 0); + + // Reset the band for another pass (Cf. Java Band.resetForSecondPass.) + void rewind() + { + cm.reset(&vs[0]); + } + + byte *&curRP() + { + return vs[0].rp; + } + byte *minRP() + { + return cm.vs0.rp; + } + byte *maxRP() + { + return rplimit; + } + size_t size() + { + return maxRP() - minRP(); + } + + int getByte() + { + assert(ix == nullptr); + return vs[0].getByte(); + } + int getInt() + { + assert(ix == nullptr); + return vs[0].getInt(); + } + entry *getRefN() + { + assert(ix != nullptr); + return getRefCommon(ix, true); + } + entry *getRef() + { + assert(ix != nullptr); + return getRefCommon(ix, false); + } + entry *getRefUsing(cpindex *ix2) + { + assert(ix == nullptr); + return getRefCommon(ix2, true); + } + entry *getRefCommon(cpindex *ix, bool nullOK); + jlong getLong(band &lo_band, bool have_hi); + + static jlong makeLong(uint hi, uint lo) + { + return ((julong)hi << 32) + (((julong)lo << 32) >> 32); + } + + int getIntTotal(); + int getIntCount(int tag); + + static band *makeBands(unpacker *u); + static void initIndexes(unpacker *u); + + void abort(const char *msg = nullptr); //{ u->abort(msg); } + bool aborting(); //{ return u->aborting(); } +}; + +extern band all_bands[]; + +#define BAND_LOCAL /* \ + band* band_temp = all_bands; \ + band* all_bands = band_temp */ + +// Band schema: +enum band_number +{ + // e_archive_magic, + // e_archive_header, + // e_band_headers, + + // constant pool contents + e_cp_Utf8_prefix, + e_cp_Utf8_suffix, + e_cp_Utf8_chars, + e_cp_Utf8_big_suffix, + e_cp_Utf8_big_chars, + e_cp_Int, + e_cp_Float, + e_cp_Long_hi, + e_cp_Long_lo, + e_cp_Double_hi, + e_cp_Double_lo, + e_cp_String, + e_cp_Class, + e_cp_Signature_form, + e_cp_Signature_classes, + e_cp_Descr_name, + e_cp_Descr_type, + e_cp_Field_class, + e_cp_Field_desc, + e_cp_Method_class, + e_cp_Method_desc, + e_cp_Imethod_class, + e_cp_Imethod_desc, + + // bands which define transmission of attributes + e_attr_definition_headers, + e_attr_definition_name, + e_attr_definition_layout, + + // band for hardwired InnerClasses attribute (shared across the package) + e_ic_this_class, + e_ic_flags, + // These bands contain data only where flags sets ACC_IC_LONG_FORM: + e_ic_outer_class, + e_ic_name, + + // bands for carrying class schema information: + e_class_this, + e_class_super, + e_class_interface_count, + e_class_interface, + + // bands for class members + e_class_field_count, + e_class_method_count, + e_field_descr, + e_field_flags_hi, + e_field_flags_lo, + e_field_attr_count, + e_field_attr_indexes, + e_field_attr_calls, + e_field_ConstantValue_KQ, + e_field_Signature_RS, + e_field_metadata_bands, + e_field_attr_bands, + e_method_descr, + e_method_flags_hi, + e_method_flags_lo, + e_method_attr_count, + e_method_attr_indexes, + e_method_attr_calls, + e_method_Exceptions_N, + e_method_Exceptions_RC, + e_method_Signature_RS, + e_method_metadata_bands, + e_method_attr_bands, + e_class_flags_hi, + e_class_flags_lo, + e_class_attr_count, + e_class_attr_indexes, + e_class_attr_calls, + e_class_SourceFile_RUN, + e_class_EnclosingMethod_RC, + e_class_EnclosingMethod_RDN, + e_class_Signature_RS, + e_class_metadata_bands, + e_class_InnerClasses_N, + e_class_InnerClasses_RC, + e_class_InnerClasses_F, + e_class_InnerClasses_outer_RCN, + e_class_InnerClasses_name_RUN, + e_class_ClassFile_version_minor_H, + e_class_ClassFile_version_major_H, + e_class_attr_bands, + e_code_headers, + e_code_max_stack, + e_code_max_na_locals, + e_code_handler_count, + e_code_handler_start_P, + e_code_handler_end_PO, + e_code_handler_catch_PO, + e_code_handler_class_RCN, + + // code attributes + e_code_flags_hi, + e_code_flags_lo, + e_code_attr_count, + e_code_attr_indexes, + e_code_attr_calls, + e_code_StackMapTable_N, + e_code_StackMapTable_frame_T, + e_code_StackMapTable_local_N, + e_code_StackMapTable_stack_N, + e_code_StackMapTable_offset, + e_code_StackMapTable_T, + e_code_StackMapTable_RC, + e_code_StackMapTable_P, + e_code_LineNumberTable_N, + e_code_LineNumberTable_bci_P, + e_code_LineNumberTable_line, + e_code_LocalVariableTable_N, + e_code_LocalVariableTable_bci_P, + e_code_LocalVariableTable_span_O, + e_code_LocalVariableTable_name_RU, + e_code_LocalVariableTable_type_RS, + e_code_LocalVariableTable_slot, + e_code_LocalVariableTypeTable_N, + e_code_LocalVariableTypeTable_bci_P, + e_code_LocalVariableTypeTable_span_O, + e_code_LocalVariableTypeTable_name_RU, + e_code_LocalVariableTypeTable_type_RS, + e_code_LocalVariableTypeTable_slot, + e_code_attr_bands, + + // bands for bytecodes + e_bc_codes, + // remaining bands provide typed opcode fields required by the bc_codes + e_bc_case_count, + e_bc_case_value, + e_bc_byte, + e_bc_short, + e_bc_local, + e_bc_label, + + // ldc* operands: + e_bc_intref, + e_bc_floatref, + e_bc_longref, + e_bc_doubleref, + e_bc_stringref, + e_bc_classref, + e_bc_fieldref, + e_bc_methodref, + e_bc_imethodref, + + // _self_linker_op family + e_bc_thisfield, + e_bc_superfield, + e_bc_thismethod, + e_bc_supermethod, + + // bc_invokeinit family: + e_bc_initref, + + // bytecode escape sequences + e_bc_escref, + e_bc_escrefsize, + e_bc_escsize, + e_bc_escbyte, + + // file attributes and contents + e_file_name, + e_file_size_hi, + e_file_size_lo, + e_file_modtime, + e_file_options, + // e_file_bits, // handled specially as an appendix + BAND_LIMIT +}; + +// Symbolic names for bands, as if in a giant global struct: +//#define archive_magic all_bands[e_archive_magic] +//#define archive_header all_bands[e_archive_header] +//#define band_headers all_bands[e_band_headers] +#define cp_Utf8_prefix all_bands[e_cp_Utf8_prefix] +#define cp_Utf8_suffix all_bands[e_cp_Utf8_suffix] +#define cp_Utf8_chars all_bands[e_cp_Utf8_chars] +#define cp_Utf8_big_suffix all_bands[e_cp_Utf8_big_suffix] +#define cp_Utf8_big_chars all_bands[e_cp_Utf8_big_chars] +#define cp_Int all_bands[e_cp_Int] +#define cp_Float all_bands[e_cp_Float] +#define cp_Long_hi all_bands[e_cp_Long_hi] +#define cp_Long_lo all_bands[e_cp_Long_lo] +#define cp_Double_hi all_bands[e_cp_Double_hi] +#define cp_Double_lo all_bands[e_cp_Double_lo] +#define cp_String all_bands[e_cp_String] +#define cp_Class all_bands[e_cp_Class] +#define cp_Signature_form all_bands[e_cp_Signature_form] +#define cp_Signature_classes all_bands[e_cp_Signature_classes] +#define cp_Descr_name all_bands[e_cp_Descr_name] +#define cp_Descr_type all_bands[e_cp_Descr_type] +#define cp_Field_class all_bands[e_cp_Field_class] +#define cp_Field_desc all_bands[e_cp_Field_desc] +#define cp_Method_class all_bands[e_cp_Method_class] +#define cp_Method_desc all_bands[e_cp_Method_desc] +#define cp_Imethod_class all_bands[e_cp_Imethod_class] +#define cp_Imethod_desc all_bands[e_cp_Imethod_desc] +#define attr_definition_headers all_bands[e_attr_definition_headers] +#define attr_definition_name all_bands[e_attr_definition_name] +#define attr_definition_layout all_bands[e_attr_definition_layout] +#define ic_this_class all_bands[e_ic_this_class] +#define ic_flags all_bands[e_ic_flags] +#define ic_outer_class all_bands[e_ic_outer_class] +#define ic_name all_bands[e_ic_name] +#define class_this all_bands[e_class_this] +#define class_super all_bands[e_class_super] +#define class_interface_count all_bands[e_class_interface_count] +#define class_interface all_bands[e_class_interface] +#define class_field_count all_bands[e_class_field_count] +#define class_method_count all_bands[e_class_method_count] +#define field_descr all_bands[e_field_descr] +#define field_flags_hi all_bands[e_field_flags_hi] +#define field_flags_lo all_bands[e_field_flags_lo] +#define field_attr_count all_bands[e_field_attr_count] +#define field_attr_indexes all_bands[e_field_attr_indexes] +#define field_ConstantValue_KQ all_bands[e_field_ConstantValue_KQ] +#define field_Signature_RS all_bands[e_field_Signature_RS] +#define field_attr_bands all_bands[e_field_attr_bands] +#define method_descr all_bands[e_method_descr] +#define method_flags_hi all_bands[e_method_flags_hi] +#define method_flags_lo all_bands[e_method_flags_lo] +#define method_attr_count all_bands[e_method_attr_count] +#define method_attr_indexes all_bands[e_method_attr_indexes] +#define method_Exceptions_N all_bands[e_method_Exceptions_N] +#define method_Exceptions_RC all_bands[e_method_Exceptions_RC] +#define method_Signature_RS all_bands[e_method_Signature_RS] +#define method_attr_bands all_bands[e_method_attr_bands] +#define class_flags_hi all_bands[e_class_flags_hi] +#define class_flags_lo all_bands[e_class_flags_lo] +#define class_attr_count all_bands[e_class_attr_count] +#define class_attr_indexes all_bands[e_class_attr_indexes] +#define class_SourceFile_RUN all_bands[e_class_SourceFile_RUN] +#define class_EnclosingMethod_RC all_bands[e_class_EnclosingMethod_RC] +#define class_EnclosingMethod_RDN all_bands[e_class_EnclosingMethod_RDN] +#define class_Signature_RS all_bands[e_class_Signature_RS] +#define class_InnerClasses_N all_bands[e_class_InnerClasses_N] +#define class_InnerClasses_RC all_bands[e_class_InnerClasses_RC] +#define class_InnerClasses_F all_bands[e_class_InnerClasses_F] +#define class_InnerClasses_outer_RCN all_bands[e_class_InnerClasses_outer_RCN] +#define class_InnerClasses_name_RUN all_bands[e_class_InnerClasses_name_RUN] +#define class_ClassFile_version_minor_H all_bands[e_class_ClassFile_version_minor_H] +#define class_ClassFile_version_major_H all_bands[e_class_ClassFile_version_major_H] +#define class_attr_bands all_bands[e_class_attr_bands] +#define code_headers all_bands[e_code_headers] +#define code_max_stack all_bands[e_code_max_stack] +#define code_max_na_locals all_bands[e_code_max_na_locals] +#define code_handler_count all_bands[e_code_handler_count] +#define code_handler_start_P all_bands[e_code_handler_start_P] +#define code_handler_end_PO all_bands[e_code_handler_end_PO] +#define code_handler_catch_PO all_bands[e_code_handler_catch_PO] +#define code_handler_class_RCN all_bands[e_code_handler_class_RCN] +#define code_flags_hi all_bands[e_code_flags_hi] +#define code_flags_lo all_bands[e_code_flags_lo] +#define code_attr_count all_bands[e_code_attr_count] +#define code_attr_indexes all_bands[e_code_attr_indexes] +#define code_StackMapTable_N all_bands[e_code_StackMapTable_N] +#define code_StackMapTable_frame_T all_bands[e_code_StackMapTable_frame_T] +#define code_StackMapTable_local_N all_bands[e_code_StackMapTable_local_N] +#define code_StackMapTable_stack_N all_bands[e_code_StackMapTable_stack_N] +#define code_StackMapTable_offset all_bands[e_code_StackMapTable_offset] +#define code_StackMapTable_T all_bands[e_code_StackMapTable_T] +#define code_StackMapTable_RC all_bands[e_code_StackMapTable_RC] +#define code_StackMapTable_P all_bands[e_code_StackMapTable_P] +#define code_LineNumberTable_N all_bands[e_code_LineNumberTable_N] +#define code_LineNumberTable_bci_P all_bands[e_code_LineNumberTable_bci_P] +#define code_LineNumberTable_line all_bands[e_code_LineNumberTable_line] +#define code_LocalVariableTable_N all_bands[e_code_LocalVariableTable_N] +#define code_LocalVariableTable_bci_P all_bands[e_code_LocalVariableTable_bci_P] +#define code_LocalVariableTable_span_O all_bands[e_code_LocalVariableTable_span_O] +#define code_LocalVariableTable_name_RU all_bands[e_code_LocalVariableTable_name_RU] +#define code_LocalVariableTable_type_RS all_bands[e_code_LocalVariableTable_type_RS] +#define code_LocalVariableTable_slot all_bands[e_code_LocalVariableTable_slot] +#define code_LocalVariableTypeTable_N all_bands[e_code_LocalVariableTypeTable_N] +#define code_LocalVariableTypeTable_bci_P all_bands[e_code_LocalVariableTypeTable_bci_P] +#define code_LocalVariableTypeTable_span_O all_bands[e_code_LocalVariableTypeTable_span_O] +#define code_LocalVariableTypeTable_name_RU all_bands[e_code_LocalVariableTypeTable_name_RU] +#define code_LocalVariableTypeTable_type_RS all_bands[e_code_LocalVariableTypeTable_type_RS] +#define code_LocalVariableTypeTable_slot all_bands[e_code_LocalVariableTypeTable_slot] +#define code_attr_bands all_bands[e_code_attr_bands] +#define bc_codes all_bands[e_bc_codes] +#define bc_case_count all_bands[e_bc_case_count] +#define bc_case_value all_bands[e_bc_case_value] +#define bc_byte all_bands[e_bc_byte] +#define bc_short all_bands[e_bc_short] +#define bc_local all_bands[e_bc_local] +#define bc_label all_bands[e_bc_label] +#define bc_intref all_bands[e_bc_intref] +#define bc_floatref all_bands[e_bc_floatref] +#define bc_longref all_bands[e_bc_longref] +#define bc_doubleref all_bands[e_bc_doubleref] +#define bc_stringref all_bands[e_bc_stringref] +#define bc_classref all_bands[e_bc_classref] +#define bc_fieldref all_bands[e_bc_fieldref] +#define bc_methodref all_bands[e_bc_methodref] +#define bc_imethodref all_bands[e_bc_imethodref] +#define bc_thisfield all_bands[e_bc_thisfield] +#define bc_superfield all_bands[e_bc_superfield] +#define bc_thismethod all_bands[e_bc_thismethod] +#define bc_supermethod all_bands[e_bc_supermethod] +#define bc_initref all_bands[e_bc_initref] +#define bc_escref all_bands[e_bc_escref] +#define bc_escrefsize all_bands[e_bc_escrefsize] +#define bc_escsize all_bands[e_bc_escsize] +#define bc_escbyte all_bands[e_bc_escbyte] +#define file_name all_bands[e_file_name] +#define file_size_hi all_bands[e_file_size_hi] +#define file_size_lo all_bands[e_file_size_lo] +#define file_modtime all_bands[e_file_modtime] +#define file_options all_bands[e_file_options] diff --git a/depends/pack200/src/bytes.cpp b/depends/pack200/src/bytes.cpp new file mode 100644 index 00000000..b82a987a --- /dev/null +++ b/depends/pack200/src/bytes.cpp @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include "defines.h" +#include "bytes.h" +#include "utils.h" + +static byte dummy[1 << 10]; + +bool bytes::inBounds(const void *p) +{ + return p >= ptr && p < limit(); +} + +void bytes::malloc(size_t len_) +{ + len = len_; + ptr = NEW(byte, add_size(len_, 1)); // add trailing zero byte always + if (ptr == nullptr) + { + // set ptr to some victim memory, to ease escape + set(dummy, sizeof(dummy) - 1); + unpack_abort(ERROR_ENOMEM); + } +} + +void bytes::realloc(size_t len_) +{ + if (len == len_) + return; // nothing to do + if (ptr == dummy) + return; // escaping from an error + if (ptr == nullptr) + { + malloc(len_); + return; + } + byte *oldptr = ptr; + ptr = (len_ >= PSIZE_MAX) ? nullptr : (byte *)::realloc(ptr, add_size(len_, 1)); + if (ptr != nullptr) + { + if (len < len_) + memset(ptr + len, 0, len_ - len); + ptr[len_] = 0; + len = len_; + } + else + { + ptr = oldptr; // ease our escape + unpack_abort(ERROR_ENOMEM); + } +} + +void bytes::free() +{ + if (ptr == dummy) + return; // escaping from an error + if (ptr != nullptr) + { + ::free(ptr); + } + len = 0; + ptr = 0; +} + +int bytes::indexOf(byte c) +{ + byte *p = (byte *)memchr(ptr, c, len); + return (p == 0) ? -1 : (int)(p - ptr); +} + +byte *bytes::writeTo(byte *bp) +{ + memcpy(bp, ptr, len); + return bp + len; +} + +int bytes::compareTo(bytes &other) +{ + size_t l1 = len; + size_t l2 = other.len; + int cmp = memcmp(ptr, other.ptr, (l1 < l2) ? l1 : l2); + if (cmp != 0) + return cmp; + return (l1 < l2) ? -1 : (l1 > l2) ? 1 : 0; +} + +void bytes::saveFrom(const void *ptr_, size_t len_) +{ + malloc(len_); + // Save as much as possible. (Helps unpacker::abort.) + if (len_ > len) + { + assert(ptr == dummy); // error recovery + len_ = len; + } + copyFrom(ptr_, len_); +} + +//#TODO: Need to fix for exception handling +void bytes::copyFrom(const void *ptr_, size_t len_, size_t offset) +{ + assert(len_ == 0 || inBounds(ptr + offset)); + assert(len_ == 0 || inBounds(ptr + offset + len_ - 1)); + memcpy(ptr + offset, ptr_, len_); +} + +// Make sure there are 'o' bytes beyond the fill pointer, +// advance the fill pointer, and return the old fill pointer. +byte *fillbytes::grow(size_t s) +{ + size_t nlen = add_size(b.len, s); + if (nlen <= allocated) + { + b.len = nlen; + return limit() - s; + } + size_t maxlen = nlen; + if (maxlen < 128) + maxlen = 128; + if (maxlen < allocated * 2) + maxlen = allocated * 2; + if (allocated == 0) + { + // Initial buffer was not malloced. Do not reallocate it. + bytes old = b; + b.malloc(maxlen); + if (b.len == maxlen) + old.writeTo(b.ptr); + } + else + { + b.realloc(maxlen); + } + allocated = b.len; + if (allocated != maxlen) + { + assert(unpack_aborting()); + b.len = nlen - s; // back up + return dummy; // scribble during error recov. + } + // after realloc, recompute pointers + b.len = nlen; + assert(b.len <= allocated); + return limit() - s; +} + +void fillbytes::ensureSize(size_t s) +{ + if (allocated >= s) + return; + size_t len0 = b.len; + grow(s - size()); + b.len = len0; // put it back +} + +int ptrlist::indexOf(const void *x) +{ + int len = length(); + for (int i = 0; i < len; i++) + { + if (get(i) == x) + return i; + } + return -1; +} + +void ptrlist::freeAll() +{ + int len = length(); + for (int i = 0; i < len; i++) + { + void *p = (void *)get(i); + if (p != nullptr) + { + ::free(p); + } + } + free(); +} + +int intlist::indexOf(int x) +{ + int len = length(); + for (int i = 0; i < len; i++) + { + if (get(i) == x) + return i; + } + return -1; +} diff --git a/depends/pack200/src/bytes.h b/depends/pack200/src/bytes.h new file mode 100644 index 00000000..3926f9f2 --- /dev/null +++ b/depends/pack200/src/bytes.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +struct bytes +{ + int8_t *ptr; + size_t len; + int8_t *limit() + { + return ptr + len; + } + + void set(int8_t *ptr_, size_t len_) + { + ptr = ptr_; + len = len_; + } + void set(const char *str) + { + ptr = (int8_t *)str; + len = strlen(str); + } + bool inBounds(const void *p); // p in [ptr, limit) + void malloc(size_t len_); + void realloc(size_t len_); + void free(); + void copyFrom(const void *ptr_, size_t len_, size_t offset = 0); + void saveFrom(const void *ptr_, size_t len_); + void saveFrom(const char *str) + { + saveFrom(str, strlen(str)); + } + void copyFrom(bytes &other, size_t offset = 0) + { + copyFrom(other.ptr, other.len, offset); + } + void saveFrom(bytes &other) + { + saveFrom(other.ptr, other.len); + } + void clear(int fill_byte = 0) + { + memset(ptr, fill_byte, len); + } + int8_t *writeTo(int8_t *bp); + bool equals(bytes &other) + { + return 0 == compareTo(other); + } + int compareTo(bytes &other); + bool contains(int8_t c) + { + return indexOf(c) >= 0; + } + int indexOf(int8_t c); + // substrings: + static bytes of(int8_t *ptr, size_t len) + { + bytes res; + res.set(ptr, len); + return res; + } + bytes slice(size_t beg, size_t end) + { + bytes res; + res.ptr = ptr + beg; + res.len = end - beg; + assert(res.len == 0 || inBounds(res.ptr) && inBounds(res.limit() - 1)); + return res; + } + // building C strings inside byte buffers: + bytes &strcat(const char *str) + { + ::strcat((char *)ptr, str); + return *this; + } + bytes &strcat(bytes &other) + { + ::strncat((char *)ptr, (char *)other.ptr, other.len); + return *this; + } + char *strval() + { + assert(strlen((char *)ptr) == len); + return (char *)ptr; + } +}; +#define BYTES_OF(var) (bytes::of((int8_t *)&(var), sizeof(var))) + +struct fillbytes +{ + bytes b; + size_t allocated; + + int8_t *base() + { + return b.ptr; + } + size_t size() + { + return b.len; + } + int8_t *limit() + { + return b.limit(); + } // logical limit + void setLimit(int8_t *lp) + { + assert(isAllocated(lp)); + b.len = lp - b.ptr; + } + int8_t *end() + { + return b.ptr + allocated; + } // physical limit + int8_t *loc(size_t o) + { + assert(o < b.len); + return b.ptr + o; + } + void init() + { + allocated = 0; + b.set(nullptr, 0); + } + void init(size_t s) + { + init(); + ensureSize(s); + } + void free() + { + if (allocated != 0) + b.free(); + allocated = 0; + } + void empty() + { + b.len = 0; + } + int8_t *grow(size_t s); // grow so that limit() += s + int getByte(uint i) + { + return *loc(i) & 0xFF; + } + void addByte(int8_t x) + { + *grow(1) = x; + } + void ensureSize(size_t s); // make sure allocated >= s + void trimToSize() + { + if (allocated > size()) + b.realloc(allocated = size()); + } + bool canAppend(size_t s) + { + return allocated > b.len + s; + } + bool isAllocated(int8_t *p) + { + return p >= base() && p <= end(); + } // asserts + void set(bytes &src) + { + set(src.ptr, src.len); + } + + void set(int8_t *ptr, size_t len) + { + b.set(ptr, len); + allocated = 0; // mark as not reallocatable + } + + // block operations on resizing byte buffer: + fillbytes &append(const void *ptr_, size_t len_) + { + memcpy(grow(len_), ptr_, len_); + return (*this); + } + fillbytes &append(bytes &other) + { + return append(other.ptr, other.len); + } + fillbytes &append(const char *str) + { + return append(str, strlen(str)); + } +}; + +struct ptrlist : fillbytes +{ + typedef const void *cvptr; + int length() + { + return (int)(size() / sizeof(cvptr)); + } + cvptr *base() + { + return (cvptr *)fillbytes::base(); + } + cvptr &get(int i) + { + return *(cvptr *)loc(i * sizeof(cvptr)); + } + cvptr *limit() + { + return (cvptr *)fillbytes::limit(); + } + void add(cvptr x) + { + *(cvptr *)grow(sizeof(x)) = x; + } + void popTo(int l) + { + assert(l <= length()); + b.len = l * sizeof(cvptr); + } + int indexOf(cvptr x); + bool contains(cvptr x) + { + return indexOf(x) >= 0; + } + void freeAll(); // frees every ptr on the list, plus the list itself +}; +// Use a macro rather than mess with subtle mismatches +// between member and non-member function pointers. +#define PTRLIST_QSORT(ptrls, fn) ::qsort((ptrls).base(), (ptrls).length(), sizeof(void *), fn) + +struct intlist : fillbytes +{ + int length() + { + return (int)(size() / sizeof(int)); + } + int *base() + { + return (int *)fillbytes::base(); + } + int &get(int i) + { + return *(int *)loc(i * sizeof(int)); + } + int *limit() + { + return (int *)fillbytes::limit(); + } + void add(int x) + { + *(int *)grow(sizeof(x)) = x; + } + void popTo(int l) + { + assert(l <= length()); + b.len = l * sizeof(int); + } + int indexOf(int x); + bool contains(int x) + { + return indexOf(x) >= 0; + } +}; diff --git a/depends/pack200/src/coding.cpp b/depends/pack200/src/coding.cpp new file mode 100644 index 00000000..32977e05 --- /dev/null +++ b/depends/pack200/src/coding.cpp @@ -0,0 +1,1049 @@ +/* + * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// -*- C++ -*- +// Small program for unpacking specially compressed Java packages. +// John R. Rose + +#include +#include +#include +#include +#include + +#include "defines.h" +#include "bytes.h" +#include "utils.h" +#include "coding.h" + +#include "constants.h" +#include "unpack.h" + +extern coding basic_codings[]; + +#define CODING_PRIVATE(spec) \ + int spec_ = spec; \ + int B = CODING_B(spec_); \ + int H = CODING_H(spec_); \ + int L = 256 - H; \ + int S = CODING_S(spec_); \ + int D = CODING_D(spec_) + +#define IS_NEG_CODE(S, codeVal) ((((int)(codeVal) + 1) & ((1 << S) - 1)) == 0) + +#define DECODE_SIGN_S1(ux) (((uint)(ux) >> 1) ^ -((int)(ux) & 1)) + +static int decode_sign(int S, uint ux) +{ // == Coding.decodeSign32 + assert(S > 0); + uint sigbits = (ux >> S); + if (IS_NEG_CODE(S, ux)) + return (int)(~sigbits); + else + return (int)(ux - sigbits); + // Note that (int)(ux-sigbits) can be negative, if ux is large enough. +} + +coding *coding::init() +{ + if (umax > 0) + return this; // already done + assert(spec != 0); // sanity + + // fill in derived fields + CODING_PRIVATE(spec); + + // Return nullptr if 'arb(BHSD)' parameter constraints are not met: + if (B < 1 || B > B_MAX) + return nullptr; + if (H < 1 || H > 256) + return nullptr; + if (S < 0 || S > 2) + return nullptr; + if (D < 0 || D > 1) + return nullptr; + if (B == 1 && H != 256) + return nullptr; // 1-byte coding must be fixed-size + if (B >= 5 && H == 256) + return nullptr; // no 5-byte fixed-size coding + + // first compute the range of the coding, in 64 bits + jlong range = 0; + { + jlong H_i = 1; + for (int i = 0; i < B; i++) + { + range += H_i; + H_i *= H; + } + range *= L; + range += H_i; + } + assert(range > 0); // no useless codings, please + + int this_umax; + + // now, compute min and max + if (range >= ((jlong)1 << 32)) + { + this_umax = INT_MAX_VALUE; + this->umin = INT_MIN_VALUE; + this->max = INT_MAX_VALUE; + this->min = INT_MIN_VALUE; + } + else + { + this_umax = (range > INT_MAX_VALUE) ? INT_MAX_VALUE : (int)range - 1; + this->max = this_umax; + this->min = this->umin = 0; + if (S != 0 && range != 0) + { + int Smask = (1 << S) - 1; + jlong maxPosCode = range - 1; + jlong maxNegCode = range - 1; + while (IS_NEG_CODE(S, maxPosCode)) + --maxPosCode; + while (!IS_NEG_CODE(S, maxNegCode)) + --maxNegCode; + int maxPos = decode_sign(S, (uint)maxPosCode); + if (maxPos < 0) + this->max = INT_MAX_VALUE; // 32-bit wraparound + else + this->max = maxPos; + if (maxNegCode < 0) + this->min = 0; // No negative codings at all. + else + this->min = decode_sign(S, (uint)maxNegCode); + } + } + + assert(!(isFullRange | isSigned | isSubrange)); // init + if (min < 0) + this->isSigned = true; + if (max < INT_MAX_VALUE && range <= INT_MAX_VALUE) + this->isSubrange = true; + if (max == INT_MAX_VALUE && min == INT_MIN_VALUE) + this->isFullRange = true; + + // do this last, to reduce MT exposure (should have a membar too) + this->umax = this_umax; + + return this; +} + +coding *coding::findBySpec(int spec) +{ + for (coding *scan = &basic_codings[0];; scan++) + { + if (scan->spec == spec) + return scan->init(); + if (scan->spec == 0) + break; + } + coding *ptr = NEW(coding, 1); + CHECK_NULL_0(ptr); + coding *c = ptr->initFrom(spec); + if (c == nullptr) + { + ::free(ptr); + } + else + // else caller should free it... + c->isMalloc = true; + return c; +} + +coding *coding::findBySpec(int B, int H, int S, int D) +{ + if (B < 1 || B > B_MAX) + return nullptr; + if (H < 1 || H > 256) + return nullptr; + if (S < 0 || S > 2) + return nullptr; + if (D < 0 || D > 1) + return nullptr; + return findBySpec(CODING_SPEC(B, H, S, D)); +} + +void coding::free() +{ + if (isMalloc) + { + ::free(this); + } +} + +void coding_method::reset(value_stream *state) +{ + assert(state->rp == state->rplimit); // not in mid-stream, please + // assert(this == vs0.cm); + state[0] = vs0; + if (uValues != nullptr) + { + uValues->reset(state->helper()); + } +} + +uint coding::parse(byte *&rp, int B, int H) +{ + int L = 256 - H; + byte *ptr = rp; + // hand peel the i==0 part of the loop: + uint b_i = *ptr++ & 0xFF; + if (B == 1 || b_i < (uint)L) + { + rp = ptr; + return b_i; + } + uint sum = b_i; + uint H_i = H; + assert(B <= B_MAX); + for (int i = 2; i <= B_MAX; i++) + { // easy for compilers to unroll if desired + b_i = *ptr++ & 0xFF; + sum += b_i * H_i; + if (i == B || b_i < (uint)L) + { + rp = ptr; + return sum; + } + H_i *= H; + } + assert(false); + return 0; +} + +uint coding::parse_lgH(byte *&rp, int B, int H, int lgH) +{ + assert(H == (1 << lgH)); + int L = 256 - (1 << lgH); + byte *ptr = rp; + // hand peel the i==0 part of the loop: + uint b_i = *ptr++ & 0xFF; + if (B == 1 || b_i < (uint)L) + { + rp = ptr; + return b_i; + } + uint sum = b_i; + uint lg_H_i = lgH; + assert(B <= B_MAX); + for (int i = 2; i <= B_MAX; i++) + { // easy for compilers to unroll if desired + b_i = *ptr++ & 0xFF; + sum += b_i << lg_H_i; + if (i == B || b_i < (uint)L) + { + rp = ptr; + return sum; + } + lg_H_i += lgH; + } + assert(false); + return 0; +} + +static const char ERB[] = "EOF reading band"; + +void coding::parseMultiple(byte *&rp, int N, byte *limit, int B, int H) +{ + if (N < 0) + { + abort("bad value count"); + return; + } + byte *ptr = rp; + if (B == 1 || H == 256) + { + size_t len = (size_t)N * B; + if (len / B != (size_t)N || ptr + len > limit) + { + abort(ERB); + return; + } + rp = ptr + len; + return; + } + // Note: We assume rp has enough zero-padding. + int L = 256 - H; + int n = B; + while (N > 0) + { + ptr += 1; + if (--n == 0) + { + // end of encoding at B bytes, regardless of byte value + } + else + { + int b = (ptr[-1] & 0xFF); + if (b >= L) + { + // keep going, unless we find a byte < L + continue; + } + } + // found the last byte + N -= 1; + n = B; // reset length counter + // do an error check here + if (ptr > limit) + { + abort(ERB); + return; + } + } + rp = ptr; + return; +} + +bool value_stream::hasHelper() +{ + // If my coding method is a pop-style method, + // then I need a second value stream to transmit + // unfavored values. + // This can be determined by examining fValues. + return cm->fValues != nullptr; +} + +void value_stream::init(byte *rp_, byte *rplimit_, coding *defc) +{ + rp = rp_; + rplimit = rplimit_; + sum = 0; + cm = nullptr; // no need in the simple case + setCoding(defc); +} + +void value_stream::setCoding(coding *defc) +{ + if (defc == nullptr) + { + unpack_abort("bad coding"); + defc = coding::findByIndex(_meta_canon_min); // random pick for recovery + } + + c = (*defc); + + // choose cmk + cmk = cmk_ERROR; + switch (c.spec) + { + case BYTE1_spec: + cmk = cmk_BYTE1; + break; + case CHAR3_spec: + cmk = cmk_CHAR3; + break; + case UNSIGNED5_spec: + cmk = cmk_UNSIGNED5; + break; + case DELTA5_spec: + cmk = cmk_DELTA5; + break; + case BCI5_spec: + cmk = cmk_BCI5; + break; + case BRANCH5_spec: + cmk = cmk_BRANCH5; + break; + default: + if (c.D() == 0) + { + switch (c.S()) + { + case 0: + cmk = cmk_BHS0; + break; + case 1: + cmk = cmk_BHS1; + break; + default: + cmk = cmk_BHS; + break; + } + } + else + { + if (c.S() == 1) + { + if (c.isFullRange) + cmk = cmk_BHS1D1full; + if (c.isSubrange) + cmk = cmk_BHS1D1sub; + } + if (cmk == cmk_ERROR) + cmk = cmk_BHSD1; + } + } +} + +static int getPopValue(value_stream *self, uint uval) +{ + if (uval > 0) + { + // note that the initial parse performed a range check + assert(uval <= (uint)self->cm->fVlength); + return self->cm->fValues[uval - 1]; + } + else + { + // take an unfavored value + return self->helper()->getInt(); + } +} + +int coding::sumInUnsignedRange(int x, int y) +{ + assert(isSubrange); + int range = (int)(umax + 1); + assert(range > 0); + x += y; + if (x != (int)((jlong)(x - y) + (jlong)y)) + { + // 32-bit overflow interferes with range reduction. + // Back off from the overflow by adding a multiple of range: + if (x < 0) + { + x -= range; + assert(x >= 0); + } + else + { + x += range; + assert(x < 0); + } + } + if (x < 0) + { + x += range; + if (x >= 0) + return x; + } + else if (x >= range) + { + x -= range; + if (x < range) + return x; + } + else + { + // in range + return x; + } + // do it the hard way + x %= range; + if (x < 0) + x += range; + return x; +} + +static int getDeltaValue(value_stream *self, uint uval, bool isSubrange) +{ + assert((uint)(self->c.isSubrange) == (uint)isSubrange); + assert(self->c.isSubrange | self->c.isFullRange); + if (isSubrange) + return self->sum = self->c.sumInUnsignedRange(self->sum, (int)uval); + else + return self->sum += (int)uval; +} + +bool value_stream::hasValue() +{ + if (rp < rplimit) + return true; + if (cm == nullptr) + return false; + if (cm->next == nullptr) + return false; + cm->next->reset(this); + return hasValue(); +} + +int value_stream::getInt() +{ + if (rp >= rplimit) + { + // Advance to next coding segment. + if (rp > rplimit || cm == nullptr || cm->next == nullptr) + { + // Must perform this check and throw an exception on bad input. + unpack_abort(ERB); + return 0; + } + cm->next->reset(this); + return getInt(); + } + + CODING_PRIVATE(c.spec); + uint uval; + enum + { + B5 = 5, + B3 = 3, + H128 = 128, + H64 = 64, + H4 = 4 + }; + switch (cmk) + { + case cmk_BHS: + assert(D == 0); + uval = coding::parse(rp, B, H); + if (S == 0) + return (int)uval; + return decode_sign(S, uval); + + case cmk_BHS0: + assert(S == 0 && D == 0); + uval = coding::parse(rp, B, H); + return (int)uval; + + case cmk_BHS1: + assert(S == 1 && D == 0); + uval = coding::parse(rp, B, H); + return DECODE_SIGN_S1(uval); + + case cmk_BYTE1: + assert(c.spec == BYTE1_spec); + assert(B == 1 && H == 256 && S == 0 && D == 0); + return *rp++ & 0xFF; + + case cmk_CHAR3: + assert(c.spec == CHAR3_spec); + assert(B == B3 && H == H128 && S == 0 && D == 0); + return coding::parse_lgH(rp, B3, H128, 7); + + case cmk_UNSIGNED5: + assert(c.spec == UNSIGNED5_spec); + assert(B == B5 && H == H64 && S == 0 && D == 0); + return coding::parse_lgH(rp, B5, H64, 6); + + case cmk_BHSD1: + assert(D == 1); + uval = coding::parse(rp, B, H); + if (S != 0) + uval = (uint)decode_sign(S, uval); + return getDeltaValue(this, uval, (bool)c.isSubrange); + + case cmk_BHS1D1full: + assert(S == 1 && D == 1 && c.isFullRange); + uval = coding::parse(rp, B, H); + uval = (uint)DECODE_SIGN_S1(uval); + return getDeltaValue(this, uval, false); + + case cmk_BHS1D1sub: + assert(S == 1 && D == 1 && c.isSubrange); + uval = coding::parse(rp, B, H); + uval = (uint)DECODE_SIGN_S1(uval); + return getDeltaValue(this, uval, true); + + case cmk_DELTA5: + assert(c.spec == DELTA5_spec); + assert(B == B5 && H == H64 && S == 1 && D == 1 && c.isFullRange); + uval = coding::parse_lgH(rp, B5, H64, 6); + sum += DECODE_SIGN_S1(uval); + return sum; + + case cmk_BCI5: + assert(c.spec == BCI5_spec); + assert(B == B5 && H == H4 && S == 0 && D == 0); + return coding::parse_lgH(rp, B5, H4, 2); + + case cmk_BRANCH5: + assert(c.spec == BRANCH5_spec); + assert(B == B5 && H == H4 && S == 2 && D == 0); + uval = coding::parse_lgH(rp, B5, H4, 2); + return decode_sign(S, uval); + + case cmk_pop: + uval = coding::parse(rp, B, H); + if (S != 0) + { + uval = (uint)decode_sign(S, uval); + } + if (D != 0) + { + assert(c.isSubrange | c.isFullRange); + if (c.isSubrange) + sum = c.sumInUnsignedRange(sum, (int)uval); + else + sum += (int)uval; + uval = (uint)sum; + } + return getPopValue(this, uval); + + case cmk_pop_BHS0: + assert(S == 0 && D == 0); + uval = coding::parse(rp, B, H); + return getPopValue(this, uval); + + case cmk_pop_BYTE1: + assert(c.spec == BYTE1_spec); + assert(B == 1 && H == 256 && S == 0 && D == 0); + return getPopValue(this, *rp++ & 0xFF); + + default: + break; + } + assert(false); + return 0; +} + +static int moreCentral(int x, int y) +{ // used to find end of Pop.{F} + // Suggested implementation from the Pack200 specification: + uint kx = (x >> 31) ^ (x << 1); + uint ky = (y >> 31) ^ (y << 1); + return (kx < ky ? x : y); +} +// static maybe_inline +// int moreCentral2(int x, int y, int min) { +// // Strict implementation of buggy 150.7 specification. +// // The bug is that the spec. says absolute-value ties are broken +// // in favor of positive numbers, but the suggested implementation +// // (also mentioned in the spec.) breaks ties in favor of negative numbers. +// if ((x + y) != 0) +// return min; +// else +// // return the other value, which breaks a tie in the positive direction +// return (x > y)? x: y; +//} + +static const byte *no_meta[] = {nullptr}; +#define NO_META (*(byte **)no_meta) +enum +{ + POP_FAVORED_N = -2 +}; + +// mode bits +#define DISABLE_RUN 1 // used immediately inside ACodee +#define DISABLE_POP 2 // used recursively in all pop sub-bands + +// This function knows all about meta-coding. +void coding_method::init(byte *&band_rp, byte *band_limit, byte *&meta_rp, int mode, + coding *defc, int N, intlist *valueSink) +{ + assert(N != 0); + + assert(u != nullptr); // must be pre-initialized + // if (u == nullptr) u = unpacker::current(); // expensive + + int op = (meta_rp == nullptr) ? _meta_default : (*meta_rp++ & 0xFF); + coding *foundc = nullptr; + coding *to_free = nullptr; + + if (op == _meta_default) + { + foundc = defc; + // and fall through + } + else if (op >= _meta_canon_min && op <= _meta_canon_max) + { + foundc = coding::findByIndex(op); + // and fall through + } + else if (op == _meta_arb) + { + int args = (*meta_rp++ & 0xFF); + // args = (D:[0..1] + 2*S[0..2] + 8*(B:[1..5]-1)) + int D = ((args >> 0) & 1); + int S = ((args >> 1) & 3); + int B = ((args >> 3) & -1) + 1; + // & (H[1..256]-1) + int H = (*meta_rp++ & 0xFF) + 1; + foundc = coding::findBySpec(B, H, S, D); + to_free = foundc; // findBySpec may dynamically allocate + if (foundc == nullptr) + { + abort("illegal arb. coding"); + return; + } + // and fall through + } + else if (op >= _meta_run && op < _meta_pop) + { + int args = (op - _meta_run); + // args: KX:[0..3] + 4*(KBFlag:[0..1]) + 8*(ABDef:[0..2]) + int KX = ((args >> 0) & 3); + int KBFlag = ((args >> 2) & 1); + int ABDef = ((args >> 3) & -1); + assert(ABDef <= 2); + // & KB: one of [0..255] if KBFlag=1 + int KB = (!KBFlag ? 3 : (*meta_rp++ & 0xFF)); + int K = (KB + 1) << (KX * 4); + int N2 = (N >= 0) ? N - K : N; + if (N == 0 || (N2 <= 0 && N2 != N)) + { + abort("illegal run encoding"); + return; + } + if ((mode & DISABLE_RUN) != 0) + { + abort("illegal nested run encoding"); + return; + } + + // & Enc{ ACode } if ADef=0 (ABDef != 1) + // No direct nesting of 'run' in ACode, but in BCode it's OK. + int disRun = mode | DISABLE_RUN; + if (ABDef == 1) + { + this->init(band_rp, band_limit, NO_META, disRun, defc, K, valueSink); + } + else + { + this->init(band_rp, band_limit, meta_rp, disRun, defc, K, valueSink); + } + CHECK; + + // & Enc{ BCode } if BDef=0 (ABDef != 2) + coding_method *tail = U_NEW(coding_method, 1); + CHECK_NULL(tail); + tail->u = u; + + // The 'run' codings may be nested indirectly via 'pop' codings. + // This means that this->next may already be filled in, if + // ACode was of type 'pop' with a 'run' token coding. + // No problem: Just chain the upcoming BCode onto the end. + for (coding_method *self = this;; self = self->next) + { + if (self->next == nullptr) + { + self->next = tail; + break; + } + } + + if (ABDef == 2) + { + tail->init(band_rp, band_limit, NO_META, mode, defc, N2, valueSink); + } + else + { + tail->init(band_rp, band_limit, meta_rp, mode, defc, N2, valueSink); + } + // Note: The preceding calls to init should be tail-recursive. + + return; // done; no falling through + } + else if (op >= _meta_pop && op < _meta_limit) + { + int args = (op - _meta_pop); + // args: (FDef:[0..1]) + 2*UDef:[0..1] + 4*(TDefL:[0..11]) + int FDef = ((args >> 0) & 1); + int UDef = ((args >> 1) & 1); + int TDefL = ((args >> 2) & -1); + assert(TDefL <= 11); + int TDef = (TDefL > 0); + int TL = (TDefL <= 6) ? (2 << TDefL) : (256 - (4 << (11 - TDefL))); + int TH = (256 - TL); + if (N <= 0) + { + abort("illegal pop encoding"); + return; + } + if ((mode & DISABLE_POP) != 0) + { + abort("illegal nested pop encoding"); + return; + } + + // No indirect nesting of 'pop', but 'run' is OK. + int disPop = DISABLE_POP; + + // & Enc{ FCode } if FDef=0 + int FN = POP_FAVORED_N; + assert(valueSink == nullptr); + intlist fValueSink; + fValueSink.init(); + coding_method fval; + BYTES_OF(fval).clear(); + fval.u = u; + if (FDef != 0) + { + fval.init(band_rp, band_limit, NO_META, disPop, defc, FN, &fValueSink); + } + else + { + fval.init(band_rp, band_limit, meta_rp, disPop, defc, FN, &fValueSink); + } + bytes fvbuf; + fValues = (u->saveTo(fvbuf, fValueSink.b), (int *)fvbuf.ptr); + fVlength = fValueSink.length(); // i.e., the parameter K + fValueSink.free(); + CHECK; + + // Skip the first {F} run in all subsequent passes. + // The next call to this->init(...) will set vs0.rp to point after the {F}. + + // & Enc{ TCode } if TDef=0 (TDefL==0) + if (TDef != 0) + { + coding *tcode = coding::findBySpec(1, 256); // BYTE1 + // find the most narrowly sufficient code: + for (int B = 2; B <= B_MAX; B++) + { + if (fVlength <= tcode->umax) + break; // found it + tcode->free(); + tcode = coding::findBySpec(B, TH); + CHECK_NULL(tcode); + } + if (!(fVlength <= tcode->umax)) + { + abort("pop.L value too small"); + return; + } + this->init(band_rp, band_limit, NO_META, disPop, tcode, N, nullptr); + tcode->free(); + } + else + { + this->init(band_rp, band_limit, meta_rp, disPop, defc, N, nullptr); + } + CHECK; + + // Count the number of zero tokens right now. + // Also verify that they are in bounds. + int UN = 0; // one {U} for each zero in {T} + value_stream vs = vs0; + for (int i = 0; i < N; i++) + { + uint val = vs.getInt(); + if (val == 0) + UN += 1; + if (!(val <= (uint)fVlength)) + { + abort("pop token out of range"); + return; + } + } + vs.done(); + + // & Enc{ UCode } if UDef=0 + if (UN != 0) + { + uValues = U_NEW(coding_method, 1); + CHECK_NULL(uValues); + uValues->u = u; + if (UDef != 0) + { + uValues->init(band_rp, band_limit, NO_META, disPop, defc, UN, nullptr); + } + else + { + uValues->init(band_rp, band_limit, meta_rp, disPop, defc, UN, nullptr); + } + } + else + { + if (UDef == 0) + { + int uop = (*meta_rp++ & 0xFF); + if (uop > _meta_canon_max) + // %%% Spec. requires the more strict (uop != _meta_default). + abort("bad meta-coding for empty pop/U"); + } + } + + // Bug fix for 6259542 + // Last of all, adjust vs0.cmk to the 'pop' flavor + for (coding_method *self = this; self != nullptr; self = self->next) + { + coding_method_kind cmk2 = cmk_pop; + switch (self->vs0.cmk) + { + case cmk_BHS0: + cmk2 = cmk_pop_BHS0; + break; + case cmk_BYTE1: + cmk2 = cmk_pop_BYTE1; + break; + default: + break; + } + self->vs0.cmk = cmk2; + if (self != this) + { + assert(self->fValues == nullptr); // no double init + self->fValues = this->fValues; + self->fVlength = this->fVlength; + assert(self->uValues == nullptr); // must stay nullptr + } + } + + return; // done; no falling through + } + else + { + abort("bad meta-coding"); + return; + } + + // Common code here skips a series of values with one coding. + assert(foundc != nullptr); + + assert(vs0.cmk == cmk_ERROR); // no garbage, please + assert(vs0.rp == nullptr); // no garbage, please + assert(vs0.rplimit == nullptr); // no garbage, please + assert(vs0.sum == 0); // no garbage, please + + vs0.init(band_rp, band_limit, foundc); + + // Done with foundc. Free if necessary. + if (to_free != nullptr) + { + to_free->free(); + to_free = nullptr; + } + foundc = nullptr; + + coding &c = vs0.c; + CODING_PRIVATE(c.spec); + // assert sane N + assert((uint)N < INT_MAX_VALUE || N == POP_FAVORED_N); + + // Look at the values, or at least skip over them quickly. + if (valueSink == nullptr) + { + // Skip and ignore values in the first pass. + c.parseMultiple(band_rp, N, band_limit, B, H); + } + else if (N >= 0) + { + // Pop coding, {F} sequence, initial run of values... + assert((mode & DISABLE_POP) != 0); + value_stream vs = vs0; + for (int n = 0; n < N; n++) + { + int val = vs.getInt(); + valueSink->add(val); + } + band_rp = vs.rp; + } + else + { + // Pop coding, {F} sequence, final run of values... + assert((mode & DISABLE_POP) != 0); + assert(N == POP_FAVORED_N); + int min = INT_MIN_VALUE; // farthest from the center + // min2 is based on the buggy specification of centrality in version 150.7 + // no known implementations transmit this value, but just in case... + // int min2 = INT_MIN_VALUE; + int last = 0; + // if there were initial runs, find the potential sentinels in them: + for (int i = 0; i < valueSink->length(); i++) + { + last = valueSink->get(i); + min = moreCentral(min, last); + // min2 = moreCentral2(min2, last, min); + } + value_stream vs = vs0; + for (;;) + { + int val = vs.getInt(); + if (valueSink->length() > 0 && (val == last || val == min)) //|| val == min2 + break; + valueSink->add(val); + CHECK; + last = val; + min = moreCentral(min, last); + // min2 = moreCentral2(min2, last, min); + } + band_rp = vs.rp; + } + CHECK; + + // Get an accurate upper limit now. + vs0.rplimit = band_rp; + vs0.cm = this; + + return; // success +} + +coding basic_codings[] = { + // This one is not a usable irregular coding, but is used by cp_Utf8_chars. + CODING_INIT(3, 128, 0, 0), + + // Fixed-length codings: + CODING_INIT(1, 256, 0, 0), CODING_INIT(1, 256, 1, 0), CODING_INIT(1, 256, 0, 1), + CODING_INIT(1, 256, 1, 1), CODING_INIT(2, 256, 0, 0), CODING_INIT(2, 256, 1, 0), + CODING_INIT(2, 256, 0, 1), CODING_INIT(2, 256, 1, 1), CODING_INIT(3, 256, 0, 0), + CODING_INIT(3, 256, 1, 0), CODING_INIT(3, 256, 0, 1), CODING_INIT(3, 256, 1, 1), + CODING_INIT(4, 256, 0, 0), CODING_INIT(4, 256, 1, 0), CODING_INIT(4, 256, 0, 1), + CODING_INIT(4, 256, 1, 1), + + // Full-range variable-length codings: + CODING_INIT(5, 4, 0, 0), CODING_INIT(5, 4, 1, 0), CODING_INIT(5, 4, 2, 0), + CODING_INIT(5, 16, 0, 0), CODING_INIT(5, 16, 1, 0), CODING_INIT(5, 16, 2, 0), + CODING_INIT(5, 32, 0, 0), CODING_INIT(5, 32, 1, 0), CODING_INIT(5, 32, 2, 0), + CODING_INIT(5, 64, 0, 0), CODING_INIT(5, 64, 1, 0), CODING_INIT(5, 64, 2, 0), + CODING_INIT(5, 128, 0, 0), CODING_INIT(5, 128, 1, 0), CODING_INIT(5, 128, 2, 0), + CODING_INIT(5, 4, 0, 1), CODING_INIT(5, 4, 1, 1), CODING_INIT(5, 4, 2, 1), + CODING_INIT(5, 16, 0, 1), CODING_INIT(5, 16, 1, 1), CODING_INIT(5, 16, 2, 1), + CODING_INIT(5, 32, 0, 1), CODING_INIT(5, 32, 1, 1), CODING_INIT(5, 32, 2, 1), + CODING_INIT(5, 64, 0, 1), CODING_INIT(5, 64, 1, 1), CODING_INIT(5, 64, 2, 1), + CODING_INIT(5, 128, 0, 1), CODING_INIT(5, 128, 1, 1), CODING_INIT(5, 128, 2, 1), + + // Variable length subrange codings: + CODING_INIT(2, 192, 0, 0), CODING_INIT(2, 224, 0, 0), CODING_INIT(2, 240, 0, 0), + CODING_INIT(2, 248, 0, 0), CODING_INIT(2, 252, 0, 0), CODING_INIT(2, 8, 0, 1), + CODING_INIT(2, 8, 1, 1), CODING_INIT(2, 16, 0, 1), CODING_INIT(2, 16, 1, 1), + CODING_INIT(2, 32, 0, 1), CODING_INIT(2, 32, 1, 1), CODING_INIT(2, 64, 0, 1), + CODING_INIT(2, 64, 1, 1), CODING_INIT(2, 128, 0, 1), CODING_INIT(2, 128, 1, 1), + CODING_INIT(2, 192, 0, 1), CODING_INIT(2, 192, 1, 1), CODING_INIT(2, 224, 0, 1), + CODING_INIT(2, 224, 1, 1), CODING_INIT(2, 240, 0, 1), CODING_INIT(2, 240, 1, 1), + CODING_INIT(2, 248, 0, 1), CODING_INIT(2, 248, 1, 1), CODING_INIT(3, 192, 0, 0), + CODING_INIT(3, 224, 0, 0), CODING_INIT(3, 240, 0, 0), CODING_INIT(3, 248, 0, 0), + CODING_INIT(3, 252, 0, 0), CODING_INIT(3, 8, 0, 1), CODING_INIT(3, 8, 1, 1), + CODING_INIT(3, 16, 0, 1), CODING_INIT(3, 16, 1, 1), CODING_INIT(3, 32, 0, 1), + CODING_INIT(3, 32, 1, 1), CODING_INIT(3, 64, 0, 1), CODING_INIT(3, 64, 1, 1), + CODING_INIT(3, 128, 0, 1), CODING_INIT(3, 128, 1, 1), CODING_INIT(3, 192, 0, 1), + CODING_INIT(3, 192, 1, 1), CODING_INIT(3, 224, 0, 1), CODING_INIT(3, 224, 1, 1), + CODING_INIT(3, 240, 0, 1), CODING_INIT(3, 240, 1, 1), CODING_INIT(3, 248, 0, 1), + CODING_INIT(3, 248, 1, 1), CODING_INIT(4, 192, 0, 0), CODING_INIT(4, 224, 0, 0), + CODING_INIT(4, 240, 0, 0), CODING_INIT(4, 248, 0, 0), CODING_INIT(4, 252, 0, 0), + CODING_INIT(4, 8, 0, 1), CODING_INIT(4, 8, 1, 1), CODING_INIT(4, 16, 0, 1), + CODING_INIT(4, 16, 1, 1), CODING_INIT(4, 32, 0, 1), CODING_INIT(4, 32, 1, 1), + CODING_INIT(4, 64, 0, 1), CODING_INIT(4, 64, 1, 1), CODING_INIT(4, 128, 0, 1), + CODING_INIT(4, 128, 1, 1), CODING_INIT(4, 192, 0, 1), CODING_INIT(4, 192, 1, 1), + CODING_INIT(4, 224, 0, 1), CODING_INIT(4, 224, 1, 1), CODING_INIT(4, 240, 0, 1), + CODING_INIT(4, 240, 1, 1), CODING_INIT(4, 248, 0, 1), CODING_INIT(4, 248, 1, 1), + CODING_INIT(0, 0, 0, 0)}; +#define BASIC_INDEX_LIMIT (int)(sizeof(basic_codings) / sizeof(basic_codings[0]) - 1) + +coding *coding::findByIndex(int idx) +{ + int index_limit = BASIC_INDEX_LIMIT; + assert(_meta_canon_min == 1 && _meta_canon_max + 1 == index_limit); + + if (idx >= _meta_canon_min && idx <= _meta_canon_max) + return basic_codings[idx].init(); + else + return nullptr; +} diff --git a/depends/pack200/src/coding.h b/depends/pack200/src/coding.h new file mode 100644 index 00000000..5f017b9e --- /dev/null +++ b/depends/pack200/src/coding.h @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +struct unpacker; + +#define INT_MAX_VALUE ((int)0x7FFFFFFF) +#define INT_MIN_VALUE ((int)0x80000000) + +#define CODING_SPEC(B, H, S, D) ((B) << 20 | (H) << 8 | (S) << 4 | (D) << 0) +#define CODING_B(x) ((x) >> 20 & 0xF) +#define CODING_H(x) ((x) >> 8 & 0xFFF) +#define CODING_S(x) ((x) >> 4 & 0xF) +#define CODING_D(x) ((x) >> 0 & 0xF) + +#define CODING_INIT(B, H, S, D) \ + { \ + CODING_SPEC(B, H, S, D), 0, 0, 0, 0, 0, 0, 0, 0 \ + } + +// For debugging purposes, some compilers do not like this and will complain. +// #define long do_not_use_C_long_types_use_jlong_or_int +// Use of the type "long" is problematic, do not use it. + +struct coding +{ + int spec; // B,H,S,D + + // Handy values derived from the spec: + int B() + { + return CODING_B(spec); + } + int H() + { + return CODING_H(spec); + } + int S() + { + return CODING_S(spec); + } + int D() + { + return CODING_D(spec); + } + int L() + { + return 256 - CODING_H(spec); + } + int min, max; + int umin, umax; + char isSigned, isSubrange, isFullRange, isMalloc; + + coding *init(); // returns self or nullptr if error + coding *initFrom(int spec_) + { + assert(this->spec == 0); + this->spec = spec_; + return init(); + } + + static coding *findBySpec(int spec); + static coding *findBySpec(int B, int H, int S = 0, int D = 0); + static coding *findByIndex(int irregularCodingIndex); + + static uint parse(byte *&rp, int B, int H); + static uint parse_lgH(byte *&rp, int B, int H, int lgH); + static void parseMultiple(byte *&rp, int N, byte *limit, int B, int H); + + uint parse(byte *&rp) + { + return parse(rp, CODING_B(spec), CODING_H(spec)); + } + void parseMultiple(byte *&rp, int N, byte *limit) + { + parseMultiple(rp, N, limit, CODING_B(spec), CODING_H(spec)); + } + + bool canRepresent(int x) + { + return (x >= min && x <= max); + } + bool canRepresentUnsigned(int x) + { + return (x >= umin && x <= umax); + } + + int sumInUnsignedRange(int x, int y); + + int readFrom(byte *&rpVar, int *dbase); + void readArrayFrom(byte *&rpVar, int *dbase, int length, int *values); + void skipArrayFrom(byte *&rpVar, int length) + { + readArrayFrom(rpVar, (int *)NULL, length, (int *)NULL); + } + + void free(); // free self if isMalloc + + // error handling + static void abort(const char *msg = nullptr) + { + unpack_abort(msg); + } +}; + +enum coding_method_kind +{ + cmk_ERROR, + cmk_BHS, + cmk_BHS0, + cmk_BHS1, + cmk_BHSD1, + cmk_BHS1D1full, // isFullRange + cmk_BHS1D1sub, // isSubRange + + // special cases hand-optimized (~50% of all decoded values) + cmk_BYTE1, //(1,256) 6% + cmk_CHAR3, //(3,128) 7% + cmk_UNSIGNED5, //(5,64) 13% + cmk_DELTA5, //(5,64,1,1) 5% + cmk_BCI5, //(5,4) 18% + cmk_BRANCH5, //(5,4,2) 4% + // cmk_UNSIGNED5H16, //(5,16) 5% + // cmk_UNSIGNED2H4, //(2,4) 6% + // cmk_DELTA4H8, //(4,8,1,1) 10% + // cmk_DELTA3H16, //(3,16,1,1) 9% + cmk_BHS_LIMIT, + cmk_pop, + cmk_pop_BHS0, + cmk_pop_BYTE1, + cmk_pop_LIMIT, + cmk_LIMIT +}; + +enum +{ + BYTE1_spec = CODING_SPEC(1, 256, 0, 0), + CHAR3_spec = CODING_SPEC(3, 128, 0, 0), + UNSIGNED4_spec = CODING_SPEC(4, 256, 0, 0), + UNSIGNED5_spec = CODING_SPEC(5, 64, 0, 0), + SIGNED5_spec = CODING_SPEC(5, 64, 1, 0), + DELTA5_spec = CODING_SPEC(5, 64, 1, 1), + UDELTA5_spec = CODING_SPEC(5, 64, 0, 1), + MDELTA5_spec = CODING_SPEC(5, 64, 2, 1), + BCI5_spec = CODING_SPEC(5, 4, 0, 0), + BRANCH5_spec = CODING_SPEC(5, 4, 2, 0) +}; + +enum +{ + B_MAX = 5, + C_SLOP = B_MAX * 10 +}; + +struct coding_method; + +// iterator under the control of a meta-coding +struct value_stream +{ + // current coding of values or values + coding c; // B,H,S,D,etc. + coding_method_kind cmk; // type of decoding needed + byte *rp; // read pointer + byte *rplimit; // final value of read pointer + int sum; // partial sum of all values so far (D=1 only) + coding_method *cm; // coding method that defines this stream + + void init(byte *band_rp, byte *band_limit, coding *defc); + void init(byte *band_rp, byte *band_limit, int spec) + { + init(band_rp, band_limit, coding::findBySpec(spec)); + } + + void setCoding(coding *c); + void setCoding(int spec) + { + setCoding(coding::findBySpec(spec)); + } + + // Parse and decode a single value. + int getInt(); + + // Parse and decode a single byte, with no error checks. + int getByte() + { + assert(cmk == cmk_BYTE1); + assert(rp < rplimit); + return *rp++ & 0xFF; + } + + // Used only for asserts. + bool hasValue(); + + void done() + { + assert(!hasValue()); + } + + // Sometimes a value stream has an auxiliary (but there are never two). + value_stream *helper() + { + assert(hasHelper()); + return this + 1; + } + bool hasHelper(); + + // error handling + // inline void abort(const char* msg); + // inline void aborting(); +}; + +struct coding_method +{ + value_stream vs0; // initial state snapshot (vs.meta==this) + + coding_method *next; // what to do when we run out of bytes + + // these fields are used for pop codes only: + int *fValues; // favored value array + int fVlength; // maximum favored value token + coding_method *uValues; // unfavored value stream + + // pointer to outer unpacker, for error checks etc. + unpacker *u; + + // Initialize a value stream. + void reset(value_stream *state); + + // Parse a band header, size a band, and initialize for further action. + // band_rp advances (but not past band_limit), and meta_rp advances. + // The mode gives context, such as "inside a pop". + // The defc and N are the incoming parameters to a meta-coding. + // The value sink is used to collect output values, when desired. + void init(byte *&band_rp, byte *band_limit, byte *&meta_rp, int mode, coding *defc, int N, + intlist *valueSink); + + // error handling + void abort(const char *msg) + { + unpack_abort(msg, u); + } + bool aborting() + { + return unpack_aborting(u); + } +}; + +// inline void value_stream::abort(const char* msg) { cm->abort(msg); } +// inline void value_stream::aborting() { cm->aborting(); } diff --git a/depends/pack200/src/constants.h b/depends/pack200/src/constants.h new file mode 100644 index 00000000..aeb3335d --- /dev/null +++ b/depends/pack200/src/constants.h @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + Java Class Version numbers history + 1.0 to 1.3.X 45,3 + 1.4 to 1.4.X 46,0 + 1.5 to 1.5.X 49,0 + 1.6 to 1.5.x 50,0 NOTE Assumed for now +*/ + +// classfile constants +#define JAVA_MAGIC 0xCAFEBABE +#define JAVA_MIN_MAJOR_VERSION 45 +#define JAVA_MIN_MINOR_VERSION 3 +#define JAVA5_MAX_MAJOR_VERSION 49 +#define JAVA5_MAX_MINOR_VERSION 0 +// NOTE: Assume for now +#define JAVA6_MAX_MAJOR_VERSION 50 +#define JAVA6_MAX_MINOR_VERSION 0 + +// package file constants +#define JAVA_PACKAGE_MAGIC 0xCAFED00D +#define JAVA5_PACKAGE_MAJOR_VERSION 150 +#define JAVA5_PACKAGE_MINOR_VERSION 7 + +#define JAVA6_PACKAGE_MAJOR_VERSION 160 +#define JAVA6_PACKAGE_MINOR_VERSION 1 + +// magic number for gzip streams (for processing pack200-gzip data) +#define GZIP_MAGIC 0x1F8B0800 +#define GZIP_MAGIC_MASK 0xFFFFFF00 // last byte is variable "flg" field + +enum +{ + CONSTANT_None, + CONSTANT_Utf8, + CONSTANT_unused2, /* unused, was Unicode */ + CONSTANT_Integer, + CONSTANT_Float, + CONSTANT_Long, + CONSTANT_Double, + CONSTANT_Class, + CONSTANT_String, + CONSTANT_Fieldref, + CONSTANT_Methodref, + CONSTANT_InterfaceMethodref, + CONSTANT_NameandType, + CONSTANT_Signature = 13, + CONSTANT_All = 14, + CONSTANT_Limit = 15, + CONSTANT_NONE = 0, + CONSTANT_Literal = 20, // pseudo-tag for debugging + CONSTANT_Member = 21, // pseudo-tag for debugging + SUBINDEX_BIT = 64, // combined with CONSTANT_xxx for ixTag + ACC_STATIC = 0x0008, + ACC_IC_LONG_FORM = (1 << 16), // for ic_flags + CLASS_ATTR_SourceFile = 17, + CLASS_ATTR_EnclosingMethod = 18, + CLASS_ATTR_InnerClasses = 23, + CLASS_ATTR_ClassFile_version = 24, + FIELD_ATTR_ConstantValue = 17, + METHOD_ATTR_Code = 17, + METHOD_ATTR_Exceptions = 18, + METHOD_ATTR_RuntimeVisibleParameterAnnotations = 23, + METHOD_ATTR_RuntimeInvisibleParameterAnnotations = 24, + METHOD_ATTR_AnnotationDefault = 25, + CODE_ATTR_StackMapTable = 0, + CODE_ATTR_LineNumberTable = 1, + CODE_ATTR_LocalVariableTable = 2, + CODE_ATTR_LocalVariableTypeTable = 3, + // X_ATTR_Synthetic = 12, // ACC_SYNTHETIC; not predefined + X_ATTR_Signature = 19, + X_ATTR_Deprecated = 20, + X_ATTR_RuntimeVisibleAnnotations = 21, + X_ATTR_RuntimeInvisibleAnnotations = 22, + X_ATTR_OVERFLOW = 16, + X_ATTR_LIMIT_NO_FLAGS_HI = 32, + X_ATTR_LIMIT_FLAGS_HI = 63, + +#define O_ATTR_DO(F) \ + F(X_ATTR_OVERFLOW, 01) \ + /*(end)*/ +#define X_ATTR_DO(F) \ + O_ATTR_DO(F) F(X_ATTR_Signature, Signature) F(X_ATTR_Deprecated, Deprecated) \ + F(X_ATTR_RuntimeVisibleAnnotations, RuntimeVisibleAnnotations) \ + F(X_ATTR_RuntimeInvisibleAnnotations, RuntimeInvisibleAnnotations) \ + /*F(X_ATTR_Synthetic,Synthetic)*/ \ + /*(end)*/ +#define CLASS_ATTR_DO(F) \ + F(CLASS_ATTR_SourceFile, SourceFile) F(CLASS_ATTR_InnerClasses, InnerClasses) \ + F(CLASS_ATTR_EnclosingMethod, EnclosingMethod) F(CLASS_ATTR_ClassFile_version, 02) \ + /*(end)*/ +#define FIELD_ATTR_DO(F) \ + F(FIELD_ATTR_ConstantValue, ConstantValue) \ + /*(end)*/ +#define METHOD_ATTR_DO(F) \ + F(METHOD_ATTR_Code, Code) F(METHOD_ATTR_Exceptions, Exceptions) \ + F(METHOD_ATTR_RuntimeVisibleParameterAnnotations, RuntimeVisibleParameterAnnotations) \ + F(METHOD_ATTR_RuntimeInvisibleParameterAnnotations, \ + RuntimeInvisibleParameterAnnotations) \ + F(METHOD_ATTR_AnnotationDefault, AnnotationDefault) \ + /*(end)*/ +#define CODE_ATTR_DO(F) \ + F(CODE_ATTR_StackMapTable, StackMapTable) F(CODE_ATTR_LineNumberTable, LineNumberTable) \ + F(CODE_ATTR_LocalVariableTable, LocalVariableTable) \ + F(CODE_ATTR_LocalVariableTypeTable, LocalVariableTypeTable) \ + /*(end)*/ +#define ALL_ATTR_DO(F) \ + X_ATTR_DO(F) CLASS_ATTR_DO(F) FIELD_ATTR_DO(F) METHOD_ATTR_DO(F) CODE_ATTR_DO(F) \ + /*(end)*/ + + // attribute "context types" + ATTR_CONTEXT_CLASS = 0, + ATTR_CONTEXT_FIELD = 1, + ATTR_CONTEXT_METHOD = 2, + ATTR_CONTEXT_CODE = 3, + ATTR_CONTEXT_LIMIT = 4, + + // constants for parsed layouts (stored in band::le_kind) + EK_NONE = 0, // not a layout element + EK_INT = 'I', // B H I SH etc., also FH etc. + EK_BCI = 'P', // PH etc. + EK_BCID = 'Q', // POH etc. + EK_BCO = 'O', // OH etc. + EK_REPL = 'N', // NH[...] etc. + EK_REF = 'R', // RUH, RUNH, KQH, etc. + EK_UN = 'T', // TB(...)[...] etc. + EK_CASE = 'K', // (...)[...] etc. + EK_CALL = '(', // (0), (1), etc. + EK_CBLE = '[', // [...][...] etc. + NO_BAND_INDEX = -1, + + // File option bits, from LSB in ascending bit position. + FO_DEFLATE_HINT = 1 << 0, + FO_IS_CLASS_STUB = 1 << 1, + + // Archive option bits, from LSB in ascending bit position: + AO_HAVE_SPECIAL_FORMATS = 1 << 0, + AO_HAVE_CP_NUMBERS = 1 << 1, + AO_HAVE_ALL_CODE_FLAGS = 1 << 2, + AO_3_UNUSED_MBZ = 1 << 3, + AO_HAVE_FILE_HEADERS = 1 << 4, + AO_DEFLATE_HINT = 1 << 5, + AO_HAVE_FILE_MODTIME = 1 << 6, + AO_HAVE_FILE_OPTIONS = 1 << 7, + AO_HAVE_FILE_SIZE_HI = 1 << 8, + AO_HAVE_CLASS_FLAGS_HI = 1 << 9, + AO_HAVE_FIELD_FLAGS_HI = 1 << 10, + AO_HAVE_METHOD_FLAGS_HI = 1 << 11, + AO_HAVE_CODE_FLAGS_HI = 1 << 12, +#define ARCHIVE_BIT_DO(F) \ + F(AO_HAVE_SPECIAL_FORMATS) F(AO_HAVE_CP_NUMBERS) F(AO_HAVE_ALL_CODE_FLAGS) \ + /*F(AO_3_UNUSED_MBZ)*/ \ + F(AO_HAVE_FILE_HEADERS) F(AO_DEFLATE_HINT) F(AO_HAVE_FILE_MODTIME) \ + F(AO_HAVE_FILE_OPTIONS) F(AO_HAVE_FILE_SIZE_HI) F(AO_HAVE_CLASS_FLAGS_HI) \ + F(AO_HAVE_FIELD_FLAGS_HI) F(AO_HAVE_METHOD_FLAGS_HI) F(AO_HAVE_CODE_FLAGS_HI) \ + /*(end)*/ + + // Constants for decoding attribute definition header bytes. + ADH_CONTEXT_MASK = 0x3, // (hdr & ADH_CONTEXT_MASK) + ADH_BIT_SHIFT = 0x2, // (hdr >> ADH_BIT_SHIFT) + ADH_BIT_IS_LSB = 1, // (hdr >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB +#define ADH_BYTE(context, index) ((((index) + ADH_BIT_IS_LSB) << ADH_BIT_SHIFT) + (context)) +#define ADH_BYTE_CONTEXT(adhb) ((adhb) & ADH_CONTEXT_MASK) +#define ADH_BYTE_INDEX(adhb) (((adhb) >> ADH_BIT_SHIFT) - ADH_BIT_IS_LSB) + NO_MODTIME = 0, // nullptr modtime value + + // meta-coding + _meta_default = 0, + _meta_canon_min = 1, + _meta_canon_max = 115, + _meta_arb = 116, + _meta_run = 117, + _meta_pop = 141, + _meta_limit = 189, + _meta_error = 255, + _xxx_1_end +}; + +// Bytecodes. + +enum +{ + bc_nop = 0, // 0x00 + bc_aconst_null = 1, // 0x01 + bc_iconst_m1 = 2, // 0x02 + bc_iconst_0 = 3, // 0x03 + bc_iconst_1 = 4, // 0x04 + bc_iconst_2 = 5, // 0x05 + bc_iconst_3 = 6, // 0x06 + bc_iconst_4 = 7, // 0x07 + bc_iconst_5 = 8, // 0x08 + bc_lconst_0 = 9, // 0x09 + bc_lconst_1 = 10, // 0x0a + bc_fconst_0 = 11, // 0x0b + bc_fconst_1 = 12, // 0x0c + bc_fconst_2 = 13, // 0x0d + bc_dconst_0 = 14, // 0x0e + bc_dconst_1 = 15, // 0x0f + bc_bipush = 16, // 0x10 + bc_sipush = 17, // 0x11 + bc_ldc = 18, // 0x12 + bc_ldc_w = 19, // 0x13 + bc_ldc2_w = 20, // 0x14 + bc_iload = 21, // 0x15 + bc_lload = 22, // 0x16 + bc_fload = 23, // 0x17 + bc_dload = 24, // 0x18 + bc_aload = 25, // 0x19 + bc_iload_0 = 26, // 0x1a + bc_iload_1 = 27, // 0x1b + bc_iload_2 = 28, // 0x1c + bc_iload_3 = 29, // 0x1d + bc_lload_0 = 30, // 0x1e + bc_lload_1 = 31, // 0x1f + bc_lload_2 = 32, // 0x20 + bc_lload_3 = 33, // 0x21 + bc_fload_0 = 34, // 0x22 + bc_fload_1 = 35, // 0x23 + bc_fload_2 = 36, // 0x24 + bc_fload_3 = 37, // 0x25 + bc_dload_0 = 38, // 0x26 + bc_dload_1 = 39, // 0x27 + bc_dload_2 = 40, // 0x28 + bc_dload_3 = 41, // 0x29 + bc_aload_0 = 42, // 0x2a + bc_aload_1 = 43, // 0x2b + bc_aload_2 = 44, // 0x2c + bc_aload_3 = 45, // 0x2d + bc_iaload = 46, // 0x2e + bc_laload = 47, // 0x2f + bc_faload = 48, // 0x30 + bc_daload = 49, // 0x31 + bc_aaload = 50, // 0x32 + bc_baload = 51, // 0x33 + bc_caload = 52, // 0x34 + bc_saload = 53, // 0x35 + bc_istore = 54, // 0x36 + bc_lstore = 55, // 0x37 + bc_fstore = 56, // 0x38 + bc_dstore = 57, // 0x39 + bc_astore = 58, // 0x3a + bc_istore_0 = 59, // 0x3b + bc_istore_1 = 60, // 0x3c + bc_istore_2 = 61, // 0x3d + bc_istore_3 = 62, // 0x3e + bc_lstore_0 = 63, // 0x3f + bc_lstore_1 = 64, // 0x40 + bc_lstore_2 = 65, // 0x41 + bc_lstore_3 = 66, // 0x42 + bc_fstore_0 = 67, // 0x43 + bc_fstore_1 = 68, // 0x44 + bc_fstore_2 = 69, // 0x45 + bc_fstore_3 = 70, // 0x46 + bc_dstore_0 = 71, // 0x47 + bc_dstore_1 = 72, // 0x48 + bc_dstore_2 = 73, // 0x49 + bc_dstore_3 = 74, // 0x4a + bc_astore_0 = 75, // 0x4b + bc_astore_1 = 76, // 0x4c + bc_astore_2 = 77, // 0x4d + bc_astore_3 = 78, // 0x4e + bc_iastore = 79, // 0x4f + bc_lastore = 80, // 0x50 + bc_fastore = 81, // 0x51 + bc_dastore = 82, // 0x52 + bc_aastore = 83, // 0x53 + bc_bastore = 84, // 0x54 + bc_castore = 85, // 0x55 + bc_sastore = 86, // 0x56 + bc_pop = 87, // 0x57 + bc_pop2 = 88, // 0x58 + bc_dup = 89, // 0x59 + bc_dup_x1 = 90, // 0x5a + bc_dup_x2 = 91, // 0x5b + bc_dup2 = 92, // 0x5c + bc_dup2_x1 = 93, // 0x5d + bc_dup2_x2 = 94, // 0x5e + bc_swap = 95, // 0x5f + bc_iadd = 96, // 0x60 + bc_ladd = 97, // 0x61 + bc_fadd = 98, // 0x62 + bc_dadd = 99, // 0x63 + bc_isub = 100, // 0x64 + bc_lsub = 101, // 0x65 + bc_fsub = 102, // 0x66 + bc_dsub = 103, // 0x67 + bc_imul = 104, // 0x68 + bc_lmul = 105, // 0x69 + bc_fmul = 106, // 0x6a + bc_dmul = 107, // 0x6b + bc_idiv = 108, // 0x6c + bc_ldiv = 109, // 0x6d + bc_fdiv = 110, // 0x6e + bc_ddiv = 111, // 0x6f + bc_irem = 112, // 0x70 + bc_lrem = 113, // 0x71 + bc_frem = 114, // 0x72 + bc_drem = 115, // 0x73 + bc_ineg = 116, // 0x74 + bc_lneg = 117, // 0x75 + bc_fneg = 118, // 0x76 + bc_dneg = 119, // 0x77 + bc_ishl = 120, // 0x78 + bc_lshl = 121, // 0x79 + bc_ishr = 122, // 0x7a + bc_lshr = 123, // 0x7b + bc_iushr = 124, // 0x7c + bc_lushr = 125, // 0x7d + bc_iand = 126, // 0x7e + bc_land = 127, // 0x7f + bc_ior = 128, // 0x80 + bc_lor = 129, // 0x81 + bc_ixor = 130, // 0x82 + bc_lxor = 131, // 0x83 + bc_iinc = 132, // 0x84 + bc_i2l = 133, // 0x85 + bc_i2f = 134, // 0x86 + bc_i2d = 135, // 0x87 + bc_l2i = 136, // 0x88 + bc_l2f = 137, // 0x89 + bc_l2d = 138, // 0x8a + bc_f2i = 139, // 0x8b + bc_f2l = 140, // 0x8c + bc_f2d = 141, // 0x8d + bc_d2i = 142, // 0x8e + bc_d2l = 143, // 0x8f + bc_d2f = 144, // 0x90 + bc_i2b = 145, // 0x91 + bc_i2c = 146, // 0x92 + bc_i2s = 147, // 0x93 + bc_lcmp = 148, // 0x94 + bc_fcmpl = 149, // 0x95 + bc_fcmpg = 150, // 0x96 + bc_dcmpl = 151, // 0x97 + bc_dcmpg = 152, // 0x98 + bc_ifeq = 153, // 0x99 + bc_ifne = 154, // 0x9a + bc_iflt = 155, // 0x9b + bc_ifge = 156, // 0x9c + bc_ifgt = 157, // 0x9d + bc_ifle = 158, // 0x9e + bc_if_icmpeq = 159, // 0x9f + bc_if_icmpne = 160, // 0xa0 + bc_if_icmplt = 161, // 0xa1 + bc_if_icmpge = 162, // 0xa2 + bc_if_icmpgt = 163, // 0xa3 + bc_if_icmple = 164, // 0xa4 + bc_if_acmpeq = 165, // 0xa5 + bc_if_acmpne = 166, // 0xa6 + bc_goto = 167, // 0xa7 + bc_jsr = 168, // 0xa8 + bc_ret = 169, // 0xa9 + bc_tableswitch = 170, // 0xaa + bc_lookupswitch = 171, // 0xab + bc_ireturn = 172, // 0xac + bc_lreturn = 173, // 0xad + bc_freturn = 174, // 0xae + bc_dreturn = 175, // 0xaf + bc_areturn = 176, // 0xb0 + bc_return = 177, // 0xb1 + bc_getstatic = 178, // 0xb2 + bc_putstatic = 179, // 0xb3 + bc_getfield = 180, // 0xb4 + bc_putfield = 181, // 0xb5 + bc_invokevirtual = 182, // 0xb6 + bc_invokespecial = 183, // 0xb7 + bc_invokestatic = 184, // 0xb8 + bc_invokeinterface = 185, // 0xb9 + bc_xxxunusedxxx = 186, // 0xba + bc_new = 187, // 0xbb + bc_newarray = 188, // 0xbc + bc_anewarray = 189, // 0xbd + bc_arraylength = 190, // 0xbe + bc_athrow = 191, // 0xbf + bc_checkcast = 192, // 0xc0 + bc_instanceof = 193, // 0xc1 + bc_monitorenter = 194, // 0xc2 + bc_monitorexit = 195, // 0xc3 + bc_wide = 196, // 0xc4 + bc_multianewarray = 197, // 0xc5 + bc_ifnull = 198, // 0xc6 + bc_ifnonnull = 199, // 0xc7 + bc_goto_w = 200, // 0xc8 + bc_jsr_w = 201, // 0xc9 + bc_bytecode_limit = 202 // 0xca +}; + +enum +{ + bc_end_marker = 255, + bc_byte_escape = 254, + bc_ref_escape = 253, + _first_linker_op = bc_getstatic, + _last_linker_op = bc_invokestatic, + _num_linker_ops = (_last_linker_op - _first_linker_op) + 1, + _self_linker_op = bc_bytecode_limit, + _self_linker_aload_flag = 1 * _num_linker_ops, + _self_linker_super_flag = 2 * _num_linker_ops, + _self_linker_limit = _self_linker_op + 4 * _num_linker_ops, + _invokeinit_op = _self_linker_limit, + _invokeinit_self_option = 0, + _invokeinit_super_option = 1, + _invokeinit_new_option = 2, + _invokeinit_limit = _invokeinit_op + 3, + _xldc_op = _invokeinit_limit, + bc_aldc = bc_ldc, + bc_cldc = _xldc_op + 0, + bc_ildc = _xldc_op + 1, + bc_fldc = _xldc_op + 2, + bc_aldc_w = bc_ldc_w, + bc_cldc_w = _xldc_op + 3, + bc_ildc_w = _xldc_op + 4, + bc_fldc_w = _xldc_op + 5, + bc_lldc2_w = bc_ldc2_w, + bc_dldc2_w = _xldc_op + 6, + _xldc_limit = _xldc_op + 7, + _xxx_3_end +}; diff --git a/depends/pack200/src/defines.h b/depends/pack200/src/defines.h new file mode 100644 index 00000000..63abae0a --- /dev/null +++ b/depends/pack200/src/defines.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// random definitions + +#ifdef _MSC_VER +#include +#include +#else +#include +#endif + +#ifndef FULL +#define FULL 1 /* Adds <500 bytes to the zipped final product. */ +#endif + +#if FULL // define this if you want debugging and/or compile-time attributes +#define IF_FULL(x) x +#else +#define IF_FULL(x) /*x*/ +#endif + +// Error messages that we have +#define ERROR_ENOMEM "Native allocation failed" +#define ERROR_FORMAT "Corrupted pack file" +#define ERROR_RESOURCE "Cannot extract resource file" +#define ERROR_OVERFLOW "Internal buffer overflow" +#define ERROR_INTERNAL "Internal error" + +#define LOGFILE_STDOUT "-" +#define LOGFILE_STDERR "" + +#define lengthof(array) (sizeof(array) / sizeof(array[0])) + +#define NEW(T, n) (T *) must_malloc((int)(scale_size(n, sizeof(T)))) +#define U_NEW(T, n) (T *) u->alloc(scale_size(n, sizeof(T))) +#define T_NEW(T, n) (T *) u->temp_alloc(scale_size(n, sizeof(T))) + +// bytes and byte arrays + +typedef unsigned int uint; + +#ifdef _MSC_VER +typedef LONGLONG jlong; +typedef DWORDLONG julong; +#define MKDIR(dir) mkdir(dir) +#define getpid() _getpid() +#define PATH_MAX MAX_PATH +#define dup2(a, b) _dup2(a, b) +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#define tempname _tempname +#define sleep Sleep +#else +typedef signed char byte; +#ifdef _LP64 +typedef long jlong; +typedef long unsigned julong; +#else +typedef long long jlong; +typedef long long unsigned julong; +#endif +#define MKDIR(dir) mkdir(dir, 0777); +#endif + +/* Must cast to void *, then size_t, then int. */ +#define ptrlowbits(x) ((int)(size_t)(void *)(x)) + +/* Back and forth from jlong to pointer */ +#define ptr2jlong(x) ((jlong)(size_t)(void *)(x)) +#define jlong2ptr(x) ((void *)(size_t)(x)) + +// Keys used by Java: +#define UNPACK_DEFLATE_HINT "unpack.deflate.hint" + +#define COM_PREFIX "com.sun.java.util.jar.pack." +#define UNPACK_MODIFICATION_TIME COM_PREFIX "unpack.modification.time" +#define DEBUG_VERBOSE COM_PREFIX "verbose" + +#define ZIP_ARCHIVE_MARKER_COMMENT "PACK200" + +// The following are not known to the Java classes: +#define UNPACK_REMOVE_PACKFILE COM_PREFIX "unpack.remove.packfile" + +// Called from unpacker layers +#define _CHECK_DO(t, x) \ + { \ + if (t) \ + { \ + x; \ + } \ + } + +#define CHECK _CHECK_DO(aborting(), return) +#define CHECK_(y) _CHECK_DO(aborting(), return y) +#define CHECK_0 _CHECK_DO(aborting(), return 0) + +#define CHECK_NULL(p) _CHECK_DO((p) == nullptr, return) +#define CHECK_NULL_(y, p) _CHECK_DO((p) == nullptr, return y) +#define CHECK_NULL_0(p) _CHECK_DO((p) == nullptr, return 0) + +#define CHECK_COUNT(t) \ + if (t < 0) \ + { \ + abort("bad value count"); \ + } \ + CHECK + +#define STR_TRUE "true" +#define STR_FALSE "false" + +#define STR_TF(x) ((x) ? STR_TRUE : STR_FALSE) +#define BOOL_TF(x) (((x) != nullptr &&strcmp((x), STR_TRUE) == 0) ? true : false) + +#define DEFAULT_ARCHIVE_MODTIME 1060000000 // Aug 04, 2003 5:26 PM PDT diff --git a/depends/pack200/src/main.cpp b/depends/pack200/src/main.cpp new file mode 100644 index 00000000..ad46a2a2 --- /dev/null +++ b/depends/pack200/src/main.cpp @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "defines.h" +#include "bytes.h" +#include "utils.h" +#include "coding.h" +#include "bands.h" + +#include "constants.h" + +#include "zip.h" + +#include "unpack.h" + +int main(int argc, char **argv) +{ + return unpacker::run(argc, argv); +} + +unpacker *unpacker::non_mt_current = nullptr; +unpacker *unpacker::current() +{ + return non_mt_current; +} +static void set_current_unpacker(unpacker *u) +{ + unpacker::non_mt_current = u; +} + +// Callback for fetching data, Unix style. +static jlong read_input_via_stdio(unpacker *u, void *buf, jlong minlen, jlong maxlen) +{ + assert(minlen <= maxlen); // don't talk nonsense + jlong numread = 0; + char *bufptr = (char *)buf; + while (numread < minlen) + { + // read available input, up to buf.length or maxlen + int readlen = (1 << 16); + if (readlen > (maxlen - numread)) + readlen = (int)(maxlen - numread); + int nr = 0; + if (u->infileptr != nullptr) + { + nr = (int)fread(bufptr, 1, readlen, u->infileptr); + } + else + { +#ifndef WIN32 + // we prefer unbuffered inputs + nr = (int)read(u->infileno, bufptr, readlen); +#else + nr = (int)fread(bufptr, 1, readlen, stdin); +#endif + } + if (nr <= 0) + { + if (errno != EINTR) + break; + nr = 0; + } + numread += nr; + bufptr += nr; + assert(numread <= maxlen); + } + // fprintf(u->errstrm, "readInputFn(%d,%d) => %d\n", + // (int)minlen, (int)maxlen, (int)numread); + return numread; +} + +enum +{ + EOF_MAGIC = 0, + BAD_MAGIC = -1 +}; +static int read_magic(unpacker *u, char peek[], int peeklen) +{ + assert(peeklen == 4); // magic numbers are always 4 bytes + jlong nr = (u->read_input_fn)(u, peek, peeklen, peeklen); + if (nr != peeklen) + { + return (nr == 0) ? EOF_MAGIC : BAD_MAGIC; + } + int magic = 0; + for (int i = 0; i < peeklen; i++) + { + magic <<= 8; + magic += peek[i] & 0xFF; + } + return magic; +} + +static void setup_gzin(unpacker *u) +{ + gunzip *gzin = NEW(gunzip, 1); + gzin->init(u); +} + +static const char *nbasename(const char *progname) +{ + const char *slash = strrchr(progname, '/'); + if (slash != nullptr) + progname = ++slash; + return progname; +} + +static const char *usage_lines[] = { + "Usage: %s [-opt... | --option=value]... x.pack[.gz] y.jar\n", "\n", "Unpacking Options\n", + " -H{h}, --deflate-hint={h} override transmitted deflate hint: true, false, or keep " + "(default)\n", + " -r, --remove-pack-file remove input file after unpacking\n", + " -v, --verbose increase program verbosity\n", + " -q, --quiet set verbosity to lowest level\n", + " -l{F}, --log-file={F} output to the given log file, or '-' for standard output " + "(default)\n", + " -?, -h, --help print this message\n", + " -J{X} Java VM argument (ignored)\n", nullptr}; + +static void usage(unpacker *u, const char *progname, bool full = false) +{ + // WinMain does not set argv[0] to the progrname + progname = (progname != nullptr) ? nbasename(progname) : "unpack200"; + for (int i = 0; usage_lines[i] != nullptr; i++) + { + fprintf(stderr, usage_lines[i], progname); + if (!full) + { + fprintf(stderr, "(For more information, run %s --help .)\n", progname); + break; + } + } +} + +// argument parsing +static char **init_args(int argc, char **argv, int &envargc) +{ + const char *env = getenv("UNPACK200_FLAGS"); + ptrlist envargs; + envargs.init(); + if (env != nullptr) + { + char *buf = (char *)strdup(env); + const char *delim = "\n\t "; + for (char *p = strtok(buf, delim); p != nullptr; p = strtok(nullptr, delim)) + { + envargs.add(p); + } + } + // allocate extra margin at both head and tail + char **argp = NEW(char *, envargs.length() + argc + 1); + char **argp0 = argp; + int i; + for (i = 0; i < envargs.length(); i++) + { + *argp++ = (char *)envargs.get(i); + } + for (i = 1; i < argc; i++) + { + // note: skip argv[0] (program name) + *argp++ = (char *)strdup(argv[i]); // make a scratch copy + } + *argp = nullptr; // sentinel + envargc = envargs.length(); // report this count to next_arg + envargs.free(); + return argp0; +} + +static int strpcmp(const char *str, const char *pfx) +{ + return strncmp(str, pfx, strlen(pfx)); +} + +static const char flag_opts[] = "vqrVh?"; +static const char string_opts[] = "HlJ"; + +static int next_arg(char **&argp) +{ + char *arg = *argp; + if (arg == nullptr || arg[0] != '-') + { // end of option list + return 0; + } + // printf("opt: %s\n", arg); + char ach = arg[1]; + if (ach == '\0') + { + // ++argp; // do not pop this arg + return 0; // bare "-" is stdin/stdout + } + else if (arg[1] == '-') + { // --foo option + static const char *keys[] = {"Hdeflate-hint=", "vverbose", "qquiet", + "rremove-pack-file", "llog-file=", "Vversion", + "hhelp", nullptr}; + if (arg[2] == '\0') + { // end of option list + ++argp; // pop the "--" + return 0; + } + for (int i = 0; keys[i] != nullptr; i++) + { + const char *key = keys[i]; + char kch = *key++; + if (strchr(key, '=') == nullptr) + { + if (!strcmp(arg + 2, key)) + { + ++argp; // pop option arg + return kch; + } + } + else + { + if (!strpcmp(arg + 2, key)) + { + *argp += 2 + strlen(key); // remove "--"+key from arg + return kch; + } + } + } + } + else if (strchr(flag_opts, ach) != nullptr) + { // plain option + if (arg[2] == '\0') + { + ++argp; + } + else + { + // in-place edit of "-vxyz" to "-xyz" + arg += 1; // skip original '-' + arg[0] = '-'; + *argp = arg; + } + // printf(" key => %c\n", ach); + return ach; + } + else if (strchr(string_opts, ach) != nullptr) + { // argument-bearing option + if (arg[2] == '\0') + { + if (argp[1] == nullptr) + return -1; // no next arg + ++argp; // leave the argument in place + } + else + { + // in-place edit of "-Hxyz" to "xyz" + arg += 2; // skip original '-H' + *argp = arg; + } + // printf(" key => %c\n", ach); + return ach; + } + return -1; // bad argument +} + +static const char sccsver[] = "1.30, 07/05/05"; + +// Usage: unpackage input.pack output.jar +int unpacker::run(int argc, char **argv) +{ + unpacker u; + u.init(read_input_via_stdio); + set_current_unpacker(&u); + + jar jarout; + jarout.init(&u); + + int envargc = 0; + char **argbuf = init_args(argc, argv, envargc); + char **arg0 = argbuf + envargc; + char **argp = argbuf; + + int verbose = 0; + char *logfile = nullptr; + + for (;;) + { + const char *arg = (*argp == nullptr) ? "" : u.saveStr(*argp); + bool isenvarg = (argp < arg0); + int ach = next_arg(argp); + bool hasoptarg = (ach != 0 && strchr(string_opts, ach) != nullptr); + if (ach == 0 && argp >= arg0) + break; + if (isenvarg && argp == arg0 && hasoptarg) + ach = 0; // don't pull from cmdline + switch (ach) + { + case 'H': + u.set_option(UNPACK_DEFLATE_HINT, *argp++); + break; + case 'v': + ++verbose; + break; + case 'q': + verbose = 0; + break; + case 'r': + u.set_option(UNPACK_REMOVE_PACKFILE, "1"); + break; + case 'l': + logfile = *argp++; + break; + case 'J': + argp += 1; + break; // skip ignored -Jxxx parameter + + case 'h': + case '?': + usage(&u, argv[0], true); + exit(1); + + default: + const char *inenv = isenvarg ? " in ${UNPACK200_FLAGS}" : ""; + if (hasoptarg) + fprintf(stderr, "Missing option string%s: %s\n", inenv, arg); + else + fprintf(stderr, "Unrecognized argument%s: %s\n", inenv, arg); + usage(&u, argv[0]); + exit(2); + } + } + + if (verbose != 0) + { + u.set_option(DEBUG_VERBOSE, u.saveIntStr(verbose)); + } + + const char *source_file = *argp++; + const char *destination_file = *argp++; + + if (source_file == nullptr || destination_file == nullptr || *argp != nullptr) + { + usage(&u, argv[0]); + exit(2); + } + + if (verbose != 0) + { + fprintf(stderr, "Unpacking from %s to %s\n", source_file, destination_file); + } + bool &remove_source = u.remove_packfile; + + if (strcmp(source_file, "-") == 0) + { + remove_source = false; + u.infileno = fileno(stdin); + } + else + { + u.infileptr = fopen(source_file, "rb"); + if (u.infileptr == nullptr) + { + fprintf(stderr, "Error: Could not open input file: %s\n", source_file); + exit(3); // Called only from the native standalone unpacker + } + } + + if (strcmp(destination_file, "-") == 0) + { + jarout.jarfp = stdout; + } + else + { + jarout.openJarFile(destination_file); + assert(jarout.jarfp != nullptr); + } + + if (verbose != 0) + u.dump_options(); + + char peek[4]; + int magic; + + // check for GZIP input + magic = read_magic(&u, peek, (int)sizeof(peek)); + if ((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC) + { + // Oops; must slap an input filter on this data. + setup_gzin(&u); + u.gzin->start(magic); + if (!u.aborting()) + { + u.start(); + } + } + else + { + u.start(peek, sizeof(peek)); + } + + // Note: The checks to u.aborting() are necessary to gracefully + // terminate processing when the first segment throws an error. + + for (;;) + { + if (u.aborting()) + break; + + // Each trip through this loop unpacks one segment + // and then resets the unpacker. + for (unpacker::file *filep; (filep = u.get_next_file()) != nullptr;) + { + if (u.aborting()) + break; + u.write_file_to_jar(filep); + } + if (u.aborting()) + break; + + // Peek ahead for more data. + magic = read_magic(&u, peek, (int)sizeof(peek)); + if (magic != (int)JAVA_PACKAGE_MAGIC) + { + if (magic != EOF_MAGIC) + u.abort("garbage after end of pack archive"); + break; // all done + } + + // Release all storage from parsing the old segment. + u.reset(); + + // Restart, beginning with the peek-ahead. + u.start(peek, sizeof(peek)); + } + + int status = 0; + if (u.aborting()) + { + fprintf(stderr, "Error: %s\n", u.get_abort_message()); + status = 1; + } + + if (u.infileptr != nullptr) + { + fclose(u.infileptr); + u.infileptr = nullptr; + } + + if (!u.aborting() && remove_source) + remove(source_file); + + if (verbose != 0) + { + fprintf(stderr, "unpacker completed with status=%d\n", status); + } + + u.finish(); + + u.free(); // tidy up malloc blocks + set_current_unpacker(nullptr); // clean up global pointer + + return status; +} diff --git a/depends/pack200/src/unpack.cpp b/depends/pack200/src/unpack.cpp new file mode 100644 index 00000000..722d67b5 --- /dev/null +++ b/depends/pack200/src/unpack.cpp @@ -0,0 +1,5105 @@ +/* + * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// -*- C++ -*- +// Program for unpacking specially compressed Java packages. +// John R. Rose + +/* + * When compiling for a 64bit LP64 system (longs and pointers being 64bits), + * the printf format %ld is correct and use of %lld will cause warning + * errors from some compilers (gcc/g++). + * _LP64 can be explicitly set (used on Linux). + * Solaris compilers will define __sparcv9 or __x86_64 on 64bit compilations. + */ +#if defined(_LP64) || defined(__sparcv9) || defined(__x86_64) +#define LONG_LONG_FORMAT "%ld" +#define LONG_LONG_HEX_FORMAT "%lx" +#else +#define LONG_LONG_FORMAT "%lld" +#define LONG_LONG_HEX_FORMAT "%016llx" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "defines.h" +#include "bytes.h" +#include "utils.h" +#include "coding.h" +#include "bands.h" + +#include "constants.h" + +#include "zip.h" + +#include "unpack.h" + +// tags, in canonical order: +static const byte TAGS_IN_ORDER[] = { + CONSTANT_Utf8, CONSTANT_Integer, CONSTANT_Float, CONSTANT_Long, + CONSTANT_Double, CONSTANT_String, CONSTANT_Class, CONSTANT_Signature, + CONSTANT_NameandType, CONSTANT_Fieldref, CONSTANT_Methodref, CONSTANT_InterfaceMethodref}; +#define N_TAGS_IN_ORDER (sizeof TAGS_IN_ORDER) + +// REQUESTED must be -2 for u2 and REQUESTED_LDC must be -1 for u1 +enum +{ + NOT_REQUESTED = 0, + REQUESTED = -2, + REQUESTED_LDC = -1 +}; + +#define NO_INORD ((uint) - 1) + +struct entry +{ + byte tag; + +#if 0 + byte bits; + enum { + //EB_EXTRA = 1, + EB_SUPER = 2 + }; +#endif + unsigned short nrefs; // pack w/ tag + + int outputIndex; + uint inord; // &cp.entries[cp.tag_base[this->tag]+this->inord] == this + + entry **refs; + + // put last to pack best + union + { + bytes b; + int i; + jlong l; + } value; + + void requestOutputIndex(cpool &cp, int req = REQUESTED); + int getOutputIndex() + { + assert(outputIndex > NOT_REQUESTED); + return outputIndex; + } + + entry *ref(int refnum) + { + assert((uint)refnum < nrefs); + return refs[refnum]; + } + + const char *utf8String() + { + assert(tagMatches(CONSTANT_Utf8)); + assert(value.b.len == strlen((const char *)value.b.ptr)); + return (const char *)value.b.ptr; + } + + entry *className() + { + assert(tagMatches(CONSTANT_Class)); + return ref(0); + } + + entry *memberClass() + { + assert(tagMatches(CONSTANT_Member)); + return ref(0); + } + + entry *memberDescr() + { + assert(tagMatches(CONSTANT_Member)); + return ref(1); + } + + entry *descrName() + { + assert(tagMatches(CONSTANT_NameandType)); + return ref(0); + } + + entry *descrType() + { + assert(tagMatches(CONSTANT_NameandType)); + return ref(1); + } + + int typeSize(); + + bytes &asUtf8(); + int asInteger() + { + assert(tag == CONSTANT_Integer); + return value.i; + } + + bool isUtf8(bytes &b) + { + return tagMatches(CONSTANT_Utf8) && value.b.equals(b); + } + + bool isDoubleWord() + { + return tag == CONSTANT_Double || tag == CONSTANT_Long; + } + + bool tagMatches(byte tag2) + { + return (tag2 == tag) || (tag2 == CONSTANT_Utf8 && tag == CONSTANT_Signature); + } +}; + +entry *cpindex::get(uint i) +{ + if (i >= len) + return nullptr; + else if (base1 != nullptr) + // primary index + return &base1[i]; + else + // secondary index + return base2[i]; +} + +inline bytes &entry::asUtf8() +{ + assert(tagMatches(CONSTANT_Utf8)); + return value.b; +} + +int entry::typeSize() +{ + assert(tagMatches(CONSTANT_Utf8)); + const char *sigp = (char *)value.b.ptr; + switch (*sigp) + { + case '(': + sigp++; + break; // skip opening '(' + case 'D': + case 'J': + return 2; // double field + default: + return 1; // field + } + int siglen = 0; + for (;;) + { + int ch = *sigp++; + switch (ch) + { + case 'D': + case 'J': + siglen += 1; + break; + case '[': + // Skip rest of array info. + while (ch == '[') + { + ch = *sigp++; + } + if (ch != 'L') + break; + // else fall through + case 'L': + sigp = strchr(sigp, ';'); + if (sigp == nullptr) + { + unpack_abort("bad data"); + return 0; + } + sigp += 1; + break; + case ')': // closing ')' + return siglen; + } + siglen += 1; + } +} + +inline cpindex *cpool::getFieldIndex(entry *classRef) +{ + assert(classRef->tagMatches(CONSTANT_Class)); + assert((uint)classRef->inord < (uint)tag_count[CONSTANT_Class]); + return &member_indexes[classRef->inord * 2 + 0]; +} +inline cpindex *cpool::getMethodIndex(entry *classRef) +{ + assert(classRef->tagMatches(CONSTANT_Class)); + assert((uint)classRef->inord < (uint)tag_count[CONSTANT_Class]); + return &member_indexes[classRef->inord * 2 + 1]; +} + +struct inner_class +{ + entry *inner; + entry *outer; + entry *name; + int flags; + inner_class *next_sibling; + bool requested; +}; + +// Here is where everything gets deallocated: +void unpacker::free() +{ + int i; + assert(infileptr == nullptr); // caller resp. + if (jarout != nullptr) + jarout->reset(); + if (gzin != nullptr) + { + gzin->free(); + gzin = nullptr; + } + if (free_input) + input.free(); + // free everybody ever allocated with U_NEW or (recently) with T_NEW + assert(smallbuf.base() == nullptr || mallocs.contains(smallbuf.base())); + assert(tsmallbuf.base() == nullptr || tmallocs.contains(tsmallbuf.base())); + mallocs.freeAll(); + tmallocs.freeAll(); + smallbuf.init(); + tsmallbuf.init(); + bcimap.free(); + class_fixup_type.free(); + class_fixup_offset.free(); + class_fixup_ref.free(); + code_fixup_type.free(); + code_fixup_offset.free(); + code_fixup_source.free(); + requested_ics.free(); + cur_classfile_head.free(); + cur_classfile_tail.free(); + for (i = 0; i < ATTR_CONTEXT_LIMIT; i++) + attr_defs[i].free(); + + // free CP state + cp.outputEntries.free(); + for (i = 0; i < CONSTANT_Limit; i++) + cp.tag_extras[i].free(); +} + +// input handling +// Attempts to advance rplimit so that (rplimit-rp) is at least 'more'. +// Will eagerly read ahead by larger chunks, if possible. +// Returns false if (rplimit-rp) is not at least 'more', +// unless rplimit hits input.limit(). +bool unpacker::ensure_input(jlong more) +{ + julong want = more - input_remaining(); + if ((jlong)want <= 0) + return true; // it's already in the buffer + if (rplimit == input.limit()) + return true; // not expecting any more + + if (read_input_fn == nullptr) + { + // assume it is already all there + bytes_read += input.limit() - rplimit; + rplimit = input.limit(); + return true; + } + CHECK_0; + + julong remaining = (input.limit() - rplimit); // how much left to read? + byte *rpgoal = (want >= remaining) ? input.limit() : rplimit + (size_t)want; + enum + { + CHUNK_SIZE = (1 << 14) + }; + julong fetch = want; + if (fetch < CHUNK_SIZE) + fetch = CHUNK_SIZE; + if (fetch > remaining * 3 / 4) + fetch = remaining; + // Try to fetch at least "more" bytes. + while ((jlong)fetch > 0) + { + jlong nr = (*read_input_fn)(this, rplimit, fetch, remaining); + if (nr <= 0) + { + return (rplimit >= rpgoal); + } + remaining -= nr; + rplimit += nr; + fetch -= nr; + bytes_read += nr; + assert(remaining == (julong)(input.limit() - rplimit)); + } + return true; +} + +// output handling + +fillbytes *unpacker::close_output(fillbytes *which) +{ + assert(wp != nullptr); + if (which == nullptr) + { + if (wpbase == cur_classfile_head.base()) + { + which = &cur_classfile_head; + } + else + { + which = &cur_classfile_tail; + } + } + assert(wpbase == which->base()); + assert(wplimit == which->end()); + which->setLimit(wp); + wp = nullptr; + wplimit = nullptr; + // wpbase = nullptr; + return which; +} + +// maybe_inline +void unpacker::ensure_put_space(size_t size) +{ + if (wp + size <= wplimit) + return; + // Determine which segment needs expanding. + fillbytes *which = close_output(); + byte *wp0 = which->grow(size); + wpbase = which->base(); + wplimit = which->end(); + wp = wp0; +} + +byte *unpacker::put_space(size_t size) +{ + byte *wp0 = wp; + byte *wp1 = wp0 + size; + if (wp1 > wplimit) + { + ensure_put_space(size); + wp0 = wp; + wp1 = wp0 + size; + } + wp = wp1; + return wp0; +} + +void unpacker::putu2_at(byte *wp, int n) +{ + if (n != (unsigned short)n) + { + unpack_abort(ERROR_OVERFLOW); + return; + } + wp[0] = (n) >> 8; + wp[1] = (n) >> 0; +} + +void unpacker::putu4_at(byte *wp, int n) +{ + wp[0] = (n) >> 24; + wp[1] = (n) >> 16; + wp[2] = (n) >> 8; + wp[3] = (n) >> 0; +} + +void unpacker::putu8_at(byte *wp, jlong n) +{ + putu4_at(wp + 0, (int)((julong)n >> 32)); + putu4_at(wp + 4, (int)((julong)n >> 0)); +} + +void unpacker::putu2(int n) +{ + putu2_at(put_space(2), n); +} + +void unpacker::putu4(int n) +{ + putu4_at(put_space(4), n); +} + +void unpacker::putu8(jlong n) +{ + putu8_at(put_space(8), n); +} + +int unpacker::putref_index(entry *e, int size) +{ + if (e == nullptr) + return 0; + else if (e->outputIndex > NOT_REQUESTED) + return e->outputIndex; + else if (e->tag == CONSTANT_Signature) + return putref_index(e->ref(0), size); + else + { + e->requestOutputIndex(cp, -size); + // Later on we'll fix the bits. + class_fixup_type.addByte(size); + class_fixup_offset.add((int)wpoffset()); + class_fixup_ref.add(e); + return 0; + } +} + +void unpacker::putref(entry *e) +{ + int oidx = putref_index(e, 2); + putu2_at(put_space(2), oidx); +} + +void unpacker::putu1ref(entry *e) +{ + int oidx = putref_index(e, 1); + putu1_at(put_space(1), oidx); +} + +static int total_cp_size[] = {0, 0}; +static int largest_cp_ref[] = {0, 0}; +static int hash_probes[] = {0, 0}; + +// Allocation of small and large blocks. + +enum +{ + CHUNK = (1 << 14), + SMALL = (1 << 9) +}; + +// Call malloc. Try to combine small blocks and free much later. +void *unpacker::alloc_heap(size_t size, bool smallOK, bool temp) +{ + if (!smallOK || size > SMALL) + { + void *res = must_malloc((int)size); + (temp ? &tmallocs : &mallocs)->add(res); + return res; + } + fillbytes &xsmallbuf = *(temp ? &tsmallbuf : &smallbuf); + if (!xsmallbuf.canAppend(size + 1)) + { + xsmallbuf.init(CHUNK); + (temp ? &tmallocs : &mallocs)->add(xsmallbuf.base()); + } + int growBy = (int)size; + growBy += -growBy & 7; // round up mod 8 + return xsmallbuf.grow(growBy); +} + +void unpacker::saveTo(bytes &b, byte *ptr, size_t len) +{ + b.ptr = U_NEW(byte, add_size(len, 1)); + if (aborting()) + { + b.len = 0; + return; + } + b.len = len; + b.copyFrom(ptr, len); +} + +// Read up through band_headers. +// Do the archive_size dance to set the size of the input mega-buffer. +void unpacker::read_file_header() +{ + // Read file header to determine file type and total size. + enum + { + MAGIC_BYTES = 4, + AH_LENGTH_0 = 3, // minver, majver, options are outside of archive_size + AH_LENGTH_0_MAX = AH_LENGTH_0 + 1, // options might have 2 bytes + AH_LENGTH = 26, // maximum archive header length (w/ all fields) + // Length contributions from optional header fields: + AH_FILE_HEADER_LEN = 5, // sizehi/lo/next/modtime/files + AH_ARCHIVE_SIZE_LEN = 2, // sizehi/lo only; part of AH_FILE_HEADER_LEN + AH_CP_NUMBER_LEN = 4, // int/float/long/double + AH_SPECIAL_FORMAT_LEN = 2, // layouts/band-headers + AH_LENGTH_MIN = + AH_LENGTH - (AH_FILE_HEADER_LEN + AH_SPECIAL_FORMAT_LEN + AH_CP_NUMBER_LEN), + ARCHIVE_SIZE_MIN = AH_LENGTH_MIN - (AH_LENGTH_0 + AH_ARCHIVE_SIZE_LEN), + FIRST_READ = MAGIC_BYTES + AH_LENGTH_MIN + }; + + assert(AH_LENGTH_MIN == 15); // # of UNSIGNED5 fields required after archive_magic + assert(ARCHIVE_SIZE_MIN == 10); // # of UNSIGNED5 fields required after archive_size + // An absolute minimum nullptr archive is magic[4], {minver,majver,options}[3], + // archive_size[0], cp_counts[8], class_counts[4], for a total of 19 bytes. + // (Note that archive_size is optional; it may be 0..10 bytes in length.) + // The first read must capture everything up through the options field. + // This happens to work even if {minver,majver,options} is a pathological + // 15 bytes long. Legal pack files limit those three fields to 1+1+2 bytes. + assert(FIRST_READ >= MAGIC_BYTES + AH_LENGTH_0 * B_MAX); + + // Up through archive_size, the largest possible archive header is + // magic[4], {minver,majver,options}[4], archive_size[10]. + // (Note only the low 12 bits of options are allowed to be non-zero.) + // In order to parse archive_size, we need at least this many bytes + // in the first read. Of course, if archive_size_hi is more than + // a byte, we probably will fail to allocate the buffer, since it + // will be many gigabytes long. This is a practical, not an + // architectural limit to Pack200 archive sizes. + assert(FIRST_READ >= MAGIC_BYTES + AH_LENGTH_0_MAX + 2 * B_MAX); + + bool foreign_buf = (read_input_fn == nullptr); + byte initbuf[(int)FIRST_READ + (int)C_SLOP + 200]; // 200 is for JAR I/O + if (foreign_buf) + { + // inbytes is all there is + input.set(inbytes); + rp = input.base(); + rplimit = input.limit(); + } + else + { + // inbytes, if not empty, contains some read-ahead we must use first + // ensure_input will take care of copying it into initbuf, + // then querying read_input_fn for any additional data needed. + // However, the caller must assume that we use up all of inbytes. + // There is no way to tell the caller that we used only part of them. + // Therefore, the caller must use only a bare minimum of read-ahead. + if (inbytes.len > FIRST_READ) + { + abort("too much read-ahead"); + return; + } + input.set(initbuf, sizeof(initbuf)); + input.b.clear(); + input.b.copyFrom(inbytes); + rplimit = rp = input.base(); + rplimit += inbytes.len; + bytes_read += inbytes.len; + } + // Read only 19 bytes, which is certain to contain #archive_options fields, + // but is certain not to overflow past the archive_header. + input.b.len = FIRST_READ; + if (!ensure_input(FIRST_READ)) + abort("EOF reading archive magic number"); + + if (rp[0] == 'P' && rp[1] == 'K') + { + // In the Unix-style program, we simply simulate a copy command. + // Copy until EOF; assume the JAR file is the last segment. + fprintf(stderr, "Copy-mode.\n"); + for (;;) + { + jarout->write_data(rp, (int)input_remaining()); + if (foreign_buf) + break; // one-time use of a passed in buffer + if (input.size() < CHUNK) + { + // Get some breathing room. + input.set(U_NEW(byte, (size_t)CHUNK + C_SLOP), (size_t)CHUNK); + CHECK; + } + rp = rplimit = input.base(); + if (!ensure_input(1)) + break; + } + jarout->closeJarFile(false); + return; + } + + // Read the magic number. + magic = 0; + for (int i1 = 0; i1 < (int)sizeof(magic); i1++) + { + magic <<= 8; + magic += (*rp++ & 0xFF); + } + + // Read the first 3 values from the header. + value_stream hdr; + int hdrVals = 0; + int hdrValsSkipped = 0; // debug only + hdr.init(rp, rplimit, UNSIGNED5_spec); + minver = hdr.getInt(); + majver = hdr.getInt(); + hdrVals += 2; + + if (magic != (int)JAVA_PACKAGE_MAGIC || + (majver != JAVA5_PACKAGE_MAJOR_VERSION && majver != JAVA6_PACKAGE_MAJOR_VERSION) || + (minver != JAVA5_PACKAGE_MINOR_VERSION && minver != JAVA6_PACKAGE_MINOR_VERSION)) + { + char message[200]; + sprintf(message, "@" ERROR_FORMAT ": magic/ver = " + "%08X/%d.%d should be %08X/%d.%d OR %08X/%d.%d\n", + magic, majver, minver, JAVA_PACKAGE_MAGIC, JAVA5_PACKAGE_MAJOR_VERSION, + JAVA5_PACKAGE_MINOR_VERSION, JAVA_PACKAGE_MAGIC, JAVA6_PACKAGE_MAJOR_VERSION, + JAVA6_PACKAGE_MINOR_VERSION); + abort(message); + } + CHECK; + + archive_options = hdr.getInt(); + hdrVals += 1; + assert(hdrVals == AH_LENGTH_0); // first three fields only + +#define ORBIT(bit) | (bit) + int OPTION_LIMIT = (0 ARCHIVE_BIT_DO(ORBIT)); +#undef ORBIT + if ((archive_options & ~OPTION_LIMIT) != 0) + { + fprintf(stderr, "Warning: Illegal archive options 0x%x\n", archive_options); + abort("illegal archive options"); + return; + } + + if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) + { + uint hi = hdr.getInt(); + uint lo = hdr.getInt(); + julong x = band::makeLong(hi, lo); + archive_size = (size_t)x; + if (archive_size != x) + { + // Silly size specified; force overflow. + archive_size = PSIZE_MAX + 1; + } + hdrVals += 2; + } + else + { + hdrValsSkipped += 2; + } + + // Now we can size the whole archive. + // Read everything else into a mega-buffer. + rp = hdr.rp; + int header_size_0 = (int)(rp - input.base()); // used-up header (4byte + 3int) + int header_size_1 = (int)(rplimit - rp); // buffered unused initial fragment + int header_size = header_size_0 + header_size_1; + unsized_bytes_read = header_size_0; + CHECK; + if (foreign_buf) + { + if (archive_size > (size_t)header_size_1) + { + abort("EOF reading fixed input buffer"); + return; + } + } + else if (archive_size != 0) + { + if (archive_size < ARCHIVE_SIZE_MIN) + { + abort("impossible archive size"); // bad input data + return; + } + if (archive_size < header_size_1) + { + abort("too much read-ahead"); // somehow we pre-fetched too much? + return; + } + input.set(U_NEW(byte, add_size(header_size_0, archive_size, C_SLOP)), + (size_t)header_size_0 + archive_size); + CHECK; + assert(input.limit()[0] == 0); + // Move all the bytes we read initially into the real buffer. + input.b.copyFrom(initbuf, header_size); + rp = input.b.ptr + header_size_0; + rplimit = input.b.ptr + header_size; + } + else + { + // It's more complicated and painful. + // A zero archive_size means that we must read until EOF. + input.init(CHUNK * 2); + CHECK; + input.b.len = input.allocated; + rp = rplimit = input.base(); + // Set up input buffer as if we already read the header: + input.b.copyFrom(initbuf, header_size); + CHECK; + rplimit += header_size; + while (ensure_input(input.limit() - rp)) + { + size_t dataSoFar = input_remaining(); + size_t nextSize = add_size(dataSoFar, CHUNK); + input.ensureSize(nextSize); + CHECK; + input.b.len = input.allocated; + rp = rplimit = input.base(); + rplimit += dataSoFar; + } + size_t dataSize = (rplimit - input.base()); + input.b.len = dataSize; + input.grow(C_SLOP); + CHECK; + free_input = true; // free it later + input.b.len = dataSize; + assert(input.limit()[0] == 0); + rp = rplimit = input.base(); + rplimit += dataSize; + rp += header_size_0; // already scanned these bytes... + } + live_input = true; // mark as "do not reuse" + if (aborting()) + { + abort("cannot allocate large input buffer for package file"); + return; + } + + // read the rest of the header fields + ensure_input((AH_LENGTH - AH_LENGTH_0) * B_MAX); + CHECK; + hdr.rp = rp; + hdr.rplimit = rplimit; + + if ((archive_options & AO_HAVE_FILE_HEADERS) != 0) + { + archive_next_count = hdr.getInt(); + CHECK_COUNT(archive_next_count); + archive_modtime = hdr.getInt(); + file_count = hdr.getInt(); + CHECK_COUNT(file_count); + hdrVals += 3; + } + else + { + hdrValsSkipped += 3; + } + + if ((archive_options & AO_HAVE_SPECIAL_FORMATS) != 0) + { + band_headers_size = hdr.getInt(); + CHECK_COUNT(band_headers_size); + attr_definition_count = hdr.getInt(); + CHECK_COUNT(attr_definition_count); + hdrVals += 2; + } + else + { + hdrValsSkipped += 2; + } + + int cp_counts[N_TAGS_IN_ORDER]; + for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) + { + if (!(archive_options & AO_HAVE_CP_NUMBERS)) + { + switch (TAGS_IN_ORDER[k]) + { + case CONSTANT_Integer: + case CONSTANT_Float: + case CONSTANT_Long: + case CONSTANT_Double: + cp_counts[k] = 0; + hdrValsSkipped += 1; + continue; + } + } + cp_counts[k] = hdr.getInt(); + CHECK_COUNT(cp_counts[k]); + hdrVals += 1; + } + + ic_count = hdr.getInt(); + CHECK_COUNT(ic_count); + default_class_minver = hdr.getInt(); + default_class_majver = hdr.getInt(); + class_count = hdr.getInt(); + CHECK_COUNT(class_count); + hdrVals += 4; + + // done with archive_header + hdrVals += hdrValsSkipped; + assert(hdrVals == AH_LENGTH); + + rp = hdr.rp; + if (rp > rplimit) + abort("EOF reading archive header"); + + // Now size the CP. + cp.init(this, cp_counts); + CHECK; + + default_file_modtime = archive_modtime; + if (default_file_modtime == 0 && !(archive_options & AO_HAVE_FILE_MODTIME)) + default_file_modtime = DEFAULT_ARCHIVE_MODTIME; // taken from driver + if ((archive_options & AO_DEFLATE_HINT) != 0) + default_file_options |= FO_DEFLATE_HINT; + + // meta-bytes, if any, immediately follow archive header + // band_headers.readData(band_headers_size); + ensure_input(band_headers_size); + if (input_remaining() < (size_t)band_headers_size) + { + abort("EOF reading band headers"); + return; + } + bytes band_headers; + // The "1+" allows an initial byte to be pushed on the front. + band_headers.set(1 + U_NEW(byte, 1 + band_headers_size + C_SLOP), band_headers_size); + CHECK; + // Start scanning band headers here: + band_headers.copyFrom(rp, band_headers.len); + rp += band_headers.len; + assert(rp <= rplimit); + meta_rp = band_headers.ptr; + // Put evil meta-codes at the end of the band headers, + // so we are sure to throw an error if we run off the end. + bytes::of(band_headers.limit(), C_SLOP).clear(_meta_error); +} + +void unpacker::finish() +{ + if (verbose >= 1) + { + fprintf(stderr, "A total of " LONG_LONG_FORMAT " bytes were read in %d segment(s).\n", + (bytes_read_before_reset + bytes_read), segments_read_before_reset + 1); + fprintf(stderr, "A total of " LONG_LONG_FORMAT " file content bytes were written.\n", + (bytes_written_before_reset + bytes_written)); + fprintf(stderr, + "A total of %d files (of which %d are classes) were written to output.\n", + files_written_before_reset + files_written, + classes_written_before_reset + classes_written); + } + if (jarout != nullptr) + jarout->closeJarFile(true); +} + +// Cf. PackageReader.readConstantPoolCounts +void cpool::init(unpacker *u_, int counts[NUM_COUNTS]) +{ + this->u = u_; + + // Fill-pointer for CP. + int next_entry = 0; + + // Size the constant pool: + for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) + { + byte tag = TAGS_IN_ORDER[k]; + int len = counts[k]; + tag_count[tag] = len; + tag_base[tag] = next_entry; + next_entry += len; + // Detect and defend against constant pool size overflow. + // (Pack200 forbids the sum of CP counts to exceed 2^29-1.) + enum + { + CP_SIZE_LIMIT = (1 << 29), + IMPLICIT_ENTRY_COUNT = 1 // empty Utf8 string + }; + if (len >= (1 << 29) || len < 0 || next_entry >= CP_SIZE_LIMIT + IMPLICIT_ENTRY_COUNT) + { + abort("archive too large: constant pool limit exceeded"); + return; + } + } + + // Close off the end of the CP: + nentries = next_entry; + + // place a limit on future CP growth: + int generous = 0; + generous = add_size(generous, u->ic_count); // implicit name + generous = add_size(generous, u->ic_count); // outer + generous = add_size(generous, u->ic_count); // outer.utf8 + generous = add_size(generous, 40); // WKUs, misc + generous = add_size(generous, u->class_count); // implicit SourceFile strings + maxentries = add_size(nentries, generous); + + // Note that this CP does not include "empty" entries + // for longs and doubles. Those are introduced when + // the entries are renumbered for classfile output. + + entries = U_NEW(entry, maxentries); + CHECK; + + first_extra_entry = &entries[nentries]; + + // Initialize the standard indexes. + tag_count[CONSTANT_All] = nentries; + tag_base[CONSTANT_All] = 0; + for (int tag = 0; tag < CONSTANT_Limit; tag++) + { + entry *cpMap = &entries[tag_base[tag]]; + tag_index[tag].init(tag_count[tag], cpMap, tag); + } + + // Initialize hashTab to a generous power-of-two size. + uint pow2 = 1; + uint target = maxentries + maxentries / 2; // 60% full + while (pow2 < target) + pow2 <<= 1; + hashTab = U_NEW(entry *, hashTabLength = pow2); +} + +static byte *store_Utf8_char(byte *cp, unsigned short ch) +{ + if (ch >= 0x001 && ch <= 0x007F) + { + *cp++ = (byte)ch; + } + else if (ch <= 0x07FF) + { + *cp++ = (byte)(0xC0 | ((ch >> 6) & 0x1F)); + *cp++ = (byte)(0x80 | ((ch >> 0) & 0x3F)); + } + else + { + *cp++ = (byte)(0xE0 | ((ch >> 12) & 0x0F)); + *cp++ = (byte)(0x80 | ((ch >> 6) & 0x3F)); + *cp++ = (byte)(0x80 | ((ch >> 0) & 0x3F)); + } + return cp; +} + +static byte *skip_Utf8_chars(byte *cp, int len) +{ + for (;; cp++) + { + int ch = *cp & 0xFF; + if ((ch & 0xC0) != 0x80) + { + if (len-- == 0) + return cp; + if (ch < 0x80 && len == 0) + return cp + 1; + } + } +} + +static int compare_Utf8_chars(bytes &b1, bytes &b2) +{ + int l1 = (int)b1.len; + int l2 = (int)b2.len; + int l0 = (l1 < l2) ? l1 : l2; + byte *p1 = b1.ptr; + byte *p2 = b2.ptr; + int c0 = 0; + for (int i = 0; i < l0; i++) + { + int c1 = p1[i] & 0xFF; + int c2 = p2[i] & 0xFF; + if (c1 != c2) + { + // Before returning the obvious answer, + // check to see if c1 or c2 is part of a 0x0000, + // which encodes as {0xC0,0x80}. The 0x0000 is the + // lowest-sorting Java char value, and yet it encodes + // as if it were the first char after 0x7F, which causes + // strings containing nulls to sort too high. All other + // comparisons are consistent between Utf8 and Java chars. + if (c1 == 0xC0 && (p1[i + 1] & 0xFF) == 0x80) + c1 = 0; + if (c2 == 0xC0 && (p2[i + 1] & 0xFF) == 0x80) + c2 = 0; + if (c0 == 0xC0) + { + assert(((c1 | c2) & 0xC0) == 0x80); // c1 & c2 are extension chars + if (c1 == 0x80) + c1 = 0; // will sort below c2 + if (c2 == 0x80) + c2 = 0; // will sort below c1 + } + return c1 - c2; + } + c0 = c1; // save away previous char + } + // common prefix is identical; return length difference if any + return l1 - l2; +} + +// Cf. PackageReader.readUtf8Bands +void unpacker::read_Utf8_values(entry *cpMap, int len) +{ + // Implicit first Utf8 string is the empty string. + enum + { + // certain bands begin with implicit zeroes + PREFIX_SKIP_2 = 2, + SUFFIX_SKIP_1 = 1 + }; + + int i; + + // First band: Read lengths of shared prefixes. + if (len > PREFIX_SKIP_2) + cp_Utf8_prefix.readData(len - PREFIX_SKIP_2); + + // Second band: Read lengths of unshared suffixes: + if (len > SUFFIX_SKIP_1) + cp_Utf8_suffix.readData(len - SUFFIX_SKIP_1); + + bytes *allsuffixes = T_NEW(bytes, len); + CHECK; + + int nbigsuf = 0; + fillbytes charbuf; // buffer to allocate small strings + charbuf.init(); + + // Third band: Read the char values in the unshared suffixes: + cp_Utf8_chars.readData(cp_Utf8_suffix.getIntTotal()); + for (i = 0; i < len; i++) + { + int suffix = (i < SUFFIX_SKIP_1) ? 0 : cp_Utf8_suffix.getInt(); + if (suffix < 0) + { + abort("bad utf8 suffix"); + return; + } + if (suffix == 0 && i >= SUFFIX_SKIP_1) + { + // chars are packed in cp_Utf8_big_chars + nbigsuf += 1; + continue; + } + bytes &chars = allsuffixes[i]; + uint size3 = suffix * 3; // max Utf8 length + bool isMalloc = (suffix > SMALL); + if (isMalloc) + { + chars.malloc(size3); + } + else + { + if (!charbuf.canAppend(size3 + 1)) + { + assert(charbuf.allocated == 0 || tmallocs.contains(charbuf.base())); + charbuf.init(CHUNK); // Reset to new buffer. + tmallocs.add(charbuf.base()); + } + chars.set(charbuf.grow(size3 + 1), size3); + } + CHECK; + byte *chp = chars.ptr; + for (int j = 0; j < suffix; j++) + { + unsigned short ch = cp_Utf8_chars.getInt(); + chp = store_Utf8_char(chp, ch); + } + // shrink to fit: + if (isMalloc) + { + chars.realloc(chp - chars.ptr); + CHECK; + tmallocs.add(chars.ptr); // free it later + } + else + { + int shrink = (int)(chars.limit() - chp); + chars.len -= shrink; + charbuf.b.len -= shrink; // ungrow to reclaim buffer space + // Note that we did not reclaim the final '\0'. + assert(chars.limit() == charbuf.limit() - 1); + assert(strlen((char *)chars.ptr) == chars.len); + } + } + // cp_Utf8_chars.done(); + + // Fourth band: Go back and size the specially packed strings. + int maxlen = 0; + cp_Utf8_big_suffix.readData(nbigsuf); + cp_Utf8_suffix.rewind(); + for (i = 0; i < len; i++) + { + int suffix = (i < SUFFIX_SKIP_1) ? 0 : cp_Utf8_suffix.getInt(); + int prefix = (i < PREFIX_SKIP_2) ? 0 : cp_Utf8_prefix.getInt(); + if (prefix < 0 || prefix + suffix < 0) + { + abort("bad utf8 prefix"); + return; + } + bytes &chars = allsuffixes[i]; + if (suffix == 0 && i >= SUFFIX_SKIP_1) + { + suffix = cp_Utf8_big_suffix.getInt(); + assert(chars.ptr == nullptr); + chars.len = suffix; // just a momentary hack + } + else + { + assert(chars.ptr != nullptr); + } + if (maxlen < prefix + suffix) + { + maxlen = prefix + suffix; + } + } + // cp_Utf8_suffix.done(); // will use allsuffixes[i].len (ptr!=nullptr) + // cp_Utf8_big_suffix.done(); // will use allsuffixes[i].len + + // Fifth band(s): Get the specially packed characters. + cp_Utf8_big_suffix.rewind(); + for (i = 0; i < len; i++) + { + bytes &chars = allsuffixes[i]; + if (chars.ptr != nullptr) + continue; // already input + int suffix = (int)chars.len; // pick up the hack + uint size3 = suffix * 3; + if (suffix == 0) + continue; // done with empty string + chars.malloc(size3); + byte *chp = chars.ptr; + band saved_band = cp_Utf8_big_chars; + cp_Utf8_big_chars.readData(suffix); + for (int j = 0; j < suffix; j++) + { + unsigned short ch = cp_Utf8_big_chars.getInt(); + chp = store_Utf8_char(chp, ch); + } + chars.realloc(chp - chars.ptr); + CHECK; + tmallocs.add(chars.ptr); // free it later + // cp_Utf8_big_chars.done(); + cp_Utf8_big_chars = saved_band; // reset the band for the next string + } + cp_Utf8_big_chars.readData(0); // zero chars + // cp_Utf8_big_chars.done(); + + // Finally, sew together all the prefixes and suffixes. + bytes bigbuf; + bigbuf.malloc(maxlen * 3 + 1); // max Utf8 length, plus slop for nullptr + CHECK; + int prevlen = 0; // previous string length (in chars) + tmallocs.add(bigbuf.ptr); // free after this block + cp_Utf8_prefix.rewind(); + for (i = 0; i < len; i++) + { + bytes &chars = allsuffixes[i]; + int prefix = (i < PREFIX_SKIP_2) ? 0 : cp_Utf8_prefix.getInt(); + int suffix = (int)chars.len; + byte *fillp; + // by induction, the buffer is already filled with the prefix + // make sure the prefix value is not corrupted, though: + if (prefix > prevlen) + { + abort("utf8 prefix overflow"); + return; + } + fillp = skip_Utf8_chars(bigbuf.ptr, prefix); + // copy the suffix into the same buffer: + fillp = chars.writeTo(fillp); + assert(bigbuf.inBounds(fillp)); + *fillp = 0; // bigbuf must contain a well-formed Utf8 string + int length = (int)(fillp - bigbuf.ptr); + bytes &value = cpMap[i].value.b; + value.set(U_NEW(byte, add_size(length, 1)), length); + value.copyFrom(bigbuf.ptr, length); + CHECK; + // Index all Utf8 strings + entry *&htref = cp.hashTabRef(CONSTANT_Utf8, value); + if (htref == nullptr) + { + // Note that if two identical strings are transmitted, + // the first is taken to be the canonical one. + htref = &cpMap[i]; + } + prevlen = prefix + suffix; + } + // cp_Utf8_prefix.done(); + + // Free intermediate buffers. + free_temps(); +} + +void unpacker::read_single_words(band &cp_band, entry *cpMap, int len) +{ + cp_band.readData(len); + for (int i = 0; i < len; i++) + { + cpMap[i].value.i = cp_band.getInt(); // coding handles signs OK + } +} + +void unpacker::read_double_words(band &cp_bands, entry *cpMap, int len) +{ + band &cp_band_hi = cp_bands; + band &cp_band_lo = cp_bands.nextBand(); + cp_band_hi.readData(len); + cp_band_lo.readData(len); + for (int i = 0; i < len; i++) + { + cpMap[i].value.l = cp_band_hi.getLong(cp_band_lo, true); + } + // cp_band_hi.done(); + // cp_band_lo.done(); +} + +void unpacker::read_single_refs(band &cp_band, byte refTag, entry *cpMap, int len) +{ + assert(refTag == CONSTANT_Utf8); + cp_band.setIndexByTag(refTag); + cp_band.readData(len); + CHECK; + int indexTag = (cp_band.bn == e_cp_Class) ? CONSTANT_Class : 0; + for (int i = 0; i < len; i++) + { + entry &e = cpMap[i]; + e.refs = U_NEW(entry *, e.nrefs = 1); + entry *utf = cp_band.getRef(); + CHECK; + e.refs[0] = utf; + e.value.b = utf->value.b; // copy value of Utf8 string to self + if (indexTag != 0) + { + // Maintain cross-reference: + entry *&htref = cp.hashTabRef(indexTag, e.value.b); + if (htref == nullptr) + { + // Note that if two identical classes are transmitted, + // the first is taken to be the canonical one. + htref = &e; + } + } + } + // cp_band.done(); +} + +void unpacker::read_double_refs(band &cp_band, byte ref1Tag, byte ref2Tag, entry *cpMap, + int len) +{ + band &cp_band1 = cp_band; + band &cp_band2 = cp_band.nextBand(); + cp_band1.setIndexByTag(ref1Tag); + cp_band2.setIndexByTag(ref2Tag); + cp_band1.readData(len); + cp_band2.readData(len); + CHECK; + for (int i = 0; i < len; i++) + { + entry &e = cpMap[i]; + e.refs = U_NEW(entry *, e.nrefs = 2); + e.refs[0] = cp_band1.getRef(); + e.refs[1] = cp_band2.getRef(); + CHECK; + } + // cp_band1.done(); + // cp_band2.done(); +} + +// Cf. PackageReader.readSignatureBands +void unpacker::read_signature_values(entry *cpMap, int len) +{ + cp_Signature_form.setIndexByTag(CONSTANT_Utf8); + cp_Signature_form.readData(len); + CHECK; + int ncTotal = 0; + int i; + for (i = 0; i < len; i++) + { + entry &e = cpMap[i]; + entry &form = *cp_Signature_form.getRef(); + CHECK; + int nc = 0; + + for (const char *ncp = form.utf8String(); *ncp; ncp++) + { + if (*ncp == 'L') + nc++; + } + + ncTotal += nc; + e.refs = U_NEW(entry *, cpMap[i].nrefs = 1 + nc); + CHECK; + e.refs[0] = &form; + } + // cp_Signature_form.done(); + cp_Signature_classes.setIndexByTag(CONSTANT_Class); + cp_Signature_classes.readData(ncTotal); + for (i = 0; i < len; i++) + { + entry &e = cpMap[i]; + for (int j = 1; j < e.nrefs; j++) + { + e.refs[j] = cp_Signature_classes.getRef(); + CHECK; + } + } + // cp_Signature_classes.done(); +} + +// Cf. PackageReader.readConstantPool +void unpacker::read_cp() +{ + byte *rp0 = rp; + + int i; + + for (int k = 0; k < (int)N_TAGS_IN_ORDER; k++) + { + byte tag = TAGS_IN_ORDER[k]; + int len = cp.tag_count[tag]; + int base = cp.tag_base[tag]; + + entry *cpMap = &cp.entries[base]; + for (i = 0; i < len; i++) + { + cpMap[i].tag = tag; + cpMap[i].inord = i; + } + + switch (tag) + { + case CONSTANT_Utf8: + read_Utf8_values(cpMap, len); + break; + case CONSTANT_Integer: + read_single_words(cp_Int, cpMap, len); + break; + case CONSTANT_Float: + read_single_words(cp_Float, cpMap, len); + break; + case CONSTANT_Long: + read_double_words(cp_Long_hi /*& cp_Long_lo*/, cpMap, len); + break; + case CONSTANT_Double: + read_double_words(cp_Double_hi /*& cp_Double_lo*/, cpMap, len); + break; + case CONSTANT_String: + read_single_refs(cp_String, CONSTANT_Utf8, cpMap, len); + break; + case CONSTANT_Class: + read_single_refs(cp_Class, CONSTANT_Utf8, cpMap, len); + break; + case CONSTANT_Signature: + read_signature_values(cpMap, len); + break; + case CONSTANT_NameandType: + read_double_refs(cp_Descr_name /*& cp_Descr_type*/, CONSTANT_Utf8, + CONSTANT_Signature, cpMap, len); + break; + case CONSTANT_Fieldref: + read_double_refs(cp_Field_class /*& cp_Field_desc*/, CONSTANT_Class, + CONSTANT_NameandType, cpMap, len); + break; + case CONSTANT_Methodref: + read_double_refs(cp_Method_class /*& cp_Method_desc*/, CONSTANT_Class, + CONSTANT_NameandType, cpMap, len); + break; + case CONSTANT_InterfaceMethodref: + read_double_refs(cp_Imethod_class /*& cp_Imethod_desc*/, CONSTANT_Class, + CONSTANT_NameandType, cpMap, len); + break; + default: + assert(false); + break; + } + CHECK; + } + + cp.expandSignatures(); + CHECK; + cp.initMemberIndexes(); + CHECK; + +#define SNAME(n, s) #s "\0" + const char *symNames = (ALL_ATTR_DO(SNAME) ""); +#undef SNAME + + for (int sn = 0; sn < cpool::s_LIMIT; sn++) + { + assert(symNames[0] >= '0' && symNames[0] <= 'Z'); // sanity + bytes name; + name.set(symNames); + if (name.len > 0 && name.ptr[0] != '0') + { + cp.sym[sn] = cp.ensureUtf8(name); + } + symNames += name.len + 1; // skip trailing nullptr to next name + } + + band::initIndexes(this); +} + +static band *no_bands[] = {nullptr}; // shared empty body + +inline band &unpacker::attr_definitions::fixed_band(int e_class_xxx) +{ + return u->all_bands[xxx_flags_hi_bn + (e_class_xxx - e_class_flags_hi)]; +} +inline band &unpacker::attr_definitions::xxx_flags_hi() +{ + return fixed_band(e_class_flags_hi); +} +inline band &unpacker::attr_definitions::xxx_flags_lo() +{ + return fixed_band(e_class_flags_lo); +} +inline band &unpacker::attr_definitions::xxx_attr_count() +{ + return fixed_band(e_class_attr_count); +} +inline band &unpacker::attr_definitions::xxx_attr_indexes() +{ + return fixed_band(e_class_attr_indexes); +} +inline band &unpacker::attr_definitions::xxx_attr_calls() +{ + return fixed_band(e_class_attr_calls); +} + +inline unpacker::layout_definition * +unpacker::attr_definitions::defineLayout(int idx, entry *nameEntry, const char *layout) +{ + const char *name = nameEntry->value.b.strval(); + layout_definition *lo = defineLayout(idx, name, layout); + CHECK_0; + lo->nameEntry = nameEntry; + return lo; +} + +unpacker::layout_definition *unpacker::attr_definitions::defineLayout(int idx, const char *name, + const char *layout) +{ + assert(flag_limit != 0); // must be set up already + if (idx >= 0) + { + // Fixed attr. + if (idx >= (int)flag_limit) + abort("attribute index too large"); + if (isRedefined(idx)) + abort("redefined attribute index"); + redef |= ((julong)1 << idx); + } + else + { + idx = flag_limit + overflow_count.length(); + overflow_count.add(0); // make a new counter + } + layout_definition *lo = U_NEW(layout_definition, 1); + CHECK_0; + lo->idx = idx; + lo->name = name; + lo->layout = layout; + for (int adds = (idx + 1) - layouts.length(); adds > 0; adds--) + { + layouts.add(nullptr); + } + CHECK_0; + layouts.get(idx) = lo; + return lo; +} + +band **unpacker::attr_definitions::buildBands(unpacker::layout_definition *lo) +{ + int i; + if (lo->elems != nullptr) + return lo->bands(); + if (lo->layout[0] == '\0') + { + lo->elems = no_bands; + } + else + { + // Create bands for this attribute by parsing the layout. + bool hasCallables = lo->hasCallables(); + bands_made = 0x10000; // base number for bands made + const char *lp = lo->layout; + lp = parseLayout(lp, lo->elems, -1); + CHECK_0; + if (lp[0] != '\0' || band_stack.length() > 0) + { + abort("garbage at end of layout"); + } + band_stack.popTo(0); + CHECK_0; + + // Fix up callables to point at their callees. + band **bands = lo->elems; + assert(bands == lo->bands()); + int num_callables = 0; + if (hasCallables) + { + while (bands[num_callables] != nullptr) + { + if (bands[num_callables]->le_kind != EK_CBLE) + { + abort("garbage mixed with callables"); + break; + } + num_callables += 1; + } + } + for (i = 0; i < calls_to_link.length(); i++) + { + band &call = *(band *)calls_to_link.get(i); + assert(call.le_kind == EK_CALL); + // Determine the callee. + int call_num = call.le_len; + if (call_num < 0 || call_num >= num_callables) + { + abort("bad call in layout"); + break; + } + band &cble = *bands[call_num]; + // Link the call to it. + call.le_body[0] = &cble; + // Distinguish backward calls and callables: + assert(cble.le_kind == EK_CBLE); + assert(cble.le_len == call_num); + cble.le_back |= call.le_back; + } + calls_to_link.popTo(0); + } + return lo->elems; +} + +/* attribute layout language parser + + attribute_layout: + ( layout_element )* | ( callable )+ + layout_element: + ( integral | replication | union | call | reference ) + + callable: + '[' body ']' + body: + ( layout_element )+ + + integral: + ( unsigned_int | signed_int | bc_index | bc_offset | flag ) + unsigned_int: + uint_type + signed_int: + 'S' uint_type + any_int: + ( unsigned_int | signed_int ) + bc_index: + ( 'P' uint_type | 'PO' uint_type ) + bc_offset: + 'O' any_int + flag: + 'F' uint_type + uint_type: + ( 'B' | 'H' | 'I' | 'V' ) + + replication: + 'N' uint_type '[' body ']' + + union: + 'T' any_int (union_case)* '(' ')' '[' (body)? ']' + union_case: + '(' union_case_tag (',' union_case_tag)* ')' '[' (body)? ']' + union_case_tag: + ( numeral | numeral '-' numeral ) + call: + '(' numeral ')' + + reference: + reference_type ( 'N' )? uint_type + reference_type: + ( constant_ref | schema_ref | utf8_ref | untyped_ref ) + constant_ref: + ( 'KI' | 'KJ' | 'KF' | 'KD' | 'KS' | 'KQ' ) + schema_ref: + ( 'RC' | 'RS' | 'RD' | 'RF' | 'RM' | 'RI' ) + utf8_ref: + 'RU' + untyped_ref: + 'RQ' + + numeral: + '(' ('-')? (digit)+ ')' + digit: + ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' ) + +*/ + +const char *unpacker::attr_definitions::parseIntLayout(const char *lp, band *&res, byte le_kind, + bool can_be_signed) +{ + const char *lp0 = lp; + band *b = U_NEW(band, 1); + CHECK_(lp); + char le = *lp++; + int spec = UNSIGNED5_spec; + if (le == 'S' && can_be_signed) + { + // Note: This is the last use of sign. There is no 'EF_SIGN'. + spec = SIGNED5_spec; + le = *lp++; + } + else if (le == 'B') + { + spec = BYTE1_spec; // unsigned byte + } + b->init(u, bands_made++, spec); + b->le_kind = le_kind; + int le_len = 0; + switch (le) + { + case 'B': + le_len = 1; + break; + case 'H': + le_len = 2; + break; + case 'I': + le_len = 4; + break; + case 'V': + le_len = 0; + break; + default: + abort("bad layout element"); + } + b->le_len = le_len; + band_stack.add(b); + res = b; + return lp; +} + +const char *unpacker::attr_definitions::parseNumeral(const char *lp, int &res) +{ + const char *lp0 = lp; + bool sgn = false; + if (*lp == '0') + { + res = 0; + return lp + 1; + } // special case '0' + if (*lp == '-') + { + sgn = true; + lp++; + } + const char *dp = lp; + int con = 0; + while (*dp >= '0' && *dp <= '9') + { + int con0 = con; + con *= 10; + con += (*dp++) - '0'; + if (con <= con0) + { + con = -1; + break; + } // numeral overflow + } + if (lp == dp) + { + abort("missing numeral in layout"); + return ""; + } + lp = dp; + if (con < 0 && !(sgn && con == -con)) + { + // (Portability note: Misses the error if int is not 32 bits.) + abort("numeral overflow"); + return ""; + } + if (sgn) + con = -con; + res = con; + return lp; +} + +band **unpacker::attr_definitions::popBody(int bs_base) +{ + // Return everything that was pushed, as a nullptr-terminated pointer array. + int bs_limit = band_stack.length(); + if (bs_base == bs_limit) + { + return no_bands; + } + else + { + int nb = bs_limit - bs_base; + band **res = U_NEW(band *, add_size(nb, 1)); + CHECK_(no_bands); + for (int i = 0; i < nb; i++) + { + band *b = (band *)band_stack.get(bs_base + i); + res[i] = b; + } + band_stack.popTo(bs_base); + return res; + } +} + +const char *unpacker::attr_definitions::parseLayout(const char *lp, band **&res, int curCble) +{ + const char *lp0 = lp; + int bs_base = band_stack.length(); + bool top_level = (bs_base == 0); + band *b; + enum + { + can_be_signed = true + }; // optional arg to parseIntLayout + + for (bool done = false; !done;) + { + switch (*lp++) + { + case 'B': + case 'H': + case 'I': + case 'V': // unsigned_int + case 'S': // signed_int + --lp; // reparse + case 'F': + lp = parseIntLayout(lp, b, EK_INT); + break; + case 'P': + { + int le_bci = EK_BCI; + if (*lp == 'O') + { + ++lp; + le_bci = EK_BCID; + } + assert(*lp != 'S'); // no PSH, etc. + lp = parseIntLayout(lp, b, EK_INT); + b->le_bci = le_bci; + if (le_bci == EK_BCI) + b->defc = coding::findBySpec(BCI5_spec); + else + b->defc = coding::findBySpec(BRANCH5_spec); + } + break; + case 'O': + lp = parseIntLayout(lp, b, EK_INT, can_be_signed); + b->le_bci = EK_BCO; + b->defc = coding::findBySpec(BRANCH5_spec); + break; + case 'N': // replication: 'N' uint '[' elem ... ']' + lp = parseIntLayout(lp, b, EK_REPL); + assert(*lp == '['); + ++lp; + lp = parseLayout(lp, b->le_body, curCble); + CHECK_(lp); + break; + case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']' + lp = parseIntLayout(lp, b, EK_UN, can_be_signed); + { + int union_base = band_stack.length(); + for (;;) + { // for each case + band &k_case = *U_NEW(band, 1); + CHECK_(lp); + band_stack.add(&k_case); + k_case.le_kind = EK_CASE; + k_case.bn = bands_made++; + if (*lp++ != '(') + { + abort("bad union case"); + return ""; + } + if (*lp++ != ')') + { + --lp; // reparse + // Read some case values. (Use band_stack for temp. storage.) + int case_base = band_stack.length(); + for (;;) + { + int caseval = 0; + lp = parseNumeral(lp, caseval); + band_stack.add((void *)(size_t)caseval); + if (*lp == '-') + { + // new in version 160, allow (1-5) for (1,2,3,4,5) + if (u->majver < JAVA6_PACKAGE_MAJOR_VERSION) + { + abort("bad range in union case label (old archive format)"); + return ""; + } + int caselimit = caseval; + lp++; + lp = parseNumeral(lp, caselimit); + if (caseval >= caselimit || + (uint)(caselimit - caseval) > 0x10000) + { + // Note: 0x10000 is arbitrary implementation restriction. + // We can remove it later if it's important to. + abort("bad range in union case label"); + return ""; + } + for (;;) + { + ++caseval; + band_stack.add((void *)(size_t)caseval); + if (caseval == caselimit) + break; + } + } + if (*lp != ',') + break; + lp++; + } + if (*lp++ != ')') + { + abort("bad case label"); + return ""; + } + // save away the case labels + int ntags = band_stack.length() - case_base; + int *tags = U_NEW(int, add_size(ntags, 1)); + CHECK_(lp); + k_case.le_casetags = tags; + *tags++ = ntags; + for (int i = 0; i < ntags; i++) + { + *tags++ = ptrlowbits(band_stack.get(case_base + i)); + } + band_stack.popTo(case_base); + CHECK_(lp); + } + // Got le_casetags. Now grab the body. + assert(*lp == '['); + ++lp; + lp = parseLayout(lp, k_case.le_body, curCble); + CHECK_(lp); + if (k_case.le_casetags == nullptr) + break; // done + } + b->le_body = popBody(union_base); + } + break; + case '(': // call: '(' -?NN* ')' + { + band &call = *U_NEW(band, 1); + CHECK_(lp); + band_stack.add(&call); + call.le_kind = EK_CALL; + call.bn = bands_made++; + call.le_body = U_NEW(band *, 2); // fill in later + int call_num = 0; + lp = parseNumeral(lp, call_num); + call.le_back = (call_num <= 0); + call_num += curCble; // numeral is self-relative offset + call.le_len = call_num; // use le_len as scratch + calls_to_link.add(&call); + CHECK_(lp); + if (*lp++ != ')') + { + abort("bad call label"); + return ""; + } + } + break; + case 'K': // reference_type: constant_ref + case 'R': // reference_type: schema_ref + { + int ixTag = CONSTANT_None; + if (lp[-1] == 'K') + { + switch (*lp++) + { + case 'I': + ixTag = CONSTANT_Integer; + break; + case 'J': + ixTag = CONSTANT_Long; + break; + case 'F': + ixTag = CONSTANT_Float; + break; + case 'D': + ixTag = CONSTANT_Double; + break; + case 'S': + ixTag = CONSTANT_String; + break; + case 'Q': + ixTag = CONSTANT_Literal; + break; + } + } + else + { + switch (*lp++) + { + case 'C': + ixTag = CONSTANT_Class; + break; + case 'S': + ixTag = CONSTANT_Signature; + break; + case 'D': + ixTag = CONSTANT_NameandType; + break; + case 'F': + ixTag = CONSTANT_Fieldref; + break; + case 'M': + ixTag = CONSTANT_Methodref; + break; + case 'I': + ixTag = CONSTANT_InterfaceMethodref; + break; + case 'U': + ixTag = CONSTANT_Utf8; + break; // utf8_ref + case 'Q': + ixTag = CONSTANT_All; + break; // untyped_ref + } + } + if (ixTag == CONSTANT_None) + { + abort("bad reference layout"); + break; + } + bool nullOK = false; + if (*lp == 'N') + { + nullOK = true; + lp++; + } + lp = parseIntLayout(lp, b, EK_REF); + b->defc = coding::findBySpec(UNSIGNED5_spec); + b->initRef(ixTag, nullOK); + } + break; + case '[': + { + // [callable1][callable2]... + if (!top_level) + { + abort("bad nested callable"); + break; + } + curCble += 1; + band &cble = *U_NEW(band, 1); + CHECK_(lp); + band_stack.add(&cble); + cble.le_kind = EK_CBLE; + cble.bn = bands_made++; + lp = parseLayout(lp, cble.le_body, curCble); + } + break; + case ']': + // Hit a closing brace. This ends whatever body we were in. + done = true; + break; + case '\0': + // Hit a nullptr. Also ends the (top-level) body. + --lp; // back up, so caller can see the nullptr also + done = true; + break; + default: + abort("bad layout"); + break; + } + CHECK_(lp); + } + + // Return the accumulated bands: + res = popBody(bs_base); + return lp; +} + +void unpacker::read_attr_defs() +{ + int i; + + // Tell each AD which attrc it is and where its fixed flags are: + attr_defs[ATTR_CONTEXT_CLASS].attrc = ATTR_CONTEXT_CLASS; + attr_defs[ATTR_CONTEXT_CLASS].xxx_flags_hi_bn = e_class_flags_hi; + attr_defs[ATTR_CONTEXT_FIELD].attrc = ATTR_CONTEXT_FIELD; + attr_defs[ATTR_CONTEXT_FIELD].xxx_flags_hi_bn = e_field_flags_hi; + attr_defs[ATTR_CONTEXT_METHOD].attrc = ATTR_CONTEXT_METHOD; + attr_defs[ATTR_CONTEXT_METHOD].xxx_flags_hi_bn = e_method_flags_hi; + attr_defs[ATTR_CONTEXT_CODE].attrc = ATTR_CONTEXT_CODE; + attr_defs[ATTR_CONTEXT_CODE].xxx_flags_hi_bn = e_code_flags_hi; + + // Decide whether bands for the optional high flag words are present. + attr_defs[ATTR_CONTEXT_CLASS] + .setHaveLongFlags((archive_options & AO_HAVE_CLASS_FLAGS_HI) != 0); + attr_defs[ATTR_CONTEXT_FIELD] + .setHaveLongFlags((archive_options & AO_HAVE_FIELD_FLAGS_HI) != 0); + attr_defs[ATTR_CONTEXT_METHOD] + .setHaveLongFlags((archive_options & AO_HAVE_METHOD_FLAGS_HI) != 0); + attr_defs[ATTR_CONTEXT_CODE] + .setHaveLongFlags((archive_options & AO_HAVE_CODE_FLAGS_HI) != 0); + + // Set up built-in attrs. + // (The simple ones are hard-coded. The metadata layouts are not.) + const char *md_layout = ( +// parameter annotations: +#define MDL0 "[NB[(1)]]" + MDL0 +// annotations: +#define MDL1 \ + "[NH[(1)]]" \ + "[RSHNH[RUH(1)]]" + MDL1 + // member_value: + "[TB" + "(66,67,73,83,90)[KIH]" + "(68)[KDH]" + "(70)[KFH]" + "(74)[KJH]" + "(99)[RSH]" + "(101)[RSHRUH]" + "(115)[RUH]" + "(91)[NH[(0)]]" + "(64)[" + // nested annotation: + "RSH" + "NH[RUH(0)]" + "]" + "()[]" + "]"); + + const char *md_layout_P = md_layout; + const char *md_layout_A = md_layout + strlen(MDL0); + const char *md_layout_V = md_layout + strlen(MDL0 MDL1); + assert(0 == strncmp(&md_layout_A[-3], ")]][", 4)); + assert(0 == strncmp(&md_layout_V[-3], ")]][", 4)); + + for (i = 0; i < ATTR_CONTEXT_LIMIT; i++) + { + attr_definitions &ad = attr_defs[i]; + ad.defineLayout(X_ATTR_RuntimeVisibleAnnotations, "RuntimeVisibleAnnotations", + md_layout_A); + ad.defineLayout(X_ATTR_RuntimeInvisibleAnnotations, "RuntimeInvisibleAnnotations", + md_layout_A); + if (i != ATTR_CONTEXT_METHOD) + continue; + ad.defineLayout(METHOD_ATTR_RuntimeVisibleParameterAnnotations, + "RuntimeVisibleParameterAnnotations", md_layout_P); + ad.defineLayout(METHOD_ATTR_RuntimeInvisibleParameterAnnotations, + "RuntimeInvisibleParameterAnnotations", md_layout_P); + ad.defineLayout(METHOD_ATTR_AnnotationDefault, "AnnotationDefault", md_layout_V); + } + + attr_definition_headers.readData(attr_definition_count); + attr_definition_name.readData(attr_definition_count); + attr_definition_layout.readData(attr_definition_count); + + CHECK; + +// Initialize correct predef bits, to distinguish predefs from new defs. +#define ORBIT(n, s) | ((julong)1 << n) + attr_defs[ATTR_CONTEXT_CLASS].predef = (0 X_ATTR_DO(ORBIT) CLASS_ATTR_DO(ORBIT)); + attr_defs[ATTR_CONTEXT_FIELD].predef = (0 X_ATTR_DO(ORBIT) FIELD_ATTR_DO(ORBIT)); + attr_defs[ATTR_CONTEXT_METHOD].predef = (0 X_ATTR_DO(ORBIT) METHOD_ATTR_DO(ORBIT)); + attr_defs[ATTR_CONTEXT_CODE].predef = (0 O_ATTR_DO(ORBIT) CODE_ATTR_DO(ORBIT)); +#undef ORBIT + // Clear out the redef bits, folding them back into predef. + for (i = 0; i < ATTR_CONTEXT_LIMIT; i++) + { + attr_defs[i].predef |= attr_defs[i].redef; + attr_defs[i].redef = 0; + } + + // Now read the transmitted locally defined attrs. + // This will set redef bits again. + for (i = 0; i < attr_definition_count; i++) + { + int header = attr_definition_headers.getByte(); + int attrc = ADH_BYTE_CONTEXT(header); + int idx = ADH_BYTE_INDEX(header); + entry *name = attr_definition_name.getRef(); + entry *layout = attr_definition_layout.getRef(); + CHECK; + attr_defs[attrc].defineLayout(idx, name, layout->value.b.strval()); + } +} + +#define NO_ENTRY_YET ((entry *)-1) + +static bool isDigitString(bytes &x, int beg, int end) +{ + if (beg == end) + return false; // nullptr string + byte *xptr = x.ptr; + for (int i = beg; i < end; i++) + { + char ch = xptr[i]; + if (!(ch >= '0' && ch <= '9')) + return false; + } + return true; +} + +enum +{ // constants for parsing class names + SLASH_MIN = '.', + SLASH_MAX = '/', + DOLLAR_MIN = 0, + DOLLAR_MAX = '-'}; + +static int lastIndexOf(int chmin, int chmax, bytes &x, int pos) +{ + byte *ptr = x.ptr; + for (byte *cp = ptr + pos; --cp >= ptr;) + { + assert(x.inBounds(cp)); + if (*cp >= chmin && *cp <= chmax) + return (int)(cp - ptr); + } + return -1; +} + +inner_class *cpool::getIC(entry *inner) +{ + if (inner == nullptr) + return nullptr; + assert(inner->tag == CONSTANT_Class); + if (inner->inord == NO_INORD) + return nullptr; + inner_class *ic = ic_index[inner->inord]; + assert(ic == nullptr || ic->inner == inner); + return ic; +} + +inner_class *cpool::getFirstChildIC(entry *outer) +{ + if (outer == nullptr) + return nullptr; + assert(outer->tag == CONSTANT_Class); + if (outer->inord == NO_INORD) + return nullptr; + inner_class *ic = ic_child_index[outer->inord]; + assert(ic == nullptr || ic->outer == outer); + return ic; +} + +inner_class *cpool::getNextChildIC(inner_class *child) +{ + inner_class *ic = child->next_sibling; + assert(ic == nullptr || ic->outer == child->outer); + return ic; +} + +void unpacker::read_ics() +{ + int i; + int index_size = cp.tag_count[CONSTANT_Class]; + inner_class **ic_index = U_NEW(inner_class *, index_size); + inner_class **ic_child_index = U_NEW(inner_class *, index_size); + cp.ic_index = ic_index; + cp.ic_child_index = ic_child_index; + ics = U_NEW(inner_class, ic_count); + ic_this_class.readData(ic_count); + ic_flags.readData(ic_count); + CHECK; + // Scan flags to get count of long-form bands. + int long_forms = 0; + for (i = 0; i < ic_count; i++) + { + int flags = ic_flags.getInt(); // may be long form! + if ((flags & ACC_IC_LONG_FORM) != 0) + { + long_forms += 1; + ics[i].name = NO_ENTRY_YET; + } + flags &= ~ACC_IC_LONG_FORM; + entry *inner = ic_this_class.getRef(); + CHECK; + uint inord = inner->inord; + assert(inord < (uint)cp.tag_count[CONSTANT_Class]); + if (ic_index[inord] != nullptr) + { + abort("identical inner class"); + break; + } + ic_index[inord] = &ics[i]; + ics[i].inner = inner; + ics[i].flags = flags; + assert(cp.getIC(inner) == &ics[i]); + } + CHECK; + // ic_this_class.done(); + // ic_flags.done(); + ic_outer_class.readData(long_forms); + ic_name.readData(long_forms); + for (i = 0; i < ic_count; i++) + { + if (ics[i].name == NO_ENTRY_YET) + { + // Long form. + ics[i].outer = ic_outer_class.getRefN(); + ics[i].name = ic_name.getRefN(); + } + else + { + // Fill in outer and name based on inner. + bytes &n = ics[i].inner->value.b; + bytes pkgOuter; + bytes number; + bytes name; + // Parse n into pkgOuter and name (and number). + int dollar1, dollar2; // pointers to $ in the pattern + // parse n = (/)*($)?($)? + int nlen = (int)n.len; + int pkglen = lastIndexOf(SLASH_MIN, SLASH_MAX, n, nlen) + 1; + dollar2 = lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, n, nlen); + if (dollar2 < 0) + { + abort(); + return; + } + assert(dollar2 >= pkglen); + if (isDigitString(n, dollar2 + 1, nlen)) + { + // n = (/)*$ + number = n.slice(dollar2 + 1, nlen); + name.set(nullptr, 0); + dollar1 = dollar2; + } + else if (pkglen < (dollar1 = lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, n, dollar2 - 1)) && + isDigitString(n, dollar1 + 1, dollar2)) + { + // n = (/)*$$ + number = n.slice(dollar1 + 1, dollar2); + name = n.slice(dollar2 + 1, nlen); + } + else + { + // n = (/)*$ + dollar1 = dollar2; + number.set(nullptr, 0); + name = n.slice(dollar2 + 1, nlen); + } + if (number.ptr == nullptr) + pkgOuter = n.slice(0, dollar1); + else + pkgOuter.set(nullptr, 0); + + if (pkgOuter.ptr != nullptr) + ics[i].outer = cp.ensureClass(pkgOuter); + + if (name.ptr != nullptr) + ics[i].name = cp.ensureUtf8(name); + } + + // update child/sibling list + if (ics[i].outer != nullptr) + { + uint outord = ics[i].outer->inord; + if (outord != NO_INORD) + { + assert(outord < (uint)cp.tag_count[CONSTANT_Class]); + ics[i].next_sibling = ic_child_index[outord]; + ic_child_index[outord] = &ics[i]; + } + } + } + // ic_outer_class.done(); + // ic_name.done(); +} + +void unpacker::read_classes() +{ + class_this.readData(class_count); + class_super.readData(class_count); + class_interface_count.readData(class_count); + class_interface.readData(class_interface_count.getIntTotal()); + + CHECK; + +#if 0 + int i; + // Make a little mark on super-classes. + for (i = 0; i < class_count; i++) { + entry* e = class_super.getRefN(); + if (e != nullptr) e->bits |= entry::EB_SUPER; + } + class_super.rewind(); +#endif + + // Members. + class_field_count.readData(class_count); + class_method_count.readData(class_count); + + CHECK; + + int field_count = class_field_count.getIntTotal(); + int method_count = class_method_count.getIntTotal(); + + field_descr.readData(field_count); + read_attrs(ATTR_CONTEXT_FIELD, field_count); + CHECK; + + method_descr.readData(method_count); + read_attrs(ATTR_CONTEXT_METHOD, method_count); + + CHECK; + + read_attrs(ATTR_CONTEXT_CLASS, class_count); + CHECK; + + read_code_headers(); +} + +int unpacker::attr_definitions::predefCount(uint idx) +{ + return isPredefined(idx) ? flag_count[idx] : 0; +} + +void unpacker::read_attrs(int attrc, int obj_count) +{ + attr_definitions &ad = attr_defs[attrc]; + assert(ad.attrc == attrc); + + int i, idx, count; + + CHECK; + + bool haveLongFlags = ad.haveLongFlags(); + + band &xxx_flags_hi = ad.xxx_flags_hi(); + assert(endsWith(xxx_flags_hi.name, "_flags_hi")); + if (haveLongFlags) + xxx_flags_hi.readData(obj_count); + CHECK; + + band &xxx_flags_lo = ad.xxx_flags_lo(); + assert(endsWith(xxx_flags_lo.name, "_flags_lo")); + xxx_flags_lo.readData(obj_count); + CHECK; + + // pre-scan flags, counting occurrences of each index bit + julong indexMask = ad.flagIndexMask(); // which flag bits are index bits? + for (i = 0; i < obj_count; i++) + { + julong indexBits = xxx_flags_hi.getLong(xxx_flags_lo, haveLongFlags); + if ((indexBits & ~indexMask) > (ushort) - 1) + { + abort("undefined attribute flag bit"); + return; + } + indexBits &= indexMask; // ignore classfile flag bits + for (idx = 0; indexBits != 0; idx++, indexBits >>= 1) + { + ad.flag_count[idx] += (int)(indexBits & 1); + } + } + // we'll scan these again later for output: + xxx_flags_lo.rewind(); + xxx_flags_hi.rewind(); + + band &xxx_attr_count = ad.xxx_attr_count(); + assert(endsWith(xxx_attr_count.name, "_attr_count")); + // There is one count element for each 1<<16 bit set in flags: + xxx_attr_count.readData(ad.predefCount(X_ATTR_OVERFLOW)); + CHECK; + + band &xxx_attr_indexes = ad.xxx_attr_indexes(); + assert(endsWith(xxx_attr_indexes.name, "_attr_indexes")); + int overflowIndexCount = xxx_attr_count.getIntTotal(); + xxx_attr_indexes.readData(overflowIndexCount); + CHECK; + // pre-scan attr indexes, counting occurrences of each value + for (i = 0; i < overflowIndexCount; i++) + { + idx = xxx_attr_indexes.getInt(); + if (!ad.isIndex(idx)) + { + abort("attribute index out of bounds"); + return; + } + ad.getCount(idx) += 1; + } + xxx_attr_indexes.rewind(); // we'll scan it again later for output + + // We will need a backward call count for each used backward callable. + int backwardCounts = 0; + for (idx = 0; idx < ad.layouts.length(); idx++) + { + layout_definition *lo = ad.getLayout(idx); + if (lo != nullptr && ad.getCount(idx) != 0) + { + // Build the bands lazily, only when they are used. + band **bands = ad.buildBands(lo); + CHECK; + if (lo->hasCallables()) + { + for (i = 0; bands[i] != nullptr; i++) + { + if (bands[i]->le_back) + { + assert(bands[i]->le_kind == EK_CBLE); + backwardCounts += 1; + } + } + } + } + } + ad.xxx_attr_calls().readData(backwardCounts); + CHECK; + + // Read built-in bands. + // Mostly, these are hand-coded equivalents to readBandData(). + switch (attrc) + { + case ATTR_CONTEXT_CLASS: + + count = ad.predefCount(CLASS_ATTR_SourceFile); + class_SourceFile_RUN.readData(count); + CHECK; + + count = ad.predefCount(CLASS_ATTR_EnclosingMethod); + class_EnclosingMethod_RC.readData(count); + class_EnclosingMethod_RDN.readData(count); + CHECK; + + count = ad.predefCount(X_ATTR_Signature); + class_Signature_RS.readData(count); + CHECK; + + ad.readBandData(X_ATTR_RuntimeVisibleAnnotations); + ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations); + + count = ad.predefCount(CLASS_ATTR_InnerClasses); + class_InnerClasses_N.readData(count); + CHECK; + + count = class_InnerClasses_N.getIntTotal(); + class_InnerClasses_RC.readData(count); + class_InnerClasses_F.readData(count); + CHECK; + // Drop remaining columns wherever flags are zero: + count -= class_InnerClasses_F.getIntCount(0); + class_InnerClasses_outer_RCN.readData(count); + class_InnerClasses_name_RUN.readData(count); + CHECK; + + count = ad.predefCount(CLASS_ATTR_ClassFile_version); + class_ClassFile_version_minor_H.readData(count); + class_ClassFile_version_major_H.readData(count); + CHECK; + break; + + case ATTR_CONTEXT_FIELD: + + count = ad.predefCount(FIELD_ATTR_ConstantValue); + field_ConstantValue_KQ.readData(count); + CHECK; + + count = ad.predefCount(X_ATTR_Signature); + field_Signature_RS.readData(count); + CHECK; + + ad.readBandData(X_ATTR_RuntimeVisibleAnnotations); + ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations); + CHECK; + break; + + case ATTR_CONTEXT_METHOD: + + code_count = ad.predefCount(METHOD_ATTR_Code); + // Code attrs are handled very specially below... + + count = ad.predefCount(METHOD_ATTR_Exceptions); + method_Exceptions_N.readData(count); + count = method_Exceptions_N.getIntTotal(); + method_Exceptions_RC.readData(count); + CHECK; + + count = ad.predefCount(X_ATTR_Signature); + method_Signature_RS.readData(count); + CHECK; + + ad.readBandData(X_ATTR_RuntimeVisibleAnnotations); + ad.readBandData(X_ATTR_RuntimeInvisibleAnnotations); + ad.readBandData(METHOD_ATTR_RuntimeVisibleParameterAnnotations); + ad.readBandData(METHOD_ATTR_RuntimeInvisibleParameterAnnotations); + ad.readBandData(METHOD_ATTR_AnnotationDefault); + CHECK; + break; + + case ATTR_CONTEXT_CODE: + // (keep this code aligned with its brother in unpacker::write_attrs) + count = ad.predefCount(CODE_ATTR_StackMapTable); + // disable this feature in old archives! + if (count != 0 && majver < JAVA6_PACKAGE_MAJOR_VERSION) + { + abort("undefined StackMapTable attribute (old archive format)"); + return; + } + code_StackMapTable_N.readData(count); + CHECK; + count = code_StackMapTable_N.getIntTotal(); + code_StackMapTable_frame_T.readData(count); + CHECK; + // the rest of it depends in a complicated way on frame tags + { + int fat_frame_count = 0; + int offset_count = 0; + int type_count = 0; + for (int k = 0; k < count; k++) + { + int tag = code_StackMapTable_frame_T.getByte(); + if (tag <= 127) + { + // (64-127) [(2)] + if (tag >= 64) + type_count++; + } + else if (tag <= 251) + { + // (247) [(1)(2)] + // (248-251) [(1)] + if (tag >= 247) + offset_count++; + if (tag == 247) + type_count++; + } + else if (tag <= 254) + { + // (252) [(1)(2)] + // (253) [(1)(2)(2)] + // (254) [(1)(2)(2)(2)] + offset_count++; + type_count += (tag - 251); + } + else + { + // (255) [(1)NH[(2)]NH[(2)]] + fat_frame_count++; + } + } + + // done pre-scanning frame tags: + code_StackMapTable_frame_T.rewind(); + + // deal completely with fat frames: + offset_count += fat_frame_count; + code_StackMapTable_local_N.readData(fat_frame_count); + CHECK; + type_count += code_StackMapTable_local_N.getIntTotal(); + code_StackMapTable_stack_N.readData(fat_frame_count); + type_count += code_StackMapTable_stack_N.getIntTotal(); + CHECK; + // read the rest: + code_StackMapTable_offset.readData(offset_count); + code_StackMapTable_T.readData(type_count); + CHECK; + // (7) [RCH] + count = code_StackMapTable_T.getIntCount(7); + code_StackMapTable_RC.readData(count); + CHECK; + // (8) [PH] + count = code_StackMapTable_T.getIntCount(8); + code_StackMapTable_P.readData(count); + CHECK; + } + + count = ad.predefCount(CODE_ATTR_LineNumberTable); + code_LineNumberTable_N.readData(count); + count = code_LineNumberTable_N.getIntTotal(); + code_LineNumberTable_bci_P.readData(count); + code_LineNumberTable_line.readData(count); + + count = ad.predefCount(CODE_ATTR_LocalVariableTable); + code_LocalVariableTable_N.readData(count); + count = code_LocalVariableTable_N.getIntTotal(); + code_LocalVariableTable_bci_P.readData(count); + code_LocalVariableTable_span_O.readData(count); + code_LocalVariableTable_name_RU.readData(count); + code_LocalVariableTable_type_RS.readData(count); + code_LocalVariableTable_slot.readData(count); + + count = ad.predefCount(CODE_ATTR_LocalVariableTypeTable); + code_LocalVariableTypeTable_N.readData(count); + count = code_LocalVariableTypeTable_N.getIntTotal(); + code_LocalVariableTypeTable_bci_P.readData(count); + code_LocalVariableTypeTable_span_O.readData(count); + code_LocalVariableTypeTable_name_RU.readData(count); + code_LocalVariableTypeTable_type_RS.readData(count); + code_LocalVariableTypeTable_slot.readData(count); + break; + } + + // Read compressor-defined bands. + for (idx = 0; idx < ad.layouts.length(); idx++) + { + if (ad.getLayout(idx) == nullptr) + continue; // none at this fixed index <32 + if (idx < (int)ad.flag_limit && ad.isPredefined(idx)) + continue; // already handled + if (ad.getCount(idx) == 0) + continue; // no attributes of this type (then why transmit layouts?) + ad.readBandData(idx); + } +} + +void unpacker::attr_definitions::readBandData(int idx) +{ + int j; + uint count = getCount(idx); + if (count == 0) + return; + layout_definition *lo = getLayout(idx); + bool hasCallables = lo->hasCallables(); + band **bands = lo->bands(); + if (!hasCallables) + { + // Read through the rest of the bands in a regular way. + readBandData(bands, count); + } + else + { + // Deal with the callables. + // First set up the forward entry count for each callable. + // This is stored on band::length of the callable. + bands[0]->expectMoreLength(count); + for (j = 0; bands[j] != nullptr; j++) + { + band &j_cble = *bands[j]; + assert(j_cble.le_kind == EK_CBLE); + if (j_cble.le_back) + { + // Add in the predicted effects of backward calls, too. + int back_calls = xxx_attr_calls().getInt(); + j_cble.expectMoreLength(back_calls); + // In a moment, more forward calls may increment j_cble.length. + } + } + // Now consult whichever callables have non-zero entry counts. + readBandData(bands, (uint) - 1); + } +} + +// Recursive helper to the previous function: +void unpacker::attr_definitions::readBandData(band **body, uint count) +{ + int j, k; + for (j = 0; body[j] != nullptr; j++) + { + band &b = *body[j]; + if (b.defc != nullptr) + { + // It has data, so read it. + b.readData(count); + } + switch (b.le_kind) + { + case EK_REPL: + { + int reps = b.getIntTotal(); + readBandData(b.le_body, reps); + } + break; + case EK_UN: + { + int remaining = count; + for (k = 0; b.le_body[k] != nullptr; k++) + { + band &k_case = *b.le_body[k]; + int k_count = 0; + if (k_case.le_casetags == nullptr) + { + k_count = remaining; // last (empty) case + } + else + { + int *tags = k_case.le_casetags; + int ntags = *tags++; // 1st element is length (why not?) + while (ntags-- > 0) + { + int tag = *tags++; + k_count += b.getIntCount(tag); + } + } + readBandData(k_case.le_body, k_count); + remaining -= k_count; + } + assert(remaining == 0); + } + break; + case EK_CALL: + // Push the count forward, if it is not a backward call. + if (!b.le_back) + { + band &cble = *b.le_body[0]; + assert(cble.le_kind == EK_CBLE); + cble.expectMoreLength(count); + } + break; + case EK_CBLE: + assert((int)count == -1); // incoming count is meaningless + k = b.length; + assert(k >= 0); + // This is intended and required for non production mode. + assert((b.length = -1)); // make it unable to accept more calls now. + readBandData(b.le_body, k); + break; + } + } +} + +static inline band **findMatchingCase(int matchTag, band **cases) +{ + for (int k = 0; cases[k] != nullptr; k++) + { + band &k_case = *cases[k]; + if (k_case.le_casetags != nullptr) + { + // If it has tags, it must match a tag. + int *tags = k_case.le_casetags; + int ntags = *tags++; // 1st element is length + for (; ntags > 0; ntags--) + { + int tag = *tags++; + if (tag == matchTag) + break; + } + if (ntags == 0) + continue; // does not match + } + return k_case.le_body; + } + return nullptr; +} + +// write attribute band data: +void unpacker::putlayout(band **body) +{ + int i; + int prevBII = -1; + int prevBCI = -1; + if (body == NULL) + { + abort("putlayout: unexpected NULL for body"); + return; + } + for (i = 0; body[i] != nullptr; i++) + { + band &b = *body[i]; + byte le_kind = b.le_kind; + + // Handle scalar part, if any. + int x = 0; + entry *e = nullptr; + if (b.defc != nullptr) + { + // It has data, so unparse an element. + if (b.ixTag != CONSTANT_None) + { + assert(le_kind == EK_REF); + if (b.ixTag == CONSTANT_Literal) + e = b.getRefUsing(cp.getKQIndex()); + else + e = b.getRefN(); + switch (b.le_len) + { + case 0: + break; + case 1: + putu1ref(e); + break; + case 2: + putref(e); + break; + case 4: + putu2(0); + putref(e); + break; + default: + assert(false); + } + } + else + { + assert(le_kind == EK_INT || le_kind == EK_REPL || le_kind == EK_UN); + x = b.getInt(); + + assert(!b.le_bci || prevBCI == (int)to_bci(prevBII)); + switch (b.le_bci) + { + case EK_BCI: // PH: transmit R(bci), store bci + x = to_bci(prevBII = x); + prevBCI = x; + break; + case EK_BCID: // POH: transmit D(R(bci)), store bci + x = to_bci(prevBII += x); + prevBCI = x; + break; + case EK_BCO: // OH: transmit D(R(bci)), store D(bci) + x = to_bci(prevBII += x) - prevBCI; + prevBCI += x; + break; + } + assert(!b.le_bci || prevBCI == (int)to_bci(prevBII)); + + switch (b.le_len) + { + case 0: + break; + case 1: + putu1(x); + break; + case 2: + putu2(x); + break; + case 4: + putu4(x); + break; + default: + assert(false); + } + } + } + + // Handle subparts, if any. + switch (le_kind) + { + case EK_REPL: + // x is the repeat count + while (x-- > 0) + { + putlayout(b.le_body); + } + break; + case EK_UN: + // x is the tag + putlayout(findMatchingCase(x, b.le_body)); + break; + case EK_CALL: + { + band &cble = *b.le_body[0]; + assert(cble.le_kind == EK_CBLE); + assert(cble.le_len == b.le_len); + putlayout(cble.le_body); + } + break; + + case EK_CBLE: + case EK_CASE: + assert(false); // should not reach here + } + } +} + +void unpacker::read_files() +{ + file_name.readData(file_count); + if ((archive_options & AO_HAVE_FILE_SIZE_HI) != 0) + file_size_hi.readData(file_count); + file_size_lo.readData(file_count); + if ((archive_options & AO_HAVE_FILE_MODTIME) != 0) + file_modtime.readData(file_count); + int allFiles = file_count + class_count; + if ((archive_options & AO_HAVE_FILE_OPTIONS) != 0) + { + file_options.readData(file_count); + // FO_IS_CLASS_STUB might be set, causing overlap between classes and files + for (int i = 0; i < file_count; i++) + { + if ((file_options.getInt() & FO_IS_CLASS_STUB) != 0) + { + allFiles -= 1; // this one counts as both class and file + } + } + file_options.rewind(); + } + assert((default_file_options & FO_IS_CLASS_STUB) == 0); + files_remaining = allFiles; +} + +void unpacker::get_code_header(int &max_stack, int &max_na_locals, int &handler_count, + int &cflags) +{ + int sc = code_headers.getByte(); + if (sc == 0) + { + max_stack = max_na_locals = handler_count = cflags = -1; + return; + } + // Short code header is the usual case: + int nh; + int mod; + if (sc < 1 + 12 * 12) + { + sc -= 1; + nh = 0; + mod = 12; + } + else if (sc < 1 + 12 * 12 + 8 * 8) + { + sc -= 1 + 12 * 12; + nh = 1; + mod = 8; + } + else + { + assert(sc < 1 + 12 * 12 + 8 * 8 + 7 * 7); + sc -= 1 + 12 * 12 + 8 * 8; + nh = 2; + mod = 7; + } + max_stack = sc % mod; + max_na_locals = sc / mod; // caller must add static, siglen + handler_count = nh; + if ((archive_options & AO_HAVE_ALL_CODE_FLAGS) != 0) + cflags = -1; + else + cflags = 0; // this one has no attributes +} + +// Cf. PackageReader.readCodeHeaders +void unpacker::read_code_headers() +{ + code_headers.readData(code_count); + CHECK; + int totalHandlerCount = 0; + int totalFlagsCount = 0; + for (int i = 0; i < code_count; i++) + { + int max_stack, max_locals, handler_count, cflags; + get_code_header(max_stack, max_locals, handler_count, cflags); + if (max_stack < 0) + code_max_stack.expectMoreLength(1); + if (max_locals < 0) + code_max_na_locals.expectMoreLength(1); + if (handler_count < 0) + code_handler_count.expectMoreLength(1); + else + totalHandlerCount += handler_count; + if (cflags < 0) + totalFlagsCount += 1; + } + code_headers.rewind(); // replay later during writing + + code_max_stack.readData(); + code_max_na_locals.readData(); + code_handler_count.readData(); + totalHandlerCount += code_handler_count.getIntTotal(); + CHECK; + + // Read handler specifications. + // Cf. PackageReader.readCodeHandlers. + code_handler_start_P.readData(totalHandlerCount); + code_handler_end_PO.readData(totalHandlerCount); + code_handler_catch_PO.readData(totalHandlerCount); + code_handler_class_RCN.readData(totalHandlerCount); + CHECK; + + read_attrs(ATTR_CONTEXT_CODE, totalFlagsCount); + CHECK; +} + +static inline bool is_in_range(uint n, uint min, uint max) +{ + return n - min <= max - min; // unsigned arithmetic! +} +static inline bool is_field_op(int bc) +{ + return is_in_range(bc, bc_getstatic, bc_putfield); +} +static inline bool is_invoke_init_op(int bc) +{ + return is_in_range(bc, _invokeinit_op, _invokeinit_limit - 1); +} +static inline bool is_self_linker_op(int bc) +{ + return is_in_range(bc, _self_linker_op, _self_linker_limit - 1); +} +static bool is_branch_op(int bc) +{ + return is_in_range(bc, bc_ifeq, bc_jsr) || is_in_range(bc, bc_ifnull, bc_jsr_w); +} +static bool is_local_slot_op(int bc) +{ + return is_in_range(bc, bc_iload, bc_aload) || is_in_range(bc, bc_istore, bc_astore) || + bc == bc_iinc || bc == bc_ret; +} +band *unpacker::ref_band_for_op(int bc) +{ + switch (bc) + { + case bc_ildc: + case bc_ildc_w: + return &bc_intref; + case bc_fldc: + case bc_fldc_w: + return &bc_floatref; + case bc_lldc2_w: + return &bc_longref; + case bc_dldc2_w: + return &bc_doubleref; + case bc_aldc: + case bc_aldc_w: + return &bc_stringref; + case bc_cldc: + case bc_cldc_w: + return &bc_classref; + + case bc_getstatic: + case bc_putstatic: + case bc_getfield: + case bc_putfield: + return &bc_fieldref; + + case bc_invokevirtual: + case bc_invokespecial: + case bc_invokestatic: + return &bc_methodref; + case bc_invokeinterface: + return &bc_imethodref; + + case bc_new: + case bc_anewarray: + case bc_checkcast: + case bc_instanceof: + case bc_multianewarray: + return &bc_classref; + } + return nullptr; +} + +band *unpacker::ref_band_for_self_op(int bc, bool &isAloadVar, int &origBCVar) +{ + if (!is_self_linker_op(bc)) + return nullptr; + int idx = (bc - _self_linker_op); + bool isSuper = (idx >= _self_linker_super_flag); + if (isSuper) + idx -= _self_linker_super_flag; + bool isAload = (idx >= _self_linker_aload_flag); + if (isAload) + idx -= _self_linker_aload_flag; + int origBC = _first_linker_op + idx; + bool isField = is_field_op(origBC); + isAloadVar = isAload; + origBCVar = _first_linker_op + idx; + if (!isSuper) + return isField ? &bc_thisfield : &bc_thismethod; + else + return isField ? &bc_superfield : &bc_supermethod; +} + +// Cf. PackageReader.readByteCodes +inline // called exactly once => inline + void +unpacker::read_bcs() +{ + // read from bc_codes and bc_case_count + fillbytes all_switch_ops; + all_switch_ops.init(); + CHECK; + + // Read directly from rp/rplimit. + // Do this later: bc_codes.readData(...) + byte *rp0 = rp; + + band *bc_which; + byte *opptr = rp; + byte *oplimit = rplimit; + + bool isAload; // passed by ref and then ignored + int junkBC; // passed by ref and then ignored + for (int k = 0; k < code_count; k++) + { + // Scan one method: + for (;;) + { + if (opptr + 2 > oplimit) + { + rp = opptr; + ensure_input(2); + oplimit = rplimit; + rp = rp0; // back up + } + if (opptr == oplimit) + { + abort(); + break; + } + int bc = *opptr++ & 0xFF; + bool isWide = false; + if (bc == bc_wide) + { + if (opptr == oplimit) + { + abort(); + break; + } + bc = *opptr++ & 0xFF; + isWide = true; + } + // Adjust expectations of various band sizes. + switch (bc) + { + case bc_tableswitch: + case bc_lookupswitch: + all_switch_ops.addByte(bc); + break; + case bc_iinc: + bc_local.expectMoreLength(1); + bc_which = isWide ? &bc_short : &bc_byte; + bc_which->expectMoreLength(1); + break; + case bc_sipush: + bc_short.expectMoreLength(1); + break; + case bc_bipush: + bc_byte.expectMoreLength(1); + break; + case bc_newarray: + bc_byte.expectMoreLength(1); + break; + case bc_multianewarray: + assert(ref_band_for_op(bc) == &bc_classref); + bc_classref.expectMoreLength(1); + bc_byte.expectMoreLength(1); + break; + case bc_ref_escape: + bc_escrefsize.expectMoreLength(1); + bc_escref.expectMoreLength(1); + break; + case bc_byte_escape: + bc_escsize.expectMoreLength(1); + // bc_escbyte will have to be counted too + break; + default: + if (is_invoke_init_op(bc)) + { + bc_initref.expectMoreLength(1); + break; + } + bc_which = ref_band_for_self_op(bc, isAload, junkBC); + if (bc_which != nullptr) + { + bc_which->expectMoreLength(1); + break; + } + if (is_branch_op(bc)) + { + bc_label.expectMoreLength(1); + break; + } + bc_which = ref_band_for_op(bc); + if (bc_which != nullptr) + { + bc_which->expectMoreLength(1); + assert(bc != bc_multianewarray); // handled elsewhere + break; + } + if (is_local_slot_op(bc)) + { + bc_local.expectMoreLength(1); + break; + } + break; + case bc_end_marker: + // Increment k and test against code_count. + goto doneScanningMethod; + } + } + doneScanningMethod: + { + } + if (aborting()) + break; + } + + // Go through the formality, so we can use it in a regular fashion later: + assert(rp == rp0); + bc_codes.readData((int)(opptr - rp)); + + int i = 0; + + // To size instruction bands correctly, we need info on switches: + bc_case_count.readData((int)all_switch_ops.size()); + for (i = 0; i < (int)all_switch_ops.size(); i++) + { + int caseCount = bc_case_count.getInt(); + int bc = all_switch_ops.getByte(i); + bc_label.expectMoreLength(1 + caseCount); // default label + cases + bc_case_value.expectMoreLength(bc == bc_tableswitch ? 1 : caseCount); + } + bc_case_count.rewind(); // uses again for output + + all_switch_ops.free(); + + for (i = e_bc_case_value; i <= e_bc_escsize; i++) + { + all_bands[i].readData(); + } + + // The bc_escbyte band is counted by the immediately previous band. + bc_escbyte.readData(bc_escsize.getIntTotal()); +} + +void unpacker::read_bands() +{ + byte *rp0 = rp; + + read_file_header(); + CHECK; + + if (cp.nentries == 0) + { + // read_file_header failed to read a CP, because it copied a JAR. + return; + } + + // Do this after the file header has been read: + check_options(); + + read_cp(); + CHECK; + read_attr_defs(); + CHECK; + read_ics(); + CHECK; + read_classes(); + CHECK; + read_bcs(); + CHECK; + read_files(); +} + +/// CP routines + +entry *&cpool::hashTabRef(byte tag, bytes &b) +{ + uint hash = tag + (int)b.len; + for (int i = 0; i < (int)b.len; i++) + { + hash = hash * 31 + (0xFF & b.ptr[i]); + } + entry **ht = hashTab; + int hlen = hashTabLength; + assert((hlen & (hlen - 1)) == 0); // must be power of 2 + uint hash1 = hash & (hlen - 1); // == hash % hlen + uint hash2 = 0; // lazily computed (requires mod op.) + int probes = 0; + while (ht[hash1] != nullptr) + { + entry &e = *ht[hash1]; + if (e.value.b.equals(b) && e.tag == tag) + break; + if (hash2 == 0) + // Note: hash2 must be relatively prime to hlen, hence the "|1". + hash2 = (((hash % 499) & (hlen - 1)) | 1); + hash1 += hash2; + if (hash1 >= (uint)hlen) + hash1 -= hlen; + assert(hash1 < (uint)hlen); + assert(++probes < hlen); + } + return ht[hash1]; +} + +static void insert_extra(entry *e, ptrlist &extras) +{ + // This ordering helps implement the Pack200 requirement + // of a predictable CP order in the class files produced. + e->inord = NO_INORD; // mark as an "extra" + extras.add(e); + // Note: We will sort the list (by string-name) later. +} + +entry *cpool::ensureUtf8(bytes &b) +{ + entry *&ix = hashTabRef(CONSTANT_Utf8, b); + if (ix != nullptr) + return ix; + // Make one. + if (nentries == maxentries) + { + abort("cp utf8 overflow"); + return &entries[tag_base[CONSTANT_Utf8]]; // return something + } + entry &e = entries[nentries++]; + e.tag = CONSTANT_Utf8; + u->saveTo(e.value.b, b); + assert(&e >= first_extra_entry); + insert_extra(&e, tag_extras[CONSTANT_Utf8]); + return ix = &e; +} + +entry *cpool::ensureClass(bytes &b) +{ + entry *&ix = hashTabRef(CONSTANT_Class, b); + if (ix != nullptr) + return ix; + // Make one. + if (nentries == maxentries) + { + abort("cp class overflow"); + return &entries[tag_base[CONSTANT_Class]]; // return something + } + entry &e = entries[nentries++]; + e.tag = CONSTANT_Class; + e.nrefs = 1; + e.refs = U_NEW(entry *, 1); + ix = &e; // hold my spot in the index + entry *utf = ensureUtf8(b); + e.refs[0] = utf; + e.value.b = utf->value.b; + assert(&e >= first_extra_entry); + insert_extra(&e, tag_extras[CONSTANT_Class]); + return &e; +} + +void cpool::expandSignatures() +{ + int i; + int nsigs = 0; + int nreused = 0; + int first_sig = tag_base[CONSTANT_Signature]; + int sig_limit = tag_count[CONSTANT_Signature] + first_sig; + fillbytes buf; + buf.init(1 << 10); + CHECK; + for (i = first_sig; i < sig_limit; i++) + { + entry &e = entries[i]; + assert(e.tag == CONSTANT_Signature); + int refnum = 0; + bytes form = e.refs[refnum++]->asUtf8(); + buf.empty(); + for (int j = 0; j < (int)form.len; j++) + { + int c = form.ptr[j]; + buf.addByte(c); + if (c == 'L') + { + entry *cls = e.refs[refnum++]; + buf.append(cls->className()->asUtf8()); + } + } + assert(refnum == e.nrefs); + bytes &sig = buf.b; + + // try to find a pre-existing Utf8: + entry *&e2 = hashTabRef(CONSTANT_Utf8, sig); + if (e2 != nullptr) + { + assert(e2->isUtf8(sig)); + e.value.b = e2->value.b; + e.refs[0] = e2; + e.nrefs = 1; + nreused++; + } + else + { + // there is no other replacement; reuse this CP entry as a Utf8 + u->saveTo(e.value.b, sig); + e.tag = CONSTANT_Utf8; + e.nrefs = 0; + e2 = &e; + } + nsigs++; + } + buf.free(); + + // go expunge all references to remaining signatures: + for (i = 0; i < (int)nentries; i++) + { + entry &e = entries[i]; + for (int j = 0; j < e.nrefs; j++) + { + entry *&e2 = e.refs[j]; + if (e2 != nullptr && e2->tag == CONSTANT_Signature) + e2 = e2->refs[0]; + } + } +} + +void cpool::initMemberIndexes() +{ + // This function does NOT refer to any class schema. + // It is totally internal to the cpool. + int i, j; + + // Get the pre-existing indexes: + int nclasses = tag_count[CONSTANT_Class]; + entry *classes = tag_base[CONSTANT_Class] + entries; + int nfields = tag_count[CONSTANT_Fieldref]; + entry *fields = tag_base[CONSTANT_Fieldref] + entries; + int nmethods = tag_count[CONSTANT_Methodref]; + entry *methods = tag_base[CONSTANT_Methodref] + entries; + + int *field_counts = T_NEW(int, nclasses); + int *method_counts = T_NEW(int, nclasses); + cpindex *all_indexes = U_NEW(cpindex, nclasses * 2); + entry **field_ix = U_NEW(entry *, add_size(nfields, nclasses)); + entry **method_ix = U_NEW(entry *, add_size(nmethods, nclasses)); + + for (j = 0; j < nfields; j++) + { + entry &f = fields[j]; + i = f.memberClass()->inord; + assert(i < nclasses); + field_counts[i]++; + } + for (j = 0; j < nmethods; j++) + { + entry &m = methods[j]; + i = m.memberClass()->inord; + assert(i < nclasses); + method_counts[i]++; + } + + int fbase = 0, mbase = 0; + for (i = 0; i < nclasses; i++) + { + int fc = field_counts[i]; + int mc = method_counts[i]; + all_indexes[i * 2 + 0].init(fc, field_ix + fbase, CONSTANT_Fieldref + SUBINDEX_BIT); + all_indexes[i * 2 + 1].init(mc, method_ix + mbase, CONSTANT_Methodref + SUBINDEX_BIT); + // reuse field_counts and member_counts as fill pointers: + field_counts[i] = fbase; + method_counts[i] = mbase; + fbase += fc + 1; + mbase += mc + 1; + // (the +1 leaves a space between every subarray) + } + assert(fbase == nfields + nclasses); + assert(mbase == nmethods + nclasses); + + for (j = 0; j < nfields; j++) + { + entry &f = fields[j]; + i = f.memberClass()->inord; + field_ix[field_counts[i]++] = &f; + } + for (j = 0; j < nmethods; j++) + { + entry &m = methods[j]; + i = m.memberClass()->inord; + method_ix[method_counts[i]++] = &m; + } + + member_indexes = all_indexes; + + // Free intermediate buffers. + u->free_temps(); +} + +void entry::requestOutputIndex(cpool &cp, int req) +{ + assert(outputIndex <= NOT_REQUESTED); // must not have assigned indexes yet + if (tag == CONSTANT_Signature) + { + ref(0)->requestOutputIndex(cp, req); + return; + } + assert(req == REQUESTED || req == REQUESTED_LDC); + if (outputIndex != NOT_REQUESTED) + { + if (req == REQUESTED_LDC) + outputIndex = req; // this kind has precedence + return; + } + outputIndex = req; + // assert(!cp.outputEntries.contains(this)); + assert(tag != CONSTANT_Signature); + cp.outputEntries.add(this); + for (int j = 0; j < nrefs; j++) + { + ref(j)->requestOutputIndex(cp); + } +} + +void cpool::resetOutputIndexes() +{ + int i; + int noes = outputEntries.length(); + entry **oes = (entry **)outputEntries.base(); + for (i = 0; i < noes; i++) + { + entry &e = *oes[i]; + e.outputIndex = NOT_REQUESTED; + } + outputIndexLimit = 0; + outputEntries.empty(); +} + +static const byte TAG_ORDER[CONSTANT_Limit] = {0, 1, 0, 2, 3, 4, 5, 7, 6, 10, 11, 12, 9, 8}; + +extern "C" int outputEntry_cmp(const void *e1p, const void *e2p) +{ + // Sort entries according to the Pack200 rules for deterministic + // constant pool ordering. + // + // The four sort keys as follows, in order of decreasing importance: + // 1. ldc first, then non-ldc guys + // 2. normal cp_All entries by input order (i.e., address order) + // 3. after that, extra entries by lexical order (as in tag_extras[*]) + entry &e1 = *(entry *)*(void **)e1p; + entry &e2 = *(entry *)*(void **)e2p; + int oi1 = e1.outputIndex; + int oi2 = e2.outputIndex; + assert(oi1 == REQUESTED || oi1 == REQUESTED_LDC); + assert(oi2 == REQUESTED || oi2 == REQUESTED_LDC); + if (oi1 != oi2) + { + if (oi1 == REQUESTED_LDC) + return 0 - 1; + if (oi2 == REQUESTED_LDC) + return 1 - 0; + // Else fall through; neither is an ldc request. + } + if (e1.inord != NO_INORD || e2.inord != NO_INORD) + { + // One or both is normal. Use input order. + if (&e1 > &e2) + return 1 - 0; + if (&e1 < &e2) + return 0 - 1; + return 0; // equal pointers + } + // Both are extras. Sort by tag and then by value. + if (e1.tag != e2.tag) + { + return TAG_ORDER[e1.tag] - TAG_ORDER[e2.tag]; + } + // If the tags are the same, use string comparison. + return compare_Utf8_chars(e1.value.b, e2.value.b); +} + +void cpool::computeOutputIndexes() +{ + int i; + + int noes = outputEntries.length(); + entry **oes = (entry **)outputEntries.base(); + + // Sort the output constant pool into the order required by Pack200. + PTRLIST_QSORT(outputEntries, outputEntry_cmp); + + // Allocate a new index for each entry that needs one. + // We do this in two passes, one for LDC entries and one for the rest. + int nextIndex = 1; // always skip index #0 in output cpool + for (i = 0; i < noes; i++) + { + entry &e = *oes[i]; + assert(e.outputIndex == REQUESTED || e.outputIndex == REQUESTED_LDC); + e.outputIndex = nextIndex++; + if (e.isDoubleWord()) + nextIndex++; // do not use the next index + } + outputIndexLimit = nextIndex; +} + +// Unpacker Start + +const char str_tf[] = "true\0false"; +#undef STR_TRUE +#undef STR_FALSE +#define STR_TRUE (&str_tf[0]) +#define STR_FALSE (&str_tf[5]) + +const char *unpacker::get_option(const char *prop) +{ + if (prop == nullptr) + return nullptr; + if (strcmp(prop, UNPACK_DEFLATE_HINT) == 0) + { + return deflate_hint_or_zero == 0 ? nullptr : STR_TF(deflate_hint_or_zero > 0); +#ifdef HAVE_STRIP + } + else if (strcmp(prop, UNPACK_STRIP_COMPILE) == 0) + { + return STR_TF(strip_compile); + } + else if (strcmp(prop, UNPACK_STRIP_DEBUG) == 0) + { + return STR_TF(strip_debug); + } + else if (strcmp(prop, UNPACK_STRIP_JCOV) == 0) + { + return STR_TF(strip_jcov); +#endif /*HAVE_STRIP*/ + } + else if (strcmp(prop, UNPACK_REMOVE_PACKFILE) == 0) + { + return STR_TF(remove_packfile); + } + else if (strcmp(prop, DEBUG_VERBOSE) == 0) + { + return saveIntStr(verbose); + } + else if (strcmp(prop, UNPACK_MODIFICATION_TIME) == 0) + { + return (modification_time_or_zero == 0) ? nullptr + : saveIntStr(modification_time_or_zero); + } + else + { + return NULL; // unknown option ignore + } +} + +bool unpacker::set_option(const char *prop, const char *value) +{ + if (prop == NULL) + return false; + if (strcmp(prop, UNPACK_DEFLATE_HINT) == 0) + { + deflate_hint_or_zero = + ((value == nullptr || strcmp(value, "keep") == 0) ? 0 : BOOL_TF(value) ? +1 : -1); +#ifdef HAVE_STRIP + } + else if (strcmp(prop, UNPACK_STRIP_COMPILE) == 0) + { + strip_compile = STR_TF(value); + } + else if (strcmp(prop, UNPACK_STRIP_DEBUG) == 0) + { + strip_debug = STR_TF(value); + } + else if (strcmp(prop, UNPACK_STRIP_JCOV) == 0) + { + strip_jcov = STR_TF(value); +#endif /*HAVE_STRIP*/ + } + else if (strcmp(prop, UNPACK_REMOVE_PACKFILE) == 0) + { + remove_packfile = STR_TF(value); + } + else if (strcmp(prop, DEBUG_VERBOSE) == 0) + { + verbose = (value == nullptr) ? 0 : atoi(value); + } + else if (strcmp(prop, UNPACK_MODIFICATION_TIME) == 0) + { + if (value == nullptr || (strcmp(value, "keep") == 0)) + { + modification_time_or_zero = 0; + } + else if (strcmp(value, "now") == 0) + { + time_t now; + time(&now); + modification_time_or_zero = (int)now; + } + else + { + modification_time_or_zero = atoi(value); + if (modification_time_or_zero == 0) + modification_time_or_zero = 1; // make non-zero + } + } + else + { + return false; // unknown option ignore + } + return true; +} + +// Deallocate all internal storage and reset to a clean state. +// Do not disturb any input or output connections, including +// infileptr, infileno, inbytes, read_input_fn, jarout, or errstrm. +// Do not reset any unpack options. +void unpacker::reset() +{ + bytes_read_before_reset += bytes_read; + bytes_written_before_reset += bytes_written; + files_written_before_reset += files_written; + classes_written_before_reset += classes_written; + segments_read_before_reset += 1; + if (verbose >= 2) + { + fprintf(stderr, "After segment %d, " LONG_LONG_FORMAT + " bytes read and " LONG_LONG_FORMAT " bytes written.\n", + segments_read_before_reset - 1, bytes_read_before_reset, + bytes_written_before_reset); + fprintf(stderr, + "After segment %d, %d files (of which %d are classes) written to output.\n", + segments_read_before_reset - 1, files_written_before_reset, + classes_written_before_reset); + if (archive_next_count != 0) + { + fprintf(stderr, "After segment %d, %d segment%s remaining (estimated).\n", + segments_read_before_reset - 1, archive_next_count, + archive_next_count == 1 ? "" : "s"); + } + } + + unpacker save_u = (*this); // save bytewise image + infileptr = nullptr; // make asserts happy + jarout = nullptr; // do not close the output jar + gzin = nullptr; // do not close the input gzip stream + this->free(); + this->init(read_input_fn); + +// restore selected interface state: +#define SAVE(x) this->x = save_u.x + SAVE(infileptr); // buffered + SAVE(infileno); // unbuffered + SAVE(inbytes); // direct + SAVE(jarout); + SAVE(gzin); + SAVE(verbose); // verbose level, 0 means no output + SAVE(strip_compile); + SAVE(strip_debug); + SAVE(strip_jcov); + SAVE(remove_packfile); + SAVE(deflate_hint_or_zero); // ==0 means not set, otherwise -1 or 1 + SAVE(modification_time_or_zero); + SAVE(bytes_read_before_reset); + SAVE(bytes_written_before_reset); + SAVE(files_written_before_reset); + SAVE(classes_written_before_reset); + SAVE(segments_read_before_reset); +#undef SAVE + // Note: If we use strip_names, watch out: They get nuked here. +} + +void unpacker::init(read_input_fn_t input_fn) +{ + int i; + BYTES_OF(*this).clear(); + this->u = this; // self-reference for U_NEW macro + read_input_fn = input_fn; + all_bands = band::makeBands(this); + // Make a default jar buffer; caller may safely overwrite it. + jarout = U_NEW(jar, 1); + jarout->init(this); + for (i = 0; i < ATTR_CONTEXT_LIMIT; i++) + attr_defs[i].u = u; // set up outer ptr +} + +const char *unpacker::get_abort_message() +{ + return abort_message; +} + +void unpacker::dump_options() +{ + static const char *opts[] = { + UNPACK_DEFLATE_HINT, +#ifdef HAVE_STRIP + UNPACK_STRIP_COMPILE, UNPACK_STRIP_DEBUG, UNPACK_STRIP_JCOV, +#endif /*HAVE_STRIP*/ + UNPACK_REMOVE_PACKFILE, DEBUG_VERBOSE, UNPACK_MODIFICATION_TIME, nullptr}; + for (int i = 0; opts[i] != nullptr; i++) + { + const char *str = get_option(opts[i]); + if (str == nullptr) + { + if (verbose == 0) + continue; + str = "(not set)"; + } + fprintf(stderr, "%s=%s\n", opts[i], str); + } +} + +// Usage: unpack a byte buffer +// packptr is a reference to byte buffer containing a +// packed file and len is the length of the buffer. +// If nullptr, the callback is used to fill an internal buffer. +void unpacker::start(void *packptr, size_t len) +{ + if (packptr != nullptr && len != 0) + { + inbytes.set((byte *)packptr, len); + } + read_bands(); +} + +void unpacker::check_options() +{ + const char *strue = "true"; + const char *sfalse = "false"; + if (deflate_hint_or_zero != 0) + { + bool force_deflate_hint = (deflate_hint_or_zero > 0); + if (force_deflate_hint) + default_file_options |= FO_DEFLATE_HINT; + else + default_file_options &= ~FO_DEFLATE_HINT; + // Turn off per-file deflate hint by force. + suppress_file_options |= FO_DEFLATE_HINT; + } + if (modification_time_or_zero != 0) + { + default_file_modtime = modification_time_or_zero; + // Turn off per-file modtime by force. + archive_options &= ~AO_HAVE_FILE_MODTIME; + } + // %%% strip_compile, etc... +} + +// classfile writing + +void unpacker::reset_cur_classfile() +{ + // set defaults + cur_class_minver = default_class_minver; + cur_class_majver = default_class_majver; + + // reset constant pool state + cp.resetOutputIndexes(); + + // reset fixups + class_fixup_type.empty(); + class_fixup_offset.empty(); + class_fixup_ref.empty(); + requested_ics.empty(); +} + +cpindex *cpool::getKQIndex() +{ + char ch = '?'; + if (u->cur_descr != nullptr) + { + entry *type = u->cur_descr->descrType(); + ch = type->value.b.ptr[0]; + } + byte tag = CONSTANT_Integer; + switch (ch) + { + case 'L': + tag = CONSTANT_String; + break; + case 'I': + tag = CONSTANT_Integer; + break; + case 'J': + tag = CONSTANT_Long; + break; + case 'F': + tag = CONSTANT_Float; + break; + case 'D': + tag = CONSTANT_Double; + break; + case 'B': + case 'S': + case 'C': + case 'Z': + tag = CONSTANT_Integer; + break; + default: + abort("bad KQ reference"); + break; + } + return getIndex(tag); +} + +uint unpacker::to_bci(uint bii) +{ + uint len = bcimap.length(); + uint *map = (uint *)bcimap.base(); + assert(len > 0); // must be initialized before using to_bci + if (bii < len) + return map[bii]; + // Else it's a fractional or out-of-range BCI. + uint key = bii - len; + for (int i = len;; i--) + { + if (map[i - 1] - (i - 1) <= key) + break; + else + --bii; + } + return bii; +} + +void unpacker::put_stackmap_type() +{ + int tag = code_StackMapTable_T.getByte(); + putu1(tag); + switch (tag) + { + case 7: // (7) [RCH] + putref(code_StackMapTable_RC.getRef()); + break; + case 8: // (8) [PH] + putu2(to_bci(code_StackMapTable_P.getInt())); + break; + } +} + +// Functions for writing code. + +void unpacker::put_label(int curIP, int size) +{ + code_fixup_type.addByte(size); + code_fixup_offset.add((int)put_empty(size)); + code_fixup_source.add(curIP); +} + +inline // called exactly once => inline + void +unpacker::write_bc_ops() +{ + bcimap.empty(); + code_fixup_type.empty(); + code_fixup_offset.empty(); + code_fixup_source.empty(); + + band *bc_which; + + byte *opptr = bc_codes.curRP(); + // No need for oplimit, since the codes are pre-counted. + + size_t codeBase = wpoffset(); + + bool isAload; // copy-out result + int origBC; + + entry *thisClass = cur_class; + entry *superClass = cur_super; + entry *newClass = nullptr; // class of last _new opcode + + // overwrite any prior index on these bands; it changes w/ current class: + bc_thisfield.setIndex(cp.getFieldIndex(thisClass)); + bc_thismethod.setIndex(cp.getMethodIndex(thisClass)); + if (superClass != nullptr) + { + bc_superfield.setIndex(cp.getFieldIndex(superClass)); + bc_supermethod.setIndex(cp.getMethodIndex(superClass)); + } + + for (int curIP = 0;; curIP++) + { + int curPC = (int)(wpoffset() - codeBase); + bcimap.add(curPC); + ensure_put_space(10); // covers most instrs w/o further bounds check + int bc = *opptr++ & 0xFF; + + putu1_fast(bc); + // Note: See '--wp' below for pseudo-bytecodes like bc_end_marker. + + bool isWide = false; + if (bc == bc_wide) + { + bc = *opptr++ & 0xFF; + putu1_fast(bc); + isWide = true; + } + switch (bc) + { + case bc_end_marker: + --wp; // not really part of the code + assert(opptr <= bc_codes.maxRP()); + bc_codes.curRP() = opptr; // advance over this in bc_codes + goto doneScanningMethod; + case bc_tableswitch: // apc: (df, lo, hi, (hi-lo+1)*(label)) + case bc_lookupswitch: // apc: (df, nc, nc*(case, label)) + { + int caseCount = bc_case_count.getInt(); + while (((wpoffset() - codeBase) % 4) != 0) + putu1_fast(0); + ensure_put_space(30 + caseCount * 8); + put_label(curIP, 4); // int df = bc_label.getInt(); + if (bc == bc_tableswitch) + { + int lo = bc_case_value.getInt(); + int hi = lo + caseCount - 1; + putu4(lo); + putu4(hi); + for (int j = 0; j < caseCount; j++) + { + put_label(curIP, 4); // int lVal = bc_label.getInt(); + // int cVal = lo + j; + } + } + else + { + putu4(caseCount); + for (int j = 0; j < caseCount; j++) + { + int cVal = bc_case_value.getInt(); + putu4(cVal); + put_label(curIP, 4); // int lVal = bc_label.getInt(); + } + } + assert((int)to_bci(curIP) == curPC); + continue; + } + case bc_iinc: + { + int local = bc_local.getInt(); + int delta = (isWide ? bc_short : bc_byte).getInt(); + if (isWide) + { + putu2(local); + putu2(delta); + } + else + { + putu1_fast(local); + putu1_fast(delta); + } + continue; + } + case bc_sipush: + { + int val = bc_short.getInt(); + putu2(val); + continue; + } + case bc_bipush: + case bc_newarray: + { + int val = bc_byte.getByte(); + putu1_fast(val); + continue; + } + case bc_ref_escape: + { + // Note that insnMap has one entry for this. + --wp; // not really part of the code + int size = bc_escrefsize.getInt(); + entry *ref = bc_escref.getRefN(); + CHECK; + switch (size) + { + case 1: + putu1ref(ref); + break; + case 2: + putref(ref); + break; + default: + assert(false); + } + continue; + } + case bc_byte_escape: + { + // Note that insnMap has one entry for all these bytes. + --wp; // not really part of the code + int size = bc_escsize.getInt(); + ensure_put_space(size); + for (int j = 0; j < size; j++) + putu1_fast(bc_escbyte.getByte()); + continue; + } + default: + if (is_invoke_init_op(bc)) + { + origBC = bc_invokespecial; + entry *classRef; + switch (bc - _invokeinit_op) + { + case _invokeinit_self_option: + classRef = thisClass; + break; + case _invokeinit_super_option: + classRef = superClass; + break; + default: + assert(bc == _invokeinit_op + _invokeinit_new_option); + case _invokeinit_new_option: + classRef = newClass; + break; + } + wp[-1] = origBC; // overwrite with origBC + int coding = bc_initref.getInt(); + // Find the nth overloading of in classRef. + entry *ref = nullptr; + cpindex *ix = (classRef == nullptr) ? nullptr : cp.getMethodIndex(classRef); + for (int j = 0, which_init = 0;; j++) + { + ref = (ix == nullptr) ? nullptr : ix->get(j); + if (ref == nullptr) + break; // oops, bad input + assert(ref->tag == CONSTANT_Methodref); + if (ref->memberDescr()->descrName() == cp.sym[cpool::s_lt_init_gt]) + { + if (which_init++ == coding) + break; + } + } + putref(ref); + continue; + } + bc_which = ref_band_for_self_op(bc, isAload, origBC); + if (bc_which != nullptr) + { + if (!isAload) + { + wp[-1] = origBC; // overwrite with origBC + } + else + { + wp[-1] = bc_aload_0; // overwrite with _aload_0 + // Note: insnMap keeps the _aload_0 separate. + bcimap.add(++curPC); + ++curIP; + putu1_fast(origBC); + } + entry *ref = bc_which->getRef(); + CHECK; + putref(ref); + continue; + } + if (is_branch_op(bc)) + { + // int lVal = bc_label.getInt(); + if (bc < bc_goto_w) + { + put_label(curIP, 2); // putu2(lVal & 0xFFFF); + } + else + { + assert(bc <= bc_jsr_w); + put_label(curIP, 4); // putu4(lVal); + } + assert((int)to_bci(curIP) == curPC); + continue; + } + bc_which = ref_band_for_op(bc); + if (bc_which != nullptr) + { + entry *ref = bc_which->getRefCommon(bc_which->ix, bc_which->nullOK); + CHECK; + if (ref == nullptr && bc_which == &bc_classref) + { + // Shorthand for class self-references. + ref = thisClass; + } + origBC = bc; + switch (bc) + { + case bc_ildc: + case bc_cldc: + case bc_fldc: + case bc_aldc: + origBC = bc_ldc; + break; + case bc_ildc_w: + case bc_cldc_w: + case bc_fldc_w: + case bc_aldc_w: + origBC = bc_ldc_w; + break; + case bc_lldc2_w: + case bc_dldc2_w: + origBC = bc_ldc2_w; + break; + case bc_new: + newClass = ref; + break; + } + wp[-1] = origBC; // overwrite with origBC + if (origBC == bc_ldc) + { + putu1ref(ref); + } + else + { + putref(ref); + } + if (origBC == bc_multianewarray) + { + // Copy the trailing byte also. + int val = bc_byte.getByte(); + putu1_fast(val); + } + else if (origBC == bc_invokeinterface) + { + int argSize = ref->memberDescr()->descrType()->typeSize(); + putu1_fast(1 + argSize); + putu1_fast(0); + } + continue; + } + if (is_local_slot_op(bc)) + { + int local = bc_local.getInt(); + if (isWide) + { + putu2(local); + if (bc == bc_iinc) + { + int iVal = bc_short.getInt(); + putu2(iVal); + } + } + else + { + putu1_fast(local); + if (bc == bc_iinc) + { + int iVal = bc_byte.getByte(); + putu1_fast(iVal); + } + } + continue; + } + // Random bytecode. Just copy it. + assert(bc < bc_bytecode_limit); + } + } +doneScanningMethod: +{ +} + // bcimap.add(curPC); // PC limit is already also in map, from bc_end_marker + + // Armed with a bcimap, we can now fix up all the labels. + for (int i = 0; i < (int)code_fixup_type.size(); i++) + { + int type = code_fixup_type.getByte(i); + byte *bp = wp_at(code_fixup_offset.get(i)); + int curIP = code_fixup_source.get(i); + int destIP = curIP + bc_label.getInt(); + int span = to_bci(destIP) - to_bci(curIP); + switch (type) + { + case 2: + putu2_at(bp, (ushort)span); + break; + case 4: + putu4_at(bp, span); + break; + default: + assert(false); + } + } +} + +inline // called exactly once => inline + void +unpacker::write_code() +{ + int j; + + int max_stack, max_locals, handler_count, cflags; + get_code_header(max_stack, max_locals, handler_count, cflags); + + if (max_stack < 0) + max_stack = code_max_stack.getInt(); + if (max_locals < 0) + max_locals = code_max_na_locals.getInt(); + if (handler_count < 0) + handler_count = code_handler_count.getInt(); + + int siglen = cur_descr->descrType()->typeSize(); + CHECK; + if ((cur_descr_flags & ACC_STATIC) == 0) + siglen++; + max_locals += siglen; + + putu2(max_stack); + putu2(max_locals); + size_t bcbase = put_empty(4); + + // Write the bytecodes themselves. + write_bc_ops(); + CHECK; + + byte *bcbasewp = wp_at(bcbase); + putu4_at(bcbasewp, (int)(wp - (bcbasewp + 4))); // size of code attr + + putu2(handler_count); + for (j = 0; j < handler_count; j++) + { + int bii = code_handler_start_P.getInt(); + putu2(to_bci(bii)); + bii += code_handler_end_PO.getInt(); + putu2(to_bci(bii)); + bii += code_handler_catch_PO.getInt(); + putu2(to_bci(bii)); + putref(code_handler_class_RCN.getRefN()); + CHECK; + } + + julong indexBits = cflags; + if (cflags < 0) + { + bool haveLongFlags = attr_defs[ATTR_CONTEXT_CODE].haveLongFlags(); + indexBits = code_flags_hi.getLong(code_flags_lo, haveLongFlags); + } + write_attrs(ATTR_CONTEXT_CODE, indexBits); +} + +int unpacker::write_attrs(int attrc, julong indexBits) +{ + CHECK_0; + if (indexBits == 0) + { + // Quick short-circuit. + putu2(0); + return 0; + } + + attr_definitions &ad = attr_defs[attrc]; + + int i, j, j2, idx, count; + + int oiCount = 0; + if (ad.isPredefined(X_ATTR_OVERFLOW) && (indexBits & ((julong)1 << X_ATTR_OVERFLOW)) != 0) + { + indexBits -= ((julong)1 << X_ATTR_OVERFLOW); + oiCount = ad.xxx_attr_count().getInt(); + } + + int bitIndexes[X_ATTR_LIMIT_FLAGS_HI]; + int biCount = 0; + + // Fill bitIndexes with index bits, in order. + for (idx = 0; indexBits != 0; idx++, indexBits >>= 1) + { + if ((indexBits & 1) != 0) + bitIndexes[biCount++] = idx; + } + assert(biCount <= (int)lengthof(bitIndexes)); + + // Write a provisional attribute count, perhaps to be corrected later. + int naOffset = (int)wpoffset(); + int na0 = biCount + oiCount; + putu2(na0); + + int na = 0; + for (i = 0; i < na0; i++) + { + if (i < biCount) + idx = bitIndexes[i]; + else + idx = ad.xxx_attr_indexes().getInt(); + assert(ad.isIndex(idx)); + entry *aname = nullptr; + entry *ref; // scratch + size_t abase = put_empty(2 + 4); + CHECK_0; + if (idx < (int)ad.flag_limit && ad.isPredefined(idx)) + { + // Switch on the attrc and idx simultaneously. + switch (ADH_BYTE(attrc, idx)) + { + + case ADH_BYTE(ATTR_CONTEXT_CLASS, X_ATTR_OVERFLOW) : + case ADH_BYTE(ATTR_CONTEXT_FIELD, X_ATTR_OVERFLOW) : + case ADH_BYTE(ATTR_CONTEXT_METHOD, X_ATTR_OVERFLOW) : + case ADH_BYTE(ATTR_CONTEXT_CODE, X_ATTR_OVERFLOW) : + // no attribute at all, so back up on this one + wp = wp_at(abase); + continue; + + case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_ClassFile_version) : + cur_class_minver = class_ClassFile_version_minor_H.getInt(); + cur_class_majver = class_ClassFile_version_major_H.getInt(); + // back up; not a real attribute + wp = wp_at(abase); + continue; + + case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_InnerClasses) : + // note the existence of this attr, but save for later + if (cur_class_has_local_ics) + abort("too many InnerClasses attrs"); + cur_class_has_local_ics = true; + wp = wp_at(abase); + continue; + + case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_SourceFile) : + aname = cp.sym[cpool::s_SourceFile]; + ref = class_SourceFile_RUN.getRefN(); + CHECK_0; + if (ref == nullptr) + { + bytes &n = cur_class->ref(0)->value.b; + // parse n = (/)*?($)* + int pkglen = lastIndexOf(SLASH_MIN, SLASH_MAX, n, (int)n.len) + 1; + bytes prefix = n.slice(pkglen, n.len); + for (;;) + { + // Work backwards, finding all '$', '#', etc. + int dollar = + lastIndexOf(DOLLAR_MIN, DOLLAR_MAX, prefix, (int)prefix.len); + if (dollar < 0) + break; + prefix = prefix.slice(0, dollar); + } + const char *suffix = ".java"; + int len = (int)(prefix.len + strlen(suffix)); + bytes name; + name.set(T_NEW(byte, add_size(len, 1)), len); + name.strcat(prefix).strcat(suffix); + ref = cp.ensureUtf8(name); + } + putref(ref); + break; + + case ADH_BYTE(ATTR_CONTEXT_CLASS, CLASS_ATTR_EnclosingMethod) : + aname = cp.sym[cpool::s_EnclosingMethod]; + putref(class_EnclosingMethod_RC.getRefN()); + putref(class_EnclosingMethod_RDN.getRefN()); + break; + + case ADH_BYTE(ATTR_CONTEXT_FIELD, FIELD_ATTR_ConstantValue) : + aname = cp.sym[cpool::s_ConstantValue]; + putref(field_ConstantValue_KQ.getRefUsing(cp.getKQIndex())); + break; + + case ADH_BYTE(ATTR_CONTEXT_METHOD, METHOD_ATTR_Code) : + aname = cp.sym[cpool::s_Code]; + write_code(); + break; + + case ADH_BYTE(ATTR_CONTEXT_METHOD, METHOD_ATTR_Exceptions) : + aname = cp.sym[cpool::s_Exceptions]; + putu2(count = method_Exceptions_N.getInt()); + for (j = 0; j < count; j++) + { + putref(method_Exceptions_RC.getRefN()); + } + break; + + case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_StackMapTable) : + aname = cp.sym[cpool::s_StackMapTable]; + // (keep this code aligned with its brother in unpacker::read_attrs) + putu2(count = code_StackMapTable_N.getInt()); + for (j = 0; j < count; j++) + { + int tag = code_StackMapTable_frame_T.getByte(); + putu1(tag); + if (tag <= 127) + { + // (64-127) [(2)] + if (tag >= 64) + put_stackmap_type(); + } + else if (tag <= 251) + { + // (247) [(1)(2)] + // (248-251) [(1)] + if (tag >= 247) + putu2(code_StackMapTable_offset.getInt()); + if (tag == 247) + put_stackmap_type(); + } + else if (tag <= 254) + { + // (252) [(1)(2)] + // (253) [(1)(2)(2)] + // (254) [(1)(2)(2)(2)] + putu2(code_StackMapTable_offset.getInt()); + for (int k = (tag - 251); k > 0; k--) + { + put_stackmap_type(); + } + } + else + { + // (255) [(1)NH[(2)]NH[(2)]] + putu2(code_StackMapTable_offset.getInt()); + putu2(j2 = code_StackMapTable_local_N.getInt()); + while (j2-- > 0) + put_stackmap_type(); + putu2(j2 = code_StackMapTable_stack_N.getInt()); + while (j2-- > 0) + put_stackmap_type(); + } + } + break; + + case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_LineNumberTable) : + aname = cp.sym[cpool::s_LineNumberTable]; + putu2(count = code_LineNumberTable_N.getInt()); + for (j = 0; j < count; j++) + { + putu2(to_bci(code_LineNumberTable_bci_P.getInt())); + putu2(code_LineNumberTable_line.getInt()); + } + break; + + case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_LocalVariableTable) : + aname = cp.sym[cpool::s_LocalVariableTable]; + putu2(count = code_LocalVariableTable_N.getInt()); + for (j = 0; j < count; j++) + { + int bii = code_LocalVariableTable_bci_P.getInt(); + int bci = to_bci(bii); + putu2(bci); + bii += code_LocalVariableTable_span_O.getInt(); + putu2(to_bci(bii) - bci); + putref(code_LocalVariableTable_name_RU.getRefN()); + putref(code_LocalVariableTable_type_RS.getRefN()); + putu2(code_LocalVariableTable_slot.getInt()); + } + break; + + case ADH_BYTE(ATTR_CONTEXT_CODE, CODE_ATTR_LocalVariableTypeTable) : + aname = cp.sym[cpool::s_LocalVariableTypeTable]; + putu2(count = code_LocalVariableTypeTable_N.getInt()); + for (j = 0; j < count; j++) + { + int bii = code_LocalVariableTypeTable_bci_P.getInt(); + int bci = to_bci(bii); + putu2(bci); + bii += code_LocalVariableTypeTable_span_O.getInt(); + putu2(to_bci(bii) - bci); + putref(code_LocalVariableTypeTable_name_RU.getRefN()); + putref(code_LocalVariableTypeTable_type_RS.getRefN()); + putu2(code_LocalVariableTypeTable_slot.getInt()); + } + break; + + case ADH_BYTE(ATTR_CONTEXT_CLASS, X_ATTR_Signature) : + aname = cp.sym[cpool::s_Signature]; + putref(class_Signature_RS.getRefN()); + break; + + case ADH_BYTE(ATTR_CONTEXT_FIELD, X_ATTR_Signature) : + aname = cp.sym[cpool::s_Signature]; + putref(field_Signature_RS.getRefN()); + break; + + case ADH_BYTE(ATTR_CONTEXT_METHOD, X_ATTR_Signature) : + aname = cp.sym[cpool::s_Signature]; + putref(method_Signature_RS.getRefN()); + break; + + case ADH_BYTE(ATTR_CONTEXT_CLASS, X_ATTR_Deprecated) : + case ADH_BYTE(ATTR_CONTEXT_FIELD, X_ATTR_Deprecated) : + case ADH_BYTE(ATTR_CONTEXT_METHOD, X_ATTR_Deprecated) : + aname = cp.sym[cpool::s_Deprecated]; + // no data + break; + } + } + + if (aname == nullptr) + { + // Unparse a compressor-defined attribute. + layout_definition *lo = ad.getLayout(idx); + if (lo == nullptr) + { + abort("bad layout index"); + break; + } + assert((int)lo->idx == idx); + aname = lo->nameEntry; + if (aname == nullptr) + { + bytes nameb; + nameb.set(lo->name); + aname = cp.ensureUtf8(nameb); + // Cache the name entry for next time. + lo->nameEntry = aname; + } + // Execute all the layout elements. + band **bands = lo->bands(); + if (lo->hasCallables()) + { + band &cble = *bands[0]; + assert(cble.le_kind == EK_CBLE); + bands = cble.le_body; + } + putlayout(bands); + } + + if (aname == nullptr) + abort("bad attribute index"); + CHECK_0; + + byte *wp1 = wp; + wp = wp_at(abase); + + // DTRT if this attr is on the strip-list. + // (Note that we emptied the data out of the band first.) + if (ad.strip_names.contains(aname)) + { + continue; + } + + // patch the name and length + putref(aname); + putu4((int)(wp1 - (wp + 4))); // put the attr size + wp = wp1; + na++; // count the attrs actually written + } + + if (na != na0) + // Refresh changed count. + putu2_at(wp_at(naOffset), na); + return na; +} + +void unpacker::write_members(int num, int attrc) +{ + CHECK; + attr_definitions &ad = attr_defs[attrc]; + band &member_flags_hi = ad.xxx_flags_hi(); + band &member_flags_lo = ad.xxx_flags_lo(); + band &member_descr = (&member_flags_hi)[e_field_descr - e_field_flags_hi]; + assert(endsWith(member_descr.name, "_descr")); + assert(endsWith(member_flags_lo.name, "_flags_lo")); + assert(endsWith(member_flags_lo.name, "_flags_lo")); + bool haveLongFlags = ad.haveLongFlags(); + + putu2(num); + julong indexMask = attr_defs[attrc].flagIndexMask(); + for (int i = 0; i < num; i++) + { + julong mflags = member_flags_hi.getLong(member_flags_lo, haveLongFlags); + entry *mdescr = member_descr.getRef(); + cur_descr = mdescr; + putu2(cur_descr_flags = (ushort)(mflags & ~indexMask)); + CHECK; + putref(mdescr->descrName()); + putref(mdescr->descrType()); + write_attrs(attrc, (mflags & indexMask)); + CHECK; + } + cur_descr = nullptr; +} + +extern "C" int raw_address_cmp(const void *p1p, const void *p2p) +{ + void *p1 = *(void **)p1p; + void *p2 = *(void **)p2p; + return (p1 > p2) ? 1 : (p1 < p2) ? -1 : 0; +} + +void unpacker::write_classfile_tail() +{ + cur_classfile_tail.empty(); + set_output(&cur_classfile_tail); + + int i, num; + + attr_definitions &ad = attr_defs[ATTR_CONTEXT_CLASS]; + + bool haveLongFlags = ad.haveLongFlags(); + julong kflags = class_flags_hi.getLong(class_flags_lo, haveLongFlags); + julong indexMask = ad.flagIndexMask(); + + cur_class = class_this.getRef(); + cur_super = class_super.getRef(); + + CHECK; + + if (cur_super == cur_class) + cur_super = nullptr; + // special representation for java/lang/Object + + putu2((ushort)(kflags & ~indexMask)); + putref(cur_class); + putref(cur_super); + + putu2(num = class_interface_count.getInt()); + for (i = 0; i < num; i++) + { + putref(class_interface.getRef()); + } + + write_members(class_field_count.getInt(), ATTR_CONTEXT_FIELD); + write_members(class_method_count.getInt(), ATTR_CONTEXT_METHOD); + CHECK; + + cur_class_has_local_ics = false; // may be set true by write_attrs + + int naOffset = (int)wpoffset(); + int na = write_attrs(ATTR_CONTEXT_CLASS, (kflags & indexMask)); + +// at the very last, choose which inner classes (if any) pertain to k: +#ifdef ASSERT + for (i = 0; i < ic_count; i++) + { + assert(!ics[i].requested); + } +#endif + // First, consult the global table and the local constant pool, + // and decide on the globally implied inner classes. + // (Note that we read the cpool's outputIndex fields, but we + // do not yet write them, since the local IC attribute might + // reverse a global decision to declare an IC.) + assert(requested_ics.length() == 0); // must start out empty + // Always include all members of the current class. + for (inner_class *child = cp.getFirstChildIC(cur_class); child != nullptr; + child = cp.getNextChildIC(child)) + { + child->requested = true; + requested_ics.add(child); + } + // And, for each inner class mentioned in the constant pool, + // include it and all its outers. + int noes = cp.outputEntries.length(); + entry **oes = (entry **)cp.outputEntries.base(); + for (i = 0; i < noes; i++) + { + entry &e = *oes[i]; + if (e.tag != CONSTANT_Class) + continue; // wrong sort + for (inner_class *ic = cp.getIC(&e); ic != nullptr; ic = cp.getIC(ic->outer)) + { + if (ic->requested) + break; // already processed + ic->requested = true; + requested_ics.add(ic); + } + } + int local_ics = requested_ics.length(); + // Second, consult a local attribute (if any) and adjust the global set. + inner_class *extra_ics = nullptr; + int num_extra_ics = 0; + if (cur_class_has_local_ics) + { + // adjust the set of ICs by symmetric set difference w/ the locals + num_extra_ics = class_InnerClasses_N.getInt(); + if (num_extra_ics == 0) + { + // Explicit zero count has an irregular meaning: It deletes the attr. + local_ics = 0; // (short-circuit all tests of requested bits) + } + else + { + extra_ics = T_NEW(inner_class, num_extra_ics); + // Note: extra_ics will be freed up by next call to get_next_file(). + } + } + for (i = 0; i < num_extra_ics; i++) + { + inner_class &extra_ic = extra_ics[i]; + extra_ic.inner = class_InnerClasses_RC.getRef(); + CHECK; + // Find the corresponding equivalent global IC: + inner_class *global_ic = cp.getIC(extra_ic.inner); + int flags = class_InnerClasses_F.getInt(); + if (flags == 0) + { + // The extra IC is simply a copy of a global IC. + if (global_ic == nullptr) + { + abort("bad reference to inner class"); + break; + } + extra_ic = (*global_ic); // fill in rest of fields + } + else + { + flags &= ~ACC_IC_LONG_FORM; // clear high bit if set to get clean zero + extra_ic.flags = flags; + extra_ic.outer = class_InnerClasses_outer_RCN.getRefN(); + extra_ic.name = class_InnerClasses_name_RUN.getRefN(); + // Detect if this is an exact copy of the global tuple. + if (global_ic != nullptr) + { + if (global_ic->flags != extra_ic.flags || global_ic->outer != extra_ic.outer || + global_ic->name != extra_ic.name) + { + global_ic = nullptr; // not really the same, so break the link + } + } + } + if (global_ic != nullptr && global_ic->requested) + { + // This local repetition reverses the globally implied request. + global_ic->requested = false; + extra_ic.requested = false; + local_ics -= 1; + } + else + { + // The global either does not exist, or is not yet requested. + extra_ic.requested = true; + local_ics += 1; + } + } + // Finally, if there are any that survived, put them into an attribute. + // (Note that a zero-count attribute is always deleted.) + // The putref calls below will tell the constant pool to add any + // necessary local CP references to support the InnerClasses attribute. + // This step must be the last round of additions to the local CP. + if (local_ics > 0) + { + // append the new attribute: + putref(cp.sym[cpool::s_InnerClasses]); + putu4(2 + 2 * 4 * local_ics); + putu2(local_ics); + PTRLIST_QSORT(requested_ics, raw_address_cmp); + int num_global_ics = requested_ics.length(); + for (i = -num_global_ics; i < num_extra_ics; i++) + { + inner_class *ic; + if (i < 0) + ic = (inner_class *)requested_ics.get(num_global_ics + i); + else + ic = &extra_ics[i]; + if (ic->requested) + { + putref(ic->inner); + putref(ic->outer); + putref(ic->name); + putu2(ic->flags); + } + } + assert(local_ics == 0); // must balance + putu2_at(wp_at(naOffset), ++na); // increment class attr count + } + + // Tidy up global 'requested' bits: + for (i = requested_ics.length(); --i >= 0;) + { + inner_class *ic = (inner_class *)requested_ics.get(i); + ic->requested = false; + } + requested_ics.empty(); + + CHECK; + close_output(); + + // rewrite CP references in the tail + cp.computeOutputIndexes(); + int nextref = 0; + for (i = 0; i < (int)class_fixup_type.size(); i++) + { + int type = class_fixup_type.getByte(i); + byte *fixp = wp_at(class_fixup_offset.get(i)); + entry *e = (entry *)class_fixup_ref.get(nextref++); + int idx = e->getOutputIndex(); + switch (type) + { + case 1: + putu1_at(fixp, idx); + break; + case 2: + putu2_at(fixp, idx); + break; + default: + assert(false); // should not reach here + } + } + CHECK; +} + +void unpacker::write_classfile_head() +{ + cur_classfile_head.empty(); + set_output(&cur_classfile_head); + + putu4(JAVA_MAGIC); + putu2(cur_class_minver); + putu2(cur_class_majver); + putu2(cp.outputIndexLimit); + + int checkIndex = 1; + int noes = cp.outputEntries.length(); + entry **oes = (entry **)cp.outputEntries.base(); + for (int i = 0; i < noes; i++) + { + entry &e = *oes[i]; + assert(e.getOutputIndex() == checkIndex++); + byte tag = e.tag; + assert(tag != CONSTANT_Signature); + putu1(tag); + switch (tag) + { + case CONSTANT_Utf8: + putu2((int)e.value.b.len); + put_bytes(e.value.b); + break; + case CONSTANT_Integer: + case CONSTANT_Float: + putu4(e.value.i); + break; + case CONSTANT_Long: + case CONSTANT_Double: + putu8(e.value.l); + assert(checkIndex++); + break; + case CONSTANT_Class: + case CONSTANT_String: + // just write the ref + putu2(e.refs[0]->getOutputIndex()); + break; + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + case CONSTANT_NameandType: + putu2(e.refs[0]->getOutputIndex()); + putu2(e.refs[1]->getOutputIndex()); + break; + default: + abort(ERROR_INTERNAL); + } + } + close_output(); +} + +unpacker::file *unpacker::get_next_file() +{ + CHECK_0; + free_temps(); + if (files_remaining == 0) + { + // Leave a clue that we're exhausted. + cur_file.name = nullptr; + cur_file.size = 0; + if (archive_size != 0) + { + julong predicted_size = unsized_bytes_read + archive_size; + if (predicted_size != bytes_read) + abort("archive header had incorrect size"); + } + return nullptr; + } + files_remaining -= 1; + assert(files_written < file_count || classes_written < class_count); + cur_file.name = ""; + cur_file.size = 0; + cur_file.modtime = default_file_modtime; + cur_file.options = default_file_options; + cur_file.data[0].set(nullptr, 0); + cur_file.data[1].set(nullptr, 0); + if (files_written < file_count) + { + entry *e = file_name.getRef(); + CHECK_0; + cur_file.name = e->utf8String(); + bool haveLongSize = ((archive_options & AO_HAVE_FILE_SIZE_HI) != 0); + cur_file.size = file_size_hi.getLong(file_size_lo, haveLongSize); + if ((archive_options & AO_HAVE_FILE_MODTIME) != 0) + cur_file.modtime += file_modtime.getInt(); // relative to archive modtime + if ((archive_options & AO_HAVE_FILE_OPTIONS) != 0) + cur_file.options |= file_options.getInt() & ~suppress_file_options; + } + else if (classes_written < class_count) + { + // there is a class for a missing file record + cur_file.options |= FO_IS_CLASS_STUB; + } + if ((cur_file.options & FO_IS_CLASS_STUB) != 0) + { + assert(classes_written < class_count); + classes_written += 1; + if (cur_file.size != 0) + { + abort("class file size transmitted"); + return nullptr; + } + reset_cur_classfile(); + + // write the meat of the classfile: + write_classfile_tail(); + cur_file.data[1] = cur_classfile_tail.b; + CHECK_0; + + // write the CP of the classfile, second: + write_classfile_head(); + cur_file.data[0] = cur_classfile_head.b; + CHECK_0; + + cur_file.size += cur_file.data[0].len; + cur_file.size += cur_file.data[1].len; + if (cur_file.name[0] == '\0') + { + bytes &prefix = cur_class->ref(0)->value.b; + const char *suffix = ".class"; + int len = (int)(prefix.len + strlen(suffix)); + bytes name; + name.set(T_NEW(byte, add_size(len, 1)), len); + cur_file.name = name.strcat(prefix).strcat(suffix).strval(); + } + } + else + { + // If there is buffered file data, produce a pointer to it. + if (cur_file.size != (size_t)cur_file.size) + { + // Silly size specified. + abort("resource file too large"); + return nullptr; + } + size_t rpleft = input_remaining(); + if (rpleft > 0) + { + if (rpleft > cur_file.size) + rpleft = (size_t)cur_file.size; + cur_file.data[0].set(rp, rpleft); + rp += rpleft; + } + if (rpleft < cur_file.size) + { + // Caller must read the rest. + size_t fleft = (size_t)cur_file.size - rpleft; + bytes_read += fleft; // Credit it to the overall archive size. + } + } + CHECK_0; + bytes_written += cur_file.size; + files_written += 1; + return &cur_file; +} + +// Write a file to jarout. +void unpacker::write_file_to_jar(unpacker::file *f) +{ + size_t htsize = f->data[0].len + f->data[1].len; + julong fsize = f->size; + if (htsize == fsize) + { + jarout->addJarEntry(f->name, f->deflate_hint(), f->modtime, f->data[0], f->data[1]); + } + else + { + assert(input_remaining() == 0); + bytes part1, part2; + part1.len = f->data[0].len; + part1.set(T_NEW(byte, part1.len), part1.len); + part1.copyFrom(f->data[0]); + assert(f->data[1].len == 0); + part2.set(nullptr, 0); + size_t fleft = (size_t)fsize - part1.len; + assert(bytes_read > fleft); // part2 already credited by get_next_file + bytes_read -= fleft; + if (fleft > 0) + { + // Must read some more. + if (live_input) + { + // Stop using the input buffer. Make a new one: + if (free_input) + input.free(); + input.init(fleft > (1 << 12) ? fleft : (1 << 12)); + free_input = true; + live_input = false; + } + else + { + // Make it large enough. + assert(free_input); // must be reallocable + input.ensureSize(fleft); + } + rplimit = rp = input.base(); + CHECK; + input.setLimit(rp + fleft); + if (!ensure_input(fleft)) + abort("EOF reading resource file"); + part2.ptr = input_scan(); + part2.len = input_remaining(); + rplimit = rp = input.base(); + } + jarout->addJarEntry(f->name, f->deflate_hint(), f->modtime, part1, part2); + } + if (verbose >= 3) + { + fprintf(stderr, "Wrote " LONG_LONG_FORMAT " bytes to: %s\n", fsize, f->name); + } +} + +void unpacker::abort(const char *message) +{ + if (message == nullptr) + message = "error unpacking archive"; + if (message[0] == '@') + ++message; + fprintf(stderr, "%s\n", message); + fflush(stderr); + exit(-1); +} diff --git a/depends/pack200/src/unpack.h b/depends/pack200/src/unpack.h new file mode 100644 index 00000000..11f7bbe1 --- /dev/null +++ b/depends/pack200/src/unpack.h @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2002, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Global Structures +struct jar; +struct gunzip; +struct band; +struct cpool; +struct entry; +struct cpindex; +struct inner_class; +struct value_stream; + +struct cpindex +{ + uint len; + entry *base1; // base of primary index + entry **base2; // base of secondary index + byte ixTag; // type of entries (!= CONSTANT_None), plus 64 if sub-index + enum + { + SUB_TAG = 64 + }; + + entry *get(uint i); + + void init(int len_, entry *base1_, int ixTag_) + { + len = len_; + base1 = base1_; + base2 = nullptr; + ixTag = ixTag_; + } + void init(int len_, entry **base2_, int ixTag_) + { + len = len_; + base1 = nullptr; + base2 = base2_; + ixTag = ixTag_; + } +}; + +struct cpool +{ + uint nentries; + entry *entries; + entry *first_extra_entry; + uint maxentries; // total allocated size of entries + + // Position and size of each homogeneous subrange: + int tag_count[CONSTANT_Limit]; + int tag_base[CONSTANT_Limit]; + cpindex tag_index[CONSTANT_Limit]; + ptrlist tag_extras[CONSTANT_Limit]; + + cpindex *member_indexes; // indexed by 2*CONSTANT_Class.inord + cpindex *getFieldIndex(entry *classRef); + cpindex *getMethodIndex(entry *classRef); + + inner_class **ic_index; + inner_class **ic_child_index; + inner_class *getIC(entry *inner); + inner_class *getFirstChildIC(entry *outer); + inner_class *getNextChildIC(inner_class *child); + + int outputIndexLimit; // index limit after renumbering + ptrlist outputEntries; // list of entry* needing output idx assigned + + entry **hashTab; + uint hashTabLength; + entry *&hashTabRef(byte tag, bytes &b); + entry *ensureUtf8(bytes &b); + entry *ensureClass(bytes &b); + + // Well-known Utf8 symbols. + enum + { +#define SNAME(n, s) s_##s, + ALL_ATTR_DO(SNAME) +#undef SNAME + s_lt_init_gt, // + s_LIMIT + }; + entry *sym[s_LIMIT]; + + // read counts from hdr, allocate main arrays + enum + { + NUM_COUNTS = 12 + }; + void init(unpacker *u, int counts[NUM_COUNTS]); + + // pointer to outer unpacker, for error checks etc. + unpacker *u; + + int getCount(byte tag) + { + assert((uint)tag < CONSTANT_Limit); + return tag_count[tag]; + } + cpindex *getIndex(byte tag) + { + assert((uint)tag < CONSTANT_Limit); + return &tag_index[tag]; + } + cpindex *getKQIndex(); // uses cur_descr + + void expandSignatures(); + void initMemberIndexes(); + + void computeOutputOrder(); + void computeOutputIndexes(); + void resetOutputIndexes(); + + // error handling + inline void abort(const char *msg); + inline bool aborting(); +}; + +/* + * The unpacker provides the entry points to the unpack engine, + * as well as maintains the state of the engine. + */ +struct unpacker +{ + // One element of the resulting JAR. + struct file + { + const char *name; + julong size; + int modtime; + int options; + bytes data[2]; + // Note: If Sum(data[*].len) < size, + // remaining bytes must be read directly from the input stream. + bool deflate_hint() + { + return ((options & FO_DEFLATE_HINT) != 0); + } + }; + + // global pointer to self, if not running under JNI (not multi-thread safe) + static unpacker *non_mt_current; + + // if running Unix-style, here are the inputs and outputs + FILE *infileptr; // buffered + int infileno; // unbuffered + bytes inbytes; // direct + gunzip *gzin; // gunzip filter, if any + jar *jarout; // output JAR file + + // pointer to self, for U_NEW macro + unpacker *u; + + // private abort message string, allocated to PATH_MAX*2 + const char *abort_message; + ptrlist mallocs; // list of guys to free when we are all done + ptrlist tmallocs; // list of guys to free on next client request + fillbytes smallbuf; // supplies small alloc requests + fillbytes tsmallbuf; // supplies temporary small alloc requests + + // option management members + int verbose; // verbose level, 0 means no output + bool strip_compile; + bool strip_debug; + bool strip_jcov; + bool remove_packfile; + int deflate_hint_or_zero; // ==0 means not set, otherwise -1 or 1 + int modification_time_or_zero; + + // input stream + fillbytes input; // the whole block (size is predicted, has slop too) + bool live_input; // is the data in this block live? + bool free_input; // must the input buffer be freed? + byte *rp; // read pointer (< rplimit <= input.limit()) + byte *rplimit; // how much of the input block has been read? + julong bytes_read; + int unsized_bytes_read; + + // callback to read at least one byte, up to available input + typedef jlong (*read_input_fn_t)(unpacker *self, void *buf, jlong minlen, jlong maxlen); + read_input_fn_t read_input_fn; + + // archive header fields + int magic, minver, majver; + size_t archive_size; + int archive_next_count, archive_options, archive_modtime; + int band_headers_size; + int file_count, attr_definition_count, ic_count, class_count; + int default_class_minver, default_class_majver; + int default_file_options, suppress_file_options; // not header fields + int default_archive_modtime, default_file_modtime; // not header fields + int code_count; // not a header field + int files_remaining; // not a header field + + // engine state + band *all_bands; // indexed by band_number + byte *meta_rp; // read-pointer into (copy of) band_headers + cpool cp; // all constant pool information + inner_class *ics; // InnerClasses + + // output stream + bytes output; // output block (either classfile head or tail) + byte *wp; // write pointer (< wplimit == output.limit()) + byte *wpbase; // write pointer starting address (<= wp) + byte *wplimit; // how much of the output block has been written? + + // output state + file cur_file; + entry *cur_class; // CONSTANT_Class entry + entry *cur_super; // CONSTANT_Class entry or nullptr + entry *cur_descr; // CONSTANT_NameandType entry + int cur_descr_flags; // flags corresponding to cur_descr + int cur_class_minver, cur_class_majver; + bool cur_class_has_local_ics; + fillbytes cur_classfile_head; + fillbytes cur_classfile_tail; + int files_written; // also tells which file we're working on + int classes_written; // also tells which class we're working on + julong bytes_written; + intlist bcimap; + fillbytes class_fixup_type; + intlist class_fixup_offset; + ptrlist class_fixup_ref; + fillbytes code_fixup_type; // which format of branch operand? + intlist code_fixup_offset; // location of operand needing fixup + intlist code_fixup_source; // encoded ID of branch insn + ptrlist requested_ics; // which ics need output? + + // stats pertaining to multiple segments (updated on reset) + julong bytes_read_before_reset; + julong bytes_written_before_reset; + int files_written_before_reset; + int classes_written_before_reset; + int segments_read_before_reset; + + // attribute state + struct layout_definition + { + uint idx; // index (0..31...) which identifies this layout + const char *name; // name of layout + entry *nameEntry; + const char *layout; // string of layout (not yet parsed) + band **elems; // array of top-level layout elems (or callables) + + bool hasCallables() + { + return layout[0] == '['; + } + band **bands() + { + assert(elems != nullptr); + return elems; + } + }; + struct attr_definitions + { + unpacker *u; // pointer to self, for U_NEW macro + int xxx_flags_hi_bn; // locator for flags, count, indexes, calls bands + int attrc; // ATTR_CONTEXT_CLASS, etc. + uint flag_limit; // 32 or 63, depending on archive_options bit + julong predef; // mask of built-in definitions + julong redef; // mask of local flag definitions or redefinitions + ptrlist layouts; // local (compressor-defined) defs, in index order + int flag_count[X_ATTR_LIMIT_FLAGS_HI]; + intlist overflow_count; + ptrlist strip_names; // what attribute names are being stripped? + ptrlist band_stack; // Temp., used during layout parsing. + ptrlist calls_to_link; // (ditto) + int bands_made; // (ditto) + + void free() + { + layouts.free(); + overflow_count.free(); + strip_names.free(); + band_stack.free(); + calls_to_link.free(); + } + + // Locate the five fixed bands. + band &xxx_flags_hi(); + band &xxx_flags_lo(); + band &xxx_attr_count(); + band &xxx_attr_indexes(); + band &xxx_attr_calls(); + band &fixed_band(int e_class_xxx); + + // Register a new layout, and make bands for it. + layout_definition *defineLayout(int idx, const char *name, const char *layout); + layout_definition *defineLayout(int idx, entry *nameEntry, const char *layout); + band **buildBands(layout_definition *lo); + + // Parse a layout string or part of one, recursively if necessary. + const char *parseLayout(const char *lp, band **&res, int curCble); + const char *parseNumeral(const char *lp, int &res); + const char *parseIntLayout(const char *lp, band *&res, byte le_kind, + bool can_be_signed = false); + band **popBody(int band_stack_base); // pops a body off band_stack + + // Read data into the bands of the idx-th layout. + void readBandData(int idx); // parse layout, make bands, read data + void readBandData(band **body, uint count); // recursive helper + + layout_definition *getLayout(uint idx) + { + if (idx >= (uint)layouts.length()) + return nullptr; + return (layout_definition *)layouts.get(idx); + } + + void setHaveLongFlags(bool z) + { + assert(flag_limit == 0); // not set up yet + flag_limit = (z ? X_ATTR_LIMIT_FLAGS_HI : X_ATTR_LIMIT_NO_FLAGS_HI); + } + bool haveLongFlags() + { + assert(flag_limit == X_ATTR_LIMIT_NO_FLAGS_HI || + flag_limit == X_ATTR_LIMIT_FLAGS_HI); + return flag_limit == X_ATTR_LIMIT_FLAGS_HI; + } + + // Return flag_count if idx is predef and not redef, else zero. + int predefCount(uint idx); + + bool isRedefined(uint idx) + { + if (idx >= flag_limit) + return false; + return (bool)((redef >> idx) & 1); + } + bool isPredefined(uint idx) + { + if (idx >= flag_limit) + return false; + return (bool)(((predef & ~redef) >> idx) & 1); + } + julong flagIndexMask() + { + return (predef | redef); + } + bool isIndex(uint idx) + { + assert(flag_limit != 0); // must be set up already + if (idx < flag_limit) + return (bool)(((predef | redef) >> idx) & 1); + else + return (idx - flag_limit < (uint)overflow_count.length()); + } + int &getCount(uint idx) + { + assert(isIndex(idx)); + if (idx < flag_limit) + return flag_count[idx]; + else + return overflow_count.get(idx - flag_limit); + } + bool aborting() + { + return u->aborting(); + } + void abort(const char *msg) + { + u->abort(msg); + } + }; + + attr_definitions attr_defs[ATTR_CONTEXT_LIMIT]; + + // Initialization + void init(read_input_fn_t input_fn = nullptr); + // Resets to a known sane state + void reset(); + // Deallocates all storage. + void free(); + // Deallocates temporary storage (volatile after next client call). + void free_temps() + { + tsmallbuf.init(); + tmallocs.freeAll(); + } + + // Option management methods + bool set_option(const char *option, const char *value); + const char *get_option(const char *option); + + void dump_options(); + + // Fetching input. + bool ensure_input(jlong more); + byte *input_scan() + { + return rp; + } + size_t input_remaining() + { + return rplimit - rp; + } + size_t input_consumed() + { + return rp - input.base(); + } + + // Entry points to the unpack engine + static int run(int argc, char **argv); // Unix-style entry point. + void check_options(); + void start(void *packptr = nullptr, size_t len = 0); + void write_file_to_jar(file *f); + void finish(); + + // Public post unpack methods + int get_files_remaining() + { + return files_remaining; + } + int get_segments_remaining() + { + return archive_next_count; + } + file *get_next_file(); // returns nullptr on last file + + // General purpose methods + void *alloc(size_t size) + { + return alloc_heap(size, true); + } + void *temp_alloc(size_t size) + { + return alloc_heap(size, true, true); + } + void *alloc_heap(size_t size, bool smallOK = false, bool temp = false); + void saveTo(bytes &b, const char *str) + { + saveTo(b, (byte *)str, strlen(str)); + } + void saveTo(bytes &b, bytes &data) + { + saveTo(b, data.ptr, data.len); + } + void saveTo(bytes &b, byte *ptr, size_t len); //{ b.ptr = U_NEW...} + const char *saveStr(const char *str) + { + bytes buf; + saveTo(buf, str); + return buf.strval(); + } + const char *saveIntStr(int num) + { + char buf[30]; + sprintf(buf, "%d", num); + return saveStr(buf); + } + const char *get_abort_message(); + void abort(const char *s = nullptr); + bool aborting() + { + return abort_message != nullptr; + } + static unpacker *current(); // find current instance + + // Output management + void set_output(fillbytes *which) + { + assert(wp == nullptr); + which->ensureSize(1 << 12); // covers the average classfile + wpbase = which->base(); + wp = which->limit(); + wplimit = which->end(); + } + fillbytes *close_output(fillbytes *which = nullptr); // inverse of set_output + + // These take an implicit parameter of wp/wplimit, and resize as necessary: + byte *put_space(size_t len); // allocates space at wp, returns pointer + size_t put_empty(size_t s) + { + byte *p = put_space(s); + return p - wpbase; + } + void ensure_put_space(size_t len); + void put_bytes(bytes &b) + { + b.writeTo(put_space(b.len)); + } + void putu1(int n) + { + putu1_at(put_space(1), n); + } + void putu1_fast(int n) + { + putu1_at(wp++, n); + } + void putu2(int n); // { putu2_at(put_space(2), n); } + void putu4(int n); // { putu4_at(put_space(4), n); } + void putu8(jlong n); // { putu8_at(put_space(8), n); } + void putref(entry *e); // { putu2_at(put_space(2), putref_index(e, 2)); } + void putu1ref(entry *e); // { putu1_at(put_space(1), putref_index(e, 1)); } + int putref_index(entry *e, int size); // size in [1..2] + void put_label(int curIP, int size); // size in {2,4} + void putlayout(band **body); + void put_stackmap_type(); + + size_t wpoffset() + { + return (size_t)(wp - wpbase); + } // (unvariant across overflow) + byte *wp_at(size_t offset) + { + return wpbase + offset; + } + uint to_bci(uint bii); + void get_code_header(int &max_stack, int &max_na_locals, int &handler_count, int &cflags); + band *ref_band_for_self_op(int bc, bool &isAloadVar, int &origBCVar); + band *ref_band_for_op(int bc); + + // Definitions of standard classfile int formats: + static void putu1_at(byte *wp, int n) + { + assert(n == (n & 0xFF)); + wp[0] = n; + } + static void putu2_at(byte *wp, int n); + static void putu4_at(byte *wp, int n); + static void putu8_at(byte *wp, jlong n); + + // Private stuff + void reset_cur_classfile(); + void write_classfile_tail(); + void write_classfile_head(); + void write_code(); + void write_bc_ops(); + void write_members(int num, int attrc); // attrc=ATTR_CONTEXT_FIELD/METHOD + int write_attrs(int attrc, julong indexBits); + + // The readers + void read_bands(); + void read_file_header(); + void read_cp(); + void read_cp_counts(value_stream &hdr); + void read_attr_defs(); + void read_ics(); + void read_attrs(int attrc, int obj_count); + void read_classes(); + void read_code_headers(); + void read_bcs(); + void read_bc_ops(); + void read_files(); + void read_Utf8_values(entry *cpMap, int len); + void read_single_words(band &cp_band, entry *cpMap, int len); + void read_double_words(band &cp_bands, entry *cpMap, int len); + void read_single_refs(band &cp_band, byte refTag, entry *cpMap, int len); + void read_double_refs(band &cp_band, byte ref1Tag, byte ref2Tag, entry *cpMap, int len); + void read_signature_values(entry *cpMap, int len); +}; + +inline void cpool::abort(const char *msg) +{ + u->abort(msg); +} +inline bool cpool::aborting() +{ + return u->aborting(); +} diff --git a/depends/pack200/src/utils.cpp b/depends/pack200/src/utils.cpp new file mode 100644 index 00000000..3ea8c92e --- /dev/null +++ b/depends/pack200/src/utils.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#include +#include +#include +#else +#include +#endif + +#include "constants.h" +#include "defines.h" +#include "bytes.h" +#include "utils.h" + +#include "unpack.h" + +void *must_malloc(size_t size) +{ + size_t msize = size; + void *ptr = (msize > PSIZE_MAX) ? nullptr : malloc(msize); + if (ptr != nullptr) + { + memset(ptr, 0, size); + } + else + { + unpack_abort(ERROR_ENOMEM); + } + return ptr; +} + +void unpack_abort(const char *msg, unpacker *u) +{ + if (msg == nullptr) + msg = "corrupt pack file or internal error"; + if (u == nullptr) + u = unpacker::current(); + if (u == nullptr) + { + fprintf(stderr, "Error: unpacker: %s\n", msg); + ::abort(); + return; + } + u->abort(msg); +} + +bool unpack_aborting(unpacker *u) +{ + if (u == nullptr) + u = unpacker::current(); + if (u == nullptr) + { + fprintf(stderr, "Error: unpacker: no current instance\n"); + ::abort(); + return true; + } + return u->aborting(); +} diff --git a/depends/pack200/src/utils.h b/depends/pack200/src/utils.h new file mode 100644 index 00000000..0ce6b7d8 --- /dev/null +++ b/depends/pack200/src/utils.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// Definitions of our util functions + +void *must_malloc(size_t size); + +// overflow management +#define OVERFLOW ((size_t) - 1) +#define PSIZE_MAX (OVERFLOW / 2) /* normal size limit */ + +inline size_t scale_size(size_t size, size_t scale) +{ + return (size > PSIZE_MAX / scale) ? OVERFLOW : size * scale; +} + +inline size_t add_size(size_t size1, size_t size2) +{ + return ((size1 | size2 | (size1 + size2)) > PSIZE_MAX) ? OVERFLOW : size1 + size2; +} + +inline size_t add_size(size_t size1, size_t size2, int size3) +{ + return add_size(add_size(size1, size2), size3); +} + +// These may be expensive, because they have to go via Java TSD, +// if the optional u argument is missing. +struct unpacker; +extern void unpack_abort(const char *msg, unpacker *u = nullptr); +extern bool unpack_aborting(unpacker *u = nullptr); + diff --git a/depends/pack200/src/zip.cpp b/depends/pack200/src/zip.cpp new file mode 100644 index 00000000..f1bc25ad --- /dev/null +++ b/depends/pack200/src/zip.cpp @@ -0,0 +1,610 @@ +/* + * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * Note: Lifted from uncrunch.c from jdk sources + */ +#include +#include +#include +#include + +#include +#include + +#ifndef _MSC_VER +#include +#endif + +#include "defines.h" +#include "bytes.h" +#include "utils.h" + +#include "constants.h" +#include "unpack.h" + +#include "zip.h" + +#ifdef NO_ZLIB + +inline bool jar::deflate_bytes(bytes &head, bytes &tail) +{ + return false; +} +inline uint jar::get_crc32(uint c, uchar *ptr, uint len) +{ + return 0; +} +#define Z_NULL NULL + +#else // Have ZLIB + +#include + +inline uint jar::get_crc32(uint c, uchar *ptr, uint len) +{ + return crc32(c, ptr, len); +} + +#endif // End of ZLIB + +#ifdef sparc +#define SWAP_BYTES(a) ((((a) << 8) & 0xff00) | 0x00ff) & (((a) >> 8) | 0xff00) +#else +#define SWAP_BYTES(a) (a) +#endif + +#define GET_INT_LO(a) SWAP_BYTES(a & 0xFFFF) + +#define GET_INT_HI(a) SWAP_BYTES((a >> 16) & 0xFFFF); + +void jar::init(unpacker *u_) +{ + BYTES_OF(*this).clear(); + u = u_; + u->jarout = this; +} + +// Write data to the ZIP output stream. +void jar::write_data(void *buff, int len) +{ + while (len > 0) + { + int rc = (int)fwrite(buff, 1, len, jarfp); + if (rc <= 0) + { + fprintf(stderr, "Error: write on output file failed err=%d\n", errno); + exit(1); // Called only from the native standalone unpacker + } + output_file_offset += rc; + buff = ((char *)buff) + rc; + len -= rc; + } +} + +void jar::add_to_jar_directory(const char *fname, bool store, int modtime, int len, int clen, + uint32_t crc) +{ + uint fname_length = (uint)strlen(fname); + ushort header[23]; + if (modtime == 0) + modtime = default_modtime; + uint32_t dostime = get_dostime(modtime); + + header[0] = (ushort)SWAP_BYTES(0x4B50); + header[1] = (ushort)SWAP_BYTES(0x0201); + header[2] = (ushort)SWAP_BYTES(0xA); + + // required version + header[3] = (ushort)SWAP_BYTES(0xA); + + // flags 02 = maximum sub-compression flag + header[4] = (store) ? 0x0 : SWAP_BYTES(0x2); + + // Compression method 8=deflate. + header[5] = (store) ? 0x0 : SWAP_BYTES(0x08); + + // Last modified date and time. + header[6] = (ushort)GET_INT_LO(dostime); + header[7] = (ushort)GET_INT_HI(dostime); + + // CRC + header[8] = (ushort)GET_INT_LO(crc); + header[9] = (ushort)GET_INT_HI(crc); + + // Compressed length: + header[10] = (ushort)GET_INT_LO(clen); + header[11] = (ushort)GET_INT_HI(clen); + + // Uncompressed length. + header[12] = (ushort)GET_INT_LO(len); + header[13] = (ushort)GET_INT_HI(len); + + // Filename length + header[14] = (ushort)SWAP_BYTES(fname_length); + // So called "extra field" length. + header[15] = 0; + // So called "comment" length. + header[16] = 0; + // Disk number start + header[17] = 0; + // File flags => binary + header[18] = 0; + // More file flags + header[19] = 0; + header[20] = 0; + // Offset within ZIP file. + header[21] = (ushort)GET_INT_LO(output_file_offset); + header[22] = (ushort)GET_INT_HI(output_file_offset); + + // Copy the whole thing into the central directory. + central_directory.append(header, sizeof(header)); + + // Copy the fname to the header. + central_directory.append(fname, fname_length); + + central_directory_count++; +} + +void jar::write_jar_header(const char *fname, bool store, int modtime, int len, int clen, + uint crc) +{ + uint fname_length = (uint)strlen(fname); + ushort header[15]; + if (modtime == 0) + modtime = default_modtime; + uint32_t dostime = get_dostime(modtime); + + // ZIP LOC magic. + header[0] = (ushort)SWAP_BYTES(0x4B50); + header[1] = (ushort)SWAP_BYTES(0x0403); + + // Version + header[2] = (ushort)SWAP_BYTES(0xA); + + // flags 02 = maximum sub-compression flag + header[3] = (store) ? 0x0 : SWAP_BYTES(0x2); + + // Compression method = deflate + header[4] = (store) ? 0x0 : SWAP_BYTES(0x08); + + // Last modified date and time. + header[5] = (ushort)GET_INT_LO(dostime); + header[6] = (ushort)GET_INT_HI(dostime); + + // CRC + header[7] = (ushort)GET_INT_LO(crc); + header[8] = (ushort)GET_INT_HI(crc); + + // Compressed length: + header[9] = (ushort)GET_INT_LO(clen); + header[10] = (ushort)GET_INT_HI(clen); + + // Uncompressed length. + header[11] = (ushort)GET_INT_LO(len); + header[12] = (ushort)GET_INT_HI(len); + + // Filename length + header[13] = (ushort)SWAP_BYTES(fname_length); + // So called "extra field" length. + header[14] = 0; + + // Write the LOC header to the output file. + write_data(header, (int)sizeof(header)); + + // Copy the fname to the header. + write_data((char *)fname, (int)fname_length); +} + +static const char marker_comment[] = ZIP_ARCHIVE_MARKER_COMMENT; + +void jar::write_central_directory() +{ + bytes mc; + mc.set(marker_comment); + + ushort header[11]; + + // Create the End of Central Directory structure. + header[0] = (ushort)SWAP_BYTES(0x4B50); + header[1] = (ushort)SWAP_BYTES(0x0605); + // disk numbers + header[2] = 0; + header[3] = 0; + // Number of entries in central directory. + header[4] = (ushort)SWAP_BYTES(central_directory_count); + header[5] = (ushort)SWAP_BYTES(central_directory_count); + // Size of the central directory} + header[6] = (ushort)GET_INT_LO((int)central_directory.size()); + header[7] = (ushort)GET_INT_HI((int)central_directory.size()); + // Offset of central directory within disk. + header[8] = (ushort)GET_INT_LO(output_file_offset); + header[9] = (ushort)GET_INT_HI(output_file_offset); + // zipfile comment length; + header[10] = (ushort)SWAP_BYTES((int)mc.len); + + // Write the central directory. + write_data(central_directory.b); + + // Write the End of Central Directory structure. + write_data(header, (int)sizeof(header)); + + // Write the comment. + write_data(mc); +} + +// Public API + +// Open a Jar file and initialize. +void jar::openJarFile(const char *fname) +{ + if (!jarfp) + { + jarfp = fopen(fname, "wb"); + if (!jarfp) + { + fprintf(stderr, "Error: Could not open jar file: %s\n", fname); + exit(3); // Called only from the native standalone unpacker + } + } +} + +// Add a ZIP entry and copy the file data +void jar::addJarEntry(const char *fname, bool deflate_hint, int modtime, bytes &head, + bytes &tail) +{ + int len = (int)(head.len + tail.len); + int clen = 0; + + uint crc = get_crc32(0, Z_NULL, 0); + if (head.len != 0) + crc = get_crc32(crc, (uchar *)head.ptr, (uint)head.len); + if (tail.len != 0) + crc = get_crc32(crc, (uchar *)tail.ptr, (uint)tail.len); + + bool deflate = (deflate_hint && len > 0); + + if (deflate) + { + if (deflate_bytes(head, tail) == false) + { + deflate = false; + } + } + clen = (int)((deflate) ? deflated.size() : len); + add_to_jar_directory(fname, !deflate, modtime, len, clen, crc); + write_jar_header(fname, !deflate, modtime, len, clen, crc); + + if (deflate) + { + write_data(deflated.b); + } + else + { + write_data(head); + write_data(tail); + } +} + +// Add a ZIP entry for a directory name no data +void jar::addDirectoryToJarFile(const char *dir_name) +{ + bool store = true; + add_to_jar_directory((const char *)dir_name, store, default_modtime, 0, 0, 0); + write_jar_header((const char *)dir_name, store, default_modtime, 0, 0, 0); +} + +// Write out the central directory and close the jar file. +void jar::closeJarFile(bool central) +{ + if (jarfp) + { + fflush(jarfp); + if (central) + write_central_directory(); + fflush(jarfp); + fclose(jarfp); + } + reset(); +} + +/* Convert the date y/n/d and time h:m:s to a four byte DOS date and + * time (date in high two bytes, time in low two bytes allowing magnitude + * comparison). + */ +inline uint32_t jar::dostime(int y, int n, int d, int h, int m, int s) +{ + return y < 1980 ? dostime(1980, 1, 1, 0, 0, 0) + : (((uint32_t)y - 1980) << 25) | ((uint32_t)n << 21) | ((uint32_t)d << 16) | + ((uint32_t)h << 11) | ((uint32_t)m << 5) | ((uint32_t)s >> 1); +} + +#ifdef _REENTRANT // solaris +extern "C" struct tm *gmtime_r(const time_t *, struct tm *); +#else +#define gmtime_r(t, s) gmtime(t) +#endif +/* + * Return the Unix time in DOS format + */ +uint32_t jar::get_dostime(int modtime) +{ + // see defines.h + if (modtime != 0 && modtime == modtime_cache) + return dostime_cache; + if (modtime != 0 && default_modtime == 0) + default_modtime = modtime; // catch a reasonable default + time_t t = modtime; + struct tm sbuf; + (void)memset((void *)&sbuf, 0, sizeof(sbuf)); + struct tm *s = gmtime_r(&t, &sbuf); + modtime_cache = modtime; + dostime_cache = + dostime(s->tm_year + 1900, s->tm_mon + 1, s->tm_mday, s->tm_hour, s->tm_min, s->tm_sec); + // printf("modtime %d => %d\n", modtime_cache, dostime_cache); + return dostime_cache; +} + +/* Returns true on success, and will set the clen to the compressed + length, the caller should verify if true and clen less than the + input data +*/ +bool jar::deflate_bytes(bytes &head, bytes &tail) +{ + int len = (int)(head.len + tail.len); + + z_stream zs; + BYTES_OF(zs).clear(); + + // NOTE: the window size should always be -MAX_WBITS normally -15. + // unzip/zipup.c and java/Deflater.c + + int error = + deflateInit2(&zs, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + if (error != Z_OK) + { + /* + switch (error) + { + case Z_MEM_ERROR: + PRINTCR((2, "Error: deflate error : Out of memory \n")); + break; + case Z_STREAM_ERROR: + PRINTCR((2, "Error: deflate error : Invalid compression level \n")); + break; + case Z_VERSION_ERROR: + PRINTCR((2, "Error: deflate error : Invalid version\n")); + break; + default: + PRINTCR((2, "Error: Internal deflate error error = %d\n", error)); + } + */ + return false; + } + + deflated.empty(); + zs.next_out = (uchar *)deflated.grow(len + (len / 2)); + zs.avail_out = (int)deflated.size(); + + zs.next_in = (uchar *)head.ptr; + zs.avail_in = (int)head.len; + + bytes *first = &head; + bytes *last = &tail; + if (last->len == 0) + { + first = nullptr; + last = &head; + } + else if (first->len == 0) + { + first = nullptr; + } + + if (first != nullptr && error == Z_OK) + { + zs.next_in = (uchar *)first->ptr; + zs.avail_in = (int)first->len; + error = deflate(&zs, Z_NO_FLUSH); + } + if (error == Z_OK) + { + zs.next_in = (uchar *)last->ptr; + zs.avail_in = (int)last->len; + error = deflate(&zs, Z_FINISH); + } + if (error == Z_STREAM_END) + { + if (len > (int)zs.total_out) + { + deflated.b.len = zs.total_out; + deflateEnd(&zs); + return true; + } + deflateEnd(&zs); + return false; + } + + deflateEnd(&zs); + return false; +} + +// Callback for fetching data from a GZIP input stream +static jlong read_input_via_gzip(unpacker *u, void *buf, jlong minlen, jlong maxlen) +{ + assert(minlen <= maxlen); // don't talk nonsense + jlong numread = 0; + char *bufptr = (char *)buf; + char *inbuf = u->gzin->inbuf; + size_t inbuflen = sizeof(u->gzin->inbuf); + unpacker::read_input_fn_t read_gzin_fn = (unpacker::read_input_fn_t)u->gzin->read_input_fn; + z_stream &zs = *(z_stream *)u->gzin->zstream; + while (numread < minlen) + { + int readlen = (1 << 16); // pretty arbitrary + if (readlen > (maxlen - numread)) + readlen = (int)(maxlen - numread); + zs.next_out = (uchar *)bufptr; + zs.avail_out = readlen; + if (zs.avail_in == 0) + { + zs.avail_in = (int)read_gzin_fn(u, inbuf, 1, inbuflen); + zs.next_in = (uchar *)inbuf; + } + int error = inflate(&zs, Z_NO_FLUSH); + if (error != Z_OK && error != Z_STREAM_END) + { + u->abort("error inflating input"); + break; + } + int nr = readlen - zs.avail_out; + numread += nr; + bufptr += nr; + assert(numread <= maxlen); + if (error == Z_STREAM_END) + { + enum + { + TRAILER_LEN = 8 + }; + // skip 8-byte trailer + if (zs.avail_in >= TRAILER_LEN) + { + zs.avail_in -= TRAILER_LEN; + } + else + { + // Bug: 5023768,we read past the TRAILER_LEN to see if there is + // any extraneous data, as we dont support concatenated .gz + // files just yet. + int extra = (int)read_gzin_fn(u, inbuf, 1, inbuflen); + zs.avail_in += extra - TRAILER_LEN; + } + // %%% should check final CRC and length here + // %%% should check for concatenated *.gz files here + if (zs.avail_in > 0) + u->abort("garbage after end of deflated input stream"); + // pop this filter off: + u->gzin->free(); + break; + } + } + + // fprintf(u->errstrm, "readInputFn(%d,%d) => %d (gunzip)\n", + // (int)minlen, (int)maxlen, (int)numread); + return numread; +} + +void gunzip::init(unpacker *u_) +{ + BYTES_OF(*this).clear(); + u = u_; + assert(u->gzin == nullptr); // once only, please + read_input_fn = (void *)u->read_input_fn; + zstream = NEW(z_stream, 1); + u->gzin = this; + u->read_input_fn = read_input_via_gzip; +} + +void gunzip::start(int magic) +{ + assert((magic & GZIP_MAGIC_MASK) == GZIP_MAGIC); + int gz_flg = (magic & 0xFF); // keep "flg", discard other 3 bytes + enum + { + FHCRC = (1 << 1), + FEXTRA = (1 << 2), + FNAME = (1 << 3), + FCOMMENT = (1 << 4) + }; + char gz_mtime[4]; + char gz_xfl[1]; + char gz_os[1]; + char gz_extra_len[2]; + char gz_hcrc[2]; + char gz_ignore; + // do not save extra, name, comment + read_fixed_field(gz_mtime, sizeof(gz_mtime)); + read_fixed_field(gz_xfl, sizeof(gz_xfl)); + read_fixed_field(gz_os, sizeof(gz_os)); + if (gz_flg & FEXTRA) + { + read_fixed_field(gz_extra_len, sizeof(gz_extra_len)); + int extra_len = gz_extra_len[0] & 0xFF; + extra_len += (gz_extra_len[1] & 0xFF) << 8; + for (; extra_len > 0; extra_len--) + { + read_fixed_field(&gz_ignore, 1); + } + } + int null_terms = 0; + if (gz_flg & FNAME) + null_terms++; + if (gz_flg & FCOMMENT) + null_terms++; + for (; null_terms; null_terms--) + { + for (;;) + { + gz_ignore = 0; + read_fixed_field(&gz_ignore, 1); + if (gz_ignore == 0) + break; + } + } + if (gz_flg & FHCRC) + read_fixed_field(gz_hcrc, sizeof(gz_hcrc)); + + if (aborting()) + return; + + // now the input stream is ready to read into the inflater + int error = inflateInit2((z_stream *)zstream, -MAX_WBITS); + if (error != Z_OK) + { + abort("cannot create input"); + return; + } +} + +void gunzip::free() +{ + assert(u->gzin == this); + u->gzin = nullptr; + u->read_input_fn = (unpacker::read_input_fn_t) this->read_input_fn; + inflateEnd((z_stream *)zstream); + ::free(zstream); + zstream = nullptr; + ::free(this); +} + +void gunzip::read_fixed_field(char *buf, size_t buflen) +{ + if (aborting()) + return; + jlong nr = ((unpacker::read_input_fn_t)read_input_fn)(u, buf, buflen, buflen); + if ((size_t)nr != buflen) + u->abort("short stream header"); +} diff --git a/depends/pack200/src/zip.h b/depends/pack200/src/zip.h new file mode 100644 index 00000000..1b6a8b02 --- /dev/null +++ b/depends/pack200/src/zip.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned char uchar; + +struct unpacker; + +struct jar +{ + // JAR file writer + FILE *jarfp; + int default_modtime; + + // Used by unix2dostime: + int modtime_cache; + uint32_t dostime_cache; + + // Private members + fillbytes central_directory; + ushort central_directory_count; + uint output_file_offset; + fillbytes deflated; // temporary buffer + + // pointer to outer unpacker, for error checks etc. + unpacker *u; + + // Public Methods + void openJarFile(const char *fname); + void addJarEntry(const char *fname, bool deflate_hint, int modtime, bytes &head, + bytes &tail); + void addDirectoryToJarFile(const char *dir_name); + void closeJarFile(bool central); + + void init(unpacker *u_); + + void free() + { + central_directory.free(); + deflated.free(); + } + + void reset() + { + free(); + init(u); + } + + // Private Methods + void write_data(void *ptr, int len); + void write_data(bytes &b) + { + write_data(b.ptr, (int)b.len); + } + void add_to_jar_directory(const char *fname, bool store, int modtime, int len, int clen, + uint32_t crc); + void write_jar_header(const char *fname, bool store, int modtime, int len, int clen, + unsigned int crc); + void write_central_directory(); + uint32_t dostime(int y, int n, int d, int h, int m, int s); + uint32_t get_dostime(int modtime); + + // The definitions of these depend on the NO_ZLIB option: + bool deflate_bytes(bytes &head, bytes &tail); + static uint get_crc32(uint c, unsigned char *ptr, uint len); + + // error handling + void abort(const char *msg) + { + unpack_abort(msg, u); + } + bool aborting() + { + return unpack_aborting(u); + } +}; + +struct gunzip +{ + // optional gzip input stream control block + + // pointer to outer unpacker, for error checks etc. + unpacker *u; + + void *read_input_fn; // underlying byte stream + void *zstream; // inflater state + char inbuf[1 << 14]; // input buffer + + void init(unpacker *u_); // pushes new value on u->read_input_fn + + void free(); + + void start(int magic); + + // private stuff + void read_fixed_field(char *buf, size_t buflen); + + // error handling + void abort(const char *msg) + { + unpack_abort(msg, u); + } + bool aborting() + { + return unpack_aborting(u); + } +}; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index d0af8b93..73bd9403 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -189,5 +189,8 @@ void OneSixUpdate::jarlibFinished() void OneSixUpdate::jarlibFailed() { - emitFailed("Failed to download the binary garbage. Try again. Maybe. IF YOU DARE"); + QStringList failed = jarlibDownloadJob->getFailedFiles(); + QString failed_all = failed.join("\n"); + emitFailed("Failed to download the following files:\n" + failed_all + + "\n\nPlease try again."); } diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 86ba0792..35f7251e 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -152,7 +152,7 @@ void MCVListLoadTask::executeTask() void MCVListLoadTask::list_downloaded() { - if(vlistReply->error() != QNetworkReply::QNetworkReply::NoError) + if(vlistReply->error() != QNetworkReply::NoError) { vlistReply->deleteLater(); emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString()); diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp index 6ae3f121..61ecc298 100644 --- a/logic/net/ByteArrayDownload.cpp +++ b/logic/net/ByteArrayDownload.cpp @@ -31,7 +31,7 @@ void ByteArrayDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTot void ByteArrayDownload::downloadError ( QNetworkReply::NetworkError error ) { // error happened during download. - // TODO: log the reason why + qDebug() << "URL:" << m_url.toString().toLocal8Bit() << "Network error: " << error; m_status = Job_Failed; } diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index 3acba050..8da1f39b 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -7,47 +7,48 @@ #include -ByteArrayDownloadPtr DownloadJob::add ( QUrl url ) +ByteArrayDownloadPtr DownloadJob::add(QUrl url) { - ByteArrayDownloadPtr ptr (new ByteArrayDownload(url)); + ByteArrayDownloadPtr ptr(new ByteArrayDownload(url)); ptr->index_within_job = downloads.size(); downloads.append(ptr); - parts_progress.append(QPair(0,1)); + parts_progress.append(part_info()); total_progress++; return ptr; } -FileDownloadPtr DownloadJob::add ( QUrl url, QString rel_target_path) +FileDownloadPtr DownloadJob::add(QUrl url, QString rel_target_path) { - FileDownloadPtr ptr (new FileDownload(url, rel_target_path)); + FileDownloadPtr ptr(new FileDownload(url, rel_target_path)); ptr->index_within_job = downloads.size(); downloads.append(ptr); - parts_progress.append(QPair(0,1)); + parts_progress.append(part_info()); total_progress++; return ptr; } -CacheDownloadPtr DownloadJob::add ( QUrl url, MetaEntryPtr entry) +CacheDownloadPtr DownloadJob::add(QUrl url, MetaEntryPtr entry) { - CacheDownloadPtr ptr (new CacheDownload(url, entry)); + CacheDownloadPtr ptr(new CacheDownload(url, entry)); ptr->index_within_job = downloads.size(); downloads.append(ptr); - parts_progress.append(QPair(0,1)); + parts_progress.append(part_info()); total_progress++; return ptr; } -void DownloadJob::partSucceeded ( int index ) +void DownloadJob::partSucceeded(int index) { // do progress. all slots are 1 in size at least - auto & slot = parts_progress[index]; - partProgress ( index, slot.second , slot.second ); - + auto &slot = parts_progress[index]; + partProgress(index, slot.total_progress, slot.total_progress); + num_succeeded++; - qDebug() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" << downloads.size(); - if(num_failed + num_succeeded == downloads.size()) + qDebug() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" + << downloads.size(); + if (num_failed + num_succeeded == downloads.size()) { - if(num_failed) + if (num_failed) { qDebug() << m_job_name.toLocal8Bit() << " failed."; emit failed(); @@ -60,39 +61,65 @@ void DownloadJob::partSucceeded ( int index ) } } -void DownloadJob::partFailed ( int index ) +void DownloadJob::partFailed(int index) { - num_failed++; - if(num_failed + num_succeeded == downloads.size()) + auto &slot = parts_progress[index]; + if (slot.failures == 3) { - qDebug() << m_job_name.toLocal8Bit() << " failed."; - emit failed(); + qDebug() << "Part " << index << " failed 3 times (" << downloads[index]->m_url << ")"; + num_failed++; + if (num_failed + num_succeeded == downloads.size()) + { + qDebug() << m_job_name.toLocal8Bit() << " failed."; + emit failed(); + } + } + else + { + qDebug() << "Part " << index << " failed, restarting (" << downloads[index]->m_url + << ")"; + // restart the job + slot.failures++; + downloads[index]->start(); } } -void DownloadJob::partProgress ( int index, qint64 bytesReceived, qint64 bytesTotal ) +void DownloadJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTotal) { - auto & slot = parts_progress[index]; - - current_progress -= slot.first; - slot.first = bytesReceived; - current_progress += slot.first; - - total_progress -= slot.second; - slot.second = bytesTotal; - total_progress += slot.second; + auto &slot = parts_progress[index]; + + current_progress -= slot.current_progress; + slot.current_progress = bytesReceived; + current_progress += slot.current_progress; + + total_progress -= slot.total_progress; + slot.total_progress = bytesTotal; + total_progress += slot.total_progress; emit progress(current_progress, total_progress); } - void DownloadJob::start() { qDebug() << m_job_name.toLocal8Bit() << " started."; - for(auto iter: downloads) + for (auto iter : downloads) { connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); connect(iter.data(), SIGNAL(failed(int)), SLOT(partFailed(int))); - connect(iter.data(), SIGNAL(progress(int,qint64,qint64)), SLOT(partProgress(int,qint64,qint64))); + connect(iter.data(), SIGNAL(progress(int, qint64, qint64)), + SLOT(partProgress(int, qint64, qint64))); iter->start(); } } + +QStringList DownloadJob::getFailedFiles() +{ + QStringList failed; + for (auto download : downloads) + { + if (download->m_status == Job_Failed) + { + failed.push_back(download->m_url.toString()); + } + } + return failed; +} diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index c8f6a9d7..5d5ba01a 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -51,6 +51,7 @@ public: { return m_running; }; + QStringList getFailedFiles(); signals: void started(); void progress(qint64 current, qint64 total); @@ -63,9 +64,15 @@ private slots: void partSucceeded(int index); void partFailed(int index); private: + struct part_info + { + qint64 current_progress = 0; + qint64 total_progress = 1; + int failures = 0; + }; QString m_job_name; QList downloads; - QList> parts_progress; + QList parts_progress; qint64 current_progress = 0; qint64 total_progress = 0; int num_succeeded = 0; -- cgit From 8b0f8b9e597eb50ff9323037fd5fa1b9e330c467 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 30 Sep 2013 02:34:46 +0200 Subject: ``Working'' forge unpackers. Needs a lot of hardening but good for alpha. --- CMakeLists.txt | 6 +- depends/pack200/CMakeLists.txt | 1 + depends/pack200/src/unpack.cpp | 6 +- depends/pack200/src/unpack200.cpp | 3 + depends/xz-embedded/CMakeLists.txt | 20 +-- depends/xz-embedded/include/xz.h | 15 ++ gui/LegacyModEditDialog.cpp | 2 +- gui/OneSixModEditDialog.cpp | 2 +- logic/ForgeInstaller.cpp | 5 + logic/LegacyUpdate.cpp | 2 +- logic/OneSixAssets.cpp | 4 +- logic/OneSixLibrary.cpp | 14 +- logic/OneSixLibrary.h | 8 +- logic/OneSixUpdate.cpp | 9 +- logic/OneSixVersion.cpp | 12 +- logic/lists/ForgeVersionList.cpp | 2 +- logic/lists/MinecraftVersionList.h | 2 +- logic/net/DownloadJob.cpp | 16 ++- logic/net/DownloadJob.h | 8 +- logic/net/ForgeXzDownload.cpp | 277 +++++++++++++++++++++++++++++++++++++ logic/net/ForgeXzDownload.h | 35 +++++ 21 files changed, 413 insertions(+), 36 deletions(-) create mode 100644 logic/net/ForgeXzDownload.cpp create mode 100644 logic/net/ForgeXzDownload.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index bb813a09..a3afe5d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,9 +52,11 @@ add_subdirectory(depends/launcher) # Add xz decompression add_subdirectory(depends/xz-embedded) +include_directories(${XZ_INCLUDE_DIR}) # Add pack200 decompression add_subdirectory(depends/pack200) +include_directories(${PACK200_INCLUDE_DIR}) ######## MultiMC Libs ######## @@ -231,6 +233,8 @@ logic/net/ByteArrayDownload.h logic/net/ByteArrayDownload.cpp logic/net/CacheDownload.h logic/net/CacheDownload.cpp +logic/net/ForgeXzDownload.h +logic/net/ForgeXzDownload.cpp logic/net/DownloadJob.h logic/net/DownloadJob.cpp logic/net/HttpMetaCache.h @@ -354,7 +358,7 @@ ADD_EXECUTABLE(MultiMC MACOSX_BUNDLE WIN32 # Link QT5_USE_MODULES(MultiMC Widgets Network Xml) -TARGET_LINK_LIBRARIES(MultiMC quazip libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS}) +TARGET_LINK_LIBRARIES(MultiMC quazip xz-embedded unpack200 libUtil libSettings libGroupView ${MultiMC_LINK_ADDITIONAL_LIBS}) ADD_DEPENDENCIES(MultiMC MultiMCLauncher) diff --git a/depends/pack200/CMakeLists.txt b/depends/pack200/CMakeLists.txt index 657e303c..3e41d378 100644 --- a/depends/pack200/CMakeLists.txt +++ b/depends/pack200/CMakeLists.txt @@ -37,6 +37,7 @@ src/zip.cpp src/zip.h ) +SET(PACK200_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE) include_directories(include) add_library(unpack200 STATIC ${PACK200_SRC}) diff --git a/depends/pack200/src/unpack.cpp b/depends/pack200/src/unpack.cpp index 8a66d42a..d7de1b22 100644 --- a/depends/pack200/src/unpack.cpp +++ b/depends/pack200/src/unpack.cpp @@ -1523,7 +1523,8 @@ band **unpacker::attr_definitions::buildBands(unpacker::layout_definition *lo) call.le_body[0] = &cble; // Distinguish backward calls and callables: assert(cble.le_kind == EK_CBLE); - assert(cble.le_len == call_num); + //FIXME: hit this one + //assert(cble.le_len == call_num); cble.le_back |= call.le_back; } calls_to_link.popTo(0); @@ -2777,7 +2778,8 @@ void unpacker::putlayout(band **body) { band &cble = *b.le_body[0]; assert(cble.le_kind == EK_CBLE); - assert(cble.le_len == b.le_len); + //FIXME: hit this one + //assert(cble.le_len == b.le_len); putlayout(cble.le_body); } break; diff --git a/depends/pack200/src/unpack200.cpp b/depends/pack200/src/unpack200.cpp index c6aa0b02..2ff8c34a 100644 --- a/depends/pack200/src/unpack200.cpp +++ b/depends/pack200/src/unpack200.cpp @@ -156,8 +156,11 @@ void unpack_200(std::string input_path, std::string output_path) magic = read_magic(&u, peek, (int)sizeof(peek)); if (magic != (int)JAVA_PACKAGE_MAGIC) { + // we do not feel strongly about this kind of thing... + /* if (magic != EOF_MAGIC) unpack_abort("garbage after end of pack archive"); + */ break; // all done } diff --git a/depends/xz-embedded/CMakeLists.txt b/depends/xz-embedded/CMakeLists.txt index f1c6eb8d..d4987f76 100644 --- a/depends/xz-embedded/CMakeLists.txt +++ b/depends/xz-embedded/CMakeLists.txt @@ -8,33 +8,25 @@ option(XZ_BUILD_MINIDEC "Build a tiny utility that decompresses xz streams" OFF) set(CMAKE_C_FLAGS "-std=c99") include_directories(include) +SET(XZ_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include" PARENT_SCOPE) + +# See include/xz.h for manual feature configuration +# tweak this list and xz.h to fit your needs set(XZ_SOURCES include/xz.h src/xz_config.h src/xz_crc32.c +src/xz_crc64.c src/xz_dec_lzma2.c src/xz_dec_stream.c src/xz_lzma2.h src/xz_private.h src/xz_stream.h +# src/xz_dec_bcj.c ) # TODO: look into what would be needed for plain old lzma -# checksum checks -add_definitions(-DXZ_DEC_ANY_CHECK) -if(XZ_BUILD_CRC64) - add_definitions(-DXZ_USE_CRC64) - LIST(APPEND XZ_SOURCES src/xz_crc64.c) -endif() -# TODO: add SHA256 - -if(XZ_BUILD_BCJ) - add_definitions(-DXZ_DEC_X86 -DXZ_DEC_POWERPC -DXZ_DEC_IA64) - add_definitions(-DXZ_DEC_ARM -DXZ_DEC_ARMTHUMB -DXZ_DEC_SPARC) - LIST(APPEND XZ_SOURCES src/xz_dec_bcj.c) -endif() - add_library(xz-embedded STATIC ${XZ_SOURCES}) add_executable(xzminidec xzminidec.c) target_link_libraries(xzminidec xz-embedded) diff --git a/depends/xz-embedded/include/xz.h b/depends/xz-embedded/include/xz.h index 0a4b38d3..49a96f7b 100644 --- a/depends/xz-embedded/include/xz.h +++ b/depends/xz-embedded/include/xz.h @@ -23,6 +23,21 @@ extern "C" { #endif +/* Definitions that determine available features */ +#define XZ_DEC_ANY_CHECK 1 +#define XZ_USE_CRC64 1 + +// native machine code compression stuff +/* +#define XZ_DEC_X86 +#define XZ_DEC_POWERPC +#define XZ_DEC_IA64 +#define XZ_DEC_ARM +#define XZ_DEC_ARMTHUMB +#define XZ_DEC_SPARC +*/ + + /* In Linux, this is used to make extern functions static when needed. */ #ifndef XZ_EXTERN # define XZ_EXTERN extern diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index 20296769..053aef6b 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -210,7 +210,7 @@ void LegacyModEditDialog::on_addForgeBtn_clicked() if(entry->stale) { DownloadJob * fjob = new DownloadJob("Forge download"); - fjob->add(forge->universal_url, entry); + fjob->addCacheDownload(forge->universal_url, entry); ProgressDialog dlg(this); dlg.exec(fjob); if(dlg.result() == QDialog::Accepted) diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index 94fea933..f2e7c5d2 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -160,7 +160,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked() if (entry->stale) { DownloadJob *fjob = new DownloadJob("Forge download"); - fjob->add(forgeVersion->installer_url, entry); + fjob->addCacheDownload(forgeVersion->installer_url, entry); ProgressDialog dlg(this); dlg.exec(fjob); if (dlg.result() == QDialog::Accepted) diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index bcba00e9..9ae3f1e1 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -100,11 +100,16 @@ bool ForgeInstaller::apply(QSharedPointer to) for (auto lib : m_forge_version->libraries) { QString libName = lib->name(); + // WARNING: This could actually break. // if this is the actual forge lib, set an absolute url for the download if (libName.contains("minecraftforge")) { lib->setAbsoluteUrl(m_universal_url); } + else if (libName.contains("scala")) + { + lib->setHint("forge-pack-xz"); + } if (blacklist.contains(libName)) continue; diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 84d3d830..d8e622dd 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -228,7 +228,7 @@ void LegacyUpdate::jarStart() urlstr += intended_version_id + "/" + intended_version_id + ".jar"; auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); - dljob->add(QUrl(urlstr), inst->defaultBaseJar()); + dljob->addFileDownload(QUrl(urlstr), inst->defaultBaseJar()); legacyDownloadJob.reset(dljob); connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); connect(dljob, SIGNAL(failed()), SLOT(jarFailed())); diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp index 5bdd29d7..ca7a5534 100644 --- a/logic/OneSixAssets.cpp +++ b/logic/OneSixAssets.cpp @@ -113,7 +113,7 @@ void OneSixAssets::fetchXMLFinished() auto entry = metacache->resolveEntry("assets", keyStr, etagStr); if(entry->stale) { - job->add(QUrl(prefix + keyStr), entry); + job->addCacheDownload(QUrl(prefix + keyStr), entry); } } if(job->size()) @@ -130,7 +130,7 @@ void OneSixAssets::fetchXMLFinished() void OneSixAssets::start() { auto job = new DownloadJob("Assets index"); - job->add(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" )); + job->addByteArrayDownload(QUrl ( "http://s3.amazonaws.com/Minecraft.Resources/" )); connect ( job, SIGNAL(succeeded()), SLOT ( fetchXMLFinished() ) ); index_job.reset ( job ); job->start(); diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 8da1fde7..63d42646 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -105,12 +105,24 @@ QString OneSixLibrary::absoluteUrl() return m_absolute_url; } +void OneSixLibrary::setHint(QString hint) +{ + m_hint = hint; +} + +QString OneSixLibrary::hint() +{ + return m_hint; +} + QJsonObject OneSixLibrary::toJson() { QJsonObject libRoot; libRoot.insert("name", m_name); if(m_absolute_url.size()) - libRoot.insert("MMC-absulute_url", m_absolute_url); + libRoot.insert("MMC-absoluteUrl", m_absolute_url); + if(m_hint.size()) + libRoot.insert("MMC-hint", m_hint); if(m_base_url != "https://s3.amazonaws.com/Minecraft.Download/libraries/") libRoot.insert("url", m_base_url); if (isNative() && m_native_suffixes.size()) diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index f3106483..2a16d8e1 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -19,6 +19,8 @@ private: // custom values /// absolute URL. takes precedence over m_download_path, if defined QString m_absolute_url; + /// download hint - how to actually get the library + QString m_hint; // derived values used for real things /// a decent name fit for display @@ -91,8 +93,12 @@ public: QString downloadUrl(); /// Get the relative path where the library should be saved QString storagePath(); - + /// set an absolute URL for the library. This is an MMC extension. void setAbsoluteUrl(QString absolute_url); QString absoluteUrl(); + + /// set a hint about how to treat the library. This is an MMC extension. + void setHint(QString hint); + QString hint(); }; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 73bd9403..41d8f599 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -75,7 +75,7 @@ void OneSixUpdate::versionFileStart() QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; auto job = new DownloadJob("Version index"); - job->add(QUrl(urlstr)); + job->addByteArrayDownload(QUrl(urlstr)); specificVersionDownloadJob.reset(job); connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), SLOT(versionFileFinished())); @@ -158,7 +158,7 @@ void OneSixUpdate::jarlibStart() targetstr += version->id + "/" + version->id + ".jar"; auto job = new DownloadJob("Libraries for instance " + inst->name()); - job->add(QUrl(urlstr), targetstr); + job->addFileDownload(QUrl(urlstr), targetstr); jarlibDownloadJob.reset(job); auto libs = version->getActiveNativeLibs(); @@ -171,7 +171,10 @@ void OneSixUpdate::jarlibStart() auto entry = metacache->resolveEntry("libraries", lib->storagePath()); if (entry->stale) { - jarlibDownloadJob->add(download_path, entry); + if(lib->hint() == "forge-pack-xz") + jarlibDownloadJob->addForgeXzDownload(download_path, entry); + else + jarlibDownloadJob->addCacheDownload(download_path, entry); } } connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished())); diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 663d903a..64a47562 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -71,11 +71,21 @@ QSharedPointer fromJsonV4(QJsonObject root, { library->setBaseUrl(urlVal.toString()); } - auto urlAbsVal = libObj.value("MMC-absulute_url"); + auto hintVal = libObj.value("MMC-hint"); + if (hintVal.isString()) + { + library->setHint(hintVal.toString()); + } + auto urlAbsVal = libObj.value("MMC-absoluteUrl"); + auto urlAbsuVal = libObj.value("MMC-absulute_url"); // compatibility if (urlAbsVal.isString()) { library->setAbsoluteUrl(urlAbsVal.toString()); } + else if(urlAbsuVal.isString()) + { + library->setAbsoluteUrl(urlAbsuVal.toString()); + } // Extract excludes (if any) auto extractVal = libObj.value("extract"); if (extractVal.isObject()) diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index 721f2c0a..e2adbf3b 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -162,7 +162,7 @@ void ForgeListLoadTask::executeTask() auto job = new DownloadJob("Version index"); // we do not care if the version is stale or not. auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); - job->add(QUrl(JSON_URL), forgeListEntry); + job->addCacheDownload(QUrl(JSON_URL), forgeListEntry); listJob.reset(job); connect(listJob.data(), SIGNAL(succeeded()), SLOT(list_downloaded())); connect(listJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); diff --git a/logic/lists/MinecraftVersionList.h b/logic/lists/MinecraftVersionList.h index fb28ddfe..ed68efbb 100644 --- a/logic/lists/MinecraftVersionList.h +++ b/logic/lists/MinecraftVersionList.h @@ -46,7 +46,7 @@ public: protected: QList m_vlist; - bool m_loaded; + bool m_loaded = false; protected slots: virtual void updateListData(QList versions); diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index 8da1f39b..03a69555 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -7,7 +7,7 @@ #include -ByteArrayDownloadPtr DownloadJob::add(QUrl url) +ByteArrayDownloadPtr DownloadJob::addByteArrayDownload(QUrl url) { ByteArrayDownloadPtr ptr(new ByteArrayDownload(url)); ptr->index_within_job = downloads.size(); @@ -17,7 +17,7 @@ ByteArrayDownloadPtr DownloadJob::add(QUrl url) return ptr; } -FileDownloadPtr DownloadJob::add(QUrl url, QString rel_target_path) +FileDownloadPtr DownloadJob::addFileDownload(QUrl url, QString rel_target_path) { FileDownloadPtr ptr(new FileDownload(url, rel_target_path)); ptr->index_within_job = downloads.size(); @@ -27,7 +27,7 @@ FileDownloadPtr DownloadJob::add(QUrl url, QString rel_target_path) return ptr; } -CacheDownloadPtr DownloadJob::add(QUrl url, MetaEntryPtr entry) +CacheDownloadPtr DownloadJob::addCacheDownload(QUrl url, MetaEntryPtr entry) { CacheDownloadPtr ptr(new CacheDownload(url, entry)); ptr->index_within_job = downloads.size(); @@ -37,6 +37,16 @@ CacheDownloadPtr DownloadJob::add(QUrl url, MetaEntryPtr entry) return ptr; } +ForgeXzDownloadPtr DownloadJob::addForgeXzDownload(QUrl url, MetaEntryPtr entry) +{ + ForgeXzDownloadPtr ptr(new ForgeXzDownload(url, entry)); + ptr->index_within_job = downloads.size(); + downloads.append(ptr); + parts_progress.append(part_info()); + total_progress++; + return ptr; +} + void DownloadJob::partSucceeded(int index) { // do progress. all slots are 1 in size at least diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index 5d5ba01a..8c32950a 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -5,6 +5,7 @@ #include "FileDownload.h" #include "CacheDownload.h" #include "HttpMetaCache.h" +#include "ForgeXzDownload.h" #include "logic/tasks/ProgressProvider.h" class DownloadJob; @@ -20,9 +21,10 @@ public: explicit DownloadJob(QString job_name) :ProgressProvider(), m_job_name(job_name){}; - ByteArrayDownloadPtr add(QUrl url); - FileDownloadPtr add(QUrl url, QString rel_target_path); - CacheDownloadPtr add(QUrl url, MetaEntryPtr entry); + ByteArrayDownloadPtr addByteArrayDownload(QUrl url); + FileDownloadPtr addFileDownload(QUrl url, QString rel_target_path); + CacheDownloadPtr addCacheDownload(QUrl url, MetaEntryPtr entry); + ForgeXzDownloadPtr addForgeXzDownload(QUrl url, MetaEntryPtr entry); DownloadPtr operator[](int index) { diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp new file mode 100644 index 00000000..b7e7eedf --- /dev/null +++ b/logic/net/ForgeXzDownload.cpp @@ -0,0 +1,277 @@ +#include "MultiMC.h" +#include "ForgeXzDownload.h" +#include + +#include +#include +#include +#include + +ForgeXzDownload::ForgeXzDownload(QUrl url, MetaEntryPtr entry) + : Download() +{ + QString urlstr = url.toString(); + urlstr.append(".pack.xz"); + m_url = QUrl(urlstr); + m_entry = entry; + m_target_path = entry->getFullPath(); + m_status = Job_NotStarted; + m_opened_for_saving = false; +} + +void ForgeXzDownload::start() +{ + if (!m_entry->stale) + { + emit succeeded(index_within_job); + return; + } + // can we actually create the real, final file? + if (!ensureFilePathExists(m_target_path)) + { + emit failed(index_within_job); + return; + } + qDebug() << "Downloading " << m_url.toString(); + QNetworkRequest request(m_url); + request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + + auto worker = MMC->qnam(); + QNetworkReply *rep = worker->get(request); + + m_reply = QSharedPointer(rep, &QObject::deleteLater); + connect(rep, SIGNAL(downloadProgress(qint64, qint64)), + SLOT(downloadProgress(qint64, qint64))); + connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); + connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), + SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); +} + +void ForgeXzDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + emit progress(index_within_job, bytesReceived, bytesTotal); +} + +void ForgeXzDownload::downloadError(QNetworkReply::NetworkError error) +{ + // error happened during download. + // TODO: log the reason why + m_status = Job_Failed; +} + +void ForgeXzDownload::downloadFinished() +{ + // if the download succeeded + if (m_status != Job_Failed) + { + // nothing went wrong... + m_status = Job_Finished; + if (m_opened_for_saving) + { + // we actually downloaded something! process and isntall it + decompressAndInstall(); + return; + } + else + { + // something bad happened + m_pack200_xz_file.remove(); + m_reply.clear(); + emit failed(index_within_job); + return; + } + } + // else the download failed + else + { + m_pack200_xz_file.close(); + m_pack200_xz_file.remove(); + m_reply.clear(); + emit failed(index_within_job); + return; + } +} + +void ForgeXzDownload::downloadReadyRead() +{ + + if (!m_opened_for_saving) + { + if (!m_pack200_xz_file.open()) + { + /* + * Can't open the file... the job failed + */ + m_reply->abort(); + emit failed(index_within_job); + return; + } + m_opened_for_saving = true; + } + m_pack200_xz_file.write(m_reply->readAll()); +} + +#include "xz.h" +#include "unpack200.h" + +const size_t buffer_size = 8196; + +void ForgeXzDownload::decompressAndInstall() +{ + // rewind the downloaded temp file + m_pack200_xz_file.seek(0); + // de-xz'd file + QTemporaryFile pack200_file; + pack200_file.open(); + + bool xz_success = false; + // first, de-xz + { + uint8_t in[buffer_size]; + uint8_t out[buffer_size]; + struct xz_buf b; + struct xz_dec *s; + enum xz_ret ret; + xz_crc32_init(); + xz_crc64_init(); + s = xz_dec_init(XZ_DYNALLOC, 1 << 26); + if (s == nullptr) + { + xz_dec_end(s); + emit failed(index_within_job); + return; + } + b.in = in; + b.in_pos = 0; + b.in_size = 0; + b.out = out; + b.out_pos = 0; + b.out_size = buffer_size; + while (!xz_success) + { + if (b.in_pos == b.in_size) + { + b.in_size = m_pack200_xz_file.read((char*)in, sizeof(in)); + b.in_pos = 0; + } + + ret = xz_dec_run(s, &b); + + if (b.out_pos == sizeof(out)) + { + if (pack200_file.write((char*)out, b.out_pos) != b.out_pos) + { + // msg = "Write error\n"; + xz_dec_end(s); + emit failed(index_within_job); + return; + } + + b.out_pos = 0; + } + + if (ret == XZ_OK) + continue; + + if (ret == XZ_UNSUPPORTED_CHECK) + { + // unsupported check. this is OK, but we should log this + continue; + } + + if (pack200_file.write((char*)out, b.out_pos) != b.out_pos ) + { + // write error + pack200_file.close(); + xz_dec_end(s); + return; + } + + switch (ret) + { + case XZ_STREAM_END: + xz_dec_end(s); + xz_success = true; + break; + + case XZ_MEM_ERROR: + qDebug() << "Memory allocation failed\n"; + xz_dec_end(s); + emit failed(index_within_job); + return; + + case XZ_MEMLIMIT_ERROR: + qDebug() << "Memory usage limit reached\n"; + xz_dec_end(s); + emit failed(index_within_job); + return; + + case XZ_FORMAT_ERROR: + qDebug() << "Not a .xz file\n"; + xz_dec_end(s); + emit failed(index_within_job); + return; + + case XZ_OPTIONS_ERROR: + qDebug() << "Unsupported options in the .xz headers\n"; + xz_dec_end(s); + emit failed(index_within_job); + return; + + case XZ_DATA_ERROR: + case XZ_BUF_ERROR: + qDebug() << "File is corrupt\n"; + xz_dec_end(s); + emit failed(index_within_job); + return; + + default: + qDebug() << "Bug!\n"; + xz_dec_end(s); + emit failed(index_within_job); + return; + } + } + } + + // revert pack200 + pack200_file.close(); + QString pack_name = pack200_file.fileName(); + try + { + unpack_200(pack_name.toStdString(), m_target_path.toStdString()); + } + catch(std::runtime_error & err) + { + qDebug() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what(); + QFile f(m_target_path); + if(f.exists()) + f.remove(); + emit failed(index_within_job); + return; + } + + QFile jar_file(m_target_path); + + if (!jar_file.open(QIODevice::ReadOnly)) + { + jar_file.remove(); + emit failed(index_within_job); + return; + } + m_entry->md5sum = QCryptographicHash::hash(jar_file.readAll(), QCryptographicHash::Md5) + .toHex() + .constData(); + jar_file.close(); + + QFileInfo output_file_info(m_target_path); + m_entry->etag = m_reply->rawHeader("ETag").constData(); + m_entry->last_changed_timestamp = + output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); + m_entry->stale = false; + MMC->metacache()->updateEntry(m_entry); + + m_reply.clear(); + emit succeeded(index_within_job); +} diff --git a/logic/net/ForgeXzDownload.h b/logic/net/ForgeXzDownload.h new file mode 100644 index 00000000..8cb47783 --- /dev/null +++ b/logic/net/ForgeXzDownload.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Download.h" +#include "HttpMetaCache.h" +#include +#include + +class ForgeXzDownload : public Download +{ + Q_OBJECT +public: + MetaEntryPtr m_entry; + /// is the saving file already open? + bool m_opened_for_saving; + /// if saving to file, use the one specified in this string + QString m_target_path; + /// this is the output file, if any + QTemporaryFile m_pack200_xz_file; + +public: + explicit ForgeXzDownload(QUrl url, MetaEntryPtr entry); + +protected slots: + virtual void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + virtual void downloadError(QNetworkReply::NetworkError error); + virtual void downloadFinished(); + virtual void downloadReadyRead(); + +public slots: + virtual void start(); +private: + void decompressAndInstall(); +}; + +typedef QSharedPointer ForgeXzDownloadPtr; -- cgit From 751c532175780a8a2c3a6e069f93ad51599e988f Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 1 Oct 2013 09:05:58 +0200 Subject: Add missing include --- logic/net/ForgeXzDownload.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'logic') diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index b7e7eedf..dde36f4e 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -114,6 +114,7 @@ void ForgeXzDownload::downloadReadyRead() #include "xz.h" #include "unpack200.h" +#include const size_t buffer_size = 8196; -- cgit From eba9b3d759dbf6e402e91ab897059f1d274aef90 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 2 Oct 2013 23:35:45 +0200 Subject: Add user agent header to most MultiMC download requests. --- logic/lists/ForgeVersionList.cpp | 14 +++++++++++++- logic/lists/ForgeVersionList.h | 1 + logic/net/ByteArrayDownload.cpp | 30 ++++++++++++++++-------------- logic/net/CacheDownload.cpp | 1 + logic/net/FileDownload.cpp | 1 + logic/net/ForgeXzDownload.cpp | 1 + 6 files changed, 33 insertions(+), 15 deletions(-) (limited to 'logic') diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index e2adbf3b..ac1a4ef5 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -165,11 +165,23 @@ void ForgeListLoadTask::executeTask() job->addCacheDownload(QUrl(JSON_URL), forgeListEntry); listJob.reset(job); connect(listJob.data(), SIGNAL(succeeded()), SLOT(list_downloaded())); - connect(listJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); + connect(listJob.data(), SIGNAL(failed()), SLOT(list_failed())); connect(listJob.data(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); listJob->start(); } +void ForgeListLoadTask::list_failed() +{ + auto DlJob = listJob->first(); + auto reply = DlJob->m_reply; + if(reply) + { + qDebug() << "Getting forge version list failed: " << reply->errorString(); + } + else + qDebug() << "Getting forge version list failed for reasons unknown."; +} + void ForgeListLoadTask::list_downloaded() { QByteArray data; diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h index 613de8a6..4abfe9ce 100644 --- a/logic/lists/ForgeVersionList.h +++ b/logic/lists/ForgeVersionList.h @@ -101,6 +101,7 @@ public: protected slots: void list_downloaded(); + void list_failed(); protected: DownloadJobPtr listJob; diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp index 61ecc298..b7a68c60 100644 --- a/logic/net/ByteArrayDownload.cpp +++ b/logic/net/ByteArrayDownload.cpp @@ -2,8 +2,7 @@ #include "MultiMC.h" #include -ByteArrayDownload::ByteArrayDownload ( QUrl url ) - : Download() +ByteArrayDownload::ByteArrayDownload(QUrl url) : Download() { m_url = url; m_status = Job_NotStarted; @@ -12,23 +11,26 @@ ByteArrayDownload::ByteArrayDownload ( QUrl url ) void ByteArrayDownload::start() { qDebug() << "Downloading " << m_url.toString(); - QNetworkRequest request ( m_url ); + QNetworkRequest request(m_url); + request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); auto worker = MMC->qnam(); - QNetworkReply * rep = worker->get ( request ); - - m_reply = QSharedPointer ( rep, &QObject::deleteLater ); - connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); - connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) ); - connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) ); - connect ( rep, SIGNAL ( readyRead() ), SLOT ( downloadReadyRead() ) ); + QNetworkReply *rep = worker->get(request); + + m_reply = QSharedPointer(rep, &QObject::deleteLater); + connect(rep, SIGNAL(downloadProgress(qint64, qint64)), + SLOT(downloadProgress(qint64, qint64))); + connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); + connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), + SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); } -void ByteArrayDownload::downloadProgress ( qint64 bytesReceived, qint64 bytesTotal ) +void ByteArrayDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { - emit progress (index_within_job, bytesReceived, bytesTotal ); + emit progress(index_within_job, bytesReceived, bytesTotal); } -void ByteArrayDownload::downloadError ( QNetworkReply::NetworkError error ) +void ByteArrayDownload::downloadError(QNetworkReply::NetworkError error) { // error happened during download. qDebug() << "URL:" << m_url.toString().toLocal8Bit() << "Network error: " << error; @@ -38,7 +40,7 @@ void ByteArrayDownload::downloadError ( QNetworkReply::NetworkError error ) void ByteArrayDownload::downloadFinished() { // if the download succeeded - if ( m_status != Job_Failed ) + if (m_status != Job_Failed) { // nothing went wrong... m_status = Job_Finished; diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index dc2e0448..9fc1f127 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -34,6 +34,7 @@ void CacheDownload::start() qDebug() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Cached)"); auto worker = MMC->qnam(); QNetworkReply *rep = worker->get(request); diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp index aad4a991..353fd58b 100644 --- a/logic/net/FileDownload.cpp +++ b/logic/net/FileDownload.cpp @@ -46,6 +46,7 @@ void FileDownload::start() qDebug() << "Downloading " << m_url.toString(); QNetworkRequest request ( m_url ); request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); + request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Uncached)"); auto worker = MMC->qnam(); QNetworkReply * rep = worker->get ( request ); diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index dde36f4e..6d66abce 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -35,6 +35,7 @@ void ForgeXzDownload::start() qDebug() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Cached)"); auto worker = MMC->qnam(); QNetworkReply *rep = worker->get(request); -- cgit From 1dee4bb60d08995f0fd4eb229f131f2ca546d24c Mon Sep 17 00:00:00 2001 From: Sky Date: Sat, 5 Oct 2013 01:08:13 +0100 Subject: Add naive Windows Java detection - JavaUtils for finding it on other systems (incomplete) --- CMakeLists.txt | 3 ++ gui/settingsdialog.cpp | 11 ++++- gui/settingsdialog.h | 2 + gui/settingsdialog.ui | 2 +- logic/JavaUtils.cpp | 114 +++++++++++++++++++++++++++++++++++++++++++++++++ logic/JavaUtils.h | 26 +++++++++++ 6 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 logic/JavaUtils.cpp create mode 100644 logic/JavaUtils.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index e0637054..267a57d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,6 +300,9 @@ logic/tasks/ProgressProvider.h logic/tasks/Task.h logic/tasks/Task.cpp +# Utilities +logic/JavaUtils.h +logic/JavaUtils.cpp ) diff --git a/gui/settingsdialog.cpp b/gui/settingsdialog.cpp index 9736c1c7..b5ff8d56 100644 --- a/gui/settingsdialog.cpp +++ b/gui/settingsdialog.cpp @@ -13,10 +13,11 @@ * limitations under the License. */ +#include #include "settingsdialog.h" #include "ui_settingsdialog.h" +#include "logic/JavaUtils.h" -#include #include #include #include @@ -180,3 +181,11 @@ void SettingsDialog::loadSettings(SettingsObject *s) ui->preLaunchCmdTextBox->setText(s->get("PreLaunchCommand").toString()); ui->postExitCmdTextBox->setText(s->get("PostExitCommand").toString()); } + +void SettingsDialog::on_pushButton_clicked() +{ + JavaUtils jut; + QStringList paths = jut.FindJavaPath(); + + ui->javaPathTextBox->setText(paths.at(0)); +} diff --git a/gui/settingsdialog.h b/gui/settingsdialog.h index b0a8c673..2611f105 100644 --- a/gui/settingsdialog.h +++ b/gui/settingsdialog.h @@ -53,6 +53,8 @@ private slots: void on_buttonBox_accepted(); + void on_pushButton_clicked(); + private: Ui::SettingsDialog *ui; }; diff --git a/gui/settingsdialog.ui b/gui/settingsdialog.ui index 0d30e301..f0cb5c3d 100644 --- a/gui/settingsdialog.ui +++ b/gui/settingsdialog.ui @@ -33,7 +33,7 @@ QTabWidget::Rounded - 0 + 2 diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp new file mode 100644 index 00000000..d89ab9c5 --- /dev/null +++ b/logic/JavaUtils.cpp @@ -0,0 +1,114 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JavaUtils.h" +#include "osutils.h" +#include "pathutils.h" + +#include +#include +#include +#include + +#if WINDOWS +#include + +#endif + +JavaUtils::JavaUtils() +{ + +} + +#if WINDOWS +QStringList JavaUtils::FindJavaPath() +{ + QStringList paths; + + HKEY jreKey; + QString jreKeyName = "SOFTWARE\\JavaSoft\\Java Runtime Environment"; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, jreKeyName.toStdString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &jreKey) == ERROR_SUCCESS) + { + // Read the current JRE version from the registry. + // This will be used to find the key that contains the JavaHome value. + char *value = new char[0]; + DWORD valueSz = 0; + if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE*)value, &valueSz) == ERROR_MORE_DATA) + { + value = new char[valueSz]; + RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE*)value, &valueSz); + } + + RegCloseKey(jreKey); + + // Now open the registry key for the JRE version that we just got. + jreKeyName.append("\\").append(value); + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, jreKeyName.toStdString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &jreKey) == ERROR_SUCCESS) + { + // Read the JavaHome value to find where Java is installed. + value = new char[0]; + valueSz = 0; + if (RegQueryValueExA(jreKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz) == ERROR_MORE_DATA) + { + value = new char[valueSz]; + RegQueryValueExA(jreKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz); + + paths << QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"); + } + + RegCloseKey(jreKey); + } + } + + if(paths.length() <= 0) + { + qWarning() << "Failed to find Java in the Windows registry - defaulting to \"java\""; + paths << "java"; + } + + return paths; +} +#elif OSX +QStringList JavaUtils::FindJavaPath() +{ + qWarning() << "OS X Java detection incomplete - defaulting to \"java\""; + + QStringList paths; + paths << "java"; + + return paths; +} + +#elif LINUX +QStringList JavaUtils::FindJavaPath() +{ + qWarning() << "Linux Java detection incomplete - defaulting to \"java\""; + + QStringList paths; + paths << "java"; + + return paths; +} +#else +QStringList JavaUtils::FindJavaPath() +{ + qWarning() << "Unknown operating system build - defaulting to \"java\""; + + QStringList paths; + paths << "java"; + + return paths; +} +#endif diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h new file mode 100644 index 00000000..fef2a1bf --- /dev/null +++ b/logic/JavaUtils.h @@ -0,0 +1,26 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +class JavaUtils +{ +public: + JavaUtils(); + + QStringList FindJavaPath(); +}; -- cgit From f83119ce7ec3d11a903901b8eff762d2b0a9f635 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 6 Oct 2013 01:13:40 +0200 Subject: Added file logger --- CMakeLists.txt | 8 + MultiMC.cpp | 34 +++- MultiMC.h | 84 ++++---- gui/IconPickerDialog.cpp | 2 +- gui/LegacyModEditDialog.cpp | 206 ++++++++++---------- gui/LegacyModEditDialog.h | 8 +- gui/OneSixModEditDialog.cpp | 19 +- gui/OneSixModEditDialog.h | 6 +- gui/aboutdialog.ui | 97 +++++----- gui/logindialog.cpp | 10 +- gui/lwjglselectdialog.cpp | 6 +- gui/mainwindow.cpp | 360 ++++++++++++++++++----------------- gui/newinstancedialog.cpp | 2 +- gui/settingsdialog.cpp | 4 +- gui/versionselectdialog.cpp | 2 +- logger/QsDebugOutput.cpp | 52 +++++ logger/QsDebugOutput.h | 34 ++++ logger/QsLog.cpp | 141 ++++++++++++++ logger/QsLog.h | 130 +++++++++++++ logger/QsLogDest.cpp | 83 ++++++++ logger/QsLogDest.h | 53 ++++++ logic/BaseInstance.cpp | 2 +- logic/BaseInstance.h | 6 +- logic/BaseInstance_p.h | 2 +- logic/BaseVersion.h | 4 +- logic/ForgeInstaller.cpp | 4 +- logic/ForgeInstaller.h | 6 +- logic/InstanceFactory.cpp | 5 +- logic/InstanceLauncher.cpp | 2 +- logic/LegacyInstance.cpp | 8 +- logic/LegacyInstance.h | 8 +- logic/LegacyInstance_p.h | 8 +- logic/LegacyUpdate.cpp | 213 +++++++++++---------- logic/LegacyUpdate.h | 2 +- logic/Mod.cpp | 7 +- logic/ModList.cpp | 16 +- logic/OneSixAssets.cpp | 13 +- logic/OneSixInstance.cpp | 9 +- logic/OneSixInstance.h | 6 +- logic/OneSixInstance_p.h | 6 +- logic/OneSixLibrary.cpp | 2 +- logic/OneSixLibrary.h | 6 +- logic/OneSixRule.cpp | 6 +- logic/OneSixRule.h | 10 +- logic/OneSixUpdate.cpp | 25 ++- logic/OneSixUpdate.h | 2 +- logic/OneSixVersion.cpp | 28 +-- logic/OneSixVersion.h | 12 +- logic/lists/ForgeVersionList.cpp | 20 +- logic/lists/ForgeVersionList.h | 2 +- logic/lists/InstanceList.cpp | 218 +++++++++++---------- logic/lists/LwjglVersionList.cpp | 62 +++--- logic/lists/LwjglVersionList.h | 6 +- logic/lists/MinecraftVersionList.cpp | 10 +- logic/net/ByteArrayDownload.cpp | 13 +- logic/net/ByteArrayDownload.h | 2 +- logic/net/CacheDownload.cpp | 10 +- logic/net/CacheDownload.h | 2 +- logic/net/Download.h | 18 +- logic/net/DownloadJob.cpp | 22 +-- logic/net/DownloadJob.h | 2 +- logic/net/FileDownload.cpp | 12 +- logic/net/FileDownload.h | 2 +- logic/net/ForgeXzDownload.cpp | 26 +-- logic/net/ForgeXzDownload.h | 2 +- logic/net/HttpMetaCache.cpp | 6 +- logic/net/HttpMetaCache.h | 2 +- logic/net/LoginTask.cpp | 4 +- logic/tasks/Task.cpp | 2 + 69 files changed, 1375 insertions(+), 827 deletions(-) create mode 100644 logger/QsDebugOutput.cpp create mode 100644 logger/QsDebugOutput.h create mode 100644 logger/QsLog.cpp create mode 100644 logger/QsLog.h create mode 100644 logger/QsLogDest.cpp create mode 100644 logger/QsLogDest.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index e0637054..7ecfb690 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,6 +168,14 @@ MultiMC.h MultiMC.cpp MultiMCVersion.h +# Logging +logger/QsDebugOutput.cpp +logger/QsDebugOutput.h +logger/QsLog.cpp +logger/QsLog.h +logger/QsLogDest.cpp +logger/QsLogDest.h + # GUI gui/mainwindow.h gui/mainwindow.cpp diff --git a/MultiMC.cpp b/MultiMC.cpp index ee9a9bf8..b685ed13 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -20,6 +20,8 @@ #include "cmdutils.h" #include #include +#include +#include #include "config.h" using namespace Util::Commandline; @@ -123,6 +125,9 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) // change directory QDir::setCurrent(args["dir"].toString()); + // init the logger + initLogger(); + // load settings initGlobalSettings(); @@ -156,11 +161,11 @@ MultiMC::~MultiMC() { if (m_mmc_translator) { - removeTranslator(m_mmc_translator.data()); + removeTranslator(m_mmc_translator.get()); } if (m_qt_translator) { - removeTranslator(m_qt_translator.data()); + removeTranslator(m_qt_translator.get()); } } @@ -172,7 +177,7 @@ void MultiMC::initTranslations() { std::cout << "Loading Qt Language File for " << QLocale::system().name().toLocal8Bit().constData() << "..."; - if (!installTranslator(m_qt_translator.data())) + if (!installTranslator(m_qt_translator.get())) { std::cout << " failed."; m_qt_translator.reset(); @@ -190,7 +195,7 @@ void MultiMC::initTranslations() { std::cout << "Loading MMC Language File for " << QLocale::system().name().toLocal8Bit().constData() << "..."; - if (!installTranslator(m_mmc_translator.data())) + if (!installTranslator(m_mmc_translator.get())) { std::cout << " failed."; m_mmc_translator.reset(); @@ -203,6 +208,19 @@ void MultiMC::initTranslations() } } +void MultiMC::initLogger() +{ + // init the logging mechanism + QsLogging::Logger &logger = QsLogging::Logger::instance(); + logger.setLoggingLevel(QsLogging::TraceLevel); + m_fileDestination = QsLogging::DestinationFactory::MakeFileDestination("MultiMC.log"); + m_debugDestination = QsLogging::DestinationFactory::MakeDebugOutputDestination(); + logger.addDestination(m_fileDestination.get()); + logger.addDestination(m_debugDestination.get()); + // log all the things + logger.setLoggingLevel(QsLogging::TraceLevel); +} + void MultiMC::initGlobalSettings() { m_settings.reset(new INISettingsObject("multimc.cfg", this)); @@ -275,7 +293,7 @@ void MultiMC::initHttpMetaCache() m_metacache->Load(); } -QSharedPointer MultiMC::icons() +std::shared_ptr MultiMC::icons() { if (!m_icons) { @@ -284,7 +302,7 @@ QSharedPointer MultiMC::icons() return m_icons; } -QSharedPointer MultiMC::lwjgllist() +std::shared_ptr MultiMC::lwjgllist() { if (!m_lwjgllist) { @@ -293,7 +311,7 @@ QSharedPointer MultiMC::lwjgllist() return m_lwjgllist; } -QSharedPointer MultiMC::forgelist() +std::shared_ptr MultiMC::forgelist() { if (!m_forgelist) { @@ -302,7 +320,7 @@ QSharedPointer MultiMC::forgelist() return m_forgelist; } -QSharedPointer MultiMC::minecraftlist() +std::shared_ptr MultiMC::minecraftlist() { if (!m_minecraftlist) { diff --git a/MultiMC.h b/MultiMC.h index c634dd33..ec28cab2 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -1,9 +1,12 @@ #pragma once #include -#include #include "MultiMCVersion.h" #include "config.h" +#include +#include "logger/QsLog.h" +#include "logger/QsLogDest.h" + class MinecraftVersionList; class LWJGLVersionList; @@ -29,67 +32,72 @@ public: Succeeded, Initialized, }; - + public: - MultiMC ( int& argc, char** argv ); + MultiMC(int &argc, char **argv); virtual ~MultiMC(); - - QSharedPointer settings() + + std::shared_ptr settings() { return m_settings; - }; - - QSharedPointer instances() + } + + std::shared_ptr instances() { return m_instances; - }; - - QSharedPointer icons(); - + } + + std::shared_ptr icons(); + Status status() { return m_status; } - + MultiMCVersion version() { return m_version; } - - QSharedPointer qnam() + + std::shared_ptr qnam() { return m_qnam; } - - QSharedPointer metacache() + + std::shared_ptr metacache() { return m_metacache; } - - QSharedPointer lwjgllist(); - - QSharedPointer forgelist(); - - QSharedPointer minecraftlist(); - + + std::shared_ptr lwjgllist(); + + std::shared_ptr forgelist(); + + std::shared_ptr minecraftlist(); + private: + void initLogger(); + void initGlobalSettings(); - + void initHttpMetaCache(); - + void initTranslations(); + private: - QSharedPointer m_qt_translator; - QSharedPointer m_mmc_translator; - QSharedPointer m_settings; - QSharedPointer m_instances; - QSharedPointer m_icons; - QSharedPointer m_qnam; - QSharedPointer m_metacache; - QSharedPointer m_lwjgllist; - QSharedPointer m_forgelist; - QSharedPointer m_minecraftlist; - + std::shared_ptr m_qt_translator; + std::shared_ptr m_mmc_translator; + std::shared_ptr m_settings; + std::shared_ptr m_instances; + std::shared_ptr m_icons; + std::shared_ptr m_qnam; + std::shared_ptr m_metacache; + std::shared_ptr m_lwjgllist; + std::shared_ptr m_forgelist; + std::shared_ptr m_minecraftlist; + QsLogging::DestinationPtr m_fileDestination; + QsLogging::DestinationPtr m_debugDestination; + Status m_status = MultiMC::Failed; MultiMCVersion m_version = {VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD}; -}; \ No newline at end of file +}; diff --git a/gui/IconPickerDialog.cpp b/gui/IconPickerDialog.cpp index 8f5d8256..6a1ca546 100644 --- a/gui/IconPickerDialog.cpp +++ b/gui/IconPickerDialog.cpp @@ -39,7 +39,7 @@ IconPickerDialog::IconPickerDialog(QWidget *parent) : contentsWidget->installEventFilter(this); - contentsWidget->setModel(MMC->icons().data()); + contentsWidget->setModel(MMC->icons().get()); auto buttonAdd = ui->buttonBox->addButton(tr("Add Icon"),QDialogButtonBox::ResetRole); auto buttonRemove = ui->buttonBox->addButton(tr("Remove Icon"),QDialogButtonBox::ResetRole); diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index 053aef6b..c240daff 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -28,49 +28,47 @@ #include #include -LegacyModEditDialog::LegacyModEditDialog( LegacyInstance* inst, QWidget* parent ) : - m_inst(inst), - QDialog(parent), - ui(new Ui::LegacyModEditDialog) +LegacyModEditDialog::LegacyModEditDialog(LegacyInstance *inst, QWidget *parent) + : m_inst(inst), QDialog(parent), ui(new Ui::LegacyModEditDialog) { ui->setupUi(this); - + // Jar mods { ensureFolderPathExists(m_inst->jarModsDir()); m_jarmods = m_inst->jarModList(); - ui->jarModsTreeView->setModel(m_jarmods.data()); + ui->jarModsTreeView->setModel(m_jarmods.get()); #ifndef Q_OS_LINUX // FIXME: internal DnD causes segfaults later ui->jarModsTreeView->setDragDropMode(QAbstractItemView::DragDrop); // FIXME: DnD is glitched with contiguous (we move only first item in selection) ui->jarModsTreeView->setSelectionMode(QAbstractItemView::SingleSelection); #endif - ui->jarModsTreeView->installEventFilter( this ); + ui->jarModsTreeView->installEventFilter(this); m_jarmods->startWatching(); } // Core mods { ensureFolderPathExists(m_inst->coreModsDir()); m_coremods = m_inst->coreModList(); - ui->coreModsTreeView->setModel(m_coremods.data()); - ui->coreModsTreeView->installEventFilter( this ); + ui->coreModsTreeView->setModel(m_coremods.get()); + ui->coreModsTreeView->installEventFilter(this); m_coremods->startWatching(); } // Loader mods { ensureFolderPathExists(m_inst->loaderModsDir()); m_mods = m_inst->loaderModList(); - ui->loaderModTreeView->setModel(m_mods.data()); - ui->loaderModTreeView->installEventFilter( this ); + ui->loaderModTreeView->setModel(m_mods.get()); + ui->loaderModTreeView->installEventFilter(this); m_mods->startWatching(); } // texture packs { ensureFolderPathExists(m_inst->texturePacksDir()); m_texturepacks = m_inst->texturePackList(); - ui->texPackTreeView->setModel(m_texturepacks.data()); - ui->texPackTreeView->installEventFilter( this ); + ui->texPackTreeView->setModel(m_texturepacks.get()); + ui->texPackTreeView->installEventFilter(this); m_texturepacks->startWatching(); } } @@ -84,113 +82,111 @@ LegacyModEditDialog::~LegacyModEditDialog() delete ui; } -bool LegacyModEditDialog::coreListFilter ( QKeyEvent* keyEvent ) +bool LegacyModEditDialog::coreListFilter(QKeyEvent *keyEvent) { - switch(keyEvent->key()) + switch (keyEvent->key()) { - case Qt::Key_Delete: - on_rmCoreBtn_clicked(); - return true; - case Qt::Key_Plus: - on_addCoreBtn_clicked(); - return true; - default: - break; + case Qt::Key_Delete: + on_rmCoreBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addCoreBtn_clicked(); + return true; + default: + break; } - return QDialog::eventFilter( ui->coreModsTreeView, keyEvent ); + return QDialog::eventFilter(ui->coreModsTreeView, keyEvent); } -bool LegacyModEditDialog::jarListFilter ( QKeyEvent* keyEvent ) +bool LegacyModEditDialog::jarListFilter(QKeyEvent *keyEvent) { - switch(keyEvent->key()) + switch (keyEvent->key()) { - case Qt::Key_Up: + case Qt::Key_Up: + { + if (keyEvent->modifiers() & Qt::ControlModifier) { - if(keyEvent->modifiers() & Qt::ControlModifier) - { - on_moveJarUpBtn_clicked(); - return true; - } - break; + on_moveJarUpBtn_clicked(); + return true; } - case Qt::Key_Down: + break; + } + case Qt::Key_Down: + { + if (keyEvent->modifiers() & Qt::ControlModifier) { - if(keyEvent->modifiers() & Qt::ControlModifier) - { - on_moveJarDownBtn_clicked(); - return true; - } - break; - } - case Qt::Key_Delete: - on_rmJarBtn_clicked(); - return true; - case Qt::Key_Plus: - on_addJarBtn_clicked(); + on_moveJarDownBtn_clicked(); return true; - default: - break; + } + break; } - return QDialog::eventFilter( ui->jarModsTreeView, keyEvent ); + case Qt::Key_Delete: + on_rmJarBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addJarBtn_clicked(); + return true; + default: + break; + } + return QDialog::eventFilter(ui->jarModsTreeView, keyEvent); } -bool LegacyModEditDialog::loaderListFilter ( QKeyEvent* keyEvent ) +bool LegacyModEditDialog::loaderListFilter(QKeyEvent *keyEvent) { - switch(keyEvent->key()) + switch (keyEvent->key()) { - case Qt::Key_Delete: - on_rmModBtn_clicked(); - return true; - case Qt::Key_Plus: - on_addModBtn_clicked(); - return true; - default: - break; + case Qt::Key_Delete: + on_rmModBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addModBtn_clicked(); + return true; + default: + break; } - return QDialog::eventFilter( ui->loaderModTreeView, keyEvent ); + return QDialog::eventFilter(ui->loaderModTreeView, keyEvent); } -bool LegacyModEditDialog::texturePackListFilter ( QKeyEvent* keyEvent ) +bool LegacyModEditDialog::texturePackListFilter(QKeyEvent *keyEvent) { - switch(keyEvent->key()) + switch (keyEvent->key()) { - case Qt::Key_Delete: - on_rmTexPackBtn_clicked(); - return true; - case Qt::Key_Plus: - on_addTexPackBtn_clicked(); - return true; - default: - break; + case Qt::Key_Delete: + on_rmTexPackBtn_clicked(); + return true; + case Qt::Key_Plus: + on_addTexPackBtn_clicked(); + return true; + default: + break; } - return QDialog::eventFilter( ui->texPackTreeView, keyEvent ); + return QDialog::eventFilter(ui->texPackTreeView, keyEvent); } - -bool LegacyModEditDialog::eventFilter ( QObject* obj, QEvent* ev ) +bool LegacyModEditDialog::eventFilter(QObject *obj, QEvent *ev) { if (ev->type() != QEvent::KeyPress) { - return QDialog::eventFilter( obj, ev ); + return QDialog::eventFilter(obj, ev); } - QKeyEvent *keyEvent = static_cast(ev); - if(obj == ui->jarModsTreeView) + QKeyEvent *keyEvent = static_cast(ev); + if (obj == ui->jarModsTreeView) return jarListFilter(keyEvent); - if(obj == ui->coreModsTreeView) + if (obj == ui->coreModsTreeView) return coreListFilter(keyEvent); - if(obj == ui->loaderModTreeView) + if (obj == ui->loaderModTreeView) return loaderListFilter(keyEvent); - if(obj == ui->texPackTreeView) + if (obj == ui->texPackTreeView) return texturePackListFilter(keyEvent); - return QDialog::eventFilter( obj, ev ); + return QDialog::eventFilter(obj, ev); } - void LegacyModEditDialog::on_addCoreBtn_clicked() { //: Title of core mod selection dialog QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Core Mods")); - for(auto filename:fileNames) + for (auto filename : fileNames) { m_coremods->stopWatching(); m_coremods->installMod(QFileInfo(filename)); @@ -199,21 +195,22 @@ void LegacyModEditDialog::on_addCoreBtn_clicked() } void LegacyModEditDialog::on_addForgeBtn_clicked() { - VersionSelectDialog vselect(MMC->forgelist().data(), this); + VersionSelectDialog vselect(MMC->forgelist().get(), this); vselect.setFilter(1, m_inst->intendedVersionId()); if (vselect.exec() && vselect.selectedVersion()) { - ForgeVersionPtr forge = vselect.selectedVersion().dynamicCast(); - if(!forge) + ForgeVersionPtr forge = + std::dynamic_pointer_cast (vselect.selectedVersion()); + if (!forge) return; auto entry = MMC->metacache()->resolveEntry("minecraftforge", forge->filename); - if(entry->stale) + if (entry->stale) { - DownloadJob * fjob = new DownloadJob("Forge download"); + DownloadJob *fjob = new DownloadJob("Forge download"); fjob->addCacheDownload(forge->universal_url, entry); ProgressDialog dlg(this); dlg.exec(fjob); - if(dlg.result() == QDialog::Accepted) + if (dlg.result() == QDialog::Accepted) { m_jarmods->stopWatching(); m_jarmods->installMod(QFileInfo(entry->getFullPath())); @@ -236,7 +233,7 @@ void LegacyModEditDialog::on_addJarBtn_clicked() { //: Title of jar mod selection dialog QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Jar Mods")); - for(auto filename:fileNames) + for (auto filename : fileNames) { m_jarmods->stopWatching(); m_jarmods->installMod(QFileInfo(filename)); @@ -247,7 +244,7 @@ void LegacyModEditDialog::on_addModBtn_clicked() { //: Title of regular mod selection dialog QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Loader Mods")); - for(auto filename:fileNames) + for (auto filename : fileNames) { m_mods->stopWatching(); m_mods->installMod(QFileInfo(filename)); @@ -258,7 +255,7 @@ void LegacyModEditDialog::on_addTexPackBtn_clicked() { //: Title of texture pack selection dialog QStringList fileNames = QFileDialog::getOpenFileNames(this, tr("Select Texture Packs")); - for(auto filename:fileNames) + for (auto filename : fileNames) { m_texturepacks->stopWatching(); m_texturepacks->installMod(QFileInfo(filename)); @@ -270,8 +267,8 @@ void LegacyModEditDialog::on_moveJarDownBtn_clicked() { int first, last; auto list = ui->jarModsTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_jarmods->moveModsDown(first, last); @@ -280,8 +277,8 @@ void LegacyModEditDialog::on_moveJarUpBtn_clicked() { int first, last; auto list = ui->jarModsTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_jarmods->moveModsUp(first, last); } @@ -289,8 +286,8 @@ void LegacyModEditDialog::on_rmCoreBtn_clicked() { int first, last; auto list = ui->coreModsTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_coremods->stopWatching(); m_coremods->deleteMods(first, last); @@ -300,8 +297,8 @@ void LegacyModEditDialog::on_rmJarBtn_clicked() { int first, last; auto list = ui->jarModsTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_jarmods->stopWatching(); m_jarmods->deleteMods(first, last); @@ -311,8 +308,8 @@ void LegacyModEditDialog::on_rmModBtn_clicked() { int first, last; auto list = ui->loaderModTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_mods->stopWatching(); m_mods->deleteMods(first, last); @@ -322,8 +319,8 @@ void LegacyModEditDialog::on_rmTexPackBtn_clicked() { int first, last; auto list = ui->texPackTreeView->selectionModel()->selectedRows(); - - if(!lastfirst(list, first, last)) + + if (!lastfirst(list, first, last)) return; m_texturepacks->stopWatching(); m_texturepacks->deleteMods(first, last); @@ -342,7 +339,6 @@ void LegacyModEditDialog::on_viewTexPackBtn_clicked() openDirInDefaultProgram(m_inst->texturePacksDir(), true); } - void LegacyModEditDialog::on_buttonBox_rejected() { close(); diff --git a/gui/LegacyModEditDialog.h b/gui/LegacyModEditDialog.h index b824a86a..5f6973d3 100644 --- a/gui/LegacyModEditDialog.h +++ b/gui/LegacyModEditDialog.h @@ -60,10 +60,10 @@ protected: bool texturePackListFilter( QKeyEvent* ev ); private: Ui::LegacyModEditDialog *ui; - QSharedPointer m_mods; - QSharedPointer m_coremods; - QSharedPointer m_jarmods; - QSharedPointer m_texturepacks; + std::shared_ptr m_mods; + std::shared_ptr m_coremods; + std::shared_ptr m_jarmods; + std::shared_ptr m_texturepacks; LegacyInstance * m_inst; DownloadJobPtr forgeJob; }; diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index f2e7c5d2..e8c7f9ed 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -42,7 +42,7 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) { main_model = new EnabledItemFilter(this); main_model->setActive(true); - main_model->setSourceModel(m_version.data()); + main_model->setSourceModel(m_version.get()); ui->libraryTreeView->setModel(main_model); ui->libraryTreeView->installEventFilter(this); ui->mainClassEdit->setText(m_version->mainClass); @@ -56,7 +56,7 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) { ensureFolderPathExists(m_inst->loaderModsDir()); m_mods = m_inst->loaderModList(); - ui->loaderModTreeView->setModel(m_mods.data()); + ui->loaderModTreeView->setModel(m_mods.get()); ui->loaderModTreeView->installEventFilter(this); m_mods->startWatching(); } @@ -64,7 +64,7 @@ OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) { ensureFolderPathExists(m_inst->resourcePacksDir()); m_resourcepacks = m_inst->resourcePackList(); - ui->resPackTreeView->setModel(m_resourcepacks.data()); + ui->resPackTreeView->setModel(m_resourcepacks.get()); ui->resPackTreeView->installEventFilter(this); m_resourcepacks->startWatching(); } @@ -97,7 +97,7 @@ void OneSixModEditDialog::on_customizeBtn_clicked() if (m_inst->customizeVersion()) { m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.data()); + main_model->setSourceModel(m_version.get()); updateVersionControls(); } } @@ -113,7 +113,7 @@ void OneSixModEditDialog::on_revertBtn_clicked() if (m_inst->revertCustomVersion()) { m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.data()); + main_model->setSourceModel(m_version.get()); updateVersionControls(); } } @@ -121,7 +121,7 @@ void OneSixModEditDialog::on_revertBtn_clicked() void OneSixModEditDialog::on_forgeBtn_clicked() { - VersionSelectDialog vselect(MMC->forgelist().data(), this); + VersionSelectDialog vselect(MMC->forgelist().get(), this); vselect.setFilter(1, m_inst->currentVersionId()); if (vselect.exec() && vselect.selectedVersion()) { @@ -139,7 +139,7 @@ void OneSixModEditDialog::on_forgeBtn_clicked() m_inst->customizeVersion(); { m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.data()); + main_model->setSourceModel(m_version.get()); updateVersionControls(); } } @@ -150,10 +150,11 @@ void OneSixModEditDialog::on_forgeBtn_clicked() { m_inst->customizeVersion(); m_version = m_inst->getFullVersion(); - main_model->setSourceModel(m_version.data()); + main_model->setSourceModel(m_version.get()); updateVersionControls(); } - ForgeVersionPtr forgeVersion = vselect.selectedVersion().dynamicCast(); + ForgeVersionPtr forgeVersion = + std::dynamic_pointer_cast(vselect.selectedVersion()); if (!forgeVersion) return; auto entry = MMC->metacache()->resolveEntry("minecraftforge", forgeVersion->filename); diff --git a/gui/OneSixModEditDialog.h b/gui/OneSixModEditDialog.h index e70bd73f..5c65fea3 100644 --- a/gui/OneSixModEditDialog.h +++ b/gui/OneSixModEditDialog.h @@ -52,9 +52,9 @@ protected: bool resourcePackListFilter( QKeyEvent* ev ); private: Ui::OneSixModEditDialog *ui; - QSharedPointer m_version; - QSharedPointer m_mods; - QSharedPointer m_resourcepacks; + std::shared_ptr m_version; + std::shared_ptr m_mods; + std::shared_ptr m_resourcepacks; EnabledItemFilter * main_model; OneSixInstance * m_inst; }; diff --git a/gui/aboutdialog.ui b/gui/aboutdialog.ui index ac4952f9..bd5cc568 100644 --- a/gui/aboutdialog.ui +++ b/gui/aboutdialog.ui @@ -7,7 +7,7 @@ 0 0 450 - 400 + 429 @@ -101,7 +101,7 @@ 0 0 432 - 159 + 179 @@ -159,8 +159,8 @@ 0 0 - 98 - 93 + 682 + 584 @@ -176,10 +176,10 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Andrew Okin &lt;</span><a href="mailto:forkk@forkk.net"><span style=" font-family:'Ubuntu'; font-size:11pt; text-decoration: underline; color:#0000ff;">forkk@forkk.net</span></a><span style=" font-family:'Ubuntu'; font-size:11pt;">&gt;</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Petr Mrázek &lt;</span><a href="mailto:peterix@gmail.com"><span style=" font-family:'Ubuntu'; font-size:11pt; text-decoration: underline; color:#0000ff;">peterix@gmail.com</span></a><span style=" font-family:'Ubuntu'; font-size:11pt;">&gt;</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:11pt;">Orochimarufan &lt;</span><a href="mailto:orochimarufan.x3@gmail.com"><span style=" font-family:'Ubuntu'; font-size:11pt; text-decoration: underline; color:#0000ff;">orochimarufan.x3@gmail.com</span></a><span style=" font-family:'Ubuntu'; font-size:11pt;">&gt;</span></p></body></html> +</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Andrew Okin &lt;</span><a href="mailto:forkk@forkk.net"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">forkk@forkk.net</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Petr Mrázek &lt;</span><a href="mailto:peterix@gmail.com"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">peterix@gmail.com</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Orochimarufan &lt;</span><a href="mailto:orochimarufan.x3@gmail.com"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">orochimarufan.x3@gmail.com</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p></body></html> @@ -190,8 +190,8 @@ p, li { white-space: pre-wrap; } 0 0 - 98 - 93 + 682 + 584 @@ -213,44 +213,45 @@ p, li { white-space: pre-wrap; } <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Copyright 2012 MultiMC Contributors</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">you may not use this file except in compliance with the License.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">You may obtain a copy of the License at</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';"> http://www.apache.org/licenses/LICENSE-2.0</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Unless required by applicable law or agreed to in writing, software</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">See the License for the specific language governing permissions and</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">limitations under the License.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">MultiMC uses bspatch, </span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Copyright 2003-2005 Colin Percival</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">All rights reserved</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Redistribution and use in source and binary forms, with or without</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">modification, are permitted providing that the following conditions</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">are met: </span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">1. Redistributions of source code must retain the above copyright</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';"> notice, this list of conditions and the following disclaimer.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">2. Redistributions in binary form must reproduce the above copyright</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';"> notice, this list of conditions and the following disclaimer in the</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';"> documentation and/or other materials provided with the distribution.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">POSSIBILITY OF SUCH DAMAGE.</span></p></body></html> +</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Copyright 2012 MultiMC Contributors</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">you may not use this file except in compliance with the License.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">You may obtain a copy of the License at</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;"> http://www.apache.org/licenses/LICENSE-2.0</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Unless required by applicable law or agreed to in writing, software</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">See the License for the specific language governing permissions and</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">limitations under the License.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">MultiMC uses QSLog, </span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Copyright (c) 2010, Razvan Petru</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">All rights reserved.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Redistribution and use in source and binary forms, with or without modification,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">are permitted provided that the following conditions are met:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">* Redistributions of source code must retain the above copyright notice, this</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;"> list of conditions and the following disclaimer.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">* Redistributions in binary form must reproduce the above copyright notice, this</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;"> list of conditions and the following disclaimer in the documentation and/or other</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;"> materials provided with the distribution.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">* The name of the contributors may not be used to endorse or promote products</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;"> derived from this software without specific prior written permission.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">OF THE POSSIBILITY OF SUCH DAMAGE.</span></p></body></html> diff --git a/gui/logindialog.cpp b/gui/logindialog.cpp index 37e30c85..fdc94ac7 100644 --- a/gui/logindialog.cpp +++ b/gui/logindialog.cpp @@ -16,7 +16,7 @@ #include "logindialog.h" #include "ui_logindialog.h" #include "keyring.h" -#include +#include LoginDialog::LoginDialog(QWidget *parent, const QString& loginErrMsg) : QDialog(parent), @@ -109,7 +109,7 @@ void LoginDialog::passwordToggled ( bool state ) blockToggles = true; if(!state) { - qDebug() << "password disabled"; + QLOG_DEBUG() << "password disabled"; } else { @@ -117,7 +117,7 @@ void LoginDialog::passwordToggled ( bool state ) { ui->rememberUsernameCheckbox->setChecked(true); } - qDebug() << "password enabled"; + QLOG_DEBUG() << "password enabled"; } blockToggles = false; } @@ -134,11 +134,11 @@ void LoginDialog::usernameToggled ( bool state ) { ui->rememberPasswordCheckbox->setChecked(false); } - qDebug() << "username disabled"; + QLOG_DEBUG() << "username disabled"; } else { - qDebug() << "username enabled"; + QLOG_DEBUG() << "username enabled"; } blockToggles = false; } diff --git a/gui/lwjglselectdialog.cpp b/gui/lwjglselectdialog.cpp index e48bb975..4518e8cd 100644 --- a/gui/lwjglselectdialog.cpp +++ b/gui/lwjglselectdialog.cpp @@ -26,10 +26,10 @@ LWJGLSelectDialog::LWJGLSelectDialog(QWidget *parent) : ui->setupUi(this); ui->labelStatus->setVisible(false); auto lwjgllist = MMC->lwjgllist(); - ui->lwjglListView->setModel(lwjgllist.data()); + ui->lwjglListView->setModel(lwjgllist.get()); - connect(lwjgllist.data(), SIGNAL(loadingStateUpdated(bool)), SLOT(loadingStateUpdated(bool))); - connect(lwjgllist.data(), SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString))); + connect(lwjgllist.get(), SIGNAL(loadingStateUpdated(bool)), SLOT(loadingStateUpdated(bool))); + connect(lwjgllist.get(), SIGNAL(loadListFailed(QString)), SLOT(loadingFailed(QString))); loadingStateUpdated(lwjgllist->isLoading()); } diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index d7b77c8b..ecf5f79d 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -68,20 +68,19 @@ #include "LabeledToolButton.h" #include "EditNotesDialog.h" -MainWindow::MainWindow ( QWidget *parent ) - :QMainWindow ( parent ), ui ( new Ui::MainWindow ) +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - ui->setupUi ( this ); - setWindowTitle ( QString ( "MultiMC %1" ).arg ( MMC->version().toString() ) ); - + ui->setupUi(this); + setWindowTitle(QString("MultiMC %1").arg(MMC->version().toString())); + // Set the selected instance to null m_selectedInstance = nullptr; // Set active instance to null. m_activeInst = nullptr; - + // OSX magic. setUnifiedTitleAndToolBarOnMac(true); - + // The instance action toolbar customizations { ui->instanceToolBar->setEnabled(false); @@ -92,44 +91,45 @@ MainWindow::MainWindow ( QWidget *parent ) connect(renameButton, SIGNAL(clicked(bool)), SLOT(on_actionRenameInstance_triggered())); ui->instanceToolBar->insertWidget(ui->actionLaunchInstance, renameButton); ui->instanceToolBar->insertSeparator(ui->actionLaunchInstance); - renameButton->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); + renameButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); } - + // Create the instance list widget { - view = new KCategorizedView ( ui->centralWidget ); - drawer = new KCategoryDrawer ( view ); - - view->setSelectionMode ( QAbstractItemView::SingleSelection ); - view->setCategoryDrawer ( drawer ); - view->setCollapsibleBlocks ( true ); - view->setViewMode ( QListView::IconMode ); - view->setFlow ( QListView::LeftToRight ); + view = new KCategorizedView(ui->centralWidget); + drawer = new KCategoryDrawer(view); + + view->setSelectionMode(QAbstractItemView::SingleSelection); + view->setCategoryDrawer(drawer); + view->setCollapsibleBlocks(true); + view->setViewMode(QListView::IconMode); + view->setFlow(QListView::LeftToRight); view->setWordWrap(true); - view->setMouseTracking ( true ); - view->viewport()->setAttribute ( Qt::WA_Hover ); + view->setMouseTracking(true); + view->viewport()->setAttribute(Qt::WA_Hover); auto delegate = new ListViewDelegate(); view->setItemDelegate(delegate); view->setSpacing(10); view->setUniformItemWidths(true); - + // do not show ugly blue border on the mac view->setAttribute(Qt::WA_MacShowFocusRect, false); - + view->installEventFilter(this); - proxymodel = new InstanceProxyModel ( this ); - proxymodel->setSortRole ( KCategorizedSortFilterProxyModel::CategorySortRole ); - proxymodel->setFilterRole ( KCategorizedSortFilterProxyModel::CategorySortRole ); - //proxymodel->setDynamicSortFilter ( true ); - - // FIXME: instList should be global-ish, or at least not tied to the main window... maybe the application itself? - proxymodel->setSourceModel ( MMC->instances().data() ); - proxymodel->sort ( 0 ); - view->setFrameShape ( QFrame::NoFrame ); - view->setModel ( proxymodel ); - - ui->horizontalLayout->addWidget ( view ); + proxymodel = new InstanceProxyModel(this); + proxymodel->setSortRole(KCategorizedSortFilterProxyModel::CategorySortRole); + proxymodel->setFilterRole(KCategorizedSortFilterProxyModel::CategorySortRole); + // proxymodel->setDynamicSortFilter ( true ); + + // FIXME: instList should be global-ish, or at least not tied to the main window... + // maybe the application itself? + proxymodel->setSourceModel(MMC->instances().get()); + proxymodel->sort(0); + view->setFrameShape(QFrame::NoFrame); + view->setModel(proxymodel); + + ui->horizontalLayout->addWidget(view); } // The cat background { @@ -139,18 +139,16 @@ MainWindow::MainWindow ( QWidget *parent ) setCatBackground(cat_enable); } // start instance when double-clicked - connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(instanceActivated(const QModelIndex &))); + connect(view, SIGNAL(doubleClicked(const QModelIndex &)), this, + SLOT(instanceActivated(const QModelIndex &))); // track the selection -- update the instance toolbar - connect( - view->selectionModel(), - SIGNAL(currentChanged(const QModelIndex &,const QModelIndex &)), - this, - SLOT(instanceChanged(const QModelIndex &,const QModelIndex &)) - ); + connect(view->selectionModel(), + SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, + SLOT(instanceChanged(const QModelIndex &, const QModelIndex &))); // model reset -> selection is invalid. All the instance pointers are wrong. // FIXME: stop using POINTERS everywhere - connect(MMC->instances().data() ,SIGNAL(dataIsInvalid()),SLOT(selectionBad())); - + connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad())); + // run the things that load and download other things... FIXME: this is NOT the place // FIXME: invisible actions in the background = NOPE. { @@ -176,57 +174,55 @@ MainWindow::~MainWindow() delete assets_downloader; } -bool MainWindow::eventFilter ( QObject* obj, QEvent* ev ) +bool MainWindow::eventFilter(QObject *obj, QEvent *ev) { - if(obj == view) + if (obj == view) { if (ev->type() == QEvent::KeyPress) { - QKeyEvent *keyEvent = static_cast(ev); - switch(keyEvent->key()) + QKeyEvent *keyEvent = static_cast(ev); + switch (keyEvent->key()) { - case Qt::Key_Enter: - case Qt::Key_Return: - on_actionLaunchInstance_triggered(); - return true; - case Qt::Key_Delete: - on_actionDeleteInstance_triggered(); - return true; - case Qt::Key_F5: - on_actionRefresh_triggered(); - return true; - case Qt::Key_F2: - on_actionRenameInstance_triggered(); - return true; - default: - break; + case Qt::Key_Enter: + case Qt::Key_Return: + on_actionLaunchInstance_triggered(); + return true; + case Qt::Key_Delete: + on_actionDeleteInstance_triggered(); + return true; + case Qt::Key_F5: + on_actionRefresh_triggered(); + return true; + case Qt::Key_F2: + on_actionRenameInstance_triggered(); + return true; + default: + break; } } } - return QMainWindow::eventFilter ( obj, ev ); + return QMainWindow::eventFilter(obj, ev); } -void MainWindow::onCatToggled ( bool state ) +void MainWindow::onCatToggled(bool state) { setCatBackground(state); MMC->settings()->set("TheCat", state); } -void MainWindow::setCatBackground ( bool enabled ) +void MainWindow::setCatBackground(bool enabled) { - if(enabled) + if (enabled) { - view->setStyleSheet( - "QListView" - "{" - "background-image: url(:/backgrounds/kitteh);" - "background-attachment: fixed;" - "background-clip: padding;" - "background-position: top right;" - "background-repeat: none;" - "background-color:palette(base);" - "}" - ); + view->setStyleSheet("QListView" + "{" + "background-image: url(:/backgrounds/kitteh);" + "background-attachment: fixed;" + "background-clip: padding;" + "background-position: top right;" + "background-repeat: none;" + "background-color:palette(base);" + "}"); } else { @@ -234,37 +230,37 @@ void MainWindow::setCatBackground ( bool enabled ) } } - -void MainWindow::instanceActivated ( QModelIndex index ) +void MainWindow::instanceActivated(QModelIndex index) { - if(!index.isValid()) + if (!index.isValid()) return; - BaseInstance * inst = (BaseInstance *) index.data(InstanceList::InstancePointerRole).value(); + BaseInstance *inst = + (BaseInstance *)index.data(InstanceList::InstancePointerRole).value(); doLogin(); } void MainWindow::on_actionAddInstance_triggered() { - if (!MMC->minecraftlist()->isLoaded() && - m_versionLoadTask && m_versionLoadTask->isRunning()) + if (!MMC->minecraftlist()->isLoaded() && m_versionLoadTask && + m_versionLoadTask->isRunning()) { QEventLoop waitLoop; waitLoop.connect(m_versionLoadTask, SIGNAL(failed(QString)), SLOT(quit())); waitLoop.connect(m_versionLoadTask, SIGNAL(succeeded()), SLOT(quit())); waitLoop.exec(); } - - NewInstanceDialog newInstDlg( this ); + + NewInstanceDialog newInstDlg(this); if (!newInstDlg.exec()) return; - + BaseInstance *newInstance = NULL; - + QString instDirName = DirNameFromString(newInstDlg.instName()); QString instDir = PathCombine(MMC->settings()->get("InstanceDir").toString(), instDirName); - + auto &loader = InstanceFactory::get(); - + auto error = loader.createInstance(newInstance, newInstDlg.selectedVersion(), instDir); QString errorMsg = QString("Failed to create instance %1: ").arg(instDirName); switch (error) @@ -274,17 +270,17 @@ void MainWindow::on_actionAddInstance_triggered() newInstance->setIconKey(newInstDlg.iconKey()); MMC->instances()->add(InstancePtr(newInstance)); return; - + case InstanceFactory::InstExists: errorMsg += "An instance with the given directory name already exists."; QMessageBox::warning(this, "Error", errorMsg); break; - + case InstanceFactory::CantCreateDir: errorMsg += "Failed to create the instance directory."; QMessageBox::warning(this, "Error", errorMsg); break; - + default: errorMsg += QString("Unknown instance loader error %1").arg(error); QMessageBox::warning(this, "Error", errorMsg); @@ -294,12 +290,12 @@ void MainWindow::on_actionAddInstance_triggered() void MainWindow::on_actionChangeInstIcon_triggered() { - if(!m_selectedInstance) + if (!m_selectedInstance) return; - + IconPickerDialog dlg(this); dlg.exec(m_selectedInstance->iconKey()); - if(dlg.result() == QDialog::Accepted) + if (dlg.result() == QDialog::Accepted) { m_selectedInstance->setIconKey(dlg.selectedIconKey); auto ico = MMC->icons()->getIcon(dlg.selectedIconKey); @@ -307,25 +303,23 @@ void MainWindow::on_actionChangeInstIcon_triggered() } } - void MainWindow::on_actionChangeInstGroup_triggered() { - if(!m_selectedInstance) + if (!m_selectedInstance) return; - + bool ok = false; - QString name ( m_selectedInstance->group() ); - name = QInputDialog::getText ( this, tr ( "Group name" ), tr ( "Enter a new group name." ), - QLineEdit::Normal, name, &ok ); - if(ok) + QString name(m_selectedInstance->group()); + name = QInputDialog::getText(this, tr("Group name"), tr("Enter a new group name."), + QLineEdit::Normal, name, &ok); + if (ok) m_selectedInstance->setGroupPost(name); } - void MainWindow::on_actionViewInstanceFolder_triggered() { - QString str = MMC->settings()->get ( "InstanceDir" ).toString(); - openDirInDefaultProgram ( str ); + QString str = MMC->settings()->get("InstanceDir").toString(); + openDirInDefaultProgram(str); } void MainWindow::on_actionRefresh_triggered() @@ -335,59 +329,58 @@ void MainWindow::on_actionRefresh_triggered() void MainWindow::on_actionViewCentralModsFolder_triggered() { - openDirInDefaultProgram ( MMC->settings()->get ( "CentralModsDir" ).toString() , true); + openDirInDefaultProgram(MMC->settings()->get("CentralModsDir").toString(), true); } void MainWindow::on_actionConfig_Folder_triggered() { - if(m_selectedInstance) + if (m_selectedInstance) { QString str = m_selectedInstance->instanceConfigFolder(); - openDirInDefaultProgram ( QDir(str).absolutePath() ); + openDirInDefaultProgram(QDir(str).absolutePath()); } } - void MainWindow::on_actionCheckUpdate_triggered() { - } void MainWindow::on_actionSettings_triggered() { - SettingsDialog dialog ( this ); + SettingsDialog dialog(this); dialog.exec(); } void MainWindow::on_actionReportBug_triggered() { - openWebPage ( QUrl ( "http://multimc.myjetbrains.com/youtrack/dashboard#newissue=yes" ) ); + openWebPage(QUrl("http://multimc.myjetbrains.com/youtrack/dashboard#newissue=yes")); } void MainWindow::on_actionNews_triggered() { - openWebPage ( QUrl ( "http://forkk.net/tag/multimc.html" ) ); + openWebPage(QUrl("http://forkk.net/tag/multimc.html")); } void MainWindow::on_actionAbout_triggered() { - AboutDialog dialog ( this ); + AboutDialog dialog(this); dialog.exec(); } -void MainWindow::on_mainToolBar_visibilityChanged ( bool ) +void MainWindow::on_mainToolBar_visibilityChanged(bool) { // Don't allow hiding the main toolbar. // This is the only way I could find to prevent it... :/ - ui->mainToolBar->setVisible ( true ); + ui->mainToolBar->setVisible(true); } void MainWindow::on_actionDeleteInstance_triggered() { if (m_selectedInstance) { - int response = QMessageBox::question(this, "CAREFUL", - QString("This is permanent! Are you sure?\nAbout to delete: ") + m_selectedInstance->name()); + int response = QMessageBox::question( + this, "CAREFUL", QString("This is permanent! Are you sure?\nAbout to delete: ") + + m_selectedInstance->name()); if (response == QMessageBox::Yes) { m_selectedInstance->nuke(); @@ -397,31 +390,31 @@ void MainWindow::on_actionDeleteInstance_triggered() void MainWindow::on_actionRenameInstance_triggered() { - if(m_selectedInstance) + if (m_selectedInstance) { bool ok = false; - QString name ( m_selectedInstance->name() ); - name = QInputDialog::getText ( this, tr ( "Instance name" ), tr ( "Enter a new instance name." ), - QLineEdit::Normal, name, &ok ); - + QString name(m_selectedInstance->name()); + name = + QInputDialog::getText(this, tr("Instance name"), tr("Enter a new instance name."), + QLineEdit::Normal, name, &ok); + if (name.length() > 0) { - if(ok && name.length()) + if (ok && name.length()) { m_selectedInstance->setName(name); renameButton->setText(name); } } - } } void MainWindow::on_actionViewSelectedInstFolder_triggered() { - if(m_selectedInstance) + if (m_selectedInstance) { QString str = m_selectedInstance->instanceRoot(); - openDirInDefaultProgram ( QDir(str).absolutePath() ); + openDirInDefaultProgram(QDir(str).absolutePath()); } } @@ -430,59 +423,61 @@ void MainWindow::on_actionEditInstMods_triggered() if (m_selectedInstance) { auto dialog = m_selectedInstance->createModEditDialog(this); - if(dialog) + if (dialog) dialog->exec(); dialog->deleteLater(); } } -void MainWindow::closeEvent ( QCloseEvent *event ) +void MainWindow::closeEvent(QCloseEvent *event) { // Save the window state and geometry. // TODO: Make this work with the new settings system. -// settings->getConfig().setValue("MainWindowGeometry", saveGeometry()); -// settings->getConfig().setValue("MainWindowState", saveState()); - QMainWindow::closeEvent ( event ); + // settings->getConfig().setValue("MainWindowGeometry", saveGeometry()); + // settings->getConfig().setValue("MainWindowState", saveState()); + QMainWindow::closeEvent(event); } -void MainWindow::on_instanceView_customContextMenuRequested ( const QPoint &pos ) +void MainWindow::on_instanceView_customContextMenuRequested(const QPoint &pos) { - QMenu *instContextMenu = new QMenu ( "Instance", this ); + QMenu *instContextMenu = new QMenu("Instance", this); // Add the actions from the toolbar to the context menu. - instContextMenu->addActions ( ui->instanceToolBar->actions() ); + instContextMenu->addActions(ui->instanceToolBar->actions()); - instContextMenu->exec ( view->mapToGlobal ( pos ) ); + instContextMenu->exec(view->mapToGlobal(pos)); } void MainWindow::on_actionLaunchInstance_triggered() { - if(m_selectedInstance) + if (m_selectedInstance) { doLogin(); } } -void MainWindow::doLogin(const QString& errorMsg) +void MainWindow::doLogin(const QString &errorMsg) { if (!m_selectedInstance) return; - - LoginDialog* loginDlg = new LoginDialog(this, errorMsg); + + LoginDialog *loginDlg = new LoginDialog(this, errorMsg); if (!m_selectedInstance->lastLaunch()) loginDlg->forceOnline(); - + loginDlg->exec(); - if(loginDlg->result() == QDialog::Accepted) + if (loginDlg->result() == QDialog::Accepted) { - if (loginDlg->isOnline()) + if (loginDlg->isOnline()) { UserInfo uInfo{loginDlg->getUsername(), loginDlg->getPassword()}; - ProgressDialog* tDialog = new ProgressDialog(this); - LoginTask* loginTask = new LoginTask(uInfo, tDialog); - connect(loginTask, SIGNAL(succeeded()),SLOT(onLoginComplete()), Qt::QueuedConnection); - connect(loginTask, SIGNAL(failed(QString)), SLOT(doLogin(QString)), Qt::QueuedConnection); + ProgressDialog *tDialog = new ProgressDialog(this); + LoginTask *loginTask = new LoginTask(uInfo, tDialog); + connect(loginTask, SIGNAL(succeeded()), SLOT(onLoginComplete()), + Qt::QueuedConnection); + connect(loginTask, SIGNAL(failed(QString)), SLOT(doLogin(QString)), + Qt::QueuedConnection); m_activeInst = m_selectedInstance; tDialog->exec(loginTask); } @@ -490,7 +485,7 @@ void MainWindow::doLogin(const QString& errorMsg) { QString user = loginDlg->getUsername(); if (user.length() == 0) - user = QString("Offline"); + user = QString("Offline"); m_activeLogin = {user, QString("Offline"), QString(), QString()}; m_activeInst = m_selectedInstance; launchInstance(m_activeInst, m_activeLogin); @@ -500,20 +495,20 @@ void MainWindow::doLogin(const QString& errorMsg) void MainWindow::onLoginComplete() { - if(!m_activeInst) + if (!m_activeInst) return; - LoginTask * task = (LoginTask *) QObject::sender(); + LoginTask *task = (LoginTask *)QObject::sender(); m_activeLogin = task->getResult(); - + BaseUpdate *updateTask = m_activeInst->doUpdate(); - if(!updateTask) + if (!updateTask) { launchInstance(m_activeInst, m_activeLogin); } else { ProgressDialog *tDialog = new ProgressDialog(this); - connect(updateTask, SIGNAL(succeeded()),SLOT(onGameUpdateComplete())); + connect(updateTask, SIGNAL(succeeded()), SLOT(onGameUpdateComplete())); connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); tDialog->exec(updateTask); } @@ -532,11 +527,11 @@ void MainWindow::onGameUpdateError(QString error) void MainWindow::launchInstance(BaseInstance *instance, LoginResponse response) { Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); - + proc = instance->prepareForLaunch(response); - if(!proc) + if (!proc) return; - + // Prepare GUI: If it shall stay open disable the required parts if (MMC->settings()->get("NoHide").toBool()) { @@ -546,11 +541,11 @@ void MainWindow::launchInstance(BaseInstance *instance, LoginResponse response) { this->hide(); } - + console = new ConsoleWindow(proc); console->show(); - connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), - console, SLOT(write(QString, MessageLevel::Enum))); + connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, + SLOT(write(QString, MessageLevel::Enum))); connect(proc, SIGNAL(ended()), this, SLOT(instanceEnded())); proc->setLogin(response.username, response.session_id); proc->launch(); @@ -566,7 +561,7 @@ void MainWindow::taskEnd() QObject *sender = QObject::sender(); if (sender == m_versionLoadTask) m_versionLoadTask = NULL; - + sender->deleteLater(); } @@ -578,20 +573,24 @@ void MainWindow::startTask(Task *task) task->start(); } - // Create A Desktop Shortcut void MainWindow::on_actionMakeDesktopShortcut_triggered() { - QString name ( "Test" ); - name = QInputDialog::getText ( this, tr ( "MultiMC Shortcut" ), tr ( "Enter a Shortcut Name." ), QLineEdit::Normal, name ); + QString name("Test"); + name = QInputDialog::getText(this, tr("MultiMC Shortcut"), tr("Enter a Shortcut Name."), + QLineEdit::Normal, name); - Util::createShortCut ( Util::getDesktopDir(), QApplication::instance()->applicationFilePath(), QStringList() << "-dl" << QDir::currentPath() << "test", name, "application-x-octet-stream" ); + Util::createShortCut(Util::getDesktopDir(), QApplication::instance()->applicationFilePath(), + QStringList() << "-dl" << QDir::currentPath() << "test", name, + "application-x-octet-stream"); - QMessageBox::warning ( this, tr("Not useful"), tr("A Dummy Shortcut was created. it will not do anything productive") ); + QMessageBox::warning( + this, tr("Not useful"), + tr("A Dummy Shortcut was created. it will not do anything productive")); } // BrowserDialog -void MainWindow::openWebPage ( QUrl url ) +void MainWindow::openWebPage(QUrl url) { QDesktopServices::openUrl(url); } @@ -600,8 +599,8 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() { if (view->selectionModel()->selectedIndexes().count() < 1) return; - - VersionSelectDialog vselect(m_selectedInstance->versionList().data(), this); + + VersionSelectDialog vselect(m_selectedInstance->versionList().get(), this); if (vselect.exec() && vselect.selectedVersion()) { m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor()); @@ -612,12 +611,12 @@ void MainWindow::on_actionChangeInstLWJGLVersion_triggered() { if (!m_selectedInstance) return; - + LWJGLSelectDialog lselect(this); lselect.exec(); if (lselect.result() == QDialog::Accepted) { - LegacyInstance * linst = (LegacyInstance *) m_selectedInstance; + LegacyInstance *linst = (LegacyInstance *)m_selectedInstance; linst->setLWJGLVersion(lselect.selectedVersion()); } } @@ -632,18 +631,23 @@ void MainWindow::on_actionInstanceSettings_triggered() settings.exec(); } -void MainWindow::instanceChanged( const QModelIndex& current, const QModelIndex& previous ) +void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex &previous) { - if(current.isValid() && nullptr != (m_selectedInstance = (BaseInstance *) current.data(InstanceList::InstancePointerRole).value())) + if (current.isValid() && + nullptr != (m_selectedInstance = + (BaseInstance *)current.data(InstanceList::InstancePointerRole) + .value())) { ui->instanceToolBar->setEnabled(true); QString iconKey = m_selectedInstance->iconKey(); renameButton->setText(m_selectedInstance->name()); - ui->actionChangeInstLWJGLVersion->setEnabled(m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion")); - ui->actionEditInstMods->setEnabled(m_selectedInstance->menuActionEnabled("actionEditInstMods")); + ui->actionChangeInstLWJGLVersion->setEnabled( + m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion")); + ui->actionEditInstMods->setEnabled( + m_selectedInstance->menuActionEnabled("actionEditInstMods")); statusBar()->clearMessage(); statusBar()->showMessage(m_selectedInstance->getStatusbarDescription()); - auto ico =MMC->icons()->getIcon(iconKey); + auto ico = MMC->icons()->getIcon(iconKey); ui->actionChangeInstIcon->setIcon(ico); } else @@ -663,19 +667,17 @@ void MainWindow::selectionBad() ui->actionChangeInstIcon->setIcon(ico); } - - void MainWindow::on_actionEditInstNotes_triggered() { if (!m_selectedInstance) return; - LegacyInstance * linst = (LegacyInstance *) m_selectedInstance; - + LegacyInstance *linst = (LegacyInstance *)m_selectedInstance; + EditNotesDialog noteedit(linst->notes(), linst->name(), this); noteedit.exec(); if (noteedit.result() == QDialog::Accepted) { - + linst->setNotes(noteedit.getText()); } } diff --git a/gui/newinstancedialog.cpp b/gui/newinstancedialog.cpp index 859077d9..c035302c 100644 --- a/gui/newinstancedialog.cpp +++ b/gui/newinstancedialog.cpp @@ -96,7 +96,7 @@ BaseVersionPtr NewInstanceDialog::selectedVersion() const void NewInstanceDialog::on_btnChangeVersion_clicked() { - VersionSelectDialog vselect(MMC->minecraftlist().data(), this); + VersionSelectDialog vselect(MMC->minecraftlist().get(), this); vselect.exec(); if (vselect.result() == QDialog::Accepted) { diff --git a/gui/settingsdialog.cpp b/gui/settingsdialog.cpp index 9736c1c7..31fe2d96 100644 --- a/gui/settingsdialog.cpp +++ b/gui/settingsdialog.cpp @@ -27,7 +27,7 @@ SettingsDialog::SettingsDialog(QWidget *parent) : { ui->setupUi(this); - loadSettings(MMC->settings().data()); + loadSettings(MMC->settings().get()); updateCheckboxStuff(); } @@ -85,7 +85,7 @@ void SettingsDialog::on_maximizedCheckBox_clicked(bool checked) void SettingsDialog::on_buttonBox_accepted() { - applySettings(MMC->settings().data()); + applySettings(MMC->settings().get()); } void SettingsDialog::applySettings(SettingsObject *s) diff --git a/gui/versionselectdialog.cpp b/gui/versionselectdialog.cpp index 1e60c7d9..ff990188 100644 --- a/gui/versionselectdialog.cpp +++ b/gui/versionselectdialog.cpp @@ -91,6 +91,6 @@ void VersionSelectDialog::setFilter(int column, QString filter) if (filteredTypes.length() > 0) regexStr = QString("^((?!%1).)*$").arg(filteredTypes.join('|')); - qDebug() << "Filter:" << regexStr; + QLOG_DEBUG() << "Filter:" << regexStr; */ } diff --git a/logger/QsDebugOutput.cpp b/logger/QsDebugOutput.cpp new file mode 100644 index 00000000..d68cd5e9 --- /dev/null +++ b/logger/QsDebugOutput.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2010, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsDebugOutput.h" +#include +#include + +#if defined(Q_OS_WIN) +#define WIN32_LEAN_AND_MEAN +#include +void QsDebugOutput::output(const QString &message) +{ + OutputDebugStringW(reinterpret_cast(message.utf16())); + OutputDebugStringW(L"\n"); +} +#elif defined(Q_OS_SYMBIAN) +#include +void QsDebugOutput::output(const QString &message) +{ + TPtrC8 symbianMessage(reinterpret_cast(qPrintable(message))); + RDebug::RawPrint(symbianMessage); +} +#elif defined(Q_OS_UNIX) +#include +void QsDebugOutput::output(const QString &message) +{ + fprintf(stderr, "%s\n", qPrintable(message)); + fflush(stderr); +} +#endif diff --git a/logger/QsDebugOutput.h b/logger/QsDebugOutput.h new file mode 100644 index 00000000..8c759a6d --- /dev/null +++ b/logger/QsDebugOutput.h @@ -0,0 +1,34 @@ +// Copyright (c) 2010, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +class QString; + +class QsDebugOutput +{ +public: + static void output(const QString &a_message); +}; diff --git a/logger/QsLog.cpp b/logger/QsLog.cpp new file mode 100644 index 00000000..b2a7f465 --- /dev/null +++ b/logger/QsLog.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2010, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsLog.h" +#include "QsLogDest.h" +#include +#include +#include +#include +#include +#include +#include + +namespace QsLogging +{ +typedef QList DestinationList; + +static const char *LevelStrings[] = {"TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" + "UNKNOWN"}; + +// not using Qt::ISODate because we need the milliseconds too +static const QString fmtDateTime("hhhh:mm:ss.zzz"); + +static const char *LevelToText(Level theLevel) +{ + if (theLevel > FatalLevel) + { + assert(!"bad log level"); + return LevelStrings[UnknownLevel]; + } + return LevelStrings[theLevel]; +} + +class LoggerImpl +{ +public: + LoggerImpl() : level(InfoLevel), start_time(QDateTime::currentDateTime()) + { + } + QMutex logMutex; + Level level; + DestinationList destList; + QDateTime start_time; +}; + +Logger::Logger() : d(new LoggerImpl) +{ +} + +Logger::~Logger() +{ + delete d; +} + +void Logger::addDestination(Destination *destination) +{ + assert(destination); + d->destList.push_back(destination); +} + +void Logger::setLoggingLevel(Level newLevel) +{ + d->level = newLevel; +} + +Level Logger::loggingLevel() const +{ + return d->level; +} + +//! creates the complete log message and passes it to the logger +void Logger::Helper::writeToLog() +{ + const char *const levelName = LevelToText(level); + const QString completeMessage(QString("%1\t%2\t%3") + .arg(QDateTime::currentDateTime().toString(fmtDateTime)) + .arg(levelName, 5) + .arg(buffer)); + + Logger &logger = Logger::instance(); + QMutexLocker lock(&logger.d->logMutex); + logger.write(completeMessage); +} + +Logger::Helper::Helper(Level logLevel) : level(logLevel), qtDebug(&buffer) +{ +} + +Logger::Helper::~Helper() +{ + try + { + writeToLog(); + } + catch (std::exception &e) + { + // you shouldn't throw exceptions from a sink + Q_UNUSED(e); + assert(!"exception in logger helper destructor"); + throw; + } +} + +//! sends the message to all the destinations +void Logger::write(const QString &message) +{ + for (DestinationList::iterator it = d->destList.begin(), endIt = d->destList.end(); + it != endIt; ++it) + { + if (!(*it)) + { + assert(!"null log destination"); + continue; + } + (*it)->write(message); + } +} + +} // end namespace diff --git a/logger/QsLog.h b/logger/QsLog.h new file mode 100644 index 00000000..a18c08de --- /dev/null +++ b/logger/QsLog.h @@ -0,0 +1,130 @@ +// Copyright (c) 2010, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +#include + +namespace QsLogging +{ +class Destination; +enum Level +{ + TraceLevel = 0, + DebugLevel, + InfoLevel, + WarnLevel, + ErrorLevel, + FatalLevel, + UnknownLevel +}; + +class LoggerImpl; // d pointer +class Logger +{ +public: + static Logger &instance() + { + static Logger staticLog; + return staticLog; + } + + //! Adds a log message destination. Don't add null destinations. + void addDestination(Destination *destination); + //! Logging at a level < 'newLevel' will be ignored + void setLoggingLevel(Level newLevel); + //! The default level is INFO + Level loggingLevel() const; + + //! The helper forwards the streaming to QDebug and builds the final + //! log message. + class Helper + { + public: + explicit Helper(Level logLevel); + ~Helper(); + QDebug &stream() + { + return qtDebug; + } + + private: + void writeToLog(); + + Level level; + QString buffer; + QDebug qtDebug; + }; + +private: + Logger(); + Logger(const Logger &); + Logger &operator=(const Logger &); + ~Logger(); + + void write(const QString &message); + + LoggerImpl *d; +}; + +} // end namespace + +#define QLOG_TRACE() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::TraceLevel) \ + QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() +#define QLOG_DEBUG() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::DebugLevel) \ + QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() +#define QLOG_INFO() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::InfoLevel) \ + QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() +#define QLOG_WARN() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::WarnLevel) \ + QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() +#define QLOG_ERROR() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::ErrorLevel) \ + QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() +#define QLOG_FATAL() QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() + +/* +#define QLOG_TRACE() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::TraceLevel) \ + QsLogging::Logger::Helper(QsLogging::TraceLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_DEBUG() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::DebugLevel) \ + QsLogging::Logger::Helper(QsLogging::DebugLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_INFO() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::InfoLevel) \ + QsLogging::Logger::Helper(QsLogging::InfoLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_WARN() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::WarnLevel) \ + QsLogging::Logger::Helper(QsLogging::WarnLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_ERROR() \ + if (QsLogging::Logger::instance().loggingLevel() <= QsLogging::ErrorLevel) \ + QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() << __FILE__ << '@' << __LINE__ +#define QLOG_FATAL() \ + QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() << __FILE__ << '@' << __LINE__ +*/ \ No newline at end of file diff --git a/logger/QsLogDest.cpp b/logger/QsLogDest.cpp new file mode 100644 index 00000000..36297a14 --- /dev/null +++ b/logger/QsLogDest.cpp @@ -0,0 +1,83 @@ +// Copyright (c) 2010, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "QsLogDest.h" +#include "QsDebugOutput.h" +#include +#include +#include + +namespace QsLogging +{ + +//! file message sink +class FileDestination : public Destination +{ +public: + FileDestination(const QString &filePath); + virtual void write(const QString &message); + +private: + QFile mFile; + QTextStream mOutputStream; +}; + +FileDestination::FileDestination(const QString &filePath) +{ + mFile.setFileName(filePath); + mFile.open(QFile::WriteOnly | QFile::Text | + QFile::Truncate); // fixme: should throw on failure + mOutputStream.setDevice(&mFile); +} + +void FileDestination::write(const QString &message) +{ + mOutputStream << message << endl; + mOutputStream.flush(); +} + +//! debugger sink +class DebugOutputDestination : public Destination +{ +public: + virtual void write(const QString &message); +}; + +void DebugOutputDestination::write(const QString &message) +{ + QsDebugOutput::output(message); +} + +DestinationPtr DestinationFactory::MakeFileDestination(const QString &filePath) +{ + return DestinationPtr(new FileDestination(filePath)); +} + +DestinationPtr DestinationFactory::MakeDebugOutputDestination() +{ + return DestinationPtr(new DebugOutputDestination); +} + +} // end namespace diff --git a/logger/QsLogDest.h b/logger/QsLogDest.h new file mode 100644 index 00000000..32f1a9d0 --- /dev/null +++ b/logger/QsLogDest.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010, Razvan Petru +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: + +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this +// list of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// * The name of the contributors may not be used to endorse or promote products +// derived from this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +// OF THE POSSIBILITY OF SUCH DAMAGE. + +#pragma once + +#include +class QString; + +namespace QsLogging +{ + +class Destination +{ +public: + virtual ~Destination() + { + } + virtual void write(const QString &message) = 0; +}; +typedef std::shared_ptr DestinationPtr; + +//! Creates logging destinations/sinks. The caller will have ownership of +//! the newly created destinations. +class DestinationFactory +{ +public: + static DestinationPtr MakeFileDestination(const QString &filePath); + static DestinationPtr MakeDebugOutputDestination(); +}; + +} // end namespace diff --git a/logic/BaseInstance.cpp b/logic/BaseInstance.cpp index ec86596a..6a6b195b 100644 --- a/logic/BaseInstance.cpp +++ b/logic/BaseInstance.cpp @@ -132,7 +132,7 @@ InstanceList *BaseInstance::instList() const return NULL; } -QSharedPointer BaseInstance::versionList() const +std::shared_ptr BaseInstance::versionList() const { return MMC->minecraftlist(); } diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index 0056327a..e360d3ae 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -135,7 +135,7 @@ public: * \brief Gets a pointer to this instance's version list. * \return A pointer to the available version list for this instance. */ - virtual QSharedPointer versionList() const; + virtual std::shared_ptr versionList() const; /*! * \brief Gets this instance's settings object. @@ -179,9 +179,9 @@ signals: void nuked(BaseInstance * inst); protected: - QSharedPointer inst_d; + std::shared_ptr inst_d; }; // pointer for lazy people -typedef QSharedPointer InstancePtr; +typedef std::shared_ptr InstancePtr; diff --git a/logic/BaseInstance_p.h b/logic/BaseInstance_p.h index a30916a4..06c0c0ba 100644 --- a/logic/BaseInstance_p.h +++ b/logic/BaseInstance_p.h @@ -4,7 +4,7 @@ class BaseInstance; -#define I_D(Class) Class##Private * const d = (Class##Private * const) inst_d.data() +#define I_D(Class) Class##Private * const d = (Class##Private * const) inst_d.get() struct BaseInstancePrivate { diff --git a/logic/BaseVersion.h b/logic/BaseVersion.h index be717fee..01745c46 100644 --- a/logic/BaseVersion.h +++ b/logic/BaseVersion.h @@ -14,7 +14,7 @@ */ #pragma once -#include +#include /*! * An abstract base class for versions. @@ -40,6 +40,6 @@ struct BaseVersion virtual QString typeString() const = 0; }; -typedef QSharedPointer BaseVersionPtr; +typedef std::shared_ptr BaseVersionPtr; Q_DECLARE_METATYPE( BaseVersionPtr ) \ No newline at end of file diff --git a/logic/ForgeInstaller.cpp b/logic/ForgeInstaller.cpp index 9ae3f1e1..a946dd44 100644 --- a/logic/ForgeInstaller.cpp +++ b/logic/ForgeInstaller.cpp @@ -10,7 +10,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) { - QSharedPointer newVersion; + std::shared_ptr newVersion; m_universal_url = universal_url; QuaZip zip(filename); @@ -88,7 +88,7 @@ ForgeInstaller::ForgeInstaller(QString filename, QString universal_url) realVersionId = m_forge_version->id = installObj.value("minecraft").toString(); } -bool ForgeInstaller::apply(QSharedPointer to) +bool ForgeInstaller::apply(std::shared_ptr to) { if (!m_forge_version) return false; diff --git a/logic/ForgeInstaller.h b/logic/ForgeInstaller.h index f4ceaaef..f6f22a2a 100644 --- a/logic/ForgeInstaller.h +++ b/logic/ForgeInstaller.h @@ -1,6 +1,6 @@ #pragma once #include -#include +#include class OneSixVersion; @@ -9,11 +9,11 @@ class ForgeInstaller public: ForgeInstaller(QString filename, QString universal_url); - bool apply(QSharedPointer to); + bool apply(std::shared_ptr to); private: // the version, read from the installer - QSharedPointer m_forge_version; + std::shared_ptr m_forge_version; QString internalPath; QString finalPath; QString realVersionId; diff --git a/logic/InstanceFactory.cpp b/logic/InstanceFactory.cpp index b5832ce5..0da62803 100644 --- a/logic/InstanceFactory.cpp +++ b/logic/InstanceFactory.cpp @@ -30,6 +30,7 @@ #include #include "pathutils.h" +#include InstanceFactory InstanceFactory::loader; @@ -72,12 +73,12 @@ InstanceFactory::InstCreateError InstanceFactory::createInstance( BaseInstance*& { QDir rootDir(instDir); - qDebug(instDir.toUtf8()); + QLOG_DEBUG() << instDir.toUtf8(); if (!rootDir.exists() && !rootDir.mkpath(".")) { return InstanceFactory::CantCreateDir; } - auto mcVer = version.dynamicCast(); + auto mcVer = std::dynamic_pointer_cast(version); if(!mcVer) return InstanceFactory::NoSuchVersion; diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp index 93b87f23..720052a3 100644 --- a/logic/InstanceLauncher.cpp +++ b/logic/InstanceLauncher.cpp @@ -61,7 +61,7 @@ int InstanceLauncher::launch() { std::cout << "Launching Instance '" << qPrintable ( instId ) << "'" << std::endl; auto instance = MMC->instances()->getInstanceById(instId); - if ( instance.isNull() ) + if ( !instance ) { std::cout << "Could not find instance requested. note that you have to specify the ID, not the NAME" << std::endl; return 1; diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 4f367980..2ffcb075 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -92,7 +92,7 @@ void LegacyInstance::cleanupAfterRun() //FIXME: delete the launcher and icons and whatnot. } -QSharedPointer< ModList > LegacyInstance::coreModList() +std::shared_ptr< ModList > LegacyInstance::coreModList() { I_D(LegacyInstance); if(!d->core_mod_list) @@ -104,7 +104,7 @@ QSharedPointer< ModList > LegacyInstance::coreModList() return d->core_mod_list; } -QSharedPointer< ModList > LegacyInstance::jarModList() +std::shared_ptr< ModList > LegacyInstance::jarModList() { I_D(LegacyInstance); if(!d->jar_mod_list) @@ -124,7 +124,7 @@ void LegacyInstance::jarModsChanged() } -QSharedPointer< ModList > LegacyInstance::loaderModList() +std::shared_ptr< ModList > LegacyInstance::loaderModList() { I_D(LegacyInstance); if(!d->loader_mod_list) @@ -136,7 +136,7 @@ QSharedPointer< ModList > LegacyInstance::loaderModList() return d->loader_mod_list; } -QSharedPointer< ModList > LegacyInstance::texturePackList() +std::shared_ptr< ModList > LegacyInstance::texturePackList() { I_D(LegacyInstance); if(!d->texture_pack_list) diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 2eab9035..d7438cca 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -19,10 +19,10 @@ public: QString modListFile() const; ////// Mod Lists ////// - QSharedPointer jarModList(); - QSharedPointer coreModList(); - QSharedPointer loaderModList(); - QSharedPointer texturePackList(); + std::shared_ptr jarModList(); + std::shared_ptr coreModList(); + std::shared_ptr loaderModList(); + std::shared_ptr texturePackList(); ////// Directories ////// QString savesDir() const; diff --git a/logic/LegacyInstance_p.h b/logic/LegacyInstance_p.h index d1f417fe..0809b8d2 100644 --- a/logic/LegacyInstance_p.h +++ b/logic/LegacyInstance_p.h @@ -9,8 +9,8 @@ class ModList; struct LegacyInstancePrivate: public BaseInstancePrivate { - QSharedPointer jar_mod_list; - QSharedPointer core_mod_list; - QSharedPointer loader_mod_list; - QSharedPointer texture_pack_list; + std::shared_ptr jar_mod_list; + std::shared_ptr core_mod_list; + std::shared_ptr loader_mod_list; + std::shared_ptr texture_pack_list; }; \ No newline at end of file diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index d8e622dd..5f5a2e52 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -9,9 +9,11 @@ #include #include #include +#include - -LegacyUpdate::LegacyUpdate ( BaseInstance* inst, QObject* parent ) : BaseUpdate ( inst, parent ) {} +LegacyUpdate::LegacyUpdate(BaseInstance *inst, QObject *parent) : BaseUpdate(inst, parent) +{ +} void LegacyUpdate::executeTask() { @@ -20,35 +22,35 @@ void LegacyUpdate::executeTask() void LegacyUpdate::lwjglStart() { - LegacyInstance * inst = (LegacyInstance *) m_inst; + LegacyInstance *inst = (LegacyInstance *)m_inst; + + lwjglVersion = inst->lwjglVersion(); + lwjglTargetPath = PathCombine("lwjgl", lwjglVersion); + lwjglNativesPath = PathCombine(lwjglTargetPath, "natives"); - lwjglVersion = inst->lwjglVersion(); - lwjglTargetPath = PathCombine("lwjgl", lwjglVersion ); - lwjglNativesPath = PathCombine( lwjglTargetPath, "natives"); - // if the 'done' file exists, we don't have to download this again QFileInfo doneFile(PathCombine(lwjglTargetPath, "done")); - if(doneFile.exists()) + if (doneFile.exists()) { jarStart(); return; } - + auto list = MMC->lwjgllist(); - if(!list->isLoaded()) + if (!list->isLoaded()) { emitFailed("Too soon! Let the LWJGL list load :)"); return; } - + setStatus("Downloading new LWJGL."); auto version = list->getVersion(lwjglVersion); - if(!version) + if (!version) { emitFailed("Game update failed: the selected LWJGL version is invalid."); return; } - + QString url = version->url(); QUrl realUrl(url); QString hostname = realUrl.host(); @@ -56,39 +58,42 @@ void LegacyUpdate::lwjglStart() QNetworkRequest req(realUrl); req.setRawHeader("Host", hostname.toLatin1()); req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)"); - QNetworkReply * rep = worker->get ( req ); - - m_reply = QSharedPointer (rep, &QObject::deleteLater); - connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); - connect(worker.data(), SIGNAL(finished(QNetworkReply*)), SLOT(lwjglFinished(QNetworkReply*))); - //connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(downloadError(QNetworkReply::NetworkError))); + QNetworkReply *rep = worker->get(req); + + m_reply = std::shared_ptr(rep); + connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); + connect(worker.get(), SIGNAL(finished(QNetworkReply *)), + SLOT(lwjglFinished(QNetworkReply *))); + // connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), + // SLOT(downloadError(QNetworkReply::NetworkError))); } -void LegacyUpdate::lwjglFinished(QNetworkReply* reply) +void LegacyUpdate::lwjglFinished(QNetworkReply *reply) { - if(m_reply != reply) + if (m_reply.get() != reply) { return; } - if(reply->error() != QNetworkReply::NoError) + if (reply->error() != QNetworkReply::NoError) { - emitFailed( "Failed to download: "+ - reply->errorString()+ - "\nSometimes you have to wait a bit if you download many LWJGL versions in a row. YMMV"); + emitFailed("Failed to download: " + reply->errorString() + + "\nSometimes you have to wait a bit if you download many LWJGL versions in " + "a row. YMMV"); return; } auto worker = MMC->qnam(); - //Here i check if there is a cookie for me in the reply and extract it - QList cookies = qvariant_cast>(reply->header(QNetworkRequest::SetCookieHeader)); - if(cookies.count() != 0) + // Here i check if there is a cookie for me in the reply and extract it + QList cookies = + qvariant_cast>(reply->header(QNetworkRequest::SetCookieHeader)); + if (cookies.count() != 0) { - //you must tell which cookie goes with which url + // you must tell which cookie goes with which url worker->cookieJar()->setCookiesFromUrl(cookies, QUrl("sourceforge.net")); } - //here you can check for the 302 or whatever other header i need + // here you can check for the 302 or whatever other header i need QVariant newLoc = reply->header(QNetworkRequest::LocationHeader); - if(newLoc.isValid()) + if (newLoc.isValid()) { QString redirectedTo = reply->header(QNetworkRequest::LocationHeader).toString(); QUrl realUrl(redirectedTo); @@ -96,9 +101,10 @@ void LegacyUpdate::lwjglFinished(QNetworkReply* reply) QNetworkRequest req(redirectedTo); req.setRawHeader("Host", hostname.toLatin1()); req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)"); - QNetworkReply * rep = worker->get(req); - connect(rep, SIGNAL(downloadProgress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); - m_reply = QSharedPointer (rep, &QObject::deleteLater); + QNetworkReply *rep = worker->get(req); + connect(rep, SIGNAL(downloadProgress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + m_reply = std::shared_ptr(rep); return; } QFile saveMe("lwjgl.zip"); @@ -114,26 +120,26 @@ void LegacyUpdate::extractLwjgl() // make sure the directories are there bool success = ensureFolderPathExists(lwjglNativesPath); - - if(!success) + + if (!success) { emitFailed("Failed to extract the lwjgl libs - error when creating required folders."); return; } - + QuaZip zip("lwjgl.zip"); - if(!zip.open(QuaZip::mdUnzip)) + if (!zip.open(QuaZip::mdUnzip)) { emitFailed("Failed to extract the lwjgl libs - not a valid archive."); return; } - + // and now we are going to access files inside it QuaZipFile file(&zip); - const QString jarNames[] = { "jinput.jar", "lwjgl_util.jar", "lwjgl.jar" }; - for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) + const QString jarNames[] = {"jinput.jar", "lwjgl_util.jar", "lwjgl.jar"}; + for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) { - if(!file.open(QIODevice::ReadOnly)) + if (!file.open(QIODevice::ReadOnly)) { zip.close(); emitFailed("Failed to extract the lwjgl libs - error while reading archive."); @@ -141,7 +147,7 @@ void LegacyUpdate::extractLwjgl() } QuaZipFileInfo info; QString name = file.getActualFileName(); - if(name.endsWith('/')) + if (name.endsWith('/')) { file.close(); continue; @@ -156,25 +162,25 @@ void LegacyUpdate::extractLwjgl() } } // Not found? look for the natives - if(destFileName.isEmpty()) + if (destFileName.isEmpty()) { #ifdef Q_OS_WIN32 QString nativesDir = "windows"; #else - #ifdef Q_OS_MAC +#ifdef Q_OS_MAC QString nativesDir = "macosx"; - #else +#else QString nativesDir = "linux"; - #endif +#endif #endif if (name.contains(nativesDir)) { int lastSlash = name.lastIndexOf('/'); int lastBackSlash = name.lastIndexOf('\\'); - if(lastSlash != -1) - name = name.mid(lastSlash+1); - else if(lastBackSlash != -1) - name = name.mid(lastBackSlash+1); + if (lastSlash != -1) + name = name.mid(lastSlash + 1); + else if (lastBackSlash != -1) + name = name.mid(lastBackSlash + 1); destFileName = PathCombine(lwjglNativesPath, name); } } @@ -190,7 +196,7 @@ void LegacyUpdate::extractLwjgl() file.close(); // do not forget to close! } zip.close(); - m_reply.clear(); + m_reply.reset(); QFile doneFile(PathCombine(lwjglTargetPath, "done")); doneFile.open(QIODevice::WriteOnly); doneFile.write("done."); @@ -204,13 +210,13 @@ void LegacyUpdate::lwjglFailed() void LegacyUpdate::jarStart() { - LegacyInstance * inst = (LegacyInstance *) m_inst; - if(!inst->shouldUpdate() || inst->shouldUseCustomBaseJar()) + LegacyInstance *inst = (LegacyInstance *)m_inst; + if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar()) { ModTheJar(); return; } - + setStatus("Checking for jar updates..."); // Make directories QDir binDir(inst->binDir()); @@ -226,13 +232,13 @@ void LegacyUpdate::jarStart() QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); QString intended_version_id = inst->intendedVersionId(); urlstr += intended_version_id + "/" + intended_version_id + ".jar"; - + auto dljob = new DownloadJob("Minecraft.jar for version " + intended_version_id); dljob->addFileDownload(QUrl(urlstr), inst->defaultBaseJar()); legacyDownloadJob.reset(dljob); connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); connect(dljob, SIGNAL(failed()), SLOT(jarFailed())); - connect(dljob, SIGNAL(progress(qint64,qint64)), SIGNAL(progress(qint64,qint64))); + connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); legacyDownloadJob->start(); } @@ -248,34 +254,36 @@ void LegacyUpdate::jarFailed() emitFailed("Failed to download the minecraft jar. Try again later."); } -bool LegacyUpdate::MergeZipFiles( QuaZip* into, QFileInfo from, QSet< QString >& contained, MetainfAction metainf ) +bool LegacyUpdate::MergeZipFiles(QuaZip *into, QFileInfo from, QSet &contained, + MetainfAction metainf) { setStatus("Installing mods - Adding " + from.fileName()); - + QuaZip modZip(from.filePath()); modZip.open(QuaZip::mdUnzip); - + QuaZipFile fileInsideMod(&modZip); - QuaZipFile zipOutFile( into ); - for(bool more=modZip.goToFirstFile(); more; more=modZip.goToNextFile()) + QuaZipFile zipOutFile(into); + for (bool more = modZip.goToFirstFile(); more; more = modZip.goToNextFile()) { QString filename = modZip.getCurrentFileName(); - if(filename.contains("META-INF") && metainf == LegacyUpdate::IgnoreMetainf) + if (filename.contains("META-INF") && metainf == LegacyUpdate::IgnoreMetainf) { - qDebug() << "Skipping META-INF " << filename << " from " << from.fileName(); + QLOG_INFO() << "Skipping META-INF " << filename << " from " << from.fileName(); continue; } - if(contained.contains(filename)) + if (contained.contains(filename)) { - qDebug() << "Skipping already contained file " << filename << " from " << from.fileName(); + QLOG_INFO() << "Skipping already contained file " << filename << " from " + << from.fileName(); continue; } contained.insert(filename); - qDebug() << "Adding file " << filename << " from " << from.fileName(); - - if(!fileInsideMod.open(QIODevice::ReadOnly)) + QLOG_INFO() << "Adding file " << filename << " from " << from.fileName(); + + if (!fileInsideMod.open(QIODevice::ReadOnly)) { - qDebug() << "Failed to open " << filename << " from " << from.fileName(); + QLOG_ERROR() << "Failed to open " << filename << " from " << from.fileName(); return false; } /* @@ -286,17 +294,17 @@ bool LegacyUpdate::MergeZipFiles( QuaZip* into, QFileInfo from, QSet< QString >& /* info_out.externalAttr = old_info.externalAttr; */ - if(!zipOutFile.open(QIODevice::WriteOnly, info_out)) + if (!zipOutFile.open(QIODevice::WriteOnly, info_out)) { - qDebug() << "Failed to open " << filename << " in the jar"; + QLOG_ERROR() << "Failed to open " << filename << " in the jar"; fileInsideMod.close(); return false; } - if(!JlCompress::copyData(fileInsideMod, zipOutFile)) + if (!JlCompress::copyData(fileInsideMod, zipOutFile)) { zipOutFile.close(); fileInsideMod.close(); - qDebug() << "Failed to copy data of " << filename << " into the jar"; + QLOG_ERROR() << "Failed to copy data of " << filename << " into the jar"; return false; } zipOutFile.close(); @@ -307,34 +315,34 @@ bool LegacyUpdate::MergeZipFiles( QuaZip* into, QFileInfo from, QSet< QString >& void LegacyUpdate::ModTheJar() { - LegacyInstance * inst = (LegacyInstance *) m_inst; - - if(!inst->shouldRebuild()) + LegacyInstance *inst = (LegacyInstance *)m_inst; + + if (!inst->shouldRebuild()) { emitSucceeded(); return; } - + // Get the mod list auto modList = inst->jarModList(); - - QFileInfo runnableJar (inst->runnableJar()); - QFileInfo baseJar (inst->baseJar()); + + QFileInfo runnableJar(inst->runnableJar()); + QFileInfo baseJar(inst->baseJar()); bool base_is_custom = inst->shouldUseCustomBaseJar(); - + // Nothing to do if there are no jar mods to install, no backup and just the mc jar - if(base_is_custom) + if (base_is_custom) { // yes, this can happen if the instance only has the runnable jar and not the base jar // it *could* be assumed that such an instance is vanilla, but that wouldn't be safe // because that's not something mmc4 guarantees - if(runnableJar.isFile() && !baseJar.exists() && modList->empty()) + if (runnableJar.isFile() && !baseJar.exists() && modList->empty()) { inst->setShouldRebuild(false); emitSucceeded(); return; } - + setStatus("Installing mods - backing up minecraft.jar..."); if (!baseJar.exists() && !QFile::copy(runnableJar.filePath(), baseJar.filePath())) { @@ -342,24 +350,24 @@ void LegacyUpdate::ModTheJar() return; } } - + if (!baseJar.exists()) { emitFailed("The base jar " + baseJar.filePath() + " does not exist"); return; } - + if (runnableJar.exists() && !QFile::remove(runnableJar.filePath())) { emitFailed("Failed to delete old minecraft.jar"); return; } - - //TaskStep(); // STEP 1 + + // TaskStep(); // STEP 1 setStatus("Installing mods - Opening minecraft.jar"); QuaZip zipOut(runnableJar.filePath()); - if(!zipOut.open(QuaZip::mdCreate)) + if (!zipOut.open(QuaZip::mdCreate)) { QFile::remove(runnableJar.filePath()); emitFailed("Failed to open the minecraft.jar for modding"); @@ -376,7 +384,7 @@ void LegacyUpdate::ModTheJar() auto &mod = modList->operator[](i); if (mod.type() == Mod::MOD_ZIPFILE) { - if(!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf)) + if (!MergeZipFiles(&zipOut, mod.filename(), addedFiles, LegacyUpdate::KeepMetainf)) { zipOut.close(); QFile::remove(runnableJar.filePath()); @@ -387,7 +395,8 @@ void LegacyUpdate::ModTheJar() else if (mod.type() == Mod::MOD_SINGLEFILE) { auto filename = mod.filename(); - if(!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) + if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), + filename.fileName())) { zipOut.close(); QFile::remove(runnableJar.filePath()); @@ -395,7 +404,8 @@ void LegacyUpdate::ModTheJar() return; } addedFiles.insert(filename.fileName()); - qDebug() << "Adding file " << filename.fileName() << " from " << filename.absoluteFilePath(); + QLOG_INFO() << "Adding file " << filename.fileName() << " from " + << filename.absoluteFilePath(); } else if (mod.type() == Mod::MOD_FOLDER) { @@ -404,35 +414,36 @@ void LegacyUpdate::ModTheJar() QDir dir(what_to_zip); dir.cdUp(); QString parent_dir = dir.absolutePath(); - if(!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, true, addedFiles)) + if (!JlCompress::compressSubDir(&zipOut, what_to_zip, parent_dir, true, addedFiles)) { zipOut.close(); QFile::remove(runnableJar.filePath()); emitFailed("Failed to add " + filename.fileName() + " to the jar"); return; } - qDebug() << "Adding folder " << filename.fileName() << " from " << filename.absoluteFilePath(); + QLOG_INFO() << "Adding folder " << filename.fileName() << " from " + << filename.absoluteFilePath(); } } - - if(!MergeZipFiles(&zipOut, baseJar, addedFiles, LegacyUpdate::IgnoreMetainf)) + + if (!MergeZipFiles(&zipOut, baseJar, addedFiles, LegacyUpdate::IgnoreMetainf)) { zipOut.close(); QFile::remove(runnableJar.filePath()); emitFailed("Failed to insert minecraft.jar contents."); return; } - + // Recompress the jar zipOut.close(); - if(zipOut.getZipError()!=0) + if (zipOut.getZipError() != 0) { QFile::remove(runnableJar.filePath()); emitFailed("Failed to finalize minecraft.jar!"); return; - } + } inst->setShouldRebuild(false); - //inst->UpdateVersion(true); + // inst->UpdateVersion(true); emitSucceeded(); return; } \ No newline at end of file diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h index 05c00495..e84ec56a 100644 --- a/logic/LegacyUpdate.h +++ b/logic/LegacyUpdate.h @@ -56,7 +56,7 @@ private: bool MergeZipFiles(QuaZip *into, QFileInfo from, QSet& contained, MetainfAction metainf); private: - QSharedPointer m_reply; + std::shared_ptr m_reply; // target version, determined during this task // MinecraftVersion *targetVersion; diff --git a/logic/Mod.cpp b/logic/Mod.cpp index 38faa760..75e0a3a9 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -20,14 +20,13 @@ #include #include #include -#include #include #include #include "Mod.h" #include #include - +#include Mod::Mod( const QFileInfo& file ) { @@ -134,8 +133,8 @@ void Mod::ReadMCModInfo(QByteArray contents) int version = val.toDouble(); if(version != 2) { - qDebug() << "BAD stuff happened to mod json:"; - qDebug() << contents; + QLOG_ERROR() << "BAD stuff happened to mod json:"; + QLOG_ERROR() << contents; return; } auto arrVal = jsonDoc.object().value("modlist"); diff --git a/logic/ModList.cpp b/logic/ModList.cpp index 84511e4c..a600afff 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -19,9 +19,9 @@ #include #include #include -#include #include #include +#include ModList::ModList ( const QString& dir, const QString& list_file ) : QAbstractListModel(), m_dir(dir), m_list_file(list_file) @@ -39,18 +39,18 @@ void ModList::startWatching() { is_watching = m_watcher->addPath(m_dir.absolutePath()); if(is_watching) - qDebug() << "Started watching " << m_dir.absolutePath(); + QLOG_INFO() << "Started watching " << m_dir.absolutePath(); else - qDebug() << "Failed to start watching " << m_dir.absolutePath(); + QLOG_INFO() << "Failed to start watching " << m_dir.absolutePath(); } void ModList::stopWatching() { is_watching = !m_watcher->removePath(m_dir.absolutePath()); if(!is_watching) - qDebug() << "Stopped watching " << m_dir.absolutePath(); + QLOG_INFO() << "Stopped watching " << m_dir.absolutePath(); else - qDebug() << "Failed to stop watching " << m_dir.absolutePath(); + QLOG_INFO() << "Failed to stop watching " << m_dir.absolutePath(); } @@ -436,7 +436,7 @@ bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int r row = rowCount(); if (column == -1) column = 0; - qDebug() << "Drop row: " << row << " column: " << column; + QLOG_INFO() << "Drop row: " << row << " column: " << column; // files dropped from outside? if(data->hasUrls()) @@ -452,7 +452,7 @@ bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int r continue; QString filename = url.toLocalFile(); installMod(filename, row); - qDebug() << "installing: " << filename; + QLOG_INFO() << "installing: " << filename; } if(was_watching) startWatching(); @@ -466,7 +466,7 @@ bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int r return false; QString remoteId = list[0]; int remoteIndex = list[1].toInt(); - qDebug() << "move: " << sourcestr; + QLOG_INFO() << "move: " << sourcestr; // no moving of things between two lists if(remoteId != m_list_id) return false; diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp index ca7a5534..6aa0a207 100644 --- a/logic/OneSixAssets.cpp +++ b/logic/OneSixAssets.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include "OneSixAssets.h" #include "net/DownloadJob.h" @@ -21,6 +21,7 @@ class ThreadedDeleter : public QThread public: void run() { + QLOG_INFO() << "Cleaning up assets folder..."; QDirIterator iter ( m_base, QDirIterator::Subdirectories ); int base_length = m_base.length(); while ( iter.hasNext() ) @@ -34,12 +35,12 @@ public: trimmedf.remove ( 0, base_length + 1 ); if ( m_whitelist.contains ( trimmedf ) ) { - // qDebug() << trimmedf << " gets to live"; + QLOG_TRACE() << trimmedf << " gets to live"; } else { // DO NOT TOLERATE JUNK - // qDebug() << trimmedf << " dies"; + QLOG_TRACE() << trimmedf << " dies"; QFile f ( filename ); f.remove(); } @@ -67,13 +68,15 @@ void OneSixAssets::fetchXMLFinished() nuke_whitelist.clear(); auto firstJob = index_job->first(); - QByteArray ba = firstJob.dynamicCast()->m_data; + QByteArray ba = std::dynamic_pointer_cast(firstJob)->m_data; QString xmlErrorMsg; QDomDocument doc; if ( !doc.setContent ( ba, false, &xmlErrorMsg ) ) { - qDebug() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << xmlErrorMsg << ba; + QLOG_ERROR() << "Failed to process s3.amazonaws.com/Minecraft.Resources. XML error:" << xmlErrorMsg << ba; + emit failed(); + return; } //QRegExp etag_match(".*([a-f0-9]{32}).*"); QDomNodeList contents = doc.elementsByTagName ( "Contents" ); diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 6e39b5b5..c5d546a3 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -9,6 +9,7 @@ #include #include #include +#include OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj, QObject *parent) @@ -102,7 +103,7 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(LoginResponse response) for (auto lib : libs_to_extract) { QString path = "libraries/" + lib->storagePath(); - qDebug() << "Will extract " << path.toLocal8Bit(); + QLOG_INFO() << "Will extract " << path.toLocal8Bit(); if (JlCompress::extractWithExceptions(path, natives_dir_raw, lib->extract_excludes) .isEmpty()) { @@ -156,7 +157,7 @@ void OneSixInstance::cleanupAfterRun() dir.removeRecursively(); } -QSharedPointer OneSixInstance::loaderModList() +std::shared_ptr OneSixInstance::loaderModList() { I_D(OneSixInstance); if (!d->loader_mod_list) @@ -168,7 +169,7 @@ QSharedPointer OneSixInstance::loaderModList() return d->loader_mod_list; } -QSharedPointer OneSixInstance::resourcePackList() +std::shared_ptr OneSixInstance::resourcePackList() { I_D(OneSixInstance); if (!d->resource_pack_list) @@ -271,7 +272,7 @@ bool OneSixInstance::reloadFullVersion() return false; } -QSharedPointer OneSixInstance::getFullVersion() +std::shared_ptr OneSixInstance::getFullVersion() { I_D(OneSixInstance); return d->version; diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 33091188..8f5c22e6 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -14,8 +14,8 @@ public: ////// Mod Lists ////// - QSharedPointer loaderModList(); - QSharedPointer resourcePackList(); + std::shared_ptr loaderModList(); + std::shared_ptr resourcePackList(); ////// Directories ////// QString resourcePacksDir() const; @@ -40,7 +40,7 @@ public: /// reload the full version json file. return true on success! bool reloadFullVersion(); /// get the current full version info - QSharedPointer getFullVersion(); + std::shared_ptr getFullVersion(); /// revert the current custom version back to base bool revertCustomVersion(); /// customize the current base version diff --git a/logic/OneSixInstance_p.h b/logic/OneSixInstance_p.h index 7b1ca82e..06737b6f 100644 --- a/logic/OneSixInstance_p.h +++ b/logic/OneSixInstance_p.h @@ -7,7 +7,7 @@ struct OneSixInstancePrivate: public BaseInstancePrivate { - QSharedPointer version; - QSharedPointer loader_mod_list; - QSharedPointer resource_pack_list; + std::shared_ptr version; + std::shared_ptr loader_mod_list; + std::shared_ptr resource_pack_list; }; \ No newline at end of file diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 63d42646..0643abe3 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -72,7 +72,7 @@ void OneSixLibrary::addNative(OpSys os, QString suffix) m_is_native = true; m_native_suffixes[os] = suffix; } -void OneSixLibrary::setRules(QList> rules) +void OneSixLibrary::setRules(QList> rules) { m_rules = rules; } diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index 2a16d8e1..a8bcc364 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include "OpSys.h" @@ -14,7 +14,7 @@ private: // basic values used internally (so far) QString m_name; QString m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; - QList > m_rules; + QList > m_rules; // custom values /// absolute URL. takes precedence over m_download_path, if defined @@ -83,7 +83,7 @@ public: /// Attach a name suffix to the specified OS native void addNative(OpSys os, QString suffix); /// Set the load rules - void setRules(QList > rules); + void setRules(QList > rules); /// Returns true if the library should be loaded (or extracted, in case of natives) bool isActive(); diff --git a/logic/OneSixRule.cpp b/logic/OneSixRule.cpp index 545cd641..cb64c9ba 100644 --- a/logic/OneSixRule.cpp +++ b/logic/OneSixRule.cpp @@ -2,9 +2,9 @@ #include #include -QList> rulesFromJsonV4(QJsonObject &objectWithRules) +QList> rulesFromJsonV4(QJsonObject &objectWithRules) { - QList> rules; + QList> rules; auto rulesVal = objectWithRules.value("rules"); if (!rulesVal.isArray()) return rules; @@ -12,7 +12,7 @@ QList> rulesFromJsonV4(QJsonObject &objectWithRules) QJsonArray ruleList = rulesVal.toArray(); for (auto ruleVal : ruleList) { - QSharedPointer rule; + std::shared_ptr rule; if (!ruleVal.isObject()) continue; auto ruleObj = ruleVal.toObject(); diff --git a/logic/OneSixRule.h b/logic/OneSixRule.h index 23d20ff4..6be01f1b 100644 --- a/logic/OneSixRule.h +++ b/logic/OneSixRule.h @@ -11,7 +11,7 @@ enum RuleAction }; RuleAction RuleAction_fromString(QString); -QList> rulesFromJsonV4(QJsonObject &objectWithRules); +QList> rulesFromJsonV4(QJsonObject &objectWithRules); class Rule { @@ -48,9 +48,9 @@ protected: : Rule(result), m_system(system), m_version_regexp(version_regexp) {} public: virtual QJsonObject toJson(); - static QSharedPointer create(RuleAction result, OpSys system, QString version_regexp) + static std::shared_ptr create(RuleAction result, OpSys system, QString version_regexp) { - return QSharedPointer (new OsRule(result, system, version_regexp)); + return std::shared_ptr (new OsRule(result, system, version_regexp)); } }; @@ -65,8 +65,8 @@ protected: : Rule(result) {} public: virtual QJsonObject toJson(); - static QSharedPointer create(RuleAction result) + static std::shared_ptr create(RuleAction result) { - return QSharedPointer (new ImplicitRule(result)); + return std::shared_ptr (new ImplicitRule(result)); } }; diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 41d8f599..008995ae 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -22,8 +22,6 @@ #include #include -#include - #include "BaseInstance.h" #include "lists/MinecraftVersionList.h" #include "OneSixVersion.h" @@ -49,8 +47,8 @@ void OneSixUpdate::executeTask() } // Get a pointer to the version object that corresponds to the instance's version. - targetVersion = - MMC->minecraftlist()->findVersion(intendedVersion).dynamicCast(); + targetVersion = std::dynamic_pointer_cast( + MMC->minecraftlist()->findVersion(intendedVersion)); if (targetVersion == nullptr) { // don't do anything if it was invalid @@ -77,10 +75,9 @@ void OneSixUpdate::versionFileStart() auto job = new DownloadJob("Version index"); job->addByteArrayDownload(QUrl(urlstr)); specificVersionDownloadJob.reset(job); - connect(specificVersionDownloadJob.data(), SIGNAL(succeeded()), - SLOT(versionFileFinished())); - connect(specificVersionDownloadJob.data(), SIGNAL(failed()), SLOT(versionFileFailed())); - connect(specificVersionDownloadJob.data(), SIGNAL(progress(qint64, qint64)), + connect(specificVersionDownloadJob.get(), SIGNAL(succeeded()), SLOT(versionFileFinished())); + connect(specificVersionDownloadJob.get(), SIGNAL(failed()), SLOT(versionFileFailed())); + connect(specificVersionDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); specificVersionDownloadJob->start(); } @@ -103,7 +100,7 @@ void OneSixUpdate::versionFileFinished() emitFailed("Can't open " + version1 + " for writing."); return; } - auto data = DlJob.dynamicCast()->m_data; + auto data = std::dynamic_pointer_cast(DlJob)->m_data; qint64 actual = 0; if ((actual = vfile1.write(data)) != data.size()) { @@ -149,7 +146,7 @@ void OneSixUpdate::jarlibStart() return; } - QSharedPointer version = inst->getFullVersion(); + std::shared_ptr version = inst->getFullVersion(); // download the right jar, save it in versions/$version/$version.jar QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); @@ -171,15 +168,15 @@ void OneSixUpdate::jarlibStart() auto entry = metacache->resolveEntry("libraries", lib->storagePath()); if (entry->stale) { - if(lib->hint() == "forge-pack-xz") + if (lib->hint() == "forge-pack-xz") jarlibDownloadJob->addForgeXzDownload(download_path, entry); else jarlibDownloadJob->addCacheDownload(download_path, entry); } } - connect(jarlibDownloadJob.data(), SIGNAL(succeeded()), SLOT(jarlibFinished())); - connect(jarlibDownloadJob.data(), SIGNAL(failed()), SLOT(jarlibFailed())); - connect(jarlibDownloadJob.data(), SIGNAL(progress(qint64, qint64)), + connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(jarlibFinished())); + connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(jarlibFailed())); + connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); jarlibDownloadJob->start(); diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index 7314a6d1..ed8dcbcd 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -47,7 +47,7 @@ private: DownloadJobPtr jarlibDownloadJob; // target version, determined during this task - QSharedPointer targetVersion; + std::shared_ptr targetVersion; }; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index 64a47562..f7efedf9 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -2,8 +2,8 @@ #include "OneSixLibrary.h" #include "OneSixRule.h" -QSharedPointer fromJsonV4(QJsonObject root, - QSharedPointer fullVersion) +std::shared_ptr fromJsonV4(QJsonObject root, + std::shared_ptr fullVersion) { fullVersion->id = root.value("id").toString(); @@ -64,7 +64,7 @@ QSharedPointer fromJsonV4(QJsonObject root, auto nameVal = libObj.value("name"); if (!nameVal.isString()) continue; - QSharedPointer library(new OneSixLibrary(nameVal.toString())); + std::shared_ptr library(new OneSixLibrary(nameVal.toString())); auto urlVal = libObj.value("url"); if (urlVal.isString()) @@ -129,9 +129,9 @@ QSharedPointer fromJsonV4(QJsonObject root, return fullVersion; } -QSharedPointer OneSixVersion::fromJson(QJsonObject root) +std::shared_ptr OneSixVersion::fromJson(QJsonObject root) { - QSharedPointer readVersion(new OneSixVersion()); + std::shared_ptr readVersion(new OneSixVersion()); int launcher_ver = readVersion->minimumLauncherVersion = root.value("minimumLauncherVersion").toDouble(); @@ -140,16 +140,16 @@ QSharedPointer OneSixVersion::fromJson(QJsonObject root) return fromJsonV4(root, readVersion); else { - return QSharedPointer(); + return std::shared_ptr(); } } -QSharedPointer OneSixVersion::fromFile(QString filepath) +std::shared_ptr OneSixVersion::fromFile(QString filepath) { QFile file(filepath); if (!file.open(QIODevice::ReadOnly)) { - return QSharedPointer(); + return std::shared_ptr(); } auto data = file.readAll(); @@ -158,12 +158,12 @@ QSharedPointer OneSixVersion::fromFile(QString filepath) if (jsonError.error != QJsonParseError::NoError) { - return QSharedPointer(); + return std::shared_ptr(); } if (!jsonDoc.isObject()) { - return QSharedPointer(); + return std::shared_ptr(); } QJsonObject root = jsonDoc.object(); auto version = fromJson(root); @@ -202,9 +202,9 @@ bool OneSixVersion::toOriginalFile() return file.commit(); } -QList> OneSixVersion::getActiveNormalLibs() +QList> OneSixVersion::getActiveNormalLibs() { - QList> output; + QList> output; for (auto lib : libraries) { if (lib->isActive() && !lib->isNative()) @@ -215,9 +215,9 @@ QList> OneSixVersion::getActiveNormalLibs() return output; } -QList> OneSixVersion::getActiveNativeLibs() +QList> OneSixVersion::getActiveNativeLibs() { - QList> output; + QList> output; for (auto lib : libraries) { if (lib->isActive() && lib->isNative()) diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 69268ecf..3529138c 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -1,5 +1,7 @@ #pragma once #include +#include + class OneSixLibrary; class OneSixVersion : public QAbstractListModel @@ -16,12 +18,12 @@ public: // serialization/deserialization public: bool toOriginalFile(); - static QSharedPointer fromJson(QJsonObject root); - static QSharedPointer fromFile(QString filepath); + static std::shared_ptr fromJson(QJsonObject root); + static std::shared_ptr fromFile(QString filepath); public: - QList> getActiveNormalLibs(); - QList> getActiveNativeLibs(); + QList> getActiveNormalLibs(); + QList> getActiveNativeLibs(); // called when something starts/stops messing with the object // FIXME: these are ugly in every possible way. void externalUpdateStart(); @@ -62,7 +64,7 @@ public: QString mainClass; /// the list of libs - both active and inactive, native and java - QList> libraries; + QList> libraries; /* FIXME: add support for those rules here? Looks like a pile of quick hacks to me though. diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index ac1a4ef5..9b698135 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -18,11 +18,11 @@ #include "MultiMC.h" #include - #include - #include +#include + #define JSON_URL "http://files.minecraftforge.net/minecraftforge/json" ForgeVersionList::ForgeVersionList(QObject *parent) : BaseVersionList(parent) @@ -62,7 +62,7 @@ QVariant ForgeVersionList::data(const QModelIndex &index, int role) const if (index.row() > count()) return QVariant(); - auto version = m_vlist[index.row()].dynamicCast(); + auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); switch (role) { case Qt::DisplayRole: @@ -164,9 +164,9 @@ void ForgeListLoadTask::executeTask() auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); job->addCacheDownload(QUrl(JSON_URL), forgeListEntry); listJob.reset(job); - connect(listJob.data(), SIGNAL(succeeded()), SLOT(list_downloaded())); - connect(listJob.data(), SIGNAL(failed()), SLOT(list_failed())); - connect(listJob.data(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); + connect(listJob.get(), SIGNAL(succeeded()), SLOT(list_downloaded())); + connect(listJob.get(), SIGNAL(failed()), SLOT(list_failed())); + connect(listJob.get(), SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); listJob->start(); } @@ -176,10 +176,10 @@ void ForgeListLoadTask::list_failed() auto reply = DlJob->m_reply; if(reply) { - qDebug() << "Getting forge version list failed: " << reply->errorString(); + QLOG_ERROR() << "Getting forge version list failed: " << reply->errorString(); } else - qDebug() << "Getting forge version list failed for reasons unknown."; + QLOG_ERROR() << "Getting forge version list failed for reasons unknown."; } void ForgeListLoadTask::list_downloaded() @@ -187,7 +187,7 @@ void ForgeListLoadTask::list_downloaded() QByteArray data; { auto DlJob = listJob->first(); - auto filename = DlJob.dynamicCast()->m_target_path; + auto filename = std::dynamic_pointer_cast(DlJob)->m_target_path; QFile listFile(filename); if(!listFile.open(QIODevice::ReadOnly)) return; @@ -272,7 +272,7 @@ void ForgeListLoadTask::list_downloaded() if (valid) { // Now, we construct the version object and add it to the list. - QSharedPointer fVersion(new ForgeVersion()); + std::shared_ptr fVersion(new ForgeVersion()); fVersion->universal_url = url; fVersion->changelog_url = changelog_url; fVersion->installer_url = installer_url; diff --git a/logic/lists/ForgeVersionList.h b/logic/lists/ForgeVersionList.h index 4abfe9ce..56a207c8 100644 --- a/logic/lists/ForgeVersionList.h +++ b/logic/lists/ForgeVersionList.h @@ -26,7 +26,7 @@ #include "logic/net/DownloadJob.h" class ForgeVersion; -typedef QSharedPointer ForgeVersionPtr; +typedef std::shared_ptr ForgeVersionPtr; struct ForgeVersion : public BaseVersion { diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index b930f781..ac054267 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -29,13 +29,13 @@ #include "logic/lists/IconList.h" #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" +#include const static int GROUP_FILE_FORMAT_VERSION = 1; -InstanceList::InstanceList(const QString &instDir, QObject *parent) : - QAbstractListModel ( parent ), m_instDir("instances") +InstanceList::InstanceList(const QString &instDir, QObject *parent) + : QAbstractListModel(parent), m_instDir("instances") { - } InstanceList::~InstanceList() @@ -43,32 +43,32 @@ InstanceList::~InstanceList() saveGroupList(); } -int InstanceList::rowCount ( const QModelIndex& parent ) const +int InstanceList::rowCount(const QModelIndex &parent) const { - Q_UNUSED ( parent ); + Q_UNUSED(parent); return m_instances.count(); } -QModelIndex InstanceList::index ( int row, int column, const QModelIndex& parent ) const +QModelIndex InstanceList::index(int row, int column, const QModelIndex &parent) const { - Q_UNUSED ( parent ); - if ( row < 0 || row >= m_instances.size() ) + Q_UNUSED(parent); + if (row < 0 || row >= m_instances.size()) return QModelIndex(); - return createIndex ( row, column, ( void* ) m_instances.at ( row ).data() ); + return createIndex(row, column, (void *)m_instances.at(row).get()); } -QVariant InstanceList::data ( const QModelIndex& index, int role ) const +QVariant InstanceList::data(const QModelIndex &index, int role) const { - if ( !index.isValid() ) + if (!index.isValid()) { return QVariant(); } - BaseInstance *pdata = static_cast ( index.internalPointer() ); - switch ( role ) + BaseInstance *pdata = static_cast(index.internalPointer()); + switch (role) { case InstancePointerRole: { - QVariant v = qVariantFromValue((void *) pdata); + QVariant v = qVariantFromValue((void *)pdata); return v; } case Qt::DisplayRole: @@ -96,12 +96,12 @@ QVariant InstanceList::data ( const QModelIndex& index, int role ) const return QVariant(); } -Qt::ItemFlags InstanceList::flags ( const QModelIndex& index ) const +Qt::ItemFlags InstanceList::flags(const QModelIndex &index) const { Qt::ItemFlags f; - if ( index.isValid() ) + if (index.isValid()) { - f |= ( Qt::ItemIsEnabled | Qt::ItemIsSelectable ); + f |= (Qt::ItemIsEnabled | Qt::ItemIsSelectable); } return f; } @@ -116,23 +116,23 @@ void InstanceList::saveGroupList() { QString groupFileName = m_instDir + "/instgroups.json"; QFile groupFile(groupFileName); - + // if you can't open the file, fail - if (!groupFile.open(QIODevice::WriteOnly| QIODevice::Truncate)) + if (!groupFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { // An error occurred. Ignore it. - qDebug("Failed to read instance group file."); + QLOG_ERROR() << "Failed to read instance group file."; return; } QTextStream out(&groupFile); - QMap > groupMap; - for(auto instance: m_instances) + QMap> groupMap; + for (auto instance : m_instances) { QString id = instance->id(); QString group = instance->group(); - if(group.isEmpty()) + if (group.isEmpty()) continue; - if(!groupMap.count(group)) + if (!groupMap.count(group)) { QSet set; set.insert(id); @@ -145,109 +145,114 @@ void InstanceList::saveGroupList() } } QJsonObject toplevel; - toplevel.insert("formatVersion",QJsonValue(QString("1"))); + toplevel.insert("formatVersion", QJsonValue(QString("1"))); QJsonObject groupsArr; - for(auto iter = groupMap.begin(); iter != groupMap.end(); iter++) + for (auto iter = groupMap.begin(); iter != groupMap.end(); iter++) { auto list = iter.value(); auto name = iter.key(); QJsonObject groupObj; QJsonArray instanceArr; - groupObj.insert("hidden",QJsonValue(QString("false"))); - for(auto item: list) + groupObj.insert("hidden", QJsonValue(QString("false"))); + for (auto item : list) { instanceArr.append(QJsonValue(item)); } - groupObj.insert("instances",instanceArr); - groupsArr.insert(name,groupObj); + groupObj.insert("instances", instanceArr); + groupsArr.insert(name, groupObj); } - toplevel.insert("groups",groupsArr); + toplevel.insert("groups", groupsArr); QJsonDocument doc(toplevel); groupFile.write(doc.toJson()); groupFile.close(); } -void InstanceList::loadGroupList(QMap & groupMap) +void InstanceList::loadGroupList(QMap &groupMap) { QString groupFileName = m_instDir + "/instgroups.json"; - + // if there's no group file, fail - if(!QFileInfo(groupFileName).exists()) + if (!QFileInfo(groupFileName).exists()) return; - + QFile groupFile(groupFileName); - + // if you can't open the file, fail if (!groupFile.open(QIODevice::ReadOnly)) { // An error occurred. Ignore it. - qDebug("Failed to read instance group file."); + QLOG_ERROR() << "Failed to read instance group file."; return; } - + QTextStream in(&groupFile); QString jsonStr = in.readAll(); groupFile.close(); - + QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonStr.toUtf8(), &error); - + // if the json was bad, fail if (error.error != QJsonParseError::NoError) { - qWarning(QString("Failed to parse instance group file: %1 at offset %2"). - arg(error.errorString(), QString::number(error.offset)).toUtf8()); + QLOG_ERROR() << QString("Failed to parse instance group file: %1 at offset %2") + .arg(error.errorString(), QString::number(error.offset)) + .toUtf8(); return; } - + // if the root of the json wasn't an object, fail if (!jsonDoc.isObject()) { qWarning("Invalid group file. Root entry should be an object."); return; } - + QJsonObject rootObj = jsonDoc.object(); - + // Make sure the format version matches, otherwise fail. if (rootObj.value("formatVersion").toVariant().toInt() != GROUP_FILE_FORMAT_VERSION) return; - + // Get the groups. if it's not an object, fail if (!rootObj.value("groups").isObject()) { qWarning("Invalid group list JSON: 'groups' should be an object."); return; } - + // Iterate through all the groups. QJsonObject groupMapping = rootObj.value("groups").toObject(); for (QJsonObject::iterator iter = groupMapping.begin(); iter != groupMapping.end(); iter++) { QString groupName = iter.key(); - + // If not an object, complain and skip to the next one. if (!iter.value().isObject()) { qWarning(QString("Group '%1' in the group list should " - "be an object.").arg(groupName).toUtf8()); + "be an object.") + .arg(groupName) + .toUtf8()); continue; } - + QJsonObject groupObj = iter.value().toObject(); if (!groupObj.value("instances").isArray()) { qWarning(QString("Group '%1' in the group list is invalid. " - "It should contain an array " - "called 'instances'.").arg(groupName).toUtf8()); + "It should contain an array " + "called 'instances'.") + .arg(groupName) + .toUtf8()); continue; } - + // Iterate through the list of instances in the group. QJsonArray instancesArray = groupObj.value("instances").toArray(); - - for (QJsonArray::iterator iter2 = instancesArray.begin(); - iter2 != instancesArray.end(); iter2++) + + for (QJsonArray::iterator iter2 = instancesArray.begin(); iter2 != instancesArray.end(); + iter2++) { groupMap[(*iter2).toString()] = groupName; } @@ -259,9 +264,9 @@ InstanceList::InstListError InstanceList::loadList() // load the instance groups QMap groupMap; loadGroupList(groupMap); - + beginResetModel(); - + m_instances.clear(); QDir dir(m_instDir); QDirIterator iter(dir); @@ -270,53 +275,55 @@ InstanceList::InstListError InstanceList::loadList() QString subDir = iter.next(); if (!QFileInfo(PathCombine(subDir, "instance.cfg")).exists()) continue; - + BaseInstance *instPtr = NULL; auto &loader = InstanceFactory::get(); auto error = loader.loadInstance(instPtr, subDir); - - switch(error) + + switch (error) { - case InstanceFactory::NoLoadError: - break; - case InstanceFactory::NotAnInstance: - break; + case InstanceFactory::NoLoadError: + break; + case InstanceFactory::NotAnInstance: + break; } - - if (error != InstanceFactory::NoLoadError && - error != InstanceFactory::NotAnInstance) + + if (error != InstanceFactory::NoLoadError && error != InstanceFactory::NotAnInstance) { - QString errorMsg = QString("Failed to load instance %1: "). - arg(QFileInfo(subDir).baseName()).toUtf8(); - + QString errorMsg = QString("Failed to load instance %1: ") + .arg(QFileInfo(subDir).baseName()) + .toUtf8(); + switch (error) { default: - errorMsg += QString("Unknown instance loader error %1"). - arg(error); + errorMsg += QString("Unknown instance loader error %1").arg(error); break; } - qDebug(errorMsg.toUtf8()); + QLOG_ERROR() << errorMsg.toUtf8(); } else if (!instPtr) { - qDebug(QString("Error loading instance %1. Instance loader returned null."). - arg(QFileInfo(subDir).baseName()).toUtf8()); + QLOG_ERROR() << QString("Error loading instance %1. Instance loader returned null.") + .arg(QFileInfo(subDir).baseName()) + .toUtf8(); } else { - QSharedPointer inst(instPtr); + std::shared_ptr inst(instPtr); auto iter = groupMap.find(inst->id()); - if(iter != groupMap.end()) + if (iter != groupMap.end()) { inst->setGroupInitial((*iter)); } - qDebug(QString("Loaded instance %1").arg(inst->name()).toUtf8()); + QLOG_INFO() << QString("Loaded instance %1").arg(inst->name()).toUtf8(); inst->setParent(this); m_instances.append(inst); - connect(instPtr, SIGNAL(propertiesChanged(BaseInstance*)),this, SLOT(propertiesChanged(BaseInstance*))); - connect(instPtr, SIGNAL(groupChanged()),this, SLOT(groupChanged())); - connect(instPtr, SIGNAL(nuked(BaseInstance*)), this, SLOT(instanceNuked(BaseInstance*))); + connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this, + SLOT(propertiesChanged(BaseInstance *))); + connect(instPtr, SIGNAL(groupChanged()), this, SLOT(groupChanged())); + connect(instPtr, SIGNAL(nuked(BaseInstance *)), this, + SLOT(instanceNuked(BaseInstance *))); } } endResetModel(); @@ -332,7 +339,8 @@ void InstanceList::clear() m_instances.clear(); endResetModel(); emit dataIsInvalid(); -}; +} +; /// Add an instance. Triggers notifications, returns the new index int InstanceList::add(InstancePtr t) @@ -340,9 +348,10 @@ int InstanceList::add(InstancePtr t) beginInsertRows(QModelIndex(), m_instances.size(), m_instances.size()); m_instances.append(t); t->setParent(this); - connect(t.data(), SIGNAL(propertiesChanged(BaseInstance*)),this, SLOT(propertiesChanged(BaseInstance*))); - connect(t.data(), SIGNAL(groupChanged()),this, SLOT(groupChanged())); - connect(t.data(), SIGNAL(nuked(BaseInstance*)), this, SLOT(instanceNuked(BaseInstance*))); + connect(t.get(), SIGNAL(propertiesChanged(BaseInstance *)), this, + SLOT(propertiesChanged(BaseInstance *))); + connect(t.get(), SIGNAL(groupChanged()), this, SLOT(groupChanged())); + connect(t.get(), SIGNAL(nuked(BaseInstance *)), this, SLOT(instanceNuked(BaseInstance *))); endInsertRows(); return count() - 1; } @@ -351,7 +360,7 @@ InstancePtr InstanceList::getInstanceById(QString instId) { QListIterator iter(m_instances); InstancePtr inst; - while(iter.hasNext()) + while (iter.hasNext()) { inst = iter.next(); if (inst->id() == instId) @@ -363,11 +372,11 @@ InstancePtr InstanceList::getInstanceById(QString instId) return iter.peekPrevious(); } -int InstanceList::getInstIndex ( BaseInstance* inst ) +int InstanceList::getInstIndex(BaseInstance *inst) { - for(int i = 0; i < m_instances.count(); i++) + for (int i = 0; i < m_instances.count(); i++) { - if(inst == m_instances[i].data()) + if (inst == m_instances[i].get()) { return i; } @@ -375,40 +384,39 @@ int InstanceList::getInstIndex ( BaseInstance* inst ) return -1; } - -void InstanceList::instanceNuked ( BaseInstance* inst ) +void InstanceList::instanceNuked(BaseInstance *inst) { int i = getInstIndex(inst); - if(i != -1) + if (i != -1) { - beginRemoveRows(QModelIndex(),i,i); + beginRemoveRows(QModelIndex(), i, i); m_instances.removeAt(i); endRemoveRows(); } } - -void InstanceList::propertiesChanged(BaseInstance * inst) +void InstanceList::propertiesChanged(BaseInstance *inst) { int i = getInstIndex(inst); - if(i != -1) + if (i != -1) { emit dataChanged(index(i), index(i)); } } -InstanceProxyModel::InstanceProxyModel ( QObject *parent ) - : KCategorizedSortFilterProxyModel ( parent ) +InstanceProxyModel::InstanceProxyModel(QObject *parent) + : KCategorizedSortFilterProxyModel(parent) { // disable since by default we are globally sorting by date: setCategorizedModel(true); } -bool InstanceProxyModel::subSortLessThan (const QModelIndex& left, const QModelIndex& right ) const +bool InstanceProxyModel::subSortLessThan(const QModelIndex &left, + const QModelIndex &right) const { - BaseInstance *pdataLeft = static_cast ( left.internalPointer() ); - BaseInstance *pdataRight = static_cast ( right.internalPointer() ); - //kDebug() << *pdataLeft << *pdataRight; + BaseInstance *pdataLeft = static_cast(left.internalPointer()); + BaseInstance *pdataRight = static_cast(right.internalPointer()); + // kDebug() << *pdataLeft << *pdataRight; return QString::localeAwareCompare(pdataLeft->name(), pdataRight->name()) < 0; - //return pdataLeft->name() < pdataRight->name(); + // return pdataLeft->name() < pdataRight->name(); } diff --git a/logic/lists/LwjglVersionList.cpp b/logic/lists/LwjglVersionList.cpp index b3e2dab4..6bf401ec 100644 --- a/logic/lists/LwjglVersionList.cpp +++ b/logic/lists/LwjglVersionList.cpp @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -17,15 +17,14 @@ #include "MultiMC.h" #include - #include - #include +#include + #define RSS_URL "http://sourceforge.net/api/file/index/project-id/58488/mtime/desc/rss" -LWJGLVersionList::LWJGLVersionList(QObject *parent) : - QAbstractListModel(parent) +LWJGLVersionList::LWJGLVersionList(QObject *parent) : QAbstractListModel(parent) { setLoading(false); } @@ -34,20 +33,20 @@ QVariant LWJGLVersionList::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - + if (index.row() > count()) return QVariant(); - + const PtrLWJGLVersion version = at(index.row()); - + switch (role) { case Qt::DisplayRole: return version->name(); - + case Qt::ToolTipRole: return version->url(); - + default: return QVariant(); } @@ -59,10 +58,10 @@ QVariant LWJGLVersionList::headerData(int section, Qt::Orientation orientation, { case Qt::DisplayRole: return "Version"; - + case Qt::ToolTipRole: return "LWJGL version name."; - + default: return QVariant(); } @@ -81,7 +80,7 @@ bool LWJGLVersionList::isLoading() const void LWJGLVersionList::loadList() { Q_ASSERT_X(!m_loading, "loadList", "list is already loading (m_loading is true)"); - + setLoading(true); auto worker = MMC->qnam(); reply = worker->get(QNetworkRequest(QUrl(RSS_URL))); @@ -102,68 +101,68 @@ void LWJGLVersionList::netRequestComplete() if (reply->error() == QNetworkReply::NoError) { QRegExp lwjglRegex("lwjgl-(([0-9]\\.?)+)\\.zip"); - Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", - "LWJGL regex is invalid"); - + Q_ASSERT_X(lwjglRegex.isValid(), "load LWJGL list", "LWJGL regex is invalid"); + QDomDocument doc; - + QString xmlErrorMsg; int errorLine; if (!doc.setContent(reply->readAll(), false, &xmlErrorMsg, &errorLine)) { - failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " + QString::number(errorLine)); + failed("Failed to load LWJGL list. XML error: " + xmlErrorMsg + " at line " + + QString::number(errorLine)); setLoading(false); return; } - + QDomNodeList items = doc.elementsByTagName("item"); - + QList tempList; - + for (int i = 0; i < items.length(); i++) { Q_ASSERT_X(items.at(i).isElement(), "load LWJGL list", "XML element isn't an element... wat?"); - + QDomElement linkElement = getDomElementByTagName(items.at(i).toElement(), "link"); if (linkElement.isNull()) { qWarning() << "Link element" << i << "in RSS feed doesn't exist! Skipping."; continue; } - + QString link = linkElement.text(); - + // Make sure it's a download link. if (link.endsWith("/download") && link.contains(lwjglRegex)) { QString name = link.mid(lwjglRegex.indexIn(link) + 6); // Subtract 4 here to remove the .zip file extension. name = name.left(lwjglRegex.matchedLength() - 10); - + QUrl url(link); if (!url.isValid()) { qWarning() << "LWJGL version URL isn't valid:" << link << "Skipping."; continue; } - + tempList.append(LWJGLVersion::Create(name, link)); } } - + beginResetModel(); m_vlist.swap(tempList); endResetModel(); - - qDebug("Loaded LWJGL list."); + + QLOG_INFO() << "Loaded LWJGL list."; finished(); } else { failed("Failed to load LWJGL list. Network error: " + reply->errorString()); } - + setLoading(false); reply->deleteLater(); } @@ -173,13 +172,12 @@ const PtrLWJGLVersion LWJGLVersionList::getVersion(const QString &versionName) for (int i = 0; i < count(); i++) { QString name = at(i)->name(); - if ( name == versionName) + if (name == versionName) return at(i); } return PtrLWJGLVersion(); } - void LWJGLVersionList::failed(QString msg) { qWarning() << msg; diff --git a/logic/lists/LwjglVersionList.h b/logic/lists/LwjglVersionList.h index 23e92a1a..99712292 100644 --- a/logic/lists/LwjglVersionList.h +++ b/logic/lists/LwjglVersionList.h @@ -17,13 +17,13 @@ #include #include -#include #include - #include +#include + class LWJGLVersion; -typedef QSharedPointer PtrLWJGLVersion; +typedef std::shared_ptr PtrLWJGLVersion; class LWJGLVersion : public QObject { diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 35f7251e..36611165 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -16,8 +16,6 @@ #include "MinecraftVersionList.h" #include -#include - #include #include @@ -62,8 +60,8 @@ int MinecraftVersionList::count() const bool cmpVersions(BaseVersionPtr first, BaseVersionPtr second) { - auto left = first.dynamicCast(); - auto right = second.dynamicCast(); + auto left = std::dynamic_pointer_cast(first); + auto right = std::dynamic_pointer_cast(second); return left->timestamp > right->timestamp; } @@ -78,7 +76,7 @@ BaseVersionPtr MinecraftVersionList::getLatestStable() const { for (int i = 0; i < m_vlist.length(); i++) { - auto ver = m_vlist.at(i).dynamicCast(); + auto ver =std::dynamic_pointer_cast(m_vlist.at(i)); if (ver->is_latest && !ver->is_snapshot) { return m_vlist.at(i); @@ -272,7 +270,7 @@ void MCVListLoadTask::list_downloaded() QString dlUrl = QString(MCVLIST_URLBASE) + versionID + "/"; // Now, we construct the version object and add it to the list. - QSharedPointer mcVersion(new MinecraftVersion()); + std::shared_ptr mcVersion(new MinecraftVersion()); mcVersion->m_name = mcVersion->m_descriptor = versionID; mcVersion->timestamp = versionTime.toMSecsSinceEpoch(); mcVersion->download_url = dlUrl; diff --git a/logic/net/ByteArrayDownload.cpp b/logic/net/ByteArrayDownload.cpp index b7a68c60..ba771eef 100644 --- a/logic/net/ByteArrayDownload.cpp +++ b/logic/net/ByteArrayDownload.cpp @@ -1,6 +1,6 @@ #include "ByteArrayDownload.h" #include "MultiMC.h" -#include +#include ByteArrayDownload::ByteArrayDownload(QUrl url) : Download() { @@ -10,13 +10,13 @@ ByteArrayDownload::ByteArrayDownload(QUrl url) : Download() void ByteArrayDownload::start() { - qDebug() << "Downloading " << m_url.toString(); + QLOG_INFO() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); auto worker = MMC->qnam(); QNetworkReply *rep = worker->get(request); - m_reply = QSharedPointer(rep, &QObject::deleteLater); + m_reply = std::shared_ptr(rep); connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); @@ -33,7 +33,8 @@ void ByteArrayDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal void ByteArrayDownload::downloadError(QNetworkReply::NetworkError error) { // error happened during download. - qDebug() << "URL:" << m_url.toString().toLocal8Bit() << "Network error: " << error; + QLOG_ERROR() << "Error getting URL:" << m_url.toString().toLocal8Bit() + << "Network error: " << error; m_status = Job_Failed; } @@ -45,14 +46,14 @@ void ByteArrayDownload::downloadFinished() // nothing went wrong... m_status = Job_Finished; m_data = m_reply->readAll(); - m_reply.clear(); + m_reply.reset(); emit succeeded(index_within_job); return; } // else the download failed else { - m_reply.clear(); + m_reply.reset(); emit failed(index_within_job); return; } diff --git a/logic/net/ByteArrayDownload.h b/logic/net/ByteArrayDownload.h index 428b21db..cfc6a8d0 100644 --- a/logic/net/ByteArrayDownload.h +++ b/logic/net/ByteArrayDownload.h @@ -21,4 +21,4 @@ protected slots: void downloadReadyRead(); }; -typedef QSharedPointer ByteArrayDownloadPtr; +typedef std::shared_ptr ByteArrayDownloadPtr; diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index 9fc1f127..0e653e05 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include CacheDownload::CacheDownload(QUrl url, MetaEntryPtr entry) : Download(), md5sum(QCryptographicHash::Md5) @@ -31,7 +31,7 @@ void CacheDownload::start() emit failed(index_within_job); return; } - qDebug() << "Downloading " << m_url.toString(); + QLOG_INFO() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Cached)"); @@ -39,7 +39,7 @@ void CacheDownload::start() auto worker = MMC->qnam(); QNetworkReply *rep = worker->get(request); - m_reply = QSharedPointer(rep, &QObject::deleteLater); + m_reply = std::shared_ptr(rep); connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); @@ -92,7 +92,7 @@ void CacheDownload::downloadFinished() m_entry->stale = false; MMC->metacache()->updateEntry(m_entry); - m_reply.clear(); + m_reply.reset(); emit succeeded(index_within_job); return; } @@ -101,7 +101,7 @@ void CacheDownload::downloadFinished() { m_output_file.close(); m_output_file.remove(); - m_reply.clear(); + m_reply.reset(); emit failed(index_within_job); return; } diff --git a/logic/net/CacheDownload.h b/logic/net/CacheDownload.h index f95dabd5..295391b1 100644 --- a/logic/net/CacheDownload.h +++ b/logic/net/CacheDownload.h @@ -31,4 +31,4 @@ public slots: virtual void start(); }; -typedef QSharedPointer CacheDownloadPtr; +typedef std::shared_ptr CacheDownloadPtr; diff --git a/logic/net/Download.h b/logic/net/Download.h index 91f09dec..ca4bee9f 100644 --- a/logic/net/Download.h +++ b/logic/net/Download.h @@ -2,10 +2,9 @@ #include #include -#include +#include #include - enum JobStatus { Job_NotStarted, @@ -18,20 +17,21 @@ class Download : public QObject { Q_OBJECT protected: - explicit Download(): QObject(0){}; + explicit Download() : QObject(0) {}; + public: virtual ~Download() {}; public: /// the network reply - QSharedPointer m_reply; - + std::shared_ptr m_reply; + /// source URL QUrl m_url; - + /// The file's status JobStatus m_status; - + /// index within the parent job int index_within_job = 0; @@ -46,9 +46,9 @@ protected slots: virtual void downloadError(QNetworkReply::NetworkError error) = 0; virtual void downloadFinished() = 0; virtual void downloadReadyRead() = 0; - + public slots: virtual void start() = 0; }; -typedef QSharedPointer DownloadPtr; +typedef std::shared_ptr DownloadPtr; diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index 03a69555..f6275b7a 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -5,7 +5,7 @@ #include "ByteArrayDownload.h" #include "CacheDownload.h" -#include +#include ByteArrayDownloadPtr DownloadJob::addByteArrayDownload(QUrl url) { @@ -54,18 +54,18 @@ void DownloadJob::partSucceeded(int index) partProgress(index, slot.total_progress, slot.total_progress); num_succeeded++; - qDebug() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" + QLOG_INFO() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" << downloads.size(); if (num_failed + num_succeeded == downloads.size()) { if (num_failed) { - qDebug() << m_job_name.toLocal8Bit() << " failed."; + QLOG_ERROR() << m_job_name.toLocal8Bit() << " failed."; emit failed(); } else { - qDebug() << m_job_name.toLocal8Bit() << " succeeded."; + QLOG_INFO() << m_job_name.toLocal8Bit() << " succeeded."; emit succeeded(); } } @@ -76,17 +76,17 @@ void DownloadJob::partFailed(int index) auto &slot = parts_progress[index]; if (slot.failures == 3) { - qDebug() << "Part " << index << " failed 3 times (" << downloads[index]->m_url << ")"; + QLOG_ERROR() << "Part " << index << " failed 3 times (" << downloads[index]->m_url << ")"; num_failed++; if (num_failed + num_succeeded == downloads.size()) { - qDebug() << m_job_name.toLocal8Bit() << " failed."; + QLOG_ERROR() << m_job_name.toLocal8Bit() << " failed."; emit failed(); } } else { - qDebug() << "Part " << index << " failed, restarting (" << downloads[index]->m_url + QLOG_ERROR() << "Part " << index << " failed, restarting (" << downloads[index]->m_url << ")"; // restart the job slot.failures++; @@ -110,12 +110,12 @@ void DownloadJob::partProgress(int index, qint64 bytesReceived, qint64 bytesTota void DownloadJob::start() { - qDebug() << m_job_name.toLocal8Bit() << " started."; + QLOG_INFO() << m_job_name.toLocal8Bit() << " started."; for (auto iter : downloads) { - connect(iter.data(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); - connect(iter.data(), SIGNAL(failed(int)), SLOT(partFailed(int))); - connect(iter.data(), SIGNAL(progress(int, qint64, qint64)), + connect(iter.get(), SIGNAL(succeeded(int)), SLOT(partSucceeded(int))); + connect(iter.get(), SIGNAL(failed(int)), SLOT(partFailed(int))); + connect(iter.get(), SIGNAL(progress(int, qint64, qint64)), SLOT(partProgress(int, qint64, qint64))); iter->start(); } diff --git a/logic/net/DownloadJob.h b/logic/net/DownloadJob.h index 8c32950a..91b014ad 100644 --- a/logic/net/DownloadJob.h +++ b/logic/net/DownloadJob.h @@ -9,7 +9,7 @@ #include "logic/tasks/ProgressProvider.h" class DownloadJob; -typedef QSharedPointer DownloadJobPtr; +typedef std::shared_ptr DownloadJobPtr; /** * A single file for the downloader/cache to process. diff --git a/logic/net/FileDownload.cpp b/logic/net/FileDownload.cpp index 353fd58b..3f38b0fa 100644 --- a/logic/net/FileDownload.cpp +++ b/logic/net/FileDownload.cpp @@ -2,7 +2,7 @@ #include "FileDownload.h" #include #include -#include +#include FileDownload::FileDownload ( QUrl url, QString target_path ) @@ -28,7 +28,7 @@ void FileDownload::start() // skip this file if they match if ( m_check_md5 && hash == m_expected_md5 ) { - qDebug() << "Skipping " << m_url.toString() << ": md5 match."; + QLOG_INFO() << "Skipping " << m_url.toString() << ": md5 match."; emit succeeded(index_within_job); return; } @@ -43,7 +43,7 @@ void FileDownload::start() return; } - qDebug() << "Downloading " << m_url.toString(); + QLOG_INFO() << "Downloading " << m_url.toString(); QNetworkRequest request ( m_url ); request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Uncached)"); @@ -51,7 +51,7 @@ void FileDownload::start() auto worker = MMC->qnam(); QNetworkReply * rep = worker->get ( request ); - m_reply = QSharedPointer ( rep, &QObject::deleteLater ); + m_reply = std::shared_ptr ( rep ); connect ( rep, SIGNAL ( downloadProgress ( qint64,qint64 ) ), SLOT ( downloadProgress ( qint64,qint64 ) ) ); connect ( rep, SIGNAL ( finished() ), SLOT ( downloadFinished() ) ); connect ( rep, SIGNAL ( error ( QNetworkReply::NetworkError ) ), SLOT ( downloadError ( QNetworkReply::NetworkError ) ) ); @@ -79,7 +79,7 @@ void FileDownload::downloadFinished() m_status = Job_Finished; m_output_file.close(); - m_reply.clear(); + m_reply.reset(); emit succeeded(index_within_job); return; } @@ -87,7 +87,7 @@ void FileDownload::downloadFinished() else { m_output_file.close(); - m_reply.clear(); + m_reply.reset(); emit failed(index_within_job); return; } diff --git a/logic/net/FileDownload.h b/logic/net/FileDownload.h index 190bee58..9abb590d 100644 --- a/logic/net/FileDownload.h +++ b/logic/net/FileDownload.h @@ -32,4 +32,4 @@ public slots: virtual void start(); }; -typedef QSharedPointer FileDownloadPtr; +typedef std::shared_ptr FileDownloadPtr; diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index 6d66abce..3ec2155b 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include ForgeXzDownload::ForgeXzDownload(QUrl url, MetaEntryPtr entry) : Download() @@ -32,7 +32,7 @@ void ForgeXzDownload::start() emit failed(index_within_job); return; } - qDebug() << "Downloading " << m_url.toString(); + QLOG_INFO() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Cached)"); @@ -40,7 +40,7 @@ void ForgeXzDownload::start() auto worker = MMC->qnam(); QNetworkReply *rep = worker->get(request); - m_reply = QSharedPointer(rep, &QObject::deleteLater); + m_reply = std::shared_ptr(rep); connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SLOT(downloadProgress(qint64, qint64))); connect(rep, SIGNAL(finished()), SLOT(downloadFinished())); @@ -78,7 +78,7 @@ void ForgeXzDownload::downloadFinished() { // something bad happened m_pack200_xz_file.remove(); - m_reply.clear(); + m_reply.reset(); emit failed(index_within_job); return; } @@ -88,7 +88,7 @@ void ForgeXzDownload::downloadFinished() { m_pack200_xz_file.close(); m_pack200_xz_file.remove(); - m_reply.clear(); + m_reply.reset(); emit failed(index_within_job); return; } @@ -198,38 +198,38 @@ void ForgeXzDownload::decompressAndInstall() break; case XZ_MEM_ERROR: - qDebug() << "Memory allocation failed\n"; + QLOG_ERROR() << "Memory allocation failed\n"; xz_dec_end(s); emit failed(index_within_job); return; case XZ_MEMLIMIT_ERROR: - qDebug() << "Memory usage limit reached\n"; + QLOG_ERROR() << "Memory usage limit reached\n"; xz_dec_end(s); emit failed(index_within_job); return; case XZ_FORMAT_ERROR: - qDebug() << "Not a .xz file\n"; + QLOG_ERROR() << "Not a .xz file\n"; xz_dec_end(s); emit failed(index_within_job); return; case XZ_OPTIONS_ERROR: - qDebug() << "Unsupported options in the .xz headers\n"; + QLOG_ERROR() << "Unsupported options in the .xz headers\n"; xz_dec_end(s); emit failed(index_within_job); return; case XZ_DATA_ERROR: case XZ_BUF_ERROR: - qDebug() << "File is corrupt\n"; + QLOG_ERROR() << "File is corrupt\n"; xz_dec_end(s); emit failed(index_within_job); return; default: - qDebug() << "Bug!\n"; + QLOG_ERROR() << "Bug!\n"; xz_dec_end(s); emit failed(index_within_job); return; @@ -246,7 +246,7 @@ void ForgeXzDownload::decompressAndInstall() } catch(std::runtime_error & err) { - qDebug() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what(); + QLOG_ERROR() << "Error unpacking " << pack_name.toUtf8() << " : " << err.what(); QFile f(m_target_path); if(f.exists()) f.remove(); @@ -274,6 +274,6 @@ void ForgeXzDownload::decompressAndInstall() m_entry->stale = false; MMC->metacache()->updateEntry(m_entry); - m_reply.clear(); + m_reply.reset(); emit succeeded(index_within_job); } diff --git a/logic/net/ForgeXzDownload.h b/logic/net/ForgeXzDownload.h index 8cb47783..5d677947 100644 --- a/logic/net/ForgeXzDownload.h +++ b/logic/net/ForgeXzDownload.h @@ -32,4 +32,4 @@ private: void decompressAndInstall(); }; -typedef QSharedPointer ForgeXzDownloadPtr; +typedef std::shared_ptr ForgeXzDownloadPtr; diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp index 46801ab3..9c642a0f 100644 --- a/logic/net/HttpMetaCache.cpp +++ b/logic/net/HttpMetaCache.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -105,12 +105,12 @@ bool HttpMetaCache::updateEntry ( MetaEntryPtr stale_entry ) { if(!m_entries.contains(stale_entry->base)) { - qDebug() << "Cannot add entry with unknown base: " << stale_entry->base.toLocal8Bit(); + QLOG_ERROR() << "Cannot add entry with unknown base: " << stale_entry->base.toLocal8Bit(); return false; } if(stale_entry->stale) { - qDebug() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); + QLOG_ERROR() << "Cannot add stale entry: " << stale_entry->getFullPath().toLocal8Bit(); return false; } m_entries[stale_entry->base].entry_list[stale_entry->path] = stale_entry; diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index daf6c43f..8107839e 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -15,7 +15,7 @@ struct MetaEntry QString getFullPath(); }; -typedef QSharedPointer MetaEntryPtr; +typedef std::shared_ptr MetaEntryPtr; class HttpMetaCache : public QObject { diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp index 2a45400e..5de8efa9 100644 --- a/logic/net/LoginTask.cpp +++ b/logic/net/LoginTask.cpp @@ -40,7 +40,7 @@ void LoginTask::legacyLogin() { setStatus(tr("Logging in...")); auto worker = MMC->qnam(); - connect(worker.data(), SIGNAL(finished(QNetworkReply *)), this, + connect(worker.get(), SIGNAL(finished(QNetworkReply *)), this, SLOT(processLegacyReply(QNetworkReply *))); QUrl loginURL("https://login.minecraft.net/"); @@ -134,7 +134,7 @@ void LoginTask::yggdrasilLogin() { setStatus(tr("Logging in...")); auto worker = MMC->qnam(); - connect(worker.data(), SIGNAL(finished(QNetworkReply *)), this, + connect(worker.get(), SIGNAL(finished(QNetworkReply *)), this, SLOT(processYggdrasilReply(QNetworkReply *))); /* diff --git a/logic/tasks/Task.cpp b/logic/tasks/Task.cpp index c75bcb8f..2f137c55 100644 --- a/logic/tasks/Task.cpp +++ b/logic/tasks/Task.cpp @@ -14,6 +14,7 @@ */ #include "Task.h" +#include Task::Task(QObject *parent) : ProgressProvider(parent) @@ -55,6 +56,7 @@ void Task::start() void Task::emitFailed(QString reason) { m_running = false; + QLOG_ERROR() << "Task failed: " << reason; emit failed(reason); } -- cgit From 8b18af051585108a65a01347d333ce79e2e82d23 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 6 Oct 2013 03:07:57 +0200 Subject: Get rid of junky timestamps, along with some select pointless log messages --- MultiMC.cpp | 2 +- depends/util/src/pathutils.cpp | 2 - gui/mainwindow.cpp | 4 +- gui/mainwindow.h | 98 +++++++++++++++++++++--------------------- logger/QsLog.cpp | 8 +--- logic/lists/InstanceList.cpp | 2 +- 6 files changed, 54 insertions(+), 62 deletions(-) (limited to 'logic') diff --git a/MultiMC.cpp b/MultiMC.cpp index b685ed13..ef720c33 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -133,7 +133,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) // and instances m_instances.reset(new InstanceList(m_settings->get("InstanceDir").toString(), this)); - std::cout << "Loading Instances..." << std::endl; + QLOG_INFO() << "Loading Instances..."; m_instances->loadList(); // init the http meta cache diff --git a/depends/util/src/pathutils.cpp b/depends/util/src/pathutils.cpp index 0836567d..4c24fa5d 100644 --- a/depends/util/src/pathutils.cpp +++ b/depends/util/src/pathutils.cpp @@ -75,7 +75,6 @@ bool ensureFilePathExists(QString filenamepath) QDir dir; QString ensuredPath = a.path(); bool success = dir.mkpath ( ensuredPath ); - qDebug() << "ensureFilePathExists:" << success << ensuredPath << filenamepath; return success; } @@ -85,7 +84,6 @@ bool ensureFolderPathExists(QString foldernamepath) QDir dir; QString ensuredPath = a.filePath(); bool success = dir.mkpath ( ensuredPath ); - qDebug() << "ensureFolderPathExists:" << success << ensuredPath << foldernamepath; return success; } diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index ecf5f79d..da767709 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -437,7 +437,7 @@ void MainWindow::closeEvent(QCloseEvent *event) // settings->getConfig().setValue("MainWindowState", saveState()); QMainWindow::closeEvent(event); } - +/* void MainWindow::on_instanceView_customContextMenuRequested(const QPoint &pos) { QMenu *instContextMenu = new QMenu("Instance", this); @@ -447,7 +447,7 @@ void MainWindow::on_instanceView_customContextMenuRequested(const QPoint &pos) instContextMenu->exec(view->mapToGlobal(pos)); } - +*/ void MainWindow::on_actionLaunchInstance_triggered() { if (m_selectedInstance) diff --git a/gui/mainwindow.h b/gui/mainwindow.h index cc9b0b7b..86f21113 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -39,115 +39,113 @@ class MainWindow; class MainWindow : public QMainWindow { Q_OBJECT - + public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); - + void closeEvent(QCloseEvent *event); // Browser Dialog void openWebPage(QUrl url); - - + private slots: void onCatToggled(bool); - + void on_actionAbout_triggered(); - + void on_actionAddInstance_triggered(); - + void on_actionChangeInstGroup_triggered(); - + void on_actionChangeInstIcon_triggered(); - + void on_actionViewInstanceFolder_triggered(); - + void on_actionConfig_Folder_triggered(); - + void on_actionViewSelectedInstFolder_triggered(); void on_actionRefresh_triggered(); - + void on_actionViewCentralModsFolder_triggered(); - + void on_actionCheckUpdate_triggered(); - + void on_actionSettings_triggered(); - + void on_actionReportBug_triggered(); - + void on_actionNews_triggered(); - + void on_mainToolBar_visibilityChanged(bool); - - void on_instanceView_customContextMenuRequested(const QPoint &pos); - + + // void on_instanceView_customContextMenuRequested(const QPoint &pos); + void on_actionLaunchInstance_triggered(); - + void on_actionDeleteInstance_triggered(); - + void on_actionRenameInstance_triggered(); - + void on_actionMakeDesktopShortcut_triggered(); - + void on_actionChangeInstMCVersion_triggered(); - + void on_actionEditInstMods_triggered(); - + void on_actionEditInstNotes_triggered(); - - void doLogin(const QString& errorMsg = ""); - - + + void doLogin(const QString &errorMsg = ""); + void onLoginComplete(); - - + void onGameUpdateComplete(); void onGameUpdateError(QString error); - + void taskStart(); void taskEnd(); void on_actionChangeInstLWJGLVersion_triggered(); - + void instanceEnded(); - - void on_actionInstanceSettings_triggered(); + + void on_actionInstanceSettings_triggered(); public slots: - void instanceActivated ( QModelIndex ); + void instanceActivated(QModelIndex); + + void instanceChanged(const QModelIndex ¤t, const QModelIndex &previous); - void instanceChanged (const QModelIndex & current,const QModelIndex & previous); - void selectionBad(); - + void startTask(Task *task); - + void launchInstance(BaseInstance *inst, LoginResponse response); protected: bool eventFilter(QObject *obj, QEvent *ev); void setCatBackground(bool enabled); + private: - + Ui::MainWindow *ui; - KCategoryDrawer * drawer; - KCategorizedView * view; - InstanceProxyModel * proxymodel; + KCategoryDrawer *drawer; + KCategorizedView *view; + InstanceProxyModel *proxymodel; MinecraftProcess *proc; ConsoleWindow *console; OneSixAssets *assets_downloader; - LabeledToolButton * renameButton; - + LabeledToolButton *renameButton; + BaseInstance *m_selectedInstance; - + // A pointer to the instance we are actively doing stuff with. // This is set when the user launches an instance and is used to refer to that // instance throughout the launching process. BaseInstance *m_activeInst; LoginResponse m_activeLogin; - + Task *m_versionLoadTask; }; diff --git a/logger/QsLog.cpp b/logger/QsLog.cpp index b2a7f465..8cf68a53 100644 --- a/logger/QsLog.cpp +++ b/logger/QsLog.cpp @@ -56,13 +56,12 @@ static const char *LevelToText(Level theLevel) class LoggerImpl { public: - LoggerImpl() : level(InfoLevel), start_time(QDateTime::currentDateTime()) + LoggerImpl() : level(InfoLevel) { } QMutex logMutex; Level level; DestinationList destList; - QDateTime start_time; }; Logger::Logger() : d(new LoggerImpl) @@ -94,10 +93,7 @@ Level Logger::loggingLevel() const void Logger::Helper::writeToLog() { const char *const levelName = LevelToText(level); - const QString completeMessage(QString("%1\t%2\t%3") - .arg(QDateTime::currentDateTime().toString(fmtDateTime)) - .arg(levelName, 5) - .arg(buffer)); + const QString completeMessage(QString("%1\t%2").arg(levelName, 5).arg(buffer)); Logger &logger = Logger::instance(); QMutexLocker lock(&logger.d->logMutex); diff --git a/logic/lists/InstanceList.cpp b/logic/lists/InstanceList.cpp index ac054267..9740d5a5 100644 --- a/logic/lists/InstanceList.cpp +++ b/logic/lists/InstanceList.cpp @@ -316,7 +316,7 @@ InstanceList::InstListError InstanceList::loadList() { inst->setGroupInitial((*iter)); } - QLOG_INFO() << QString("Loaded instance %1").arg(inst->name()).toUtf8(); + QLOG_INFO() << "Loaded instance " << inst->name(); inst->setParent(this); m_instances.append(inst); connect(instPtr, SIGNAL(propertiesChanged(BaseInstance *)), this, -- cgit From 77d5ea36ae096a212280d34b978f4cf0b030431f Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 6 Oct 2013 03:20:19 +0200 Subject: Log java util stuff using the new logging method --- logic/JavaUtils.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'logic') diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index d89ab9c5..1cfd0a77 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #if WINDOWS #include @@ -74,7 +74,7 @@ QStringList JavaUtils::FindJavaPath() if(paths.length() <= 0) { - qWarning() << "Failed to find Java in the Windows registry - defaulting to \"java\""; + QLOG_WARN() << "Failed to find Java in the Windows registry - defaulting to \"java\""; paths << "java"; } @@ -83,7 +83,7 @@ QStringList JavaUtils::FindJavaPath() #elif OSX QStringList JavaUtils::FindJavaPath() { - qWarning() << "OS X Java detection incomplete - defaulting to \"java\""; + QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\""; QStringList paths; paths << "java"; @@ -94,7 +94,7 @@ QStringList JavaUtils::FindJavaPath() #elif LINUX QStringList JavaUtils::FindJavaPath() { - qWarning() << "Linux Java detection incomplete - defaulting to \"java\""; + QLOG_INFO() << "Linux Java detection incomplete - defaulting to \"java\""; QStringList paths; paths << "java"; @@ -104,7 +104,7 @@ QStringList JavaUtils::FindJavaPath() #else QStringList JavaUtils::FindJavaPath() { - qWarning() << "Unknown operating system build - defaulting to \"java\""; + QLOG_INFO() << "Unknown operating system build - defaulting to \"java\""; QStringList paths; paths << "java"; -- cgit From 651bed91a0d6aff551fd6880a7cdbca403064c4f Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 6 Oct 2013 03:47:41 +0200 Subject: Log failure reasons of cache downloads --- logic/net/CacheDownload.cpp | 2 +- logic/net/DownloadJob.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'logic') diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index 0e653e05..6a76a4ae 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -56,7 +56,7 @@ void CacheDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) void CacheDownload::downloadError(QNetworkReply::NetworkError error) { // error happened during download. - // TODO: log the reason why + QLOG_ERROR() << "Failed" << m_url.toString() << "with reason" << error; m_status = Job_Failed; } void CacheDownload::downloadFinished() diff --git a/logic/net/DownloadJob.cpp b/logic/net/DownloadJob.cpp index f6275b7a..fa3e655e 100644 --- a/logic/net/DownloadJob.cpp +++ b/logic/net/DownloadJob.cpp @@ -54,18 +54,18 @@ void DownloadJob::partSucceeded(int index) partProgress(index, slot.total_progress, slot.total_progress); num_succeeded++; - QLOG_INFO() << m_job_name.toLocal8Bit() << " progress: " << num_succeeded << "/" + QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_succeeded << "/" << downloads.size(); if (num_failed + num_succeeded == downloads.size()) { if (num_failed) { - QLOG_ERROR() << m_job_name.toLocal8Bit() << " failed."; + QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed."; emit failed(); } else { - QLOG_INFO() << m_job_name.toLocal8Bit() << " succeeded."; + QLOG_INFO() << m_job_name.toLocal8Bit() << "succeeded."; emit succeeded(); } } @@ -76,17 +76,17 @@ void DownloadJob::partFailed(int index) auto &slot = parts_progress[index]; if (slot.failures == 3) { - QLOG_ERROR() << "Part " << index << " failed 3 times (" << downloads[index]->m_url << ")"; + QLOG_ERROR() << "Part" << index << "failed 3 times (" << downloads[index]->m_url << ")"; num_failed++; if (num_failed + num_succeeded == downloads.size()) { - QLOG_ERROR() << m_job_name.toLocal8Bit() << " failed."; + QLOG_ERROR() << m_job_name.toLocal8Bit() << "failed."; emit failed(); } } else { - QLOG_ERROR() << "Part " << index << " failed, restarting (" << downloads[index]->m_url + QLOG_ERROR() << "Part" << index << "failed, restarting (" << downloads[index]->m_url << ")"; // restart the job slot.failures++; -- cgit From d5e4802adef575d1d2d20a3e6e5addd4b5f4d2ca Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 6 Oct 2013 10:37:39 +0200 Subject: Don't use SSL for lib downloads --- logic/OneSixLibrary.cpp | 9 +++++---- logic/OneSixLibrary.h | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'logic') diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index 0643abe3..9c1caaa7 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -86,7 +86,7 @@ bool OneSixLibrary::isNative() } QString OneSixLibrary::downloadUrl() { - if(m_absolute_url.size()) + if (m_absolute_url.size()) return m_absolute_url; return m_download_url; } @@ -119,11 +119,12 @@ QJsonObject OneSixLibrary::toJson() { QJsonObject libRoot; libRoot.insert("name", m_name); - if(m_absolute_url.size()) + if (m_absolute_url.size()) libRoot.insert("MMC-absoluteUrl", m_absolute_url); - if(m_hint.size()) + if (m_hint.size()) libRoot.insert("MMC-hint", m_hint); - if(m_base_url != "https://s3.amazonaws.com/Minecraft.Download/libraries/") + if (m_base_url != "http://s3.amazonaws.com/Minecraft.Download/libraries/" && + m_base_url != "https://s3.amazonaws.com/Minecraft.Download/libraries/") libRoot.insert("url", m_base_url); if (isNative() && m_native_suffixes.size()) { diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index a8bcc364..5e58ef89 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -13,7 +13,7 @@ class OneSixLibrary private: // basic values used internally (so far) QString m_name; - QString m_base_url = "https://s3.amazonaws.com/Minecraft.Download/libraries/"; + QString m_base_url = "http://s3.amazonaws.com/Minecraft.Download/libraries/"; QList > m_rules; // custom values -- cgit From 17c98655f86f8ea41c4528e3fc25d12388a36861 Mon Sep 17 00:00:00 2001 From: Sky Date: Sun, 6 Oct 2013 19:54:52 +0100 Subject: First draft of multiple Java installation detection on Windows --- gui/settingsdialog.cpp | 4 +- gui/settingsdialog.ui | 18 +++++-- logic/JavaUtils.cpp | 129 ++++++++++++++++++++++++++++++++----------------- logic/JavaUtils.h | 20 +++++++- 4 files changed, 122 insertions(+), 49 deletions(-) (limited to 'logic') diff --git a/gui/settingsdialog.cpp b/gui/settingsdialog.cpp index c6fe893d..011925b7 100644 --- a/gui/settingsdialog.cpp +++ b/gui/settingsdialog.cpp @@ -185,9 +185,9 @@ void SettingsDialog::loadSettings(SettingsObject *s) void SettingsDialog::on_pushButton_clicked() { JavaUtils jut; - QStringList paths = jut.FindJavaPath(); + auto javas = jut.FindJavaPaths(); - ui->javaPathTextBox->setText(paths.at(0)); + ui->javaPathTextBox->setText(std::get(javas.at(0))); } void SettingsDialog::on_btnBrowse_clicked() diff --git a/gui/settingsdialog.ui b/gui/settingsdialog.ui index d7a134fb..8fa5e96e 100644 --- a/gui/settingsdialog.ui +++ b/gui/settingsdialog.ui @@ -374,6 +374,12 @@ + + + 0 + 0 + + Java path: @@ -381,6 +387,12 @@ + + + 0 + 0 + + JVM arguments: @@ -402,9 +414,6 @@ - - - @@ -418,6 +427,9 @@ + + + diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index 1cfd0a77..91fa271c 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -14,7 +14,6 @@ */ #include "JavaUtils.h" -#include "osutils.h" #include "pathutils.h" #include @@ -22,27 +21,33 @@ #include #include -#if WINDOWS -#include +JavaUtils::JavaUtils() +{ -#endif +} -JavaUtils::JavaUtils() +std::vector JavaUtils::GetDefaultJava() { + std::vector javas; + javas.push_back(std::make_tuple("java", "unknown", "java", false)); + return javas; } #if WINDOWS -QStringList JavaUtils::FindJavaPath() +std::vector JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName) { - QStringList paths; + std::vector javas; + + QString archType = "unknown"; + if(keyType == KEY_WOW64_64KEY) archType = "64"; + else if(keyType == KEY_WOW64_32KEY) archType = "32"; HKEY jreKey; - QString jreKeyName = "SOFTWARE\\JavaSoft\\Java Runtime Environment"; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, jreKeyName.toStdString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &jreKey) == ERROR_SUCCESS) + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName.toStdString().c_str(), 0, KEY_READ | keyType | KEY_ENUMERATE_SUB_KEYS, &jreKey) == ERROR_SUCCESS) { - // Read the current JRE version from the registry. - // This will be used to find the key that contains the JavaHome value. + // Read the current type version from the registry. + // This will be used to find any key that contains the JavaHome value. char *value = new char[0]; DWORD valueSz = 0; if (RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE*)value, &valueSz) == ERROR_MORE_DATA) @@ -51,64 +56,102 @@ QStringList JavaUtils::FindJavaPath() RegQueryValueExA(jreKey, "CurrentVersion", NULL, NULL, (BYTE*)value, &valueSz); } - RegCloseKey(jreKey); + QString recommended = value; + + TCHAR subKeyName[255]; + DWORD subKeyNameSize, numSubKeys, retCode; + + // Get the number of subkeys + RegQueryInfoKey(jreKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - // Now open the registry key for the JRE version that we just got. - jreKeyName.append("\\").append(value); - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, jreKeyName.toStdString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &jreKey) == ERROR_SUCCESS) + // Iterate until RegEnumKeyEx fails + if(numSubKeys > 0) { - // Read the JavaHome value to find where Java is installed. - value = new char[0]; - valueSz = 0; - if (RegQueryValueExA(jreKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz) == ERROR_MORE_DATA) + for(int i = 0; i < numSubKeys; i++) { - value = new char[valueSz]; - RegQueryValueExA(jreKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz); - - paths << QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"); + subKeyNameSize = 255; + retCode = RegEnumKeyEx(jreKey, i, subKeyName, &subKeyNameSize, NULL, NULL, NULL, NULL); + if(retCode == ERROR_SUCCESS) + { + // Now open the registry key for the version that we just got. + QString newKeyName = keyName + "\\" + subKeyName; + + HKEY newKey; + if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, newKeyName.toStdString().c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &newKey) == ERROR_SUCCESS) + { + // Read the JavaHome value to find where Java is installed. + value = new char[0]; + valueSz = 0; + if (RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz) == ERROR_MORE_DATA) + { + value = new char[valueSz]; + RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz); + + javas.push_back(std::make_tuple(subKeyName, archType, QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"), (recommended == subKeyName))); + } + + RegCloseKey(newKey); + } + } } - - RegCloseKey(jreKey); } + + RegCloseKey(jreKey); } - if(paths.length() <= 0) + return javas; +} + +std::vector JavaUtils::FindJavaPaths() +{ + std::vector JRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + std::vector JDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); + std::vector JRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + std::vector JDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); + + std::vector javas; + javas.insert(javas.end(), JRE64s.begin(), JRE64s.end()); + javas.insert(javas.end(), JDK64s.begin(), JDK64s.end()); + javas.insert(javas.end(), JRE32s.begin(), JRE32s.end()); + javas.insert(javas.end(), JDK32s.begin(), JDK32s.end()); + + if(javas.size() <= 0) { QLOG_WARN() << "Failed to find Java in the Windows registry - defaulting to \"java\""; - paths << "java"; + return this->GetDefaultJava(); + } + + QLOG_INFO() << "Found the following Java installations (64 -> 32, JRE -> JDK): "; + + for(auto &java : javas) + { + QString sRec; + if(std::get(java)) sRec = "(Recommended)"; + QLOG_INFO() << std::get(java) << std::get(java) << " at " << std::get(java) << sRec; } - return paths; + return javas; } #elif OSX -QStringList JavaUtils::FindJavaPath() +std::vector JavaUtils::FindJavaPath() { QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\""; - QStringList paths; - paths << "java"; - - return paths; + return this->GetDefaultPath(); } #elif LINUX -QStringList JavaUtils::FindJavaPath() +std::vector JavaUtils::FindJavaPath() { QLOG_INFO() << "Linux Java detection incomplete - defaulting to \"java\""; - QStringList paths; - paths << "java"; - - return paths; + return this->GetDefaultPath(); } #else -QStringList JavaUtils::FindJavaPath() +std::vector JavaUtils::FindJavaPath() { QLOG_INFO() << "Unknown operating system build - defaulting to \"java\""; - QStringList paths; - paths << "java"; - - return paths; + return this->GetDefaultPath(); } #endif diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h index fef2a1bf..63daac12 100644 --- a/logic/JavaUtils.h +++ b/logic/JavaUtils.h @@ -17,10 +17,28 @@ #include +#include "osutils.h" + +#if WINDOWS + #include +#endif + +#define JI_ID 0 +#define JI_ARCH 1 +#define JI_PATH 2 +#define JI_REC 3 +typedef std::tuple java_install; + class JavaUtils { public: JavaUtils(); - QStringList FindJavaPath(); + std::vector FindJavaPaths(); + +private: + std::vector GetDefaultJava(); +#if WINDOWS + std::vector FindJavaFromRegistryKey(DWORD keyType, QString keyName); +#endif }; -- cgit From 8450807c06b3831dfb4279529e93db9444a9e612 Mon Sep 17 00:00:00 2001 From: Sky Date: Sun, 6 Oct 2013 22:00:47 +0100 Subject: Fix non-Windows naming derp --- logic/JavaUtils.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'logic') diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index 91fa271c..5cec35b6 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -133,25 +133,25 @@ std::vector JavaUtils::FindJavaPaths() return javas; } #elif OSX -std::vector JavaUtils::FindJavaPath() +std::vector JavaUtils::FindJavaPaths() { QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\""; - return this->GetDefaultPath(); + return this->GetDefaultJava(); } #elif LINUX -std::vector JavaUtils::FindJavaPath() +std::vector JavaUtils::FindJavaPaths() { QLOG_INFO() << "Linux Java detection incomplete - defaulting to \"java\""; - return this->GetDefaultPath(); + return this->GetDefaultJava(); } #else -std::vector JavaUtils::FindJavaPath() +std::vector JavaUtils::FindJavaPaths() { QLOG_INFO() << "Unknown operating system build - defaulting to \"java\""; - return this->GetDefaultPath(); + return this->GetDefaultJava(); } #endif -- cgit From b30a97d4c9315e482341ec863dc5722bf78c4631 Mon Sep 17 00:00:00 2001 From: Sky Date: Mon, 7 Oct 2013 14:56:14 +0100 Subject: Make starting window size set properly on 1.6 instances --- logic/OneSixInstance.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'logic') diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index c5d546a3..a2a6bf17 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -143,6 +143,10 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(LoginResponse response) args << version->mainClass; args.append(processMinecraftArgs(response)); + // Set the width and height for 1.6 instances + args << QString("--width") << settings().get("MinecraftWinWidth").toString(); + args << QString("--height") << settings().get("MinecraftWinHeight").toString(); + // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); proc->setMinecraftArguments(args); -- cgit From a58912eaf7e98c1bc9e960fbf77b6293e57c28a1 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 7 Oct 2013 23:09:50 +0200 Subject: Basic version changing (OneSix only for now) --- gui/mainwindow.cpp | 3 +++ gui/mainwindow.ui | 4 +--- logic/LegacyInstance.cpp | 2 ++ logic/NostalgiaInstance.cpp | 4 ++++ logic/NostalgiaInstance.h | 1 + logic/lists/MinecraftVersionList.cpp | 4 ++++ 6 files changed, 15 insertions(+), 3 deletions(-) (limited to 'logic') diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 080ce4b2..70d26e02 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -602,6 +602,7 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() VersionSelectDialog vselect(m_selectedInstance->versionList().get(), tr("Change Minecraft version"), this); + vselect.setFilter(1, "OneSix"); if (vselect.exec() && vselect.selectedVersion()) { m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor()); @@ -646,6 +647,8 @@ void MainWindow::instanceChanged(const QModelIndex ¤t, const QModelIndex & m_selectedInstance->menuActionEnabled("actionChangeInstLWJGLVersion")); ui->actionEditInstMods->setEnabled( m_selectedInstance->menuActionEnabled("actionEditInstMods")); + ui->actionChangeInstMCVersion->setEnabled( + m_selectedInstance->menuActionEnabled("actionChangeInstMCVersion")); statusBar()->clearMessage(); statusBar()->showMessage(m_selectedInstance->getStatusbarDescription()); auto ico = MMC->icons()->getIcon(iconKey); diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index 1cda7a34..4c288d5f 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -107,6 +107,7 @@ + @@ -371,9 +372,6 @@ - - false - Change Version diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 2ffcb075..205f6873 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -314,6 +314,8 @@ QString LegacyInstance::defaultCustomBaseJar() const bool LegacyInstance::menuActionEnabled ( QString action_name ) const { + if (action_name == "actionChangeInstMCVersion") + return false; return true; } diff --git a/logic/NostalgiaInstance.cpp b/logic/NostalgiaInstance.cpp index 039cd9ce..efd8f46b 100644 --- a/logic/NostalgiaInstance.cpp +++ b/logic/NostalgiaInstance.cpp @@ -11,6 +11,10 @@ QString NostalgiaInstance::getStatusbarDescription() return "Nostalgia : " + intendedVersionId(); } +bool NostalgiaInstance::menuActionEnabled(QString action_name) const +{ + return false; +} /* ADD MORE diff --git a/logic/NostalgiaInstance.h b/logic/NostalgiaInstance.h index 1436e48d..64eb7a81 100644 --- a/logic/NostalgiaInstance.h +++ b/logic/NostalgiaInstance.h @@ -8,5 +8,6 @@ class NostalgiaInstance : public OneSixInstance public: explicit NostalgiaInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0); virtual QString getStatusbarDescription(); + virtual bool menuActionEnabled(QString action_name) const; }; diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 36611165..fbf609b5 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -122,9 +122,13 @@ MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist) legacyWhitelist.insert("1.4.6"); legacyWhitelist.insert("1.4.5"); legacyWhitelist.insert("1.4.4"); + legacyWhitelist.insert("1.4.3"); legacyWhitelist.insert("1.4.2"); + legacyWhitelist.insert("1.4.1"); + legacyWhitelist.insert("1.4"); legacyWhitelist.insert("1.3.2"); legacyWhitelist.insert("1.3.1"); + legacyWhitelist.insert("1.3"); legacyWhitelist.insert("1.2.5"); legacyWhitelist.insert("1.2.4"); legacyWhitelist.insert("1.2.3"); -- cgit From 05e2da51d8d25374140dce3c1646a2a1a0a2a553 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Tue, 8 Oct 2013 01:36:11 +0200 Subject: Add mod website button thing feature widget. It is super effective. --- gui/LegacyModEditDialog.cpp | 35 +++++++++++- gui/LegacyModEditDialog.h | 39 ++++++++------ gui/LegacyModEditDialog.ui | 21 ++++++++ gui/ModEditDialogCommon.cpp | 37 ++++++++++--- gui/ModEditDialogCommon.h | 5 +- gui/OneSixModEditDialog.cpp | 11 ++++ gui/OneSixModEditDialog.h | 1 + gui/OneSixModEditDialog.ui | 7 +++ logic/Mod.cpp | 102 ++++++++++++++++++----------------- logic/Mod.h | 69 ++++++++++++++++-------- logic/lists/MinecraftVersionList.cpp | 77 +++++++++++++------------- 11 files changed, 270 insertions(+), 134 deletions(-) (limited to 'logic') diff --git a/gui/LegacyModEditDialog.cpp b/gui/LegacyModEditDialog.cpp index 45f041f3..87647e0f 100644 --- a/gui/LegacyModEditDialog.cpp +++ b/gui/LegacyModEditDialog.cpp @@ -24,6 +24,7 @@ #include #include +//#include #include #include #include @@ -342,4 +343,36 @@ void LegacyModEditDialog::on_viewTexPackBtn_clicked() void LegacyModEditDialog::on_buttonBox_rejected() { close(); -} \ No newline at end of file +} + +//FIXME: too much copypasta makes peterix a sad hacker. BUT IT'S SO DELICIOUS! + +void LegacyModEditDialog::on_coreWebsite_clicked() +{ + int first, last; + auto list = ui->coreModsTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + showWebsiteForMod(this, m_coremods->operator[](first)); +} + +void LegacyModEditDialog::on_jarWebsite_clicked() +{ + int first, last; + auto list = ui->jarModsTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + showWebsiteForMod(this, m_jarmods->operator[](first)); +} + +void LegacyModEditDialog::on_loaderWebsite_clicked() +{ + int first, last; + auto list = ui->loaderModTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + showWebsiteForMod(this, m_mods->operator[](first)); +} diff --git a/gui/LegacyModEditDialog.h b/gui/LegacyModEditDialog.h index 5f6973d3..f12d9a7b 100644 --- a/gui/LegacyModEditDialog.h +++ b/gui/LegacyModEditDialog.h @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -19,51 +19,60 @@ #include "logic/LegacyInstance.h" #include -namespace Ui { +namespace Ui +{ class LegacyModEditDialog; } class LegacyModEditDialog : public QDialog { Q_OBJECT - + public: - explicit LegacyModEditDialog(LegacyInstance* inst, QWidget *parent = 0); + explicit LegacyModEditDialog(LegacyInstance *inst, QWidget *parent = 0); ~LegacyModEditDialog(); - -private slots: - + +private +slots: + void on_addJarBtn_clicked(); void on_rmJarBtn_clicked(); void on_addForgeBtn_clicked(); void on_moveJarUpBtn_clicked(); void on_moveJarDownBtn_clicked(); - + void on_addCoreBtn_clicked(); void on_rmCoreBtn_clicked(); void on_viewCoreBtn_clicked(); - + void on_addModBtn_clicked(); void on_rmModBtn_clicked(); void on_viewModBtn_clicked(); - + void on_addTexPackBtn_clicked(); void on_rmTexPackBtn_clicked(); void on_viewTexPackBtn_clicked(); + + void on_jarWebsite_clicked(); + void on_loaderWebsite_clicked(); + void on_coreWebsite_clicked(); + // Questionable: SettingsDialog doesn't need this for some reason? void on_buttonBox_rejected(); + protected: bool eventFilter(QObject *obj, QEvent *ev); - bool jarListFilter( QKeyEvent* ev ); - bool coreListFilter( QKeyEvent* ev ); - bool loaderListFilter( QKeyEvent* ev ); - bool texturePackListFilter( QKeyEvent* ev ); + bool jarListFilter(QKeyEvent *ev); + bool coreListFilter(QKeyEvent *ev); + bool loaderListFilter(QKeyEvent *ev); + bool texturePackListFilter(QKeyEvent *ev); + private: Ui::LegacyModEditDialog *ui; std::shared_ptr m_mods; std::shared_ptr m_coremods; std::shared_ptr m_jarmods; std::shared_ptr m_texturepacks; - LegacyInstance * m_inst; + LegacyInstance *m_inst; DownloadJobPtr forgeJob; }; diff --git a/gui/LegacyModEditDialog.ui b/gui/LegacyModEditDialog.ui index 73b767dc..6a4b6d1e 100644 --- a/gui/LegacyModEditDialog.ui +++ b/gui/LegacyModEditDialog.ui @@ -57,6 +57,13 @@ + + + + Website + + + @@ -116,6 +123,13 @@ + + + + Website + + + @@ -171,6 +185,13 @@ + + + + Website + + + diff --git a/gui/ModEditDialogCommon.cpp b/gui/ModEditDialogCommon.cpp index 5da0a039..692ac0c4 100644 --- a/gui/ModEditDialogCommon.cpp +++ b/gui/ModEditDialogCommon.cpp @@ -1,17 +1,40 @@ #include "ModEditDialogCommon.h" - -bool lastfirst (QModelIndexList & list, int & first, int & last) +#include +#include +#include +#include +bool lastfirst(QModelIndexList &list, int &first, int &last) { - if(!list.size()) + if (!list.size()) return false; first = last = list[0].row(); - for(auto item: list) + for (auto item : list) { int row = item.row(); - if(row < first) + if (row < first) first = row; - if(row > last) + if (row > last) last = row; } return true; -} \ No newline at end of file +} + +void showWebsiteForMod(QWidget *parentDlg, Mod &m) +{ + QString url = m.homeurl(); + if (url.size()) + { + // catch the cases where the protocol is missing + if(!url.startsWith("http")) + { + url = "http://" + url; + } + QDesktopServices::openUrl(url); + } + else + { + QMessageBox::warning( + parentDlg, parentDlg->tr("How sad!"), + parentDlg->tr("The mod author didn't provide a website link for this mod.")); + } +} diff --git a/gui/ModEditDialogCommon.h b/gui/ModEditDialogCommon.h index a27a8a82..bc8e223f 100644 --- a/gui/ModEditDialogCommon.h +++ b/gui/ModEditDialogCommon.h @@ -1,4 +1,7 @@ #pragma once #include +#include -bool lastfirst (QModelIndexList & list, int & first, int & last); \ No newline at end of file +bool lastfirst (QModelIndexList & list, int & first, int & last); + +void showWebsiteForMod(QWidget * parentDlg, Mod& m); \ No newline at end of file diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index 24f01c53..788a61d0 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -30,6 +30,7 @@ #include #include #include +#include OneSixModEditDialog::OneSixModEditDialog(OneSixInstance *inst, QWidget *parent) : m_inst(inst), QDialog(parent), ui(new Ui::OneSixModEditDialog) @@ -296,3 +297,13 @@ void OneSixModEditDialog::on_viewResPackBtn_clicked() { openDirInDefaultProgram(m_inst->resourcePacksDir(), true); } + +void OneSixModEditDialog::on_loaderWebsite_clicked() +{ + int first, last; + auto list = ui->loaderModTreeView->selectionModel()->selectedRows(); + + if (!lastfirst(list, first, last)) + return; + showWebsiteForMod(this, m_mods->operator[](first)); +} diff --git a/gui/OneSixModEditDialog.h b/gui/OneSixModEditDialog.h index 5c65fea3..ce4da4fe 100644 --- a/gui/OneSixModEditDialog.h +++ b/gui/OneSixModEditDialog.h @@ -44,6 +44,7 @@ private slots: void on_forgeBtn_clicked(); void on_customizeBtn_clicked(); void on_revertBtn_clicked(); + void on_loaderWebsite_clicked(); void updateVersionControls(); void disableVersionControls(); protected: diff --git a/gui/OneSixModEditDialog.ui b/gui/OneSixModEditDialog.ui index 527899ca..b97d4304 100644 --- a/gui/OneSixModEditDialog.ui +++ b/gui/OneSixModEditDialog.ui @@ -184,6 +184,13 @@ + + + + Website + + + diff --git a/logic/Mod.cpp b/logic/Mod.cpp index 75e0a3a9..825e663f 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -1,12 +1,12 @@ -// +// // Copyright 2012 MultiMC Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -28,17 +28,17 @@ #include #include -Mod::Mod( const QFileInfo& file ) +Mod::Mod(const QFileInfo &file) { repath(file); } -void Mod::repath ( const QFileInfo& file ) +void Mod::repath(const QFileInfo &file) { m_file = file; m_name = file.completeBaseName(); m_id = file.fileName(); - + m_type = Mod::MOD_UNKNOWN; if (m_file.isDir()) m_type = MOD_FOLDER; @@ -50,19 +50,19 @@ void Mod::repath ( const QFileInfo& file ) else m_type = MOD_SINGLEFILE; } - if(m_type == MOD_ZIPFILE) + if (m_type == MOD_ZIPFILE) { QuaZip zip(m_file.filePath()); - if(!zip.open(QuaZip::mdUnzip)) + if (!zip.open(QuaZip::mdUnzip)) return; - + QuaZipFile file(&zip); - for(bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) + for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) { QString name = zip.getCurrentFileName(); - if(name == "mcmod.info") + if (name == "mcmod.info") { - if(!file.open(QIODevice::ReadOnly)) + if (!file.open(QIODevice::ReadOnly)) { zip.close(); return; @@ -72,9 +72,9 @@ void Mod::repath ( const QFileInfo& file ) zip.close(); return; } - else if(name == "forgeversion.properties") + else if (name == "forgeversion.properties") { - if(!file.open(QIODevice::ReadOnly)) + if (!file.open(QIODevice::ReadOnly)) { zip.close(); return; @@ -87,16 +87,16 @@ void Mod::repath ( const QFileInfo& file ) } zip.close(); } - else if(m_type == MOD_FOLDER) + else if (m_type == MOD_FOLDER) { QFileInfo mcmod_info(PathCombine(m_file.filePath(), "mcmod.info")); if (mcmod_info.isFile()) { QFile mcmod(mcmod_info.filePath()); - if(!mcmod.open(QIODevice::ReadOnly)) + if (!mcmod.open(QIODevice::ReadOnly)) return; auto data = mcmod.readAll(); - if(data.isEmpty() || data.isNull()) + if (data.isEmpty() || data.isNull()) return; ReadMCModInfo(data); } @@ -110,35 +110,37 @@ void Mod::repath ( const QFileInfo& file ) // https://github.com/MinecraftForge/FML/wiki/FML-mod-information-file/5bf6a2d05145ec79387acc0d45c958642fb049fc void Mod::ReadMCModInfo(QByteArray contents) { - auto getInfoFromArray = [&]( QJsonArray arr ) -> void + auto getInfoFromArray = [&](QJsonArray arr)->void { - if(!arr.at(0).isObject()) + if (!arr.at(0).isObject()) return; auto firstObj = arr.at(0).toObject(); m_id = firstObj.value("modid").toString(); m_name = firstObj.value("name").toString(); m_version = firstObj.value("version").toString(); + m_homeurl = firstObj.value("url").toString(); return; - }; + } + ; QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(contents, &jsonError); // this is the very old format that had just the array - if(jsonDoc.isArray()) + if (jsonDoc.isArray()) { getInfoFromArray(jsonDoc.array()); } - else if(jsonDoc.isObject()) + else if (jsonDoc.isObject()) { auto val = jsonDoc.object().value("modinfoversion"); int version = val.toDouble(); - if(version != 2) + if (version != 2) { QLOG_ERROR() << "BAD stuff happened to mod json:"; QLOG_ERROR() << contents; return; } auto arrVal = jsonDoc.object().value("modlist"); - if(arrVal.isArray()) + if (arrVal.isArray()) { getInfoFromArray(arrVal.toArray()); } @@ -150,33 +152,34 @@ void Mod::ReadForgeInfo(QByteArray contents) // Read the data m_name = "Minecraft Forge"; m_id = "Forge"; + m_homeurl = "http://www.minecraftforge.net/forum/"; INIFile ini; - if(!ini.loadFile(contents)) + if (!ini.loadFile(contents)) return; - - QString major = ini.get("forge.major.number","0").toString(); - QString minor = ini.get("forge.minor.number","0").toString(); - QString revision = ini.get("forge.revision.number","0").toString(); - QString build = ini.get("forge.build.number","0").toString(); - + + QString major = ini.get("forge.major.number", "0").toString(); + QString minor = ini.get("forge.minor.number", "0").toString(); + QString revision = ini.get("forge.revision.number", "0").toString(); + QString build = ini.get("forge.build.number", "0").toString(); + m_version = major + "." + minor + "." + revision + "." + build; } -bool Mod::replace ( Mod& with ) +bool Mod::replace(Mod &with) { - if(!destroy()) + if (!destroy()) return false; bool success = false; auto t = with.type(); - if(t == MOD_ZIPFILE || t == MOD_SINGLEFILE) + if (t == MOD_ZIPFILE || t == MOD_SINGLEFILE) { success = QFile::copy(with.m_file.filePath(), m_file.path()); } - if(t == MOD_FOLDER) + if (t == MOD_FOLDER) { success = copyPath(with.m_file.filePath(), m_file.path()); } - if(success) + if (success) { m_id = with.m_id; m_mcversion = with.m_mcversion; @@ -189,10 +192,10 @@ bool Mod::replace ( Mod& with ) bool Mod::destroy() { - if(m_type == MOD_FOLDER) + if (m_type == MOD_FOLDER) { QDir d(m_file.filePath()); - if(d.removeRecursively()) + if (d.removeRecursively()) { m_type = MOD_UNKNOWN; return true; @@ -202,7 +205,7 @@ bool Mod::destroy() else if (m_type == MOD_SINGLEFILE || m_type == MOD_ZIPFILE) { QFile f(m_file.filePath()); - if(f.remove()) + if (f.remove()) { m_type = MOD_UNKNOWN; return true; @@ -212,18 +215,17 @@ bool Mod::destroy() return true; } - QString Mod::version() const { - switch(type()) + switch (type()) { - case MOD_ZIPFILE: - return m_version; - case MOD_FOLDER: - return "Folder"; - case MOD_SINGLEFILE: - return "File"; - default: - return "VOID"; + case MOD_ZIPFILE: + return m_version; + case MOD_FOLDER: + return "Folder"; + case MOD_SINGLEFILE: + return "File"; + default: + return "VOID"; } } diff --git a/logic/Mod.h b/logic/Mod.h index fcfcc139..9831fdc0 100644 --- a/logic/Mod.h +++ b/logic/Mod.h @@ -1,12 +1,12 @@ -// +// // Copyright 2012 MultiMC Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,46 +22,72 @@ class Mod public: enum ModType { - MOD_UNKNOWN, //!< Indicates an unspecified mod type. - MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files. + MOD_UNKNOWN, //!< Indicates an unspecified mod type. + MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files. MOD_SINGLEFILE, //!< The mod is a single file (not a zip file). - MOD_FOLDER, //!< The mod is in a folder on the filesystem. + MOD_FOLDER, //!< The mod is in a folder on the filesystem. }; Mod(const QFileInfo &file); - - QFileInfo filename() const { return m_file; } - QString id() const { return m_id; } - ModType type() const { return m_type; } - QString mcversion() const { return m_mcversion; }; - bool valid() {return m_type != MOD_UNKNOWN;} - QString name() const {return m_name; }; - + + QFileInfo filename() const + { + return m_file; + } + QString id() const + { + return m_id; + } + ModType type() const + { + return m_type; + } + QString mcversion() const + { + return m_mcversion; + } + ; + bool valid() + { + return m_type != MOD_UNKNOWN; + } + QString name() const + { + return m_name; + } + QString version() const; - - + + QString homeurl() const + { + return m_homeurl; + } + // delete all the files of this mod bool destroy(); // replace this mod with a copy of the other - bool replace(Mod & with); + bool replace(Mod &with); // change the mod's filesystem path (used by mod lists for *MAGIC* purposes) void repath(const QFileInfo &file); // WEAK compare operator - used for replacing mods - bool operator ==(const Mod &other) const + bool operator==(const Mod &other) const { return filename() == other.filename(); } bool strongCompare(const Mod &other) const { - return filename() == other.filename() && id() == other.id() && version() == other.version() && type() == other.type(); + return filename() == other.filename() && id() == other.id() && + version() == other.version() && type() == other.type(); } + private: void ReadMCModInfo(QByteArray contents); void ReadForgeInfo(QByteArray contents); + protected: - //FIXME: what do do with those? HMM... + // FIXME: what do do with those? HMM... /* void ReadModInfoData(QString info); void ReadForgeInfoData(QString infoFileData); @@ -72,6 +98,7 @@ protected: QString m_name; QString m_version; QString m_mcversion; + QString m_homeurl; ModType m_type; }; diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index fbf609b5..dd26714a 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -32,10 +32,8 @@ #define ASSETS_URLBASE "http://assets.minecraft.net/" #define MCN_URLBASE "http://sonicrules.org/mcnweb.py" -MinecraftVersionList::MinecraftVersionList(QObject *parent) : - BaseVersionList(parent) +MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent) { - } Task *MinecraftVersionList::getLoadTask() @@ -76,7 +74,7 @@ BaseVersionPtr MinecraftVersionList::getLatestStable() const { for (int i = 0; i < m_vlist.length(); i++) { - auto ver =std::dynamic_pointer_cast(m_vlist.at(i)); + auto ver = std::dynamic_pointer_cast(m_vlist.at(i)); if (ver->is_latest && !ver->is_snapshot) { return m_vlist.at(i); @@ -85,7 +83,7 @@ BaseVersionPtr MinecraftVersionList::getLatestStable() const return BaseVersionPtr(); } -void MinecraftVersionList::updateListData(QList versions) +void MinecraftVersionList::updateListData(QList versions) { beginResetModel(); m_vlist = versions; @@ -109,7 +107,6 @@ inline QDateTime timeFromS3Time(QString str) return QDateTime::fromString(str, Qt::ISODate); } - MCVListLoadTask::MCVListLoadTask(MinecraftVersionList *vlist) { m_list = vlist; @@ -151,91 +148,91 @@ void MCVListLoadTask::executeTask() connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded())); } - void MCVListLoadTask::list_downloaded() { - if(vlistReply->error() != QNetworkReply::NoError) + if (vlistReply->error() != QNetworkReply::NoError) { vlistReply->deleteLater(); emitFailed("Failed to load Minecraft main version list" + vlistReply->errorString()); return; } - + QJsonParseError jsonError; QJsonDocument jsonDoc = QJsonDocument::fromJson(vlistReply->readAll(), &jsonError); vlistReply->deleteLater(); - + if (jsonError.error != QJsonParseError::NoError) { emitFailed("Error parsing version list JSON:" + jsonError.errorString()); return; } - if(!jsonDoc.isObject()) + if (!jsonDoc.isObject()) { emitFailed("Error parsing version list JSON: jsonDoc is not an object"); return; } - + QJsonObject root = jsonDoc.object(); - + // Get the ID of the latest release and the latest snapshot. - if(!root.value("latest").isObject()) + if (!root.value("latest").isObject()) { emitFailed("Error parsing version list JSON: version list is missing 'latest' object"); return; } - + QJsonObject latest = root.value("latest").toObject(); - + QString latestReleaseID = latest.value("release").toString(""); QString latestSnapshotID = latest.value("snapshot").toString(""); - if(latestReleaseID.isEmpty()) + if (latestReleaseID.isEmpty()) { emitFailed("Error parsing version list JSON: latest release field is missing"); return; } - if(latestSnapshotID.isEmpty()) + if (latestSnapshotID.isEmpty()) { emitFailed("Error parsing version list JSON: latest snapshot field is missing"); return; } // Now, get the array of versions. - if(!root.value("versions").isArray()) + if (!root.value("versions").isArray()) { - emitFailed("Error parsing version list JSON: version list object is missing 'versions' array"); + emitFailed( + "Error parsing version list JSON: version list object is missing 'versions' array"); return; } QJsonArray versions = root.value("versions").toArray(); - - QList tempList; + + QList tempList; for (int i = 0; i < versions.count(); i++) { bool is_snapshot = false; bool is_latest = false; - + // Load the version info. - if(!versions[i].isObject()) + if (!versions[i].isObject()) { - //FIXME: log this somewhere + // FIXME: log this somewhere continue; } QJsonObject version = versions[i].toObject(); QString versionID = version.value("id").toString(""); QString versionTimeStr = version.value("releaseTime").toString(""); QString versionTypeStr = version.value("type").toString(""); - if(versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty()) + if (versionID.isEmpty() || versionTimeStr.isEmpty() || versionTypeStr.isEmpty()) { - //FIXME: log this somewhere + // FIXME: log this somewhere continue; } - + // Parse the timestamp. QDateTime versionTime = timeFromS3Time(versionTimeStr); - if(!versionTime.isValid()) + if (!versionTime.isValid()) { - //FIXME: log this somewhere + // FIXME: log this somewhere continue; } // Parse the type. @@ -243,23 +240,25 @@ void MCVListLoadTask::list_downloaded() // OneSix or Legacy. use filter to determine type if (versionTypeStr == "release") { - versionType = legacyWhitelist.contains(versionID)?MinecraftVersion::Legacy:MinecraftVersion::OneSix; + versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy + : MinecraftVersion::OneSix; is_latest = (versionID == latestReleaseID); is_snapshot = false; } - else if(versionTypeStr == "snapshot") // It's a snapshot... yay + else if (versionTypeStr == "snapshot") // It's a snapshot... yay { - versionType = legacyWhitelist.contains(versionID)?MinecraftVersion::Legacy:MinecraftVersion::OneSix; + versionType = legacyWhitelist.contains(versionID) ? MinecraftVersion::Legacy + : MinecraftVersion::OneSix; is_latest = (versionID == latestSnapshotID); is_snapshot = true; } - else if(versionTypeStr == "old_alpha") + else if (versionTypeStr == "old_alpha") { versionType = MinecraftVersion::Nostalgia; is_latest = false; is_snapshot = false; } - else if(versionTypeStr == "old_beta") + else if (versionTypeStr == "old_beta") { versionType = MinecraftVersion::Legacy; is_latest = false; @@ -267,12 +266,12 @@ void MCVListLoadTask::list_downloaded() } else { - //FIXME: log this somewhere + // FIXME: log this somewhere continue; } // Get the download URL. QString dlUrl = QString(MCVLIST_URLBASE) + versionID + "/"; - + // Now, we construct the version object and add it to the list. std::shared_ptr mcVersion(new MinecraftVersion()); mcVersion->m_name = mcVersion->m_descriptor = versionID; @@ -284,7 +283,7 @@ void MCVListLoadTask::list_downloaded() tempList.append(mcVersion); } m_list->updateListData(tempList); - + emitSucceeded(); return; } -- cgit From 60e7e019fe62f48ebdb5cea0ab83ab58f3379fdf Mon Sep 17 00:00:00 2001 From: Sky Date: Tue, 8 Oct 2013 17:07:54 +0100 Subject: Start mcmod.info panel. Needs to be its own widget and included in legacy mod edit window, text labels need eliding --- gui/OneSixModEditDialog.cpp | 31 +++- gui/OneSixModEditDialog.h | 4 +- gui/OneSixModEditDialog.ui | 365 ++++++++++++++++++++++++++++++++++++++------ logic/Mod.cpp | 12 ++ logic/Mod.h | 18 +++ 5 files changed, 377 insertions(+), 53 deletions(-) (limited to 'logic') diff --git a/gui/OneSixModEditDialog.cpp b/gui/OneSixModEditDialog.cpp index 788a61d0..d97967e8 100644 --- a/gui/OneSixModEditDialog.cpp +++ b/gui/OneSixModEditDialog.cpp @@ -298,12 +298,39 @@ void OneSixModEditDialog::on_viewResPackBtn_clicked() openDirInDefaultProgram(m_inst->resourcePacksDir(), true); } -void OneSixModEditDialog::on_loaderWebsite_clicked() +void OneSixModEditDialog::on_loaderModTreeView_pressed(const QModelIndex &index) { int first, last; auto list = ui->loaderModTreeView->selectionModel()->selectedRows(); if (!lastfirst(list, first, last)) return; - showWebsiteForMod(this, m_mods->operator[](first)); + + Mod &m = m_mods->operator[](first); + + QString missing = "

Missing from mcmod.info

"; + + QString name = m.name(); + if(name.isEmpty()) name = missing; + QString description = m.description(); + if(description.isEmpty()) description = missing; + QString authors = m.authors(); + if(authors.isEmpty()) authors = missing; + QString credits = m.credits(); + if(credits.isEmpty()) credits = missing; + QString website = m.homeurl(); + if(website.isEmpty()) website = missing; + else website = "" + website + ""; + + ui->label_Name->setText("

" + name + "

"); + ui->label_Description->setText(description); + ui->label_Authors->setText(authors); + ui->label_Credits->setText(credits); + ui->label_Website->setText(website); + + ui->label_Name->setToolTip("

" + name + "

"); + ui->label_Description->setToolTip(description); + ui->label_Authors->setToolTip(authors); + ui->label_Credits->setToolTip(credits); + //ui->label_Website->setToolTip(website); } diff --git a/gui/OneSixModEditDialog.h b/gui/OneSixModEditDialog.h index ce4da4fe..03ebf7a3 100644 --- a/gui/OneSixModEditDialog.h +++ b/gui/OneSixModEditDialog.h @@ -44,9 +44,11 @@ private slots: void on_forgeBtn_clicked(); void on_customizeBtn_clicked(); void on_revertBtn_clicked(); - void on_loaderWebsite_clicked(); void updateVersionControls(); void disableVersionControls(); + + void on_loaderModTreeView_pressed(const QModelIndex &index); + protected: bool eventFilter(QObject *obj, QEvent *ev); bool loaderListFilter( QKeyEvent* ev ); diff --git a/gui/OneSixModEditDialog.ui b/gui/OneSixModEditDialog.ui index b97d4304..88ba5a76 100644 --- a/gui/OneSixModEditDialog.ui +++ b/gui/OneSixModEditDialog.ui @@ -26,7 +26,7 @@
- 0 + 1 @@ -157,62 +157,327 @@ Loader Mods - + - - - true - - - QAbstractItemView::DropOnly - - - - - - - - - &Add - - - - - - - &Remove - - - - - - - Website - - - + - - - Qt::Vertical - - - - 20 - 40 - - - + + + + + + 0 + 0 + + + + true + + + QAbstractItemView::DropOnly + + + + - - - &View Folder - - + + + + + &Add + + + + + + + &Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + &View Folder + + + + + + + + + 55 + 0 + + + + + 16777215 + 150 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 250 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 9 + + + 0 + + + + + + 0 + 0 + + + + + 250 + 0 + + + + + 16777215 + 92 + + + + <html><head/><body><p><span style=" font-size:9pt; font-weight:600; font-style:italic;">Select a mod to view information...</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + 0 + 0 + + + + + 250 + 0 + + + + + 16777215 + 92 + + + + <html><head/><body><p><span style=" font-style:italic;">Mod description</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + + QLayout::SetDefaultConstraint + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + 200 + 0 + + + + + 16777215 + 92 + + + + <html><head/><body><p><span style=" font-style:italic; color:#4a4a4a;">Mod authors</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Credits: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 200 + 0 + + + + + 16777215 + 92 + + + + <html><head/><body><p><span style=" font-style:italic; color:#4a4a4a;">Mod credits</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Website: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 200 + 0 + + + + + 16777215 + 92 + + + + <html><head/><body><p><span style=" font-style:italic; color:#4a4a4a;">Mod website</span></p></body></html> + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + true + + + + + + + Authors: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + diff --git a/logic/Mod.cpp b/logic/Mod.cpp index 825e663f..c45e3ad2 100644 --- a/logic/Mod.cpp +++ b/logic/Mod.cpp @@ -119,6 +119,18 @@ void Mod::ReadMCModInfo(QByteArray contents) m_name = firstObj.value("name").toString(); m_version = firstObj.value("version").toString(); m_homeurl = firstObj.value("url").toString(); + m_description = firstObj.value("description").toString(); + QJsonArray authors = firstObj.value("authors").toArray(); + if(authors.size() == 0) m_authors = ""; + else if(authors.size() >= 1) + { + m_authors = authors.at(0).toString(); + for(int i = 1; i < authors.size(); i++) + { + m_authors += ", " + authors.at(i).toString(); + } + } + m_credits = firstObj.value("credits").toString(); return; } ; diff --git a/logic/Mod.h b/logic/Mod.h index 9831fdc0..f3aaf18b 100644 --- a/logic/Mod.h +++ b/logic/Mod.h @@ -63,6 +63,21 @@ public: return m_homeurl; } + QString description() const + { + return m_description; + } + + QString authors() const + { + return m_authors; + } + + QString credits() const + { + return m_credits; + } + // delete all the files of this mod bool destroy(); // replace this mod with a copy of the other @@ -99,6 +114,9 @@ protected: QString m_version; QString m_mcversion; QString m_homeurl; + QString m_description; + QString m_authors; + QString m_credits; ModType m_type; }; -- cgit From eaf0cbeafc5ff70bd2bb0d66b5f5980a71f824c5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Wed, 9 Oct 2013 23:16:10 +0200 Subject: Fix MMC-15 ``mod does not delete from jar'' --- gui/mainwindow.cpp | 5 +- logic/LegacyInstance.cpp | 125 ++++++++++++------------ logic/ModList.cpp | 241 +++++++++++++++++++++++------------------------ logic/OneSixInstance.cpp | 6 +- 4 files changed, 183 insertions(+), 194 deletions(-) (limited to 'logic') diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 70d26e02..5784b85a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -507,10 +507,11 @@ void MainWindow::onLoginComplete() } else { - ProgressDialog *tDialog = new ProgressDialog(this); + ProgressDialog tDialog(this); connect(updateTask, SIGNAL(succeeded()), SLOT(onGameUpdateComplete())); connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); - tDialog->exec(updateTask); + tDialog.exec(updateTask); + delete updateTask; } } diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 205f6873..f741caad 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -14,8 +14,9 @@ #define LAUNCHER_FILE "MultiMCLauncher.jar" -LegacyInstance::LegacyInstance(const QString& rootDir, SettingsObject* settings, QObject* parent) - :BaseInstance( new LegacyInstancePrivate(),rootDir, settings, parent) +LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent) + : BaseInstance(new LegacyInstancePrivate(), rootDir, settings, parent) { settings->registerSetting(new Setting("NeedsRebuild", true)); settings->registerSetting(new Setting("ShouldUpdate", false)); @@ -24,50 +25,51 @@ LegacyInstance::LegacyInstance(const QString& rootDir, SettingsObject* settings, settings->registerSetting(new Setting("IntendedJarVersion", "")); } -BaseUpdate* LegacyInstance::doUpdate() +BaseUpdate *LegacyInstance::doUpdate() { + auto list = jarModList(); return new LegacyUpdate(this, this); } -MinecraftProcess* LegacyInstance::prepareForLaunch(LoginResponse response) +MinecraftProcess *LegacyInstance::prepareForLaunch(LoginResponse response) { - MinecraftProcess * proc = new MinecraftProcess(this); - + MinecraftProcess *proc = new MinecraftProcess(this); + QIcon icon = MMC->icons()->getIcon(iconKey()); - auto pixmap = icon.pixmap(128,128); - pixmap.save(PathCombine(minecraftRoot(), "icon.png"),"PNG"); - + auto pixmap = icon.pixmap(128, 128); + pixmap.save(PathCombine(minecraftRoot(), "icon.png"), "PNG"); + // extract the legacy launcher QFile(":/launcher/launcher.jar").copy(PathCombine(minecraftRoot(), LAUNCHER_FILE)); - + // set the process arguments { QStringList args; - + // window size QString windowSize; if (settings().get("LaunchMaximized").toBool()) windowSize = "max"; else - windowSize = QString("%1x%2"). - arg(settings().get("MinecraftWinWidth").toInt()). - arg(settings().get("MinecraftWinHeight").toInt()); - + windowSize = QString("%1x%2").arg(settings().get("MinecraftWinWidth").toInt()).arg( + settings().get("MinecraftWinHeight").toInt()); + // window title QString windowTitle; windowTitle.append("MultiMC: ").append(name()); - + // Java arguments args.append(Util::Commandline::splitArgs(settings().get("JvmArgs").toString())); - + #ifdef OSX // OSX dock icon and name args << "-Xdock:icon=icon.png"; args << QString("-Xdock:name=\"%1\"").arg(windowTitle); #endif - - QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion()).absolutePath(); - + + QString lwjgl = QDir(MMC->settings()->get("LWJGLDir").toString() + "/" + lwjglVersion()) + .absolutePath(); + // launcher arguments args << QString("-Xms%1m").arg(settings().get("MinMemAlloc").toInt()); args << QString("-Xmx%1m").arg(settings().get("MaxMemAlloc").toInt()); @@ -80,41 +82,39 @@ MinecraftProcess* LegacyInstance::prepareForLaunch(LoginResponse response) args << lwjgl; proc->setMinecraftArguments(args); } - + // set the process work path proc->setMinecraftWorkdir(minecraftRoot()); - + return proc; } void LegacyInstance::cleanupAfterRun() { - //FIXME: delete the launcher and icons and whatnot. + // FIXME: delete the launcher and icons and whatnot. } -std::shared_ptr< ModList > LegacyInstance::coreModList() +std::shared_ptr LegacyInstance::coreModList() { I_D(LegacyInstance); - if(!d->core_mod_list) + if (!d->core_mod_list) { d->core_mod_list.reset(new ModList(coreModsDir())); } - else - d->core_mod_list->update(); + d->core_mod_list->update(); return d->core_mod_list; } -std::shared_ptr< ModList > LegacyInstance::jarModList() +std::shared_ptr LegacyInstance::jarModList() { I_D(LegacyInstance); - if(!d->jar_mod_list) + if (!d->jar_mod_list) { auto list = new ModList(jarModsDir(), modListFile()); connect(list, SIGNAL(changed()), SLOT(jarModsChanged())); d->jar_mod_list.reset(list); } - else - d->jar_mod_list->update(); + d->jar_mod_list->update(); return d->jar_mod_list; } @@ -123,38 +123,33 @@ void LegacyInstance::jarModsChanged() setShouldRebuild(true); } - -std::shared_ptr< ModList > LegacyInstance::loaderModList() +std::shared_ptr LegacyInstance::loaderModList() { I_D(LegacyInstance); - if(!d->loader_mod_list) + if (!d->loader_mod_list) { d->loader_mod_list.reset(new ModList(loaderModsDir())); } - else - d->loader_mod_list->update(); + d->loader_mod_list->update(); return d->loader_mod_list; } -std::shared_ptr< ModList > LegacyInstance::texturePackList() +std::shared_ptr LegacyInstance::texturePackList() { I_D(LegacyInstance); - if(!d->texture_pack_list) + if (!d->texture_pack_list) { d->texture_pack_list.reset(new ModList(texturePacksDir())); } - else - d->texture_pack_list->update(); + d->texture_pack_list->update(); return d->texture_pack_list; } - -QDialog * LegacyInstance::createModEditDialog ( QWidget* parent ) +QDialog *LegacyInstance::createModEditDialog(QWidget *parent) { return new LegacyModEditDialog(this, parent); } - QString LegacyInstance::jarModsDir() const { return PathCombine(instanceRoot(), "instMods"); @@ -204,7 +199,6 @@ QString LegacyInstance::instanceConfigFolder() const return PathCombine(minecraftRoot(), "config"); } - /* bool LegacyInstance::shouldUpdateCurrentVersion() const { @@ -215,21 +209,22 @@ bool LegacyInstance::shouldUpdateCurrentVersion() const void LegacyInstance::updateCurrentVersion(bool keepCurrent) { QFileInfo jar(runnableJar()); - + if(!jar.exists()) { setLastCurrentVersionUpdate(0); setCurrentVersionId("Unknown"); return; } - + qint64 time = jar.lastModified().toUTC().toMSecsSinceEpoch(); - + setLastCurrentVersionUpdate(time); if (!keepCurrent) { // TODO: Implement GetMinecraftJarVersion function. - QString newVersion = "Unknown";//javautils::GetMinecraftJarVersion(jar.absoluteFilePath()); + QString newVersion = +"Unknown";//javautils::GetMinecraftJarVersion(jar.absoluteFilePath()); setCurrentVersionId(newVersion); } } @@ -247,41 +242,41 @@ void LegacyInstance::setLastCurrentVersionUpdate ( qint64 val ) bool LegacyInstance::shouldRebuild() const { I_D(LegacyInstance); - return d->m_settings->get ( "NeedsRebuild" ).toBool(); + return d->m_settings->get("NeedsRebuild").toBool(); } -void LegacyInstance::setShouldRebuild ( bool val ) +void LegacyInstance::setShouldRebuild(bool val) { I_D(LegacyInstance); - d->m_settings->set ( "NeedsRebuild", val ); + d->m_settings->set("NeedsRebuild", val); } QString LegacyInstance::currentVersionId() const { I_D(LegacyInstance); - return d->m_settings->get ( "JarVersion" ).toString(); + return d->m_settings->get("JarVersion").toString(); } -void LegacyInstance::setCurrentVersionId ( QString val ) +void LegacyInstance::setCurrentVersionId(QString val) { I_D(LegacyInstance); - d->m_settings->set ( "JarVersion", val ); + d->m_settings->set("JarVersion", val); } QString LegacyInstance::lwjglVersion() const { I_D(LegacyInstance); - return d->m_settings->get ( "LwjglVersion" ).toString(); + return d->m_settings->get("LwjglVersion").toString(); } -void LegacyInstance::setLWJGLVersion ( QString val ) +void LegacyInstance::setLWJGLVersion(QString val) { I_D(LegacyInstance); - d->m_settings->set ( "LwjglVersion", val ); + d->m_settings->set("LwjglVersion", val); } QString LegacyInstance::intendedVersionId() const { I_D(LegacyInstance); - return d->m_settings->get ( "IntendedJarVersion" ).toString(); + return d->m_settings->get("IntendedJarVersion").toString(); } -bool LegacyInstance::setIntendedVersionId ( QString version ) +bool LegacyInstance::setIntendedVersionId(QString version) { settings().set("IntendedJarVersion", version); setShouldUpdate(true); @@ -290,16 +285,16 @@ bool LegacyInstance::setIntendedVersionId ( QString version ) bool LegacyInstance::shouldUpdate() const { I_D(LegacyInstance); - QVariant var = settings().get ( "ShouldUpdate" ); - if ( !var.isValid() || var.toBool() == false ) + QVariant var = settings().get("ShouldUpdate"); + if (!var.isValid() || var.toBool() == false) { return intendedVersionId() != currentVersionId(); } return true; } -void LegacyInstance::setShouldUpdate ( bool val ) +void LegacyInstance::setShouldUpdate(bool val) { - settings().set ( "ShouldUpdate", val ); + settings().set("ShouldUpdate", val); } QString LegacyInstance::defaultBaseJar() const @@ -312,7 +307,7 @@ QString LegacyInstance::defaultCustomBaseJar() const return PathCombine(binDir(), "mcbackup.jar"); } -bool LegacyInstance::menuActionEnabled ( QString action_name ) const +bool LegacyInstance::menuActionEnabled(QString action_name) const { if (action_name == "actionChangeInstMCVersion") return false; @@ -321,7 +316,7 @@ bool LegacyInstance::menuActionEnabled ( QString action_name ) const QString LegacyInstance::getStatusbarDescription() { - if(shouldUpdate()) + if (shouldUpdate()) return "Legacy : " + currentVersionId() + " -> " + intendedVersionId(); else return "Legacy : " + currentVersionId(); diff --git a/logic/ModList.cpp b/logic/ModList.cpp index a600afff..236f0db6 100644 --- a/logic/ModList.cpp +++ b/logic/ModList.cpp @@ -1,12 +1,12 @@ -// +// // Copyright 2013 MultiMC Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -23,22 +23,23 @@ #include #include -ModList::ModList ( const QString& dir, const QString& list_file ) -: QAbstractListModel(), m_dir(dir), m_list_file(list_file) +ModList::ModList(const QString &dir, const QString &list_file) + : QAbstractListModel(), m_dir(dir), m_list_file(list_file) { - m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::NoSymLinks); + m_dir.setFilter(QDir::Readable | QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | + QDir::NoSymLinks); m_dir.setSorting(QDir::Name); m_list_id = QUuid::createUuid().toString(); m_watcher = new QFileSystemWatcher(this); is_watching = false; - connect(m_watcher,SIGNAL(directoryChanged(QString)), this, SLOT(directoryChanged(QString))); - update(); + connect(m_watcher, SIGNAL(directoryChanged(QString)), this, + SLOT(directoryChanged(QString))); } void ModList::startWatching() { is_watching = m_watcher->addPath(m_dir.absolutePath()); - if(is_watching) + if (is_watching) QLOG_INFO() << "Started watching " << m_dir.absolutePath(); else QLOG_INFO() << "Failed to start watching " << m_dir.absolutePath(); @@ -47,32 +48,31 @@ void ModList::startWatching() void ModList::stopWatching() { is_watching = !m_watcher->removePath(m_dir.absolutePath()); - if(!is_watching) + if (!is_watching) QLOG_INFO() << "Stopped watching " << m_dir.absolutePath(); else QLOG_INFO() << "Failed to stop watching " << m_dir.absolutePath(); } - bool ModList::update() { if (!isValid()) return false; - + QList newMods; m_dir.refresh(); auto folderContents = m_dir.entryInfoList(); bool orderWasInvalid = false; - + // first, process the ordered items (if any) int currentOrderIndex = 0; QStringList listOrder = readListFile(); - for(auto item: listOrder) + for (auto item : listOrder) { - QFileInfo info (m_dir.filePath(item)); + QFileInfo info(m_dir.filePath(item)); int idx = folderContents.indexOf(info); // if the file from the index file exists - if(idx != -1) + if (idx != -1) { // remove from the actual folder contents list folderContents.takeAt(idx); @@ -84,26 +84,27 @@ bool ModList::update() orderWasInvalid = true; } } - for(auto entry: folderContents) + for (auto entry : folderContents) { newMods.append(Mod(entry)); } - if(mods.size() != newMods.size()) + if (mods.size() != newMods.size()) { orderWasInvalid = true; } - else for(int i = 0; i < mods.size(); i++) - { - if(!mods[i].strongCompare(newMods[i])) + else + for (int i = 0; i < mods.size(); i++) { - orderWasInvalid = true; - break; + if (!mods[i].strongCompare(newMods[i])) + { + orderWasInvalid = true; + break; + } } - } beginResetModel(); mods.swap(newMods); endResetModel(); - if(orderWasInvalid) + if (orderWasInvalid) { saveListFile(); emit changed(); @@ -111,22 +112,21 @@ bool ModList::update() return true; } -void ModList::directoryChanged ( QString path ) +void ModList::directoryChanged(QString path) { update(); } - QStringList ModList::readListFile() { QStringList stringList; - if(m_list_file.isNull() || m_list_file.isEmpty()) + if (m_list_file.isNull() || m_list_file.isEmpty()) return stringList; - + QFile textFile(m_list_file); - if(!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) + if (!textFile.open(QIODevice::ReadOnly | QIODevice::Text)) return QStringList(); - + QTextStream textStream(&textFile); while (true) { @@ -144,13 +144,13 @@ QStringList ModList::readListFile() bool ModList::saveListFile() { - if(m_list_file.isNull() || m_list_file.isEmpty()) + if (m_list_file.isNull() || m_list_file.isEmpty()) return false; QFile textFile(m_list_file); - if(!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + if (!textFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) return false; QTextStream textStream(&textFile); - for(auto mod:mods) + for (auto mod : mods) { auto pathname = mod.filename(); QString filename = pathname.fileName(); @@ -160,29 +160,28 @@ bool ModList::saveListFile() return false; } - bool ModList::isValid() { return m_dir.exists() && m_dir.isReadable(); } -bool ModList::installMod ( const QFileInfo& filename, int index ) +bool ModList::installMod(const QFileInfo &filename, int index) { - if(!filename.exists() || !filename.isReadable() || index < 0) + if (!filename.exists() || !filename.isReadable() || index < 0) { return false; } Mod m(filename); - if(!m.valid()) + if (!m.valid()) return false; - + // if it's already there, replace the original mod (in place) int idx = mods.indexOf(m); - if(idx != -1) + if (idx != -1) { - if(mods[idx].replace(m)) + if (mods[idx].replace(m)) { - + auto left = this->index(index); auto right = this->index(index, columnCount(QModelIndex()) - 1); emit dataChanged(left, right); @@ -192,33 +191,33 @@ bool ModList::installMod ( const QFileInfo& filename, int index ) } return false; } - + auto type = m.type(); - if(type == Mod::MOD_UNKNOWN) + if (type == Mod::MOD_UNKNOWN) return false; - if(type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE) + if (type == Mod::MOD_SINGLEFILE || type == Mod::MOD_ZIPFILE) { QString newpath = PathCombine(m_dir.path(), filename.fileName()); - if(!QFile::copy(filename.filePath(), newpath)) + if (!QFile::copy(filename.filePath(), newpath)) return false; m.repath(newpath); beginInsertRows(QModelIndex(), index, index); - mods.insert(index,m); + mods.insert(index, m); endInsertRows(); saveListFile(); emit changed(); return true; } - else if(type == Mod::MOD_FOLDER) + else if (type == Mod::MOD_FOLDER) { - + QString from = filename.filePath(); QString to = PathCombine(m_dir.path(), filename.fileName()); - if(!copyPath(from, to)) + if (!copyPath(from, to)) return false; m.repath(to); beginInsertRows(QModelIndex(), index, index); - mods.insert(index,m); + mods.insert(index, m); endInsertRows(); saveListFile(); emit changed(); @@ -227,12 +226,12 @@ bool ModList::installMod ( const QFileInfo& filename, int index ) return false; } -bool ModList::deleteMod ( int index ) +bool ModList::deleteMod(int index) { - if(index >= mods.size() || index < 0) + if (index >= mods.size() || index < 0) return false; - Mod & m = mods[index]; - if(m.destroy()) + Mod &m = mods[index]; + if (m.destroy()) { beginRemoveRows(QModelIndex(), index, index); mods.removeAt(index); @@ -244,11 +243,11 @@ bool ModList::deleteMod ( int index ) return false; } -bool ModList::deleteMods ( int first, int last ) +bool ModList::deleteMods(int first, int last) { - for(int i = first; i <= last; i++) + for (int i = first; i <= last; i++) { - Mod & m = mods[i]; + Mod &m = mods[i]; m.destroy(); } beginRemoveRows(QModelIndex(), first, last); @@ -259,18 +258,17 @@ bool ModList::deleteMods ( int first, int last ) return true; } - -bool ModList::moveModTo ( int from, int to ) +bool ModList::moveModTo(int from, int to) { - if(from < 0 || from >= mods.size()) + if (from < 0 || from >= mods.size()) return false; if (to >= rowCount()) to = rowCount() - 1; if (to == -1) to = rowCount() - 1; - if(from == to) + if (from == to) return false; - int togap = to > from ? to + 1: to; + int togap = to > from ? to + 1 : to; beginMoveRows(QModelIndex(), from, from, QModelIndex(), togap); mods.move(from, to); endMoveRows(); @@ -279,83 +277,81 @@ bool ModList::moveModTo ( int from, int to ) return true; } -bool ModList::moveModUp ( int from ) +bool ModList::moveModUp(int from) { - if(from > 0) + if (from > 0) return moveModTo(from, from - 1); return false; } -bool ModList::moveModsUp ( int first, int last ) +bool ModList::moveModsUp(int first, int last) { - if(first == 0) + if (first == 0) return false; - + beginMoveRows(QModelIndex(), first, last, QModelIndex(), first - 1); - mods.move(first-1, last); + mods.move(first - 1, last); endMoveRows(); saveListFile(); emit changed(); return true; } - -bool ModList::moveModDown ( int from ) +bool ModList::moveModDown(int from) { - if(from < 0) + if (from < 0) return false; - if(from < mods.size() - 1) + if (from < mods.size() - 1) return moveModTo(from, from + 1); return false; } -bool ModList::moveModsDown ( int first, int last ) +bool ModList::moveModsDown(int first, int last) { - if(last == mods.size() - 1) + if (last == mods.size() - 1) return false; - + beginMoveRows(QModelIndex(), first, last, QModelIndex(), last + 2); - mods.move(last+1, first); + mods.move(last + 1, first); endMoveRows(); saveListFile(); emit changed(); return true; } - -int ModList::columnCount ( const QModelIndex& parent ) const +int ModList::columnCount(const QModelIndex &parent) const { return 2; } -QVariant ModList::data ( const QModelIndex& index, int role ) const +QVariant ModList::data(const QModelIndex &index, int role) const { - if(!index.isValid()) + if (!index.isValid()) return QVariant(); - + int row = index.row(); int column = index.column(); - - if(row < 0 || row >= mods.size()) + + if (row < 0 || row >= mods.size()) return QVariant(); - - if(role != Qt::DisplayRole) + + if (role != Qt::DisplayRole) return QVariant(); - - switch(column) + + switch (column) { - case 0: - return mods[row].name(); - case 1: - return mods[row].version(); - case 2: - return mods[row].mcversion(); - default: - return QVariant(); + case 0: + return mods[row].name(); + case 1: + return mods[row].version(); + case 2: + return mods[row].mcversion(); + default: + return QVariant(); } } -QVariant ModList::headerData ( int section, Qt::Orientation orientation, int role ) const +QVariant ModList::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole || orientation != Qt::Horizontal) return QVariant(); @@ -370,10 +366,9 @@ QVariant ModList::headerData ( int section, Qt::Orientation orientation, int rol } } - -Qt::ItemFlags ModList::flags ( const QModelIndex& index ) const +Qt::ItemFlags ModList::flags(const QModelIndex &index) const { - Qt::ItemFlags defaultFlags = QAbstractListModel::flags ( index ); + Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index); if (index.isValid()) return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; else @@ -400,36 +395,37 @@ Qt::DropActions ModList::supportedDragActions() const return Qt::MoveAction; } -QMimeData* ModList::mimeData ( const QModelIndexList& indexes ) const +QMimeData *ModList::mimeData(const QModelIndexList &indexes) const { - QMimeData * data = new QMimeData(); - - if(indexes.size() == 0) + QMimeData *data = new QMimeData(); + + if (indexes.size() == 0) return data; - + auto idx = indexes[0]; int row = idx.row(); - if(row <0 || row >= mods.size()) + if (row < 0 || row >= mods.size()) return data; - + QStringList params; params << m_list_id << QString::number(row); data->setText(params.join('|')); return data; } -bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent ) +bool ModList::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, + const QModelIndex &parent) { if (action == Qt::IgnoreAction) - return true; + return true; // check if the action is supported if (!data || !(action & supportedDropActions())) return false; - if(parent.isValid()) + if (parent.isValid()) { row = parent.row(); column = parent.column(); } - + if (row > rowCount()) row = rowCount(); if (row == -1) @@ -437,41 +433,41 @@ bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int r if (column == -1) column = 0; QLOG_INFO() << "Drop row: " << row << " column: " << column; - + // files dropped from outside? - if(data->hasUrls()) + if (data->hasUrls()) { bool was_watching = is_watching; - if(was_watching) + if (was_watching) stopWatching(); auto urls = data->urls(); - for(auto url: urls) + for (auto url : urls) { // only local files may be dropped... - if(!url.isLocalFile()) + if (!url.isLocalFile()) continue; QString filename = url.toLocalFile(); installMod(filename, row); QLOG_INFO() << "installing: " << filename; } - if(was_watching) + if (was_watching) startWatching(); return true; } - else if(data->hasText()) + else if (data->hasText()) { QString sourcestr = data->text(); auto list = sourcestr.split('|'); - if(list.size() != 2) + if (list.size() != 2) return false; QString remoteId = list[0]; int remoteIndex = list[1].toInt(); QLOG_INFO() << "move: " << sourcestr; // no moving of things between two lists - if(remoteId != m_list_id) + if (remoteId != m_list_id) return false; // no point moving to the same place... - if(row == remoteIndex) + if (row == remoteIndex) return false; // otherwise, move the mod :D moveModTo(remoteIndex, row); @@ -479,4 +475,3 @@ bool ModList::dropMimeData ( const QMimeData* data, Qt::DropAction action, int r } return false; } - diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index a2a6bf17..ad3f9f58 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -168,8 +168,7 @@ std::shared_ptr OneSixInstance::loaderModList() { d->loader_mod_list.reset(new ModList(loaderModsDir())); } - else - d->loader_mod_list->update(); + d->loader_mod_list->update(); return d->loader_mod_list; } @@ -180,8 +179,7 @@ std::shared_ptr OneSixInstance::resourcePackList() { d->resource_pack_list.reset(new ModList(resourcePacksDir())); } - else - d->resource_pack_list->update(); + d->resource_pack_list->update(); return d->resource_pack_list; } -- cgit From 73f8bc5c92cb4a9b7ce507309001c6b206b5c8eb Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Thu, 10 Oct 2013 01:47:48 +0200 Subject: Version changing removes any existing version json. --- gui/MCModInfoFrame.ui | 14 +------ gui/mainwindow.cpp | 9 +++++ logic/BaseInstance.h | 98 +++++++++++++++++++++++++----------------------- logic/LegacyInstance.h | 52 ++++++++++++++----------- logic/LegacyUpdate.cpp | 4 +- logic/OneSixInstance.cpp | 11 +++++- logic/OneSixInstance.h | 39 +++++++++---------- 7 files changed, 124 insertions(+), 103 deletions(-) (limited to 'logic') diff --git a/gui/MCModInfoFrame.ui b/gui/MCModInfoFrame.ui index b24251ae..60e0a65c 100644 --- a/gui/MCModInfoFrame.ui +++ b/gui/MCModInfoFrame.ui @@ -7,7 +7,7 @@ 0 0 527 - 68 + 113 @@ -28,12 +28,6 @@ - - - 0 - 0 - - Select a mod to view title and authors... @@ -53,12 +47,6 @@ - - - 0 - 0 - - Select a mod to view description... diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 5784b85a..c726591d 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -606,6 +606,15 @@ void MainWindow::on_actionChangeInstMCVersion_triggered() vselect.setFilter(1, "OneSix"); if (vselect.exec() && vselect.selectedVersion()) { + if (m_selectedInstance->versionIsCustom()) + { + auto result = QMessageBox::warning( + this, tr("Are you sure?"), + tr("This will remove any library/version customization you did previously. " + "This includes things like Forge install and similar."), QMessageBox::Ok, QMessageBox::Abort); + if(result != QMessageBox::Ok) + return; + } m_selectedInstance->setIntendedVersionId(vselect.selectedVersion()->descriptor()); } } diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index e360d3ae..b083c24a 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software @@ -33,9 +33,9 @@ class BaseInstancePrivate; /*! * \brief Base class for instances. - * This class implements many functions that are common between instances and + * This class implements many functions that are common between instances and * provides a standard interface for all instances. - * + * * To create a new instance type, create a new class inheriting from this class * and implement the pure virtual functions. */ @@ -44,66 +44,72 @@ class BaseInstance : public QObject Q_OBJECT protected: /// no-touchy! - BaseInstance(BaseInstancePrivate * d, const QString &rootDir, SettingsObject * settings, QObject *parent = 0); + BaseInstance(BaseInstancePrivate *d, const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + public: /// virtual destructor to make sure the destruction is COMPLETE virtual ~BaseInstance() {}; - - /// nuke thoroughly - deletes the instance contents, notifies the list/model which is responsible of cleaning up the husk + + /// nuke thoroughly - deletes the instance contents, notifies the list/model which is + /// responsible of cleaning up the husk void nuke(); - - /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to be unique. + + /// The instance's ID. The ID SHALL be determined by MMC internally. The ID IS guaranteed to + /// be unique. QString id() const; - + /// get the type of this instance QString instanceType() const; - + /// Path to the instance's root directory. QString instanceRoot() const; - + /// Path to the instance's minecraft directory. QString minecraftRoot() const; - + QString name() const; void setName(QString val); - + QString iconKey() const; void setIconKey(QString val); - + QString notes() const; void setNotes(QString val); - + QString group() const; void setGroupInitial(QString val); void setGroupPost(QString val); - - + virtual QString intendedVersionId() const = 0; virtual bool setIntendedVersionId(QString version) = 0; - + + virtual bool versionIsCustom() = 0; + /*! * The instance's current version. - * This value represents the instance's current version. If this value is + * This value represents the instance's current version. If this value is * different from the intendedVersion, the instance should be updated. * \warning Don't change this value unless you know what you're doing. */ virtual QString currentVersionId() const = 0; - //virtual void setCurrentVersionId(QString val) = 0; - + // virtual void setCurrentVersionId(QString val) = 0; + /*! * Whether or not Minecraft should be downloaded when the instance is launched. */ virtual bool shouldUpdate() const = 0; virtual void setShouldUpdate(bool val) = 0; - /// Get the curent base jar of this instance. By default, it's the versions/$version/$version.jar + /// Get the curent base jar of this instance. By default, it's the + /// versions/$version/$version.jar QString baseJar() const; /// the default base jar of this instance virtual QString defaultBaseJar() const = 0; /// the default custom base jar of this instance virtual QString defaultCustomBaseJar() const = 0; - + /*! * Whether or not custom base jar is used */ @@ -114,7 +120,7 @@ public: */ QString customBaseJar() const; void setCustomBaseJar(QString val); - + /** * Gets the time that the instance was last launched. * Stored in milliseconds since epoch. @@ -122,53 +128,54 @@ public: qint64 lastLaunch() const; /// Sets the last launched time to 'val' milliseconds since epoch void setLastLaunch(qint64 val = QDateTime::currentMSecsSinceEpoch()); - + /*! - * \brief Gets the instance list that this instance is a part of. - * Returns NULL if this instance is not in a list + * \brief Gets the instance list that this instance is a part of. + * Returns NULL if this instance is not in a list * (the parent is not an InstanceList). - * \return A pointer to the InstanceList containing this instance. + * \return A pointer to the InstanceList containing this instance. */ InstanceList *instList() const; - + /*! * \brief Gets a pointer to this instance's version list. * \return A pointer to the available version list for this instance. */ virtual std::shared_ptr versionList() const; - + /*! * \brief Gets this instance's settings object. * This settings object stores instance-specific settings. * \return A pointer to this instance's settings object. */ virtual SettingsObject &settings() const; - + /// returns a valid update task if update is needed, NULL otherwise - virtual BaseUpdate* doUpdate() = 0; - + virtual BaseUpdate *doUpdate() = 0; + /// returns a valid minecraft process, ready for launch - virtual MinecraftProcess* prepareForLaunch(LoginResponse response) = 0; - - /// do any necessary cleanups after the instance finishes. also runs before 'prepareForLaunch' + virtual MinecraftProcess *prepareForLaunch(LoginResponse response) = 0; + + /// do any necessary cleanups after the instance finishes. also runs before + /// 'prepareForLaunch' virtual void cleanupAfterRun() = 0; - + /// create a mod edit dialog for the instance - virtual QDialog * createModEditDialog ( QWidget* parent ) = 0; - + virtual QDialog *createModEditDialog(QWidget *parent) = 0; + /// is a particular action enabled with this instance selected? virtual bool menuActionEnabled(QString action_name) const = 0; - + virtual QString getStatusbarDescription() = 0; - + /// FIXME: this really should be elsewhere... virtual QString instanceConfigFolder() const = 0; - + signals: /*! * \brief Signal emitted when properties relevant to the instance view change */ - void propertiesChanged(BaseInstance * inst); + void propertiesChanged(BaseInstance *inst); /*! * \brief Signal emitted when groups are affected in any way */ @@ -176,12 +183,11 @@ signals: /*! * \brief The instance just got nuked. Hurray! */ - void nuked(BaseInstance * inst); - + void nuked(BaseInstance *inst); + protected: std::shared_ptr inst_d; }; // pointer for lazy people typedef std::shared_ptr InstancePtr; - diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index d7438cca..8bf334f6 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -9,21 +9,22 @@ class LegacyInstance : public BaseInstance { Q_OBJECT public: - - explicit LegacyInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0); - + + explicit LegacyInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + /// Path to the instance's minecraft.jar QString runnableJar() const; - + //! Path to the instance's modlist file. QString modListFile() const; - + ////// Mod Lists ////// std::shared_ptr jarModList(); std::shared_ptr coreModList(); std::shared_ptr loaderModList(); std::shared_ptr texturePackList(); - + ////// Directories ////// QString savesDir() const; QString texturePacksDir() const; @@ -33,40 +34,47 @@ public: QString coreModsDir() const; QString resourceDir() const; virtual QString instanceConfigFolder() const; - + /*! * Whether or not the instance's minecraft.jar needs to be rebuilt. - * If this is true, when the instance launches, its jar mods will be + * If this is true, when the instance launches, its jar mods will be * re-added to a fresh minecraft.jar file. */ bool shouldRebuild() const; void setShouldRebuild(bool val); - + virtual QString currentVersionId() const; virtual void setCurrentVersionId(QString val); - + //! The version of LWJGL that this instance uses. QString lwjglVersion() const; /// st the version of LWJGL libs this instance will use void setLWJGLVersion(QString val); - + virtual QString intendedVersionId() const; - virtual bool setIntendedVersionId ( QString version ); - + virtual bool setIntendedVersionId(QString version); + // the `version' of Legacy instances is defined by the launcher code. + // in contrast with OneSix, where `version' is described in a json file + virtual bool versionIsCustom() override + { + return false; + }; + virtual bool shouldUpdate() const; virtual void setShouldUpdate(bool val); - virtual BaseUpdate* doUpdate(); - - virtual MinecraftProcess* prepareForLaunch(LoginResponse response); + virtual BaseUpdate *doUpdate(); + + virtual MinecraftProcess *prepareForLaunch(LoginResponse response); virtual void cleanupAfterRun(); - virtual QDialog * createModEditDialog ( QWidget* parent ); - + virtual QDialog *createModEditDialog(QWidget *parent); + virtual QString defaultBaseJar() const; virtual QString defaultCustomBaseJar() const; - - bool menuActionEnabled ( QString action_name ) const; + + bool menuActionEnabled(QString action_name) const; virtual QString getStatusbarDescription(); - -protected slots: + +protected +slots: virtual void jarModsChanged(); }; \ No newline at end of file diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 5f5a2e52..66b4bf8a 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -57,7 +57,7 @@ void LegacyUpdate::lwjglStart() auto worker = MMC->qnam(); QNetworkRequest req(realUrl); req.setRawHeader("Host", hostname.toLatin1()); - req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)"); + req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); QNetworkReply *rep = worker->get(req); m_reply = std::shared_ptr(rep); @@ -100,7 +100,7 @@ void LegacyUpdate::lwjglFinished(QNetworkReply *reply) QString hostname = realUrl.host(); QNetworkRequest req(redirectedTo); req.setRawHeader("Host", hostname.toLatin1()); - req.setHeader(QNetworkRequest::UserAgentHeader, "Wget/1.14 (linux-gnu)"); + req.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Cached)"); QNetworkReply *rep = worker->get(req); connect(rep, SIGNAL(downloadProgress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index ad3f9f58..1b7b5bb4 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -192,6 +192,11 @@ bool OneSixInstance::setIntendedVersionId(QString version) { settings().set("IntendedVersion", version); setShouldUpdate(true); + auto pathCustom = PathCombine(instanceRoot(), "custom.json"); + auto pathOrig = PathCombine(instanceRoot(), "version.json"); + QFile::remove(pathCustom); + QFile::remove(pathOrig); + reloadFullVersion(); return true; } @@ -271,7 +276,11 @@ bool OneSixInstance::reloadFullVersion() d->version = version; return true; } - return false; + else + { + d->version.reset(); + return false; + } } std::shared_ptr OneSixInstance::getFullVersion() diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 8f5c22e6..d2276afc 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -10,33 +10,33 @@ class OneSixInstance : public BaseInstance { Q_OBJECT public: - explicit OneSixInstance(const QString &rootDir, SettingsObject * settings, QObject *parent = 0); - - + explicit OneSixInstance(const QString &rootDir, SettingsObject *settings, + QObject *parent = 0); + ////// Mod Lists ////// std::shared_ptr loaderModList(); std::shared_ptr resourcePackList(); - + ////// Directories ////// QString resourcePacksDir() const; QString loaderModsDir() const; virtual QString instanceConfigFolder() const; - - virtual BaseUpdate* doUpdate(); - virtual MinecraftProcess* prepareForLaunch ( LoginResponse response ); + + virtual BaseUpdate *doUpdate(); + virtual MinecraftProcess *prepareForLaunch(LoginResponse response); virtual void cleanupAfterRun(); - + virtual QString intendedVersionId() const; - virtual bool setIntendedVersionId ( QString version ); - + virtual bool setIntendedVersionId(QString version); + virtual QString currentVersionId() const; // virtual void setCurrentVersionId ( QString val ) {}; - + virtual bool shouldUpdate() const; virtual void setShouldUpdate(bool val); - - virtual QDialog * createModEditDialog ( QWidget* parent ); - + + virtual QDialog *createModEditDialog(QWidget *parent); + /// reload the full version json file. return true on success! bool reloadFullVersion(); /// get the current full version info @@ -46,13 +46,14 @@ public: /// customize the current base version bool customizeVersion(); /// is the current version original, or custom? - bool versionIsCustom(); - + virtual bool versionIsCustom() override; + virtual QString defaultBaseJar() const; virtual QString defaultCustomBaseJar() const; - - virtual bool menuActionEnabled ( QString action_name ) const; + + virtual bool menuActionEnabled(QString action_name) const; virtual QString getStatusbarDescription(); + private: - QStringList processMinecraftArgs( LoginResponse response ); + QStringList processMinecraftArgs(LoginResponse response); }; \ No newline at end of file -- cgit From 541c044d529a7797b8b4e3eea26b1b59d485eb22 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Thu, 10 Oct 2013 03:05:21 +0200 Subject: Meh --- logic/OneSixInstance.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'logic') diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 1b7b5bb4..853bcc48 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -57,6 +57,7 @@ QStringList OneSixInstance::processMinecraftArgs(LoginResponse response) QString args_pattern = version->minecraftArguments; QMap token_mapping; + // yggdrasil! token_mapping["auth_username"] = response.username; token_mapping["auth_session"] = response.session_id; token_mapping["auth_player_name"] = response.player_name; @@ -68,6 +69,7 @@ QStringList OneSixInstance::processMinecraftArgs(LoginResponse response) map["auth_player_name"] = "00000000-0000-0000-0000-000000000000"; */ + // these do nothing and are stupid. token_mapping["profile_name"] = name(); token_mapping["version_name"] = version->id; @@ -144,8 +146,17 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(LoginResponse response) args.append(processMinecraftArgs(response)); // Set the width and height for 1.6 instances - args << QString("--width") << settings().get("MinecraftWinWidth").toString(); - args << QString("--height") << settings().get("MinecraftWinHeight").toString(); + bool maximize = settings().get("LaunchMaximized").toBool(); + if(maximize) + { + // this is probably a BAD idea + // args << QString("--fullscreen"); + } + else + { + args << QString("--width") << settings().get("MinecraftWinWidth").toString(); + args << QString("--height") << settings().get("MinecraftWinHeight").toString(); + } // create the process and set its parameters MinecraftProcess *proc = new MinecraftProcess(this); -- cgit From f8b4c2c0b25f89017db2702b60d47df7376b32e6 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Fri, 11 Oct 2013 16:13:01 +0200 Subject: Fix auth for 13w41a --- logic/OneSixInstance.cpp | 1 + logic/net/LoginTask.cpp | 2 +- logic/net/LoginTask.h | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'logic') diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 853bcc48..d80f6b37 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -60,6 +60,7 @@ QStringList OneSixInstance::processMinecraftArgs(LoginResponse response) // yggdrasil! token_mapping["auth_username"] = response.username; token_mapping["auth_session"] = response.session_id; + token_mapping["auth_access_token"] = response.access_token; token_mapping["auth_player_name"] = response.player_name; token_mapping["auth_uuid"] = response.player_id; diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp index 5de8efa9..90aac74a 100644 --- a/logic/net/LoginTask.cpp +++ b/logic/net/LoginTask.cpp @@ -264,6 +264,6 @@ void LoginTask::parseYggdrasilReply(QByteArray data) }; */ - result = {uInfo.username, sessionID, playerName, playerID}; + result = {uInfo.username, sessionID, playerName, playerID, accessToken}; emitSucceeded(); } diff --git a/logic/net/LoginTask.h b/logic/net/LoginTask.h index ba87142d..daea18af 100644 --- a/logic/net/LoginTask.h +++ b/logic/net/LoginTask.h @@ -27,9 +27,10 @@ struct UserInfo struct LoginResponse { QString username; - QString session_id; + QString session_id; // session id is a combination of player id and the access token QString player_name; QString player_id; + QString access_token; }; class QNetworkReply; -- cgit From 40a2456646df96e0dd8731ab78cba920a734a8e3 Mon Sep 17 00:00:00 2001 From: Sky Date: Mon, 14 Oct 2013 02:59:21 +0100 Subject: Huge Java detection refactor, version dialogs on first run (no JavaPath set) and "auto detect" button --- CMakeLists.txt | 2 + MultiMC.cpp | 23 ++--- MultiMC.h | 4 + gui/mainwindow.cpp | 30 ++++++ gui/mainwindow.h | 3 +- gui/settingsdialog.cpp | 15 ++- gui/settingsdialog.ui | 2 +- gui/versionselectdialog.cpp | 16 +++- gui/versionselectdialog.h | 5 +- logic/JavaUtils.cpp | 61 +++++++----- logic/JavaUtils.h | 16 ++-- logic/lists/JavaVersionList.cpp | 203 ++++++++++++++++++++++++++++++++++++++++ logic/lists/JavaVersionList.h | 93 ++++++++++++++++++ 13 files changed, 422 insertions(+), 51 deletions(-) create mode 100644 logic/lists/JavaVersionList.cpp create mode 100644 logic/lists/JavaVersionList.h (limited to 'logic') diff --git a/CMakeLists.txt b/CMakeLists.txt index 5503c5ca..64823772 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -302,6 +302,8 @@ logic/lists/LwjglVersionList.h logic/lists/LwjglVersionList.cpp logic/lists/ForgeVersionList.h logic/lists/ForgeVersionList.cpp +logic/lists/JavaVersionList.h +logic/lists/JavaVersionList.cpp # misc model/view logic/EnabledItemFilter.h diff --git a/MultiMC.cpp b/MultiMC.cpp index be1b86da..481bb0bb 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -5,8 +5,10 @@ #include #include #include +#include #include "gui/mainwindow.h" +#include "gui/versionselectdialog.h" #include "logic/lists/InstanceList.h" #include "logic/lists/IconList.h" #include "logic/lists/LwjglVersionList.h" @@ -263,17 +265,6 @@ void MultiMC::initGlobalSettings() // Java Settings m_settings->registerSetting(new Setting("JavaPath", "")); - QString currentJavaPath = m_settings->get("JavaPath").toString(); - if(currentJavaPath.isEmpty()) - { - QLOG_INFO() << "Java path not set, attempting to set it automatically..."; - - JavaUtils jut; - auto javas = jut.FindJavaPaths(); - - m_settings->set("JavaPath", std::get(javas.at(0))); - } - m_settings->registerSetting(new Setting("JvmArgs", "")); // Custom Commands @@ -342,6 +333,15 @@ std::shared_ptr MultiMC::minecraftlist() return m_minecraftlist; } +std::shared_ptr MultiMC::javalist() +{ + if (!m_javalist) + { + m_javalist.reset(new JavaVersionList()); + } + return m_javalist; +} + int main(int argc, char *argv[]) { // initialize Qt @@ -350,6 +350,7 @@ int main(int argc, char *argv[]) // show main window MainWindow mainWin; mainWin.show(); + mainWin.checkSetDefaultJava(); switch (app.status()) { diff --git a/MultiMC.h b/MultiMC.h index ec28cab2..62375cba 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -16,6 +16,7 @@ class InstanceList; class IconList; class QNetworkAccessManager; class ForgeVersionList; +class JavaVersionList; #if defined(MMC) #undef MMC @@ -75,6 +76,8 @@ public: std::shared_ptr minecraftlist(); + std::shared_ptr javalist(); + private: void initLogger(); @@ -95,6 +98,7 @@ private: std::shared_ptr m_lwjgllist; std::shared_ptr m_forgelist; std::shared_ptr m_minecraftlist; + std::shared_ptr m_javalist; QsLogging::DestinationPtr m_fileDestination; QsLogging::DestinationPtr m_debugDestination; diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index c726591d..06329140 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -53,6 +53,7 @@ #include "logic/lists/MinecraftVersionList.h" #include "logic/lists/LwjglVersionList.h" #include "logic/lists/IconList.h" +#include "logic/lists/JavaVersionList.h" #include "logic/net/LoginTask.h" #include "logic/BaseInstance.h" @@ -60,6 +61,7 @@ #include "logic/MinecraftProcess.h" #include "logic/OneSixAssets.h" #include "logic/OneSixUpdate.h" +#include "logic/JavaUtils.h" #include "logic/LegacyInstance.h" @@ -701,3 +703,31 @@ void MainWindow::instanceEnded() this->show(); ui->actionLaunchInstance->setEnabled(m_selectedInstance); } + +void MainWindow::checkSetDefaultJava() +{ + QString currentJavaPath = MMC->settings()->get("JavaPath").toString(); + if(currentJavaPath.isEmpty()) + { + QLOG_DEBUG() << "Java path not set, showing Java selection dialog..."; + + JavaVersionPtr java; + + VersionSelectDialog vselect(MMC->javalist().get(), tr("First run: select a Java version"), this, false); + vselect.setResizeOn(2); + vselect.exec(); + + if (!vselect.selectedVersion()) + { + QMessageBox::warning( + this, tr("Invalid version selected"), tr("You didn't select a valid Java version, so MultiMC will select the default. " + "You can change this in the settings dialog.")); + + JavaUtils ju; + java = ju.GetDefaultJava(); + } + + java = std::dynamic_pointer_cast(vselect.selectedVersion()); + MMC->settings()->set("JavaPath", java->path); + } +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 86f21113..dbf7c4c3 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -49,6 +49,8 @@ public: // Browser Dialog void openWebPage(QUrl url); + void checkSetDefaultJava(); + private slots: void onCatToggled(bool); @@ -128,7 +130,6 @@ protected: void setCatBackground(bool enabled); private: - Ui::MainWindow *ui; KCategoryDrawer *drawer; KCategorizedView *view; diff --git a/gui/settingsdialog.cpp b/gui/settingsdialog.cpp index 011925b7..a3347680 100644 --- a/gui/settingsdialog.cpp +++ b/gui/settingsdialog.cpp @@ -17,6 +17,8 @@ #include "settingsdialog.h" #include "ui_settingsdialog.h" #include "logic/JavaUtils.h" +#include "gui/versionselectdialog.h" +#include "logic/lists/JavaVersionList.h" #include #include @@ -184,10 +186,17 @@ void SettingsDialog::loadSettings(SettingsObject *s) void SettingsDialog::on_pushButton_clicked() { - JavaUtils jut; - auto javas = jut.FindJavaPaths(); + JavaVersionPtr java; - ui->javaPathTextBox->setText(std::get(javas.at(0))); + VersionSelectDialog vselect(MMC->javalist().get(), tr("Select a Java version"), this, true); + vselect.setResizeOn(2); + vselect.exec(); + + if (vselect.result() == QDialog::Accepted && vselect.selectedVersion()) + { + java = std::dynamic_pointer_cast(vselect.selectedVersion()); + ui->javaPathTextBox->setText(java->path); + } } void SettingsDialog::on_btnBrowse_clicked() diff --git a/gui/settingsdialog.ui b/gui/settingsdialog.ui index 0b7985f1..f8e78b4d 100644 --- a/gui/settingsdialog.ui +++ b/gui/settingsdialog.ui @@ -423,7 +423,7 @@ - Auto-detect + Auto-detect...
diff --git a/gui/versionselectdialog.cpp b/gui/versionselectdialog.cpp index d975a7b4..900cd092 100644 --- a/gui/versionselectdialog.cpp +++ b/gui/versionselectdialog.cpp @@ -26,7 +26,7 @@ #include #include -VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent) +VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent, bool cancelable) : QDialog(parent), ui(new Ui::VersionSelectDialog) { ui->setupUi(this); @@ -40,7 +40,12 @@ VersionSelectDialog::VersionSelectDialog(BaseVersionList *vlist, QString title, ui->listView->setModel(m_proxyModel); ui->listView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - ui->listView->header()->setSectionResizeMode(0, QHeaderView::Stretch); + ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); + + if(!cancelable) + { + ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); + } } VersionSelectDialog::~VersionSelectDialog() @@ -48,6 +53,13 @@ VersionSelectDialog::~VersionSelectDialog() delete ui; } +void VersionSelectDialog::setResizeOn(int column) +{ + ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::ResizeToContents); + resizeOnColumn = column; + ui->listView->header()->setSectionResizeMode(resizeOnColumn, QHeaderView::Stretch); +} + int VersionSelectDialog::exec() { QDialog::open(); diff --git a/gui/versionselectdialog.h b/gui/versionselectdialog.h index 4be048af..319caeca 100644 --- a/gui/versionselectdialog.h +++ b/gui/versionselectdialog.h @@ -33,7 +33,7 @@ class VersionSelectDialog : public QDialog Q_OBJECT public: - explicit VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent = 0); + explicit VersionSelectDialog(BaseVersionList *vlist, QString title, QWidget *parent = 0, bool cancelable = true); ~VersionSelectDialog(); virtual int exec(); @@ -44,6 +44,7 @@ public: BaseVersionPtr selectedVersion() const; void setFilter(int column, QString filter); + void setResizeOn(int column); private slots: void on_refreshButton_clicked(); @@ -53,6 +54,8 @@ private: BaseVersionList *m_vlist; QSortFilterProxyModel *m_proxyModel; + + int resizeOnColumn = 0; }; #endif // VERSIONSELECTDIALOG_H diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index 5cec35b6..c0630545 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -15,29 +15,37 @@ #include "JavaUtils.h" #include "pathutils.h" +#include "MultiMC.h" #include #include #include +#include #include +#include +#include JavaUtils::JavaUtils() { } -std::vector JavaUtils::GetDefaultJava() +JavaVersionPtr JavaUtils::GetDefaultJava() { - std::vector javas; - javas.push_back(std::make_tuple("java", "unknown", "java", false)); + JavaVersionPtr javaVersion(new JavaVersion()); - return javas; + javaVersion->id = "java"; + javaVersion->arch = "unknown"; + javaVersion->path = "java"; + javaVersion->recommended = false; + + return javaVersion; } #if WINDOWS -std::vector JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName) +QList JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString keyName) { - std::vector javas; + QList javas; QString archType = "unknown"; if(keyType == KEY_WOW64_64KEY) archType = "64"; @@ -87,7 +95,14 @@ std::vector JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QStr value = new char[valueSz]; RegQueryValueEx(newKey, "JavaHome", NULL, NULL, (BYTE*)value, &valueSz); - javas.push_back(std::make_tuple(subKeyName, archType, QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"), (recommended == subKeyName))); + // Now, we construct the version object and add it to the list. + JavaVersionPtr javaVersion(new JavaVersion()); + + javaVersion->id = subKeyName; + javaVersion->arch = archType; + javaVersion->path = QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"); + javaVersion->recommended = (recommended == subKeyName); + javas.append(javaVersion); } RegCloseKey(newKey); @@ -102,23 +117,25 @@ std::vector JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QStr return javas; } -std::vector JavaUtils::FindJavaPaths() -{ - std::vector JRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); - std::vector JDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); - std::vector JRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); - std::vector JDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); - - std::vector javas; - javas.insert(javas.end(), JRE64s.begin(), JRE64s.end()); - javas.insert(javas.end(), JDK64s.begin(), JDK64s.end()); - javas.insert(javas.end(), JRE32s.begin(), JRE32s.end()); - javas.insert(javas.end(), JDK32s.begin(), JDK32s.end()); +QList JavaUtils::FindJavaPaths() +{ + QList javas; + + QList JRE64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + QList JDK64s = this->FindJavaFromRegistryKey(KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); + QList JRE32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); + QList JDK32s = this->FindJavaFromRegistryKey(KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); + + javas.append(JRE64s); + javas.append(JDK64s); + javas.append(JRE32s); + javas.append(JDK32s); if(javas.size() <= 0) { QLOG_WARN() << "Failed to find Java in the Windows registry - defaulting to \"java\""; - return this->GetDefaultJava(); + javas.append(this->GetDefaultJava()); + return javas; } QLOG_INFO() << "Found the following Java installations (64 -> 32, JRE -> JDK): "; @@ -126,8 +143,8 @@ std::vector JavaUtils::FindJavaPaths() for(auto &java : javas) { QString sRec; - if(std::get(java)) sRec = "(Recommended)"; - QLOG_INFO() << std::get(java) << std::get(java) << " at " << std::get(java) << sRec; + if(java->recommended) sRec = "(Recommended)"; + QLOG_INFO() << java->id << java->arch << " at " << java->path << sRec; } return javas; diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h index 63daac12..e4f777d0 100644 --- a/logic/JavaUtils.h +++ b/logic/JavaUtils.h @@ -16,29 +16,25 @@ #pragma once #include - +#include +#include #include "osutils.h" #if WINDOWS #include #endif -#define JI_ID 0 -#define JI_ARCH 1 -#define JI_PATH 2 -#define JI_REC 3 -typedef std::tuple java_install; - class JavaUtils { public: JavaUtils(); - std::vector FindJavaPaths(); + QList FindJavaPaths(); + JavaVersionPtr GetDefaultJava(); private: - std::vector GetDefaultJava(); + #if WINDOWS - std::vector FindJavaFromRegistryKey(DWORD keyType, QString keyName); + QList FindJavaFromRegistryKey(DWORD keyType, QString keyName); #endif }; diff --git a/logic/lists/JavaVersionList.cpp b/logic/lists/JavaVersionList.cpp new file mode 100644 index 00000000..5389c4cc --- /dev/null +++ b/logic/lists/JavaVersionList.cpp @@ -0,0 +1,203 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JavaVersionList.h" +#include "MultiMC.h" + +#include +#include +#include + +#include +#include + +JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent) +{ +} + +Task *JavaVersionList::getLoadTask() +{ + return new JavaListLoadTask(this); +} + + +const BaseVersionPtr JavaVersionList::at(int i) const +{ + return m_vlist.at(i); +} + +bool JavaVersionList::isLoaded() +{ + return m_loaded; +} + +int JavaVersionList::count() const +{ + return m_vlist.count(); +} + +int JavaVersionList::columnCount(const QModelIndex &parent) const +{ + return 4; +} + +QVariant JavaVersionList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + auto version = std::dynamic_pointer_cast(m_vlist[index.row()]); + switch (role) + { + case Qt::DisplayRole: + switch (index.column()) + { + case 0: + return version->id; + + case 1: + return version->arch; + + case 2: + return version->path; + + case 3: + return version->recommended ? tr("Yes") : tr("No"); + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + return version->descriptor(); + + case VersionPointerRole: + return qVariantFromValue(m_vlist[index.row()]); + + default: + return QVariant(); + } +} + +QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + switch (section) + { + case 0: + return "Version"; + + case 1: + return "Arch"; + + case 2: + return "Path"; + + case 3: + return "Recommended"; + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case 0: + return "The name of the version."; + + case 1: + return "The architecture this version is for."; + + case 2: + return "Path to this Java version."; + + case 3: + return "Whether the version is recommended or not."; + + default: + return QVariant(); + } + + default: + return QVariant(); + } +} + +BaseVersionPtr JavaVersionList::getTopRecommended() const +{ + for (int i = 0; i < m_vlist.length(); i++) + { + auto ver = std::dynamic_pointer_cast(m_vlist.at(i)); + if (ver->recommended) + { + return m_vlist.at(i); + } + } + return BaseVersionPtr(); +} + +void JavaVersionList::updateListData(QList versions) +{ + beginResetModel(); + m_vlist = versions; + m_loaded = true; + endResetModel(); + // NOW SORT!! + // sort(); +} + +void JavaVersionList::sort() +{ + // NO-OP for now +} + +JavaListLoadTask::JavaListLoadTask(JavaVersionList *vlist) +{ + m_list = vlist; + m_currentRecommended = NULL; +} + +JavaListLoadTask::~JavaListLoadTask() +{ +} + +void JavaListLoadTask::executeTask() +{ + setStatus("Detecting Java installations..."); + + JavaUtils ju; + QList javas = ju.FindJavaPaths(); + + QList javas_bvp; + for(int i = 0; i < javas.length(); i++) + { + BaseVersionPtr java = std::dynamic_pointer_cast(javas.at(i)); + + if(java) + { + javas_bvp.append(java); + } + } + + m_list->updateListData(javas_bvp); + + emitSucceeded(); +} diff --git a/logic/lists/JavaVersionList.h b/logic/lists/JavaVersionList.h new file mode 100644 index 00000000..23bccfe4 --- /dev/null +++ b/logic/lists/JavaVersionList.h @@ -0,0 +1,93 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include "BaseVersionList.h" +#include "logic/tasks/Task.h" + +class JavaListLoadTask; + +struct JavaVersion : public BaseVersion +{ + virtual QString descriptor() + { + return id; + } + + virtual QString name() + { + return id; + } + + virtual QString typeString() const + { + return arch; + } + + QString id; + QString arch; + QString path; + bool recommended; +}; + +typedef std::shared_ptr JavaVersionPtr; + +class JavaVersionList : public BaseVersionList +{ + Q_OBJECT +public: + explicit JavaVersionList(QObject *parent = 0); + + virtual Task *getLoadTask(); + virtual bool isLoaded(); + virtual const BaseVersionPtr at(int i) const; + virtual int count() const; + virtual void sort(); + + virtual BaseVersionPtr getTopRecommended() const; + + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, + int role) const; + virtual int columnCount(const QModelIndex &parent) const; + +public slots: + virtual void updateListData(QList versions); + +protected: + QList m_vlist; + + bool m_loaded = false; +}; + +class JavaListLoadTask : public Task +{ + Q_OBJECT + +public: + explicit JavaListLoadTask(JavaVersionList *vlist); + ~JavaListLoadTask(); + + virtual void executeTask(); + +protected: + JavaVersionList *m_list; + JavaVersion *m_currentRecommended; +}; -- cgit From 7aacf93c7cbfdb92c869bb0ea86e6afa69bfa421 Mon Sep 17 00:00:00 2001 From: Sky Date: Tue, 15 Oct 2013 00:30:33 +0100 Subject: Fix non-Windows derp --- logic/JavaUtils.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'logic') diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index c0630545..8e9c984f 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -150,25 +150,34 @@ QList JavaUtils::FindJavaPaths() return javas; } #elif OSX -std::vector JavaUtils::FindJavaPaths() +QList JavaUtils::FindJavaPaths() { QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\""; - return this->GetDefaultJava(); + QList javas; + javas.append(this->GetDefaultJava()); + + return javas; } #elif LINUX -std::vector JavaUtils::FindJavaPaths() +QList JavaUtils::FindJavaPaths() { QLOG_INFO() << "Linux Java detection incomplete - defaulting to \"java\""; - return this->GetDefaultJava(); + QList javas; + javas.append(this->GetDefaultJava()); + + return javas; } #else -std::vector JavaUtils::FindJavaPaths() +QList JavaUtils::FindJavaPaths() { QLOG_INFO() << "Unknown operating system build - defaulting to \"java\""; - return this->GetDefaultJava(); + QList javas; + javas.append(this->GetDefaultJava()); + + return javas; } #endif -- cgit From 0a715a7b781410e4642c1585ad071ae5ba916b9d Mon Sep 17 00:00:00 2001 From: Sky Date: Tue, 15 Oct 2013 03:11:10 +0100 Subject: Improve error message for invalid login details using yggdrasil, add debug log for unknown codes --- logic/net/LoginTask.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'logic') diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp index 90aac74a..62d6331f 100644 --- a/logic/net/LoginTask.cpp +++ b/logic/net/LoginTask.cpp @@ -201,7 +201,13 @@ void LoginTask::processYggdrasilReply(QNetworkReply *reply) emitFailed(tr("Login canceled.")); break; + // Equivalent to an HTTP 403 + case QNetworkReply::ContentOperationNotPermittedError: + emitFailed(tr("Invalid username or password.")); + break; + default: + QLOG_DEBUG() << "Login failed with QNetworkReply code:" << reply->error(); emitFailed(tr("Login failed: %1").arg(reply->errorString())); break; } -- cgit From c700b7be2e6e5632f6cea6f0c45744f494a0629f Mon Sep 17 00:00:00 2001 From: Sky Date: Thu, 17 Oct 2013 00:02:56 +0100 Subject: Un-copy-pasta the login response handler using std::function magic --- logic/net/LoginTask.cpp | 145 ++++++++++++++++++++---------------------------- logic/net/LoginTask.h | 2 + 2 files changed, 63 insertions(+), 84 deletions(-) (limited to 'logic') diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp index 62d6331f..70ef2ae6 100644 --- a/logic/net/LoginTask.cpp +++ b/logic/net/LoginTask.cpp @@ -56,45 +56,6 @@ void LoginTask::legacyLogin() netReply = worker->post(netRequest, params.query(QUrl::EncodeSpaces).toUtf8()); } -void LoginTask::processLegacyReply(QNetworkReply *reply) -{ - if (netReply != reply) - return; - // Check for errors. - switch (reply->error()) - { - case QNetworkReply::NoError: - { - // Check the response code. - int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) - { - parseLegacyReply(reply->readAll()); - } - else if (responseCode == 503) - { - emitFailed(tr("The login servers are currently unavailable. Check " - "http://help.mojang.com/ for more info.")); - } - else - { - emitFailed(tr("Login failed: Unknown HTTP error %1 occurred.") - .arg(QString::number(responseCode))); - } - break; - } - - case QNetworkReply::OperationCanceledError: - emitFailed(tr("Login canceled.")); - break; - - default: - emitFailed(tr("Login failed: %1").arg(reply->errorString())); - break; - } -} - void LoginTask::parseLegacyReply(QByteArray data) { QString responseStr = QString::fromUtf8(data); @@ -129,6 +90,67 @@ void LoginTask::parseLegacyReply(QByteArray data) } } +void LoginTask::processLegacyReply(QNetworkReply *reply) +{ + processReply(reply, &LoginTask::parseLegacyReply); +} + +void LoginTask::processYggdrasilReply(QNetworkReply *reply) +{ + processReply(reply, &LoginTask::parseYggdrasilReply); +} + +void LoginTask::processReply(QNetworkReply *reply, std::function parser) +{ + if (netReply != reply) + return; + // Check for errors. + switch (reply->error()) + { + case QNetworkReply::NoError: + { + // Check the response code. + int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + switch (responseCode) + { + case 200: + parser(this, reply->readAll()); + break; + + default: + emitFailed(tr("Login failed: Unknown HTTP code %1 encountered.").arg(responseCode)); + break; + } + + break; + } + + case QNetworkReply::OperationCanceledError: + emitFailed(tr("Login canceled.")); + break; + + default: + int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + switch (responseCode) + { + case 403: + emitFailed(tr("Invalid username or password.")); + break; + + case 503: + emitFailed(tr("The login servers are currently unavailable. Check " + "http://help.mojang.com/ for more info.")); + break; + + default: + QLOG_DEBUG() << "Login failed with QNetworkReply code:" << reply->error(); + emitFailed(tr("Login failed: %1").arg(reply->errorString())); + break; + } + } +} void LoginTask::yggdrasilLogin() { @@ -168,51 +190,6 @@ void LoginTask::yggdrasilLogin() netReply = worker->post(netRequest, requestConstent.toUtf8()); } -void LoginTask::processYggdrasilReply(QNetworkReply *reply) -{ - if (netReply != reply) - return; - // Check for errors. - switch (reply->error()) - { - case QNetworkReply::NoError: - { - // Check the response code. - int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) - { - parseYggdrasilReply(reply->readAll()); - } - else if (responseCode == 503) - { - emitFailed(tr("The login servers are currently unavailable. Check " - "http://help.mojang.com/ for more info.")); - } - else - { - emitFailed(tr("Login failed: Unknown HTTP error %1 occurred.") - .arg(QString::number(responseCode))); - } - break; - } - - case QNetworkReply::OperationCanceledError: - emitFailed(tr("Login canceled.")); - break; - - // Equivalent to an HTTP 403 - case QNetworkReply::ContentOperationNotPermittedError: - emitFailed(tr("Invalid username or password.")); - break; - - default: - QLOG_DEBUG() << "Login failed with QNetworkReply code:" << reply->error(); - emitFailed(tr("Login failed: %1").arg(reply->errorString())); - break; - } -} - /* { "accessToken": "random access token", // hexadecimal diff --git a/logic/net/LoginTask.h b/logic/net/LoginTask.h index daea18af..fa5897cb 100644 --- a/logic/net/LoginTask.h +++ b/logic/net/LoginTask.h @@ -54,6 +54,8 @@ protected slots: void processYggdrasilReply(QNetworkReply *reply); void parseYggdrasilReply(QByteArray data); + void processReply(QNetworkReply *reply, std::function); + protected: void executeTask(); -- cgit From a600286e33601a85949b9e51bd5421a45f9998ac Mon Sep 17 00:00:00 2001 From: Sky Date: Thu, 17 Oct 2013 00:46:25 +0100 Subject: Use Yggdrasil error response when available, or fall back to legacy HTTP error codes --- logic/net/LoginTask.cpp | 69 ++++++++++++++++++++++++++++++++++++------------- logic/net/LoginTask.h | 4 ++- 2 files changed, 54 insertions(+), 19 deletions(-) (limited to 'logic') diff --git a/logic/net/LoginTask.cpp b/logic/net/LoginTask.cpp index 70ef2ae6..4098783b 100644 --- a/logic/net/LoginTask.cpp +++ b/logic/net/LoginTask.cpp @@ -92,15 +92,15 @@ void LoginTask::parseLegacyReply(QByteArray data) void LoginTask::processLegacyReply(QNetworkReply *reply) { - processReply(reply, &LoginTask::parseLegacyReply); + processReply(reply, &LoginTask::parseLegacyReply, &LoginTask::parseLegacyError); } void LoginTask::processYggdrasilReply(QNetworkReply *reply) { - processReply(reply, &LoginTask::parseYggdrasilReply); + processReply(reply, &LoginTask::parseYggdrasilReply, &LoginTask::parseYggdrasilError); } -void LoginTask::processReply(QNetworkReply *reply, std::function parser) +void LoginTask::processReply(QNetworkReply *reply, std::function parser, std::function errorHandler) { if (netReply != reply) return; @@ -131,25 +131,58 @@ void LoginTask::processReply(QNetworkReply *reply, std::functionattribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + emitFailed(errorHandler(this, reply)); + break; + } +} - switch (responseCode) - { - case 403: - emitFailed(tr("Invalid username or password.")); - break; +QString LoginTask::parseLegacyError(QNetworkReply *reply) +{ + int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - case 503: - emitFailed(tr("The login servers are currently unavailable. Check " - "http://help.mojang.com/ for more info.")); - break; + switch (responseCode) + { + case 403: + return tr("Invalid username or password."); - default: - QLOG_DEBUG() << "Login failed with QNetworkReply code:" << reply->error(); - emitFailed(tr("Login failed: %1").arg(reply->errorString())); - break; - } + case 503: + return tr("The login servers are currently unavailable. Check " + "http://help.mojang.com/ for more info."); + + default: + QLOG_DEBUG() << "Login failed with QNetworkReply code:" << reply->error(); + return tr("Login failed: %1").arg(reply->errorString()); + } +} + +QString LoginTask::parseYggdrasilError(QNetworkReply *reply) +{ + QByteArray data = reply->readAll(); + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + + // If there are JSON errors fall back to using the legacy error handling using HTTP status codes + if (jsonError.error != QJsonParseError::NoError) + { + return parseLegacyError(reply); + } + + if (!jsonDoc.isObject()) + { + return parseLegacyError(reply); } + + QJsonObject root = jsonDoc.object(); + + //QString error = root.value("error").toString(); + QString errorMessage = root.value("errorMessage").toString(); + + if(errorMessage.isEmpty()) + { + return parseLegacyError(reply); + } + + return tr("Login failed: ") + errorMessage; } void LoginTask::yggdrasilLogin() diff --git a/logic/net/LoginTask.h b/logic/net/LoginTask.h index fa5897cb..aa925999 100644 --- a/logic/net/LoginTask.h +++ b/logic/net/LoginTask.h @@ -49,12 +49,14 @@ protected slots: void legacyLogin(); void processLegacyReply(QNetworkReply *reply); void parseLegacyReply(QByteArray data); + QString parseLegacyError(QNetworkReply *reply); void yggdrasilLogin(); void processYggdrasilReply(QNetworkReply *reply); void parseYggdrasilReply(QByteArray data); + QString parseYggdrasilError(QNetworkReply *reply); - void processReply(QNetworkReply *reply, std::function); + void processReply(QNetworkReply *reply, std::function, std::function); protected: void executeTask(); -- cgit From 205570be32b5cbd40eeb2b7e2d8d4fe116b07f64 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Fri, 18 Oct 2013 01:00:46 +0200 Subject: Support version format 9, fix version-related segfault, (maybe) fix forge lists. --- logic/OneSixVersion.cpp | 5 +++-- logic/lists/ForgeVersionList.cpp | 4 ++++ logic/net/CacheDownload.cpp | 12 ++++++++++-- logic/net/ForgeXzDownload.cpp | 2 +- logic/net/HttpMetaCache.cpp | 11 +++++++---- logic/net/HttpMetaCache.h | 3 ++- 6 files changed, 27 insertions(+), 10 deletions(-) (limited to 'logic') diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index f7efedf9..51958389 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -136,7 +136,7 @@ std::shared_ptr OneSixVersion::fromJson(QJsonObject root) root.value("minimumLauncherVersion").toDouble(); // ADD MORE HERE :D - if (launcher_ver > 0 && launcher_ver <= 7) + if (launcher_ver > 0 && launcher_ver <= 9) return fromJsonV4(root, readVersion); else { @@ -167,7 +167,8 @@ std::shared_ptr OneSixVersion::fromFile(QString filepath) } QJsonObject root = jsonDoc.object(); auto version = fromJson(root); - version->original_file = filepath; + if(version) + version->original_file = filepath; return version; } diff --git a/logic/lists/ForgeVersionList.cpp b/logic/lists/ForgeVersionList.cpp index 9b698135..491a43d7 100644 --- a/logic/lists/ForgeVersionList.cpp +++ b/logic/lists/ForgeVersionList.cpp @@ -162,6 +162,10 @@ void ForgeListLoadTask::executeTask() auto job = new DownloadJob("Version index"); // we do not care if the version is stale or not. auto forgeListEntry = MMC->metacache()->resolveEntry("minecraftforge", "list.json"); + + // verify by poking the server. + forgeListEntry->stale = true; + job->addCacheDownload(QUrl(JSON_URL), forgeListEntry); listJob.reset(job); connect(listJob.get(), SIGNAL(succeeded()), SLOT(list_downloaded())); diff --git a/logic/net/CacheDownload.cpp b/logic/net/CacheDownload.cpp index 6a76a4ae..309eb345 100644 --- a/logic/net/CacheDownload.cpp +++ b/logic/net/CacheDownload.cpp @@ -33,7 +33,11 @@ void CacheDownload::start() } QLOG_INFO() << "Downloading " << m_url.toString(); QNetworkRequest request(m_url); - request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + if(m_entry->remote_changed_timestamp.size()) + request.setRawHeader(QString("If-Modified-Since").toLatin1(), m_entry->remote_changed_timestamp.toLatin1()); + if(m_entry->etag.size()) + request.setRawHeader(QString("If-None-Match").toLatin1(), m_entry->etag.toLatin1()); + request.setHeader(QNetworkRequest::UserAgentHeader,"MultiMC/5.0 (Cached)"); auto worker = MMC->qnam(); @@ -87,7 +91,11 @@ void CacheDownload::downloadFinished() QFileInfo output_file_info(m_target_path); m_entry->etag = m_reply->rawHeader("ETag").constData(); - m_entry->last_changed_timestamp = + if(m_reply->hasRawHeader("Last-Modified")) + { + m_entry->remote_changed_timestamp = m_reply->rawHeader("Last-Modified").constData(); + } + m_entry->local_changed_timestamp = output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); m_entry->stale = false; MMC->metacache()->updateEntry(m_entry); diff --git a/logic/net/ForgeXzDownload.cpp b/logic/net/ForgeXzDownload.cpp index 3ec2155b..0e5287d8 100644 --- a/logic/net/ForgeXzDownload.cpp +++ b/logic/net/ForgeXzDownload.cpp @@ -269,7 +269,7 @@ void ForgeXzDownload::decompressAndInstall() QFileInfo output_file_info(m_target_path); m_entry->etag = m_reply->rawHeader("ETag").constData(); - m_entry->last_changed_timestamp = + m_entry->local_changed_timestamp = output_file_info.lastModified().toUTC().toMSecsSinceEpoch(); m_entry->stale = false; MMC->metacache()->updateEntry(m_entry); diff --git a/logic/net/HttpMetaCache.cpp b/logic/net/HttpMetaCache.cpp index 9c642a0f..5ba5b98d 100644 --- a/logic/net/HttpMetaCache.cpp +++ b/logic/net/HttpMetaCache.cpp @@ -82,7 +82,7 @@ MetaEntryPtr HttpMetaCache::resolveEntry ( QString base, QString resource_path, // if the file changed, check md5sum qint64 file_last_changed = finfo.lastModified().toUTC().toMSecsSinceEpoch(); - if(file_last_changed != entry->last_changed_timestamp) + if(file_last_changed != entry->local_changed_timestamp) { QFile input(real_path); input.open(QIODevice::ReadOnly); @@ -93,7 +93,7 @@ MetaEntryPtr HttpMetaCache::resolveEntry ( QString base, QString resource_path, return staleEntry(base, resource_path); } // md5sums matched... keep entry and save the new state to file - entry->last_changed_timestamp = file_last_changed; + entry->local_changed_timestamp = file_last_changed; SaveEventually(); } @@ -184,7 +184,8 @@ void HttpMetaCache::Load() QString path = foo->path = element_obj.value("path").toString(); foo->md5sum = element_obj.value("md5sum").toString(); foo->etag = element_obj.value("etag").toString(); - foo->last_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble(); + foo->local_changed_timestamp = element_obj.value("last_changed_timestamp").toDouble(); + foo->remote_changed_timestamp = element_obj.value("remote_changed_timestamp").toString(); // presumed innocent until closer examination foo->stale = false; entrymap.entry_list[path] = MetaEntryPtr( foo ); @@ -215,7 +216,9 @@ void HttpMetaCache::SaveNow() entryObj.insert("path", QJsonValue(entry->path)); entryObj.insert("md5sum", QJsonValue(entry->md5sum)); entryObj.insert("etag", QJsonValue(entry->etag)); - entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->last_changed_timestamp))); + entryObj.insert("last_changed_timestamp", QJsonValue(double(entry->local_changed_timestamp))); + if(!entry->remote_changed_timestamp.isEmpty()) + entryObj.insert("remote_changed_timestamp", QJsonValue(entry->remote_changed_timestamp)); entriesArr.append(entryObj); } } diff --git a/logic/net/HttpMetaCache.h b/logic/net/HttpMetaCache.h index 8107839e..557d9298 100644 --- a/logic/net/HttpMetaCache.h +++ b/logic/net/HttpMetaCache.h @@ -10,7 +10,8 @@ struct MetaEntry QString path; QString md5sum; QString etag; - qint64 last_changed_timestamp = 0; + qint64 local_changed_timestamp = 0; + QString remote_changed_timestamp; // QString for now, RFC 2822 encoded time bool stale = true; QString getFullPath(); }; -- cgit From 32ce5c5c0247c55a7db2974f0c8e07e456dd5f7d Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Sun, 20 Oct 2013 23:18:40 +0200 Subject: Local libs that won't get replaced/redownloaded --- logic/OneSixUpdate.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'logic') diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 008995ae..b5f1d78b 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -164,6 +164,8 @@ void OneSixUpdate::jarlibStart() auto metacache = MMC->metacache(); for (auto lib : libs) { + if (lib->hint() == "local") + continue; QString download_path = lib->downloadUrl(); auto entry = metacache->resolveEntry("libraries", lib->storagePath()); if (entry->stale) -- cgit