1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2023 LoRd_MuldeR <MuldeR2@GMX.de>
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.
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"
26 #include "model_options.h"
27 #include "model_preferences.h"
28 #include "model_sysinfo.h"
29 #include "job_object.h"
32 #include <MUtils/OSSupport.h>
36 #include <QMutexLocker>
38 #include <QCryptographicHash>
40 QMutex
AbstractTool::s_mutexStartProcess
;
42 // ------------------------------------------------------------
44 // ------------------------------------------------------------
46 static void APPEND_AND_CLEAR(QStringList
&list
, QString
&str
)
50 const QString temp
= str
.trimmed();
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
),
68 m_preferences(preferences
),
69 m_jobStatus(jobStatus
),
72 m_semaphorePause(semaphorePause
)
74 /*nothing to do here*/
77 // ------------------------------------------------------------
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);
90 QList
<QRegExp
*> patterns
;
93 //Init encoder-specific values
94 checkVersion_init(patterns
, cmdLine
);
96 log("Creating process:");
97 if(!startProcess(process
, getBinaryPath(), cmdLine
, true, &getExtraPaths(), &getExtraEnv()))
102 bool bTimeout
= false;
103 bool bAborted
= false;
105 unsigned int revision
= UINT_MAX
;
106 unsigned int coreVers
= UINT_MAX
;
109 while(process
.state() != QProcess::NotRunning
)
117 if(!process
.waitForReadyRead())
119 if(process
.state() == QProcess::Running
)
122 qWarning("process timed out <-- killing!");
123 log("\nPROCESS TIMEOUT !!!");
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
)
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())));
158 if((revision
== UINT_MAX
) || (coreVers
== UINT_MAX
))
160 log(tr("\nFAILED TO DETERMINE VERSION INFO !!!"));
164 return makeRevision(coreVers
, revision
);
167 bool AbstractTool::checkVersion_succeeded(const int &exitCode
)
169 return (exitCode
== EXIT_SUCCESS
);
172 // ------------------------------------------------------------
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
);
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());
198 log("Process creation has failed :-(");
199 QString errorMsg
= process
.errorString().trimmed();
200 if(!errorMsg
.isEmpty()) log(errorMsg
);
203 process
.waitForFinished(-1);
207 // ------------------------------------------------------------
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
)));
224 QStringList
AbstractTool::splitParams(const QString
¶ms
, const QString
&sourceFile
, const QString
&outputFile
)
227 bool ignoreWhitespaces
= false;
230 for(int i
= 0; i
< params
.length(); i
++)
232 const QChar c
= params
.at(i
);
233 if(c
== QLatin1Char('"'))
235 ignoreWhitespaces
= (!ignoreWhitespaces
);
238 else if((!ignoreWhitespaces
) && (c
== QChar::fromLatin1(' ')))
240 APPEND_AND_CLEAR(list
, temp
);
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
);
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);