OSDN Git Service

Bump program version + updated changelog.
[x264-launcher/x264-launcher.git] / src / tool_abstract.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2017 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.
14 //
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.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "tool_abstract.h"
23
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"
30
31 //MUtils
32 #include <MUtils/OSSupport.h>
33
34 //Qt
35 #include <QProcess>
36 #include <QMutexLocker>
37 #include <QDir>
38 #include <QCryptographicHash>
39
40 QMutex AbstractTool::s_mutexStartProcess;
41
42 // ------------------------------------------------------------
43 // Helper Macros
44 // ------------------------------------------------------------
45
46 static void APPEND_AND_CLEAR(QStringList &list, QString &str)
47 {
48         if(!str.isEmpty())
49         {
50                 const QString temp = str.trimmed();
51                 if(!temp.isEmpty())
52                 {
53                         list << temp;
54                 }
55                 str.clear();
56         }
57 }
58
59 // ------------------------------------------------------------
60 // Constructor & Destructor
61 // ------------------------------------------------------------
62
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)
64 :
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)
73 {
74         /*nothing to do here*/
75 }
76
77 // ------------------------------------------------------------
78 // Check Version
79 // ------------------------------------------------------------
80
81 unsigned int AbstractTool::checkVersion(bool &modified)
82 {
83         if(m_preferences->getSkipVersionTest())
84         {
85                 log("Warning: Skipping the version check this time!");
86                 return makeRevision(0xFFF0, 0xFFF0);
87         }
88
89         QProcess process;
90         QList<QRegExp*> patterns;
91         QStringList cmdLine;
92
93         //Init encoder-specific values
94         checkVersion_init(patterns, cmdLine);
95
96         log("Creating process:");
97         if(!startProcess(process, getBinaryPath(), cmdLine, true, &getExtraPaths()))
98         {
99                 return false;
100         }
101
102         bool bTimeout = false;
103         bool bAborted = false;
104
105         unsigned int revision = UINT_MAX;
106         unsigned int coreVers = UINT_MAX;
107         modified = false;
108
109         while(process.state() != QProcess::NotRunning)
110         {
111                 if(*m_abort)
112                 {
113                         process.kill();
114                         bAborted = true;
115                         break;
116                 }
117                 if(!process.waitForReadyRead())
118                 {
119                         if(process.state() == QProcess::Running)
120                         {
121                                 process.kill();
122                                 qWarning("process timed out <-- killing!");
123                                 log("\nPROCESS TIMEOUT !!!");
124                                 bTimeout = true;
125                                 break;
126                         }
127                 }
128                 PROCESS_PENDING_LINES(process, checkVersion_parseLine, patterns, coreVers, revision, modified);
129         }
130
131         if(!(bTimeout || bAborted))
132         {
133                 PROCESS_PENDING_LINES(process, checkVersion_parseLine, patterns, coreVers, revision, modified);
134         }
135
136         process.waitForFinished();
137         if(process.state() != QProcess::NotRunning)
138         {
139                 process.kill();
140                 process.waitForFinished(-1);
141         }
142
143         while(!patterns.isEmpty())
144         {
145                 QRegExp *pattern = patterns.takeFirst();
146                 MUTILS_DELETE(pattern);
147         }
148
149         if(bTimeout || bAborted || (!checkVersion_succeeded(process.exitCode())))
150         {
151                 if(!(bTimeout || bAborted))
152                 {
153                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
154                 }
155                 return UINT_MAX;
156         }
157
158         if((revision == UINT_MAX) || (coreVers == UINT_MAX))
159         {
160                 log(tr("\nFAILED TO DETERMINE VERSION INFO !!!"));
161                 return UINT_MAX;
162         }
163         
164         return makeRevision(coreVers, revision);
165 }
166
167 bool AbstractTool::checkVersion_succeeded(const int &exitCode)
168 {
169         return (exitCode == EXIT_SUCCESS);
170 }
171
172 // ------------------------------------------------------------
173 // Process Creation
174 // ------------------------------------------------------------
175
176 bool AbstractTool::startProcess(QProcess &process, const QString &program, const QStringList &args, bool mergeChannels, const QStringList *const extraPaths)
177 {
178         QMutexLocker lock(&s_mutexStartProcess);
179         log(commandline2string(program, args) + "\n");
180
181         MUtils::init_process(process, QDir::tempPath(), true, extraPaths);
182         if(!mergeChannels)
183         {
184                 process.setProcessChannelMode(QProcess::SeparateChannels);
185                 process.setReadChannel(QProcess::StandardError);
186         }
187
188         process.start(program, args);
189         
190         if(process.waitForStarted())
191         {
192                 m_jobObject->addProcessToJob(&process);
193                 MUtils::OS::change_process_priority(&process, m_preferences->getProcessPriority());
194                 lock.unlock();
195                 return true;
196         }
197
198         log("Process creation has failed :-(");
199         QString errorMsg= process.errorString().trimmed();
200         if(!errorMsg.isEmpty()) log(errorMsg);
201
202         process.kill();
203         process.waitForFinished(-1);
204         return false;
205 }
206
207 // ------------------------------------------------------------
208 // Utilities
209 // ------------------------------------------------------------
210
211 QString AbstractTool::commandline2string(const QString &program, const QStringList &arguments)
212 {
213         const QString nativeProgfram = QDir::toNativeSeparators(program);
214         QString commandline = (nativeProgfram.contains(' ') ? QString("\"%1\"").arg(nativeProgfram) : nativeProgfram);
215         
216         for(int i = 0; i < arguments.count(); i++)
217         {
218                 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
219         }
220
221         return commandline;
222 }
223
224 QStringList AbstractTool::splitParams(const QString &params, const QString &sourceFile, const QString &outputFile)
225 {
226         QStringList list; 
227         bool ignoreWhitespaces = false;
228         QString temp;
229
230         for(int i = 0; i < params.length(); i++)
231         {
232                 const QChar c = params.at(i);
233                 if(c == QLatin1Char('"'))
234                 {
235                         ignoreWhitespaces = (!ignoreWhitespaces);
236                         continue;
237                 }
238                 else if((!ignoreWhitespaces) && (c == QChar::fromLatin1(' ')))
239                 {
240                         APPEND_AND_CLEAR(list, temp);
241                         continue;
242                 }
243                 temp.append(c);
244         }
245         
246         APPEND_AND_CLEAR(list, temp);
247
248         if(!sourceFile.isEmpty())
249         {
250                 list.replaceInStrings("$(INPUT)",  QDir::toNativeSeparators(sourceFile), Qt::CaseInsensitive);
251         }
252
253         if(!outputFile.isEmpty())
254         {
255                 list.replaceInStrings("$(OUTPUT)", QDir::toNativeSeparators(outputFile), Qt::CaseInsensitive);
256         }
257
258         return list;
259 }
260
261 QString AbstractTool::stringToHash(const QString &string)
262 {
263         QByteArray result(10, char(0));
264         const QByteArray hash = QCryptographicHash::hash(string.toUtf8(), QCryptographicHash::Sha1);
265
266         if((hash.size() == 20) && (result.size() == 10))
267         {
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++)
271                 {
272                         out[i] = (in[i] ^ in[10+i]);
273                 }
274         }
275
276         return QString::fromLatin1(result.toHex().constData());
277 }
278
279 unsigned int AbstractTool::makeRevision(const unsigned int &core, const unsigned int &build)
280 {
281         return ((core & 0x0000FFFF) << 16) | (build & 0x0000FFFF);
282 }
283
284 void AbstractTool::splitRevision(const unsigned int &revision, unsigned int &core, unsigned int &build)
285 {
286         core  = (revision & 0xFFFF0000) >> 16;
287         build = (revision & 0x0000FFFF);
288 }