OSDN Git Service

f2f67b93dc3869fbacd8deaf9fabf9bf89921324
[x264-launcher/x264-launcher.git] / src / tool_abstract.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Simple x264 Launcher
3 // Copyright (C) 2004-2015 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 // Constructor & Destructor
44 // ------------------------------------------------------------
45
46 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)
47 :
48         m_jobObject(jobObject),
49         m_options(options),
50         m_sysinfo(sysinfo),
51         m_preferences(preferences),
52         m_jobStatus(jobStatus),
53         m_abort(abort),
54         m_pause(pause),
55         m_semaphorePause(semaphorePause)
56 {
57         /*nothing to do here*/
58 }
59
60 // ------------------------------------------------------------
61 // Check Version
62 // ------------------------------------------------------------
63
64 unsigned int AbstractTool::checkVersion(bool &modified)
65 {
66         if(m_preferences->getSkipVersionTest())
67         {
68                 log("Warning: Skipping the version check this time!");
69                 return makeRevision(0xFFF0, 0xFFF0);
70         }
71
72         QProcess process;
73         QList<QRegExp*> patterns;
74         QStringList cmdLine;
75
76         //Init encoder-specific values
77         checkVersion_init(patterns, cmdLine);
78
79         log("Creating process:");
80         if(!startProcess(process, getBinaryPath(), cmdLine))
81         {
82                 return false;
83         }
84
85         bool bTimeout = false;
86         bool bAborted = false;
87
88         unsigned int revision = UINT_MAX;
89         unsigned int coreVers = UINT_MAX;
90         modified = false;
91
92         while(process.state() != QProcess::NotRunning)
93         {
94                 if(*m_abort)
95                 {
96                         process.kill();
97                         bAborted = true;
98                         break;
99                 }
100                 if(!process.waitForReadyRead())
101                 {
102                         if(process.state() == QProcess::Running)
103                         {
104                                 process.kill();
105                                 qWarning("process timed out <-- killing!");
106                                 log("\nPROCESS TIMEOUT !!!");
107                                 bTimeout = true;
108                                 break;
109                         }
110                 }
111                 PROCESS_PENDING_LINES(process, checkVersion_parseLine, patterns, coreVers, revision, modified);
112         }
113
114         if(!(bTimeout || bAborted))
115         {
116                 PROCESS_PENDING_LINES(process, checkVersion_parseLine, patterns, coreVers, revision, modified);
117         }
118
119         process.waitForFinished();
120         if(process.state() != QProcess::NotRunning)
121         {
122                 process.kill();
123                 process.waitForFinished(-1);
124         }
125
126         while(!patterns.isEmpty())
127         {
128                 QRegExp *pattern = patterns.takeFirst();
129                 MUTILS_DELETE(pattern);
130         }
131
132         if(bTimeout || bAborted || (!checkVersion_succeeded(process.exitCode())))
133         {
134                 if(!(bTimeout || bAborted))
135                 {
136                         log(tr("\nPROCESS EXITED WITH ERROR CODE: %1").arg(QString::number(process.exitCode())));
137                 }
138                 return UINT_MAX;
139         }
140
141         if((revision == UINT_MAX) || (coreVers == UINT_MAX))
142         {
143                 log(tr("\nFAILED TO DETERMINE VERSION INFO !!!"));
144                 return UINT_MAX;
145         }
146         
147         return makeRevision(coreVers, revision);
148 }
149
150 bool AbstractTool::checkVersion_succeeded(const int &exitCode)
151 {
152         return (exitCode == EXIT_SUCCESS);
153 }
154
155 // ------------------------------------------------------------
156 // Process Creation
157 // ------------------------------------------------------------
158
159 bool AbstractTool::startProcess(QProcess &process, const QString &program, const QStringList &args, bool mergeChannels)
160 {
161         QMutexLocker lock(&s_mutexStartProcess);
162         log(commandline2string(program, args) + "\n");
163
164         process.setWorkingDirectory(QDir::tempPath());
165
166         if(mergeChannels)
167         {
168                 process.setProcessChannelMode(QProcess::MergedChannels);
169                 process.setReadChannel(QProcess::StandardOutput);
170         }
171         else
172         {
173                 process.setProcessChannelMode(QProcess::SeparateChannels);
174                 process.setReadChannel(QProcess::StandardError);
175         }
176
177         process.start(program, args);
178         
179         if(process.waitForStarted())
180         {
181                 m_jobObject->addProcessToJob(&process);
182                 MUtils::OS::change_process_priority(&process, m_preferences->getProcessPriority());
183                 lock.unlock();
184                 return true;
185         }
186
187         log("Process creation has failed :-(");
188         QString errorMsg= process.errorString().trimmed();
189         if(!errorMsg.isEmpty()) log(errorMsg);
190
191         process.kill();
192         process.waitForFinished(-1);
193         return false;
194 }
195
196 // ------------------------------------------------------------
197 // Utilities
198 // ------------------------------------------------------------
199
200 QString AbstractTool::commandline2string(const QString &program, const QStringList &arguments)
201 {
202         const QString nativeProgfram = QDir::toNativeSeparators(program);
203         QString commandline = (nativeProgfram.contains(' ') ? QString("\"%1\"").arg(nativeProgfram) : nativeProgfram);
204         
205         for(int i = 0; i < arguments.count(); i++)
206         {
207                 commandline += (arguments.at(i).contains(' ') ? QString(" \"%1\"").arg(arguments.at(i)) : QString(" %1").arg(arguments.at(i)));
208         }
209
210         return commandline;
211 }
212
213 QString AbstractTool::stringToHash(const QString &string)
214 {
215         QByteArray result(10, char(0));
216         const QByteArray hash = QCryptographicHash::hash(string.toUtf8(), QCryptographicHash::Sha1);
217
218         if((hash.size() == 20) && (result.size() == 10))
219         {
220                 unsigned char *out = reinterpret_cast<unsigned char*>(result.data());
221                 const unsigned char *in = reinterpret_cast<const unsigned char*>(hash.constData());
222                 for(int i = 0; i < 10; i++)
223                 {
224                         out[i] = (in[i] ^ in[10+i]);
225                 }
226         }
227
228         return QString::fromLatin1(result.toHex().constData());
229 }
230
231 unsigned int AbstractTool::makeRevision(const unsigned int &core, const unsigned int &build)
232 {
233         return ((core & 0x0000FFFF) << 16) | (build & 0x0000FFFF);
234 }
235
236 void AbstractTool::splitRevision(const unsigned int &revision, unsigned int &core, unsigned int &build)
237 {
238         core  = (revision & 0xFFFF0000) >> 16;
239         build = (revision & 0x0000FFFF);
240 }