OSDN Git Service

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