OSDN Git Service

2bb5c632671ede09caf2daa0e87748a9f056faab
[x264-launcher/x264-launcher.git] / src / tool_abstract.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2016 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))
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)
177 {
178         QMutexLocker lock(&s_mutexStartProcess);
179         log(commandline2string(program, args) + "\n");
180
181         process.setWorkingDirectory(QDir::tempPath());
182
183         if(mergeChannels)
184         {
185                 process.setProcessChannelMode(QProcess::MergedChannels);
186                 process.setReadChannel(QProcess::StandardOutput);
187         }
188         else
189         {
190                 process.setProcessChannelMode(QProcess::SeparateChannels);
191                 process.setReadChannel(QProcess::StandardError);
192         }
193
194         process.start(program, args);
195         
196         if(process.waitForStarted())
197         {
198                 m_jobObject->addProcessToJob(&process);
199                 MUtils::OS::change_process_priority(&process, m_preferences->getProcessPriority());
200                 lock.unlock();
201                 return true;
202         }
203
204         log("Process creation has failed :-(");
205         QString errorMsg= process.errorString().trimmed();
206         if(!errorMsg.isEmpty()) log(errorMsg);
207
208         process.kill();
209         process.waitForFinished(-1);
210         return false;
211 }
212
213 // ------------------------------------------------------------
214 // Utilities
215 // ------------------------------------------------------------
216
217 QString AbstractTool::commandline2string(const QString &program, const QStringList &arguments)
218 {
219         const QString nativeProgfram = QDir::toNativeSeparators(program);
220         QString commandline = (nativeProgfram.contains(' ') ? QString("\"%1\"").arg(nativeProgfram) : nativeProgfram);
221         
222         for(int i = 0; i < arguments.count(); i++)
223         {
224                 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
225         }
226
227         return commandline;
228 }
229
230 QStringList AbstractTool::splitParams(const QString &params, const QString &sourceFile, const QString &outputFile)
231 {
232         QStringList list; 
233         bool ignoreWhitespaces = false;
234         QString temp;
235
236         for(int i = 0; i < params.length(); i++)
237         {
238                 const QChar c = params.at(i);
239                 if(c == QLatin1Char('"'))
240                 {
241                         ignoreWhitespaces = (!ignoreWhitespaces);
242                         continue;
243                 }
244                 else if((!ignoreWhitespaces) && (c == QChar::fromLatin1(' ')))
245                 {
246                         APPEND_AND_CLEAR(list, temp);
247                         continue;
248                 }
249                 temp.append(c);
250         }
251         
252         APPEND_AND_CLEAR(list, temp);
253
254         if(!sourceFile.isEmpty())
255         {
256                 list.replaceInStrings("$(INPUT)",  QDir::toNativeSeparators(sourceFile), Qt::CaseInsensitive);
257         }
258
259         if(!outputFile.isEmpty())
260         {
261                 list.replaceInStrings("$(OUTPUT)", QDir::toNativeSeparators(outputFile), Qt::CaseInsensitive);
262         }
263
264         return list;
265 }
266
267 QString AbstractTool::stringToHash(const QString &string)
268 {
269         QByteArray result(10, char(0));
270         const QByteArray hash = QCryptographicHash::hash(string.toUtf8(), QCryptographicHash::Sha1);
271
272         if((hash.size() == 20) && (result.size() == 10))
273         {
274                 unsigned char *out = reinterpret_cast<unsigned char*>(result.data());
275                 const unsigned char *in = reinterpret_cast<const unsigned char*>(hash.constData());
276                 for(int i = 0; i < 10; i++)
277                 {
278                         out[i] = (in[i] ^ in[10+i]);
279                 }
280         }
281
282         return QString::fromLatin1(result.toHex().constData());
283 }
284
285 unsigned int AbstractTool::makeRevision(const unsigned int &core, const unsigned int &build)
286 {
287         return ((core & 0x0000FFFF) << 16) | (build & 0x0000FFFF);
288 }
289
290 void AbstractTool::splitRevision(const unsigned int &revision, unsigned int &core, unsigned int &build)
291 {
292         core  = (revision & 0xFFFF0000) >> 16;
293         build = (revision & 0x0000FFFF);
294 }