Updated copyright year.
[simple-x264-launcher.git] / src / tool_abstract.cpp
blobacbec4f46588f86a1ada47fcf2be2e4154879d03
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2023 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
22 #include "tool_abstract.h"
24 //Internal
25 #include "global.h"
26 #include "model_options.h"
27 #include "model_preferences.h"
28 #include "model_sysinfo.h"
29 #include "job_object.h"
31 //MUtils
32 #include <MUtils/OSSupport.h>
34 //Qt
35 #include <QProcess>
36 #include <QMutexLocker>
37 #include <QDir>
38 #include <QCryptographicHash>
40 QMutex AbstractTool::s_mutexStartProcess;
42 // ------------------------------------------------------------
43 // Helper Macros
44 // ------------------------------------------------------------
46 static void APPEND_AND_CLEAR(QStringList &list, QString &str)
48 if(!str.isEmpty())
50 const QString temp = str.trimmed();
51 if(!temp.isEmpty())
53 list << temp;
55 str.clear();
59 // ------------------------------------------------------------
60 // Constructor & Destructor
61 // ------------------------------------------------------------
63 AbstractTool::AbstractTool(JobObject *jobObject, const OptionsModel *options, const SysinfoModel *const sysinfo, const PreferencesModel *const preferences, JobStatus &jobStatus, volatile bool *abort, volatile bool *pause, QSemaphore *semaphorePause)
65 m_jobObject(jobObject),
66 m_options(options),
67 m_sysinfo(sysinfo),
68 m_preferences(preferences),
69 m_jobStatus(jobStatus),
70 m_abort(abort),
71 m_pause(pause),
72 m_semaphorePause(semaphorePause)
74 /*nothing to do here*/
77 // ------------------------------------------------------------
78 // Check Version
79 // ------------------------------------------------------------
81 unsigned int AbstractTool::checkVersion(bool &modified)
83 if(m_preferences->getSkipVersionTest())
85 log("Warning: Skipping the version check this time!");
86 return makeRevision(0xFFF0, 0xFFF0);
89 QProcess process;
90 QList<QRegExp*> patterns;
91 QStringList cmdLine;
93 //Init encoder-specific values
94 checkVersion_init(patterns, cmdLine);
96 log("Creating process:");
97 if(!startProcess(process, getBinaryPath(), cmdLine, true, &getExtraPaths(), &getExtraEnv()))
99 return false;
102 bool bTimeout = false;
103 bool bAborted = false;
105 unsigned int revision = UINT_MAX;
106 unsigned int coreVers = UINT_MAX;
107 modified = false;
109 while(process.state() != QProcess::NotRunning)
111 if(*m_abort)
113 process.kill();
114 bAborted = true;
115 break;
117 if(!process.waitForReadyRead())
119 if(process.state() == QProcess::Running)
121 process.kill();
122 qWarning("process timed out <-- killing!");
123 log("\nPROCESS TIMEOUT !!!");
124 bTimeout = true;
125 break;
128 PROCESS_PENDING_LINES(process, checkVersion_parseLine, patterns, coreVers, revision, modified);
131 if(!(bTimeout || bAborted))
133 PROCESS_PENDING_LINES(process, checkVersion_parseLine, patterns, coreVers, revision, modified);
136 process.waitForFinished();
137 if(process.state() != QProcess::NotRunning)
139 process.kill();
140 process.waitForFinished(-1);
143 while(!patterns.isEmpty())
145 QRegExp *pattern = patterns.takeFirst();
146 MUTILS_DELETE(pattern);
149 if(bTimeout || bAborted || (!checkVersion_succeeded(process.exitCode())))
151 if(!(bTimeout || bAborted))
153 log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
155 return UINT_MAX;
158 if((revision == UINT_MAX) || (coreVers == UINT_MAX))
160 log(tr("\nFAILED TO DETERMINE VERSION INFO !!!"));
161 return UINT_MAX;
164 return makeRevision(coreVers, revision);
167 bool AbstractTool::checkVersion_succeeded(const int &exitCode)
169 return (exitCode == EXIT_SUCCESS);
172 // ------------------------------------------------------------
173 // Process Creation
174 // ------------------------------------------------------------
176 bool AbstractTool::startProcess(QProcess &process, const QString &program, const QStringList &args, bool mergeChannels, const QStringList *const extraPaths, const QHash<QString, QString> *const extraEnv)
178 QMutexLocker lock(&s_mutexStartProcess);
179 log(commandline2string(program, args) + "\n");
181 MUtils::init_process(process, QDir::tempPath(), true, extraPaths, extraEnv);
182 if(!mergeChannels)
184 process.setProcessChannelMode(QProcess::SeparateChannels);
185 process.setReadChannel(QProcess::StandardError);
188 process.start(program, args);
190 if(process.waitForStarted())
192 m_jobObject->addProcessToJob(&process);
193 MUtils::OS::change_process_priority(&process, m_preferences->getProcessPriority());
194 lock.unlock();
195 return true;
198 log("Process creation has failed :-(");
199 QString errorMsg= process.errorString().trimmed();
200 if(!errorMsg.isEmpty()) log(errorMsg);
202 process.kill();
203 process.waitForFinished(-1);
204 return false;
207 // ------------------------------------------------------------
208 // Utilities
209 // ------------------------------------------------------------
211 QString AbstractTool::commandline2string(const QString &program, const QStringList &arguments)
213 const QString nativeProgfram = QDir::toNativeSeparators(program);
214 QString commandline = (nativeProgfram.contains(' ') ? QString("\"%1\"").arg(nativeProgfram) : nativeProgfram);
216 for(int i = 0; i < arguments.count(); i++)
218 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
221 return commandline;
224 QStringList AbstractTool::splitParams(const QString &params, const QString &sourceFile, const QString &outputFile)
226 QStringList list;
227 bool ignoreWhitespaces = false;
228 QString temp;
230 for(int i = 0; i < params.length(); i++)
232 const QChar c = params.at(i);
233 if(c == QLatin1Char('"'))
235 ignoreWhitespaces = (!ignoreWhitespaces);
236 continue;
238 else if((!ignoreWhitespaces) && (c == QChar::fromLatin1(' ')))
240 APPEND_AND_CLEAR(list, temp);
241 continue;
243 temp.append(c);
246 APPEND_AND_CLEAR(list, temp);
248 if(!sourceFile.isEmpty())
250 list.replaceInStrings("$(INPUT)", QDir::toNativeSeparators(sourceFile), Qt::CaseInsensitive);
253 if(!outputFile.isEmpty())
255 list.replaceInStrings("$(OUTPUT)", QDir::toNativeSeparators(outputFile), Qt::CaseInsensitive);
258 return list;
261 QString AbstractTool::stringToHash(const QString &string)
263 QByteArray result(10, char(0));
264 const QByteArray hash = QCryptographicHash::hash(string.toUtf8(), QCryptographicHash::Sha1);
266 if((hash.size() == 20) && (result.size() == 10))
268 unsigned char *out = reinterpret_cast<unsigned char*>(result.data());
269 const unsigned char *in = reinterpret_cast<const unsigned char*>(hash.constData());
270 for(int i = 0; i < 10; i++)
272 out[i] = (in[i] ^ in[10+i]);
276 return QString::fromLatin1(result.toHex().constData());
279 unsigned int AbstractTool::makeRevision(const unsigned int &core, const unsigned int &build)
281 return ((core & 0x0000FFFF) << 16) | (build & 0x0000FFFF);
284 void AbstractTool::splitRevision(const unsigned int &revision, unsigned int &core, unsigned int &build)
286 core = (revision & 0xFFFF0000) >> 16;
287 build = (revision & 0x0000FFFF);