CursorXP script loader fixed; script parser started
[xcurtheme.git] / src / xcr / xcrthemefx.cpp
blob4818255d7b8eda25abd5c0e4a301fa5d36c7669c
1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
3 * This program is free software. It comes without any warranty, to
4 * the extent permitted by applicable law. You can redistribute it
5 * and/or modify it under the terms of the Do What The Fuck You Want
6 * To Public License, Version 2, as published by Sam Hocevar. See
7 * http://sam.zoy.org/wtfpl/COPYING for more details.
8 */
9 #include <QDebug>
10 //#include <QtCore>
12 #include "xcrthemefx.h"
14 #include <unistd.h>
15 #include <zlib.h>
17 #include <QStringList>
18 #include <QTextStream>
20 #include "xcrimg.h"
21 #include "xcrxcur.h"
22 #include "xcrtheme.h"
25 static const char *curShapeName[] = {
26 "standard arrow",
27 "help arrow (the one with '?')",
28 "working arrow",
29 "busy cursor",
30 "precision select",
31 "text select",
32 "handwriting",
33 "unavailable",
34 "north (vert) resize",
35 "south resize",
36 "west (vert-means horiz?) resize",
37 "east resize",
38 "north-west resize",
39 "south-east resize",
40 "north-east resize",
41 "south-west resize",
42 "move",
43 "alternate select",
44 "hand",
45 "button"
49 ///////////////////////////////////////////////////////////////////////////////
50 static QByteArray zlibInflate (const void *buf, int bufSz, int destSz) {
51 QByteArray res;
52 z_stream stream;
53 int err;
55 res.resize(destSz+1);
56 stream.next_in = (Bytef *)buf;
57 stream.avail_in = bufSz;
58 stream.zalloc = (alloc_func)0;
59 stream.zfree = (free_func)0;
60 stream.next_out = (Bytef *)res.data();
61 stream.avail_out = destSz;
63 err = inflateInit(&stream);
64 if (err != Z_OK) return QByteArray();
65 err = inflate(&stream, Z_SYNC_FLUSH);
66 fprintf(stderr, "inflate result: %i\n", err);
67 switch (err) {
68 case Z_STREAM_END:
69 err = inflateEnd(&stream);
70 fprintf(stderr, "Z_STREAM_END: inflate result: %i\n", err);
71 if (err != Z_OK) return QByteArray();
72 break;
73 case Z_OK:
74 err = inflateEnd(&stream);
75 fprintf(stderr, "Z_OK: inflate result: %i\n", err);
76 if (err != Z_OK) return QByteArray();
77 break;
78 default: return QByteArray();
80 return res;
84 static quint32 baGetDW (QByteArray &ba, int &pos) {
85 const uchar *d = (const uchar *)ba.constData();
86 d += pos+3;
87 pos += 4;
88 quint32 res = 0;
89 for (int f = 4; f > 0; f--, d--) {
90 res <<= 8;
91 res |= *d;
93 return res;
97 ///////////////////////////////////////////////////////////////////////////////
98 XCursorThemeFX::XCursorThemeFX (const QString &aFileName) : XCursorTheme() {
99 if (!parseCursorFXTheme(aFileName)) {
100 qDeleteAll(mList);
101 mList.clear();
106 inline static quint8 alphaPreMul (quint8 clr, quint8 alpha) {
107 quint32 c32 = clr, a32 = alpha;
108 c32 = c32*a32/255;
109 if (c32 > a32) c32 = a32;
110 return c32&0xff;
114 bool XCursorThemeFX::parseCursorFXTheme (const QString &aFileName) {
115 qDebug() << "loading" << aFileName;
116 QFile fl(aFileName);
117 if (!fl.open(QIODevice::ReadOnly)) return false; // shit!
118 QByteArray ba(fl.readAll());
119 fl.close();
120 if (ba.size() < 0xb8) return false; // shit!
121 int pos = 0;
122 if (baGetDW(ba, pos) != 1) return false; // invalid version
123 quint32 mainHdrSize = baGetDW(ba, pos);
124 if (mainHdrSize < 0xb8) return false; // invalid header size
125 quint32 unDataSize = baGetDW(ba, pos);
126 if (unDataSize < 0x4c) return false; // no cursors anyway
127 struct {
128 quint32 ofs;
129 quint32 len;
130 } infoFields[6];
131 pos = 0x84;
132 for (int f = 0; f < 6; f++) {
133 infoFields[f].ofs = baGetDW(ba, pos);
134 infoFields[f].len = baGetDW(ba, pos);
136 pos = 0xb4;
137 quint32 ihdrSize = baGetDW(ba, pos);
138 // now read data
139 pos = mainHdrSize;
140 qDebug() << "reading data from" << pos;
141 QByteArray unp(zlibInflate(ba.constData()+pos, ba.size()-pos, unDataSize));
142 if (unp.isEmpty()) {
143 qDebug() << "CursorFX: can't depack data";
144 qWarning() << "CursorFX: can't depack data";
145 return false;
147 // process info section
148 for (int f = 0; f < 6; f++) {
149 int len = infoFields[f].len;
150 if (!len) continue;
151 pos = infoFields[f].ofs;
152 if ((quint32)pos >= ihdrSize || (quint32)pos+len >= ihdrSize) continue; // skip invalid one
153 QByteArray sBA(unp.mid(pos, len));
154 sBA.append('\0'); sBA.append('\0');
155 QString s = QString::fromUtf16((const ushort *)sBA.constData()).simplified();
156 switch (f) {
157 case 0: setTitle(s); break;
158 case 1: setAuthor(s); break;
159 //case 2: setVersion(s); break;
160 case 3: setSite(s); break;
161 case 4: setMail(s); break;
162 case 5: setDescr(s); break;
163 default: ;
166 // process resources
167 pos = ihdrSize;
168 qDebug() << "resources started at hex" << QString::number(pos, 16);
169 while (pos <= unp.size()-12) {
170 quint32 ipos = pos; // will be fixed later
171 quint32 rtype = baGetDW(unp, pos);
172 quint32 basicHdrSize = baGetDW(unp, pos);
173 quint32 itemSize = baGetDW(unp, pos);
174 qDebug() << "pos hex:" << QString::number(pos, 16) << "rtype:" << rtype << "bhdrsz hex:" << QString::number(basicHdrSize, 16) << "itemsz hex:" << QString::number(itemSize, 16);
175 if (itemSize < 12) {
176 // invalid data
177 qDebug() << "CursorFX: invalid data chunk size";
178 qWarning() << "CursorFX: invalid data chunk size";
179 return false;
181 pos = ipos+itemSize; // skip it
182 if (rtype != 2) continue; // not cursor resource
183 if (itemSize < 0x4c) {
184 qDebug() << "CursorFX: invalid cursor chunk size:" << itemSize;
185 qWarning() << "CursorFX: invalid cursor chunk size:" << itemSize;
186 return false;
188 // cursor
189 int cps = ipos+3*4;
190 rtype = baGetDW(unp, cps);
191 if (rtype != 2) {
192 qDebug() << "CursorFX: invalid cursor chunk type:" << rtype;
193 qWarning() << "CursorFX: invalid cursor chunk type:" << rtype;
194 return false;
196 quint32 curShape = baGetDW(unp, cps);
197 quint32 curType = baGetDW(unp, cps);
198 if (curShape > 19) {
199 qDebug() << "CursorFX: unknown cursor shape:" << curShape;
200 qWarning() << "CursorFX: unknown cursor shape:" << curShape;
201 return false;
203 if (curType != 1) {
204 qDebug() << "skiping 'press' cursor; shape no" << curShape << "named" << curShapeName[curShape];
205 continue;
206 // we need only 'normal' cursors
208 qDebug() << "cursor shape:" << curShape;
209 const char **nlst = findCursorByFXId((int)curShape);
210 if (!nlst) {
211 // unknown cursor type, skip it
212 qDebug() << "CursorFX: skiping cursor shape:" << curShapeName[curShape];
213 qWarning() << "CursorFX: skiping cursor shape:" << curShapeName[curShape];
214 continue;
216 qDebug() << "importing cursor" << *nlst;
217 quint32 unk0 = baGetDW(unp, cps); // unknown field
218 quint32 frameCnt = baGetDW(unp, cps);
219 if (frameCnt < 1) frameCnt = 1; // just in case
220 quint32 imgWdt = baGetDW(unp, cps);
221 quint32 imgHgt = baGetDW(unp, cps);
222 quint32 imgDelay = baGetDW(unp, cps);
223 quint32 aniFlags = baGetDW(unp, cps);
224 quint32 unk1 = baGetDW(unp, cps); // unknown field
225 quint32 imgXHot = baGetDW(unp, cps);
226 quint32 imgYHot = baGetDW(unp, cps);
227 quint32 realHdrSize = baGetDW(unp, cps);
228 quint32 imgDataSize = baGetDW(unp, cps);
229 quint32 addonOfs = baGetDW(unp, cps);
230 quint32 addonLen = baGetDW(unp, cps);
231 qDebug() <<
232 "cursor data:" <<
233 "\n frames:" << frameCnt <<
234 "\n width:" << imgWdt <<
235 "\n height:" << imgHgt <<
236 "\n delay:" << imgDelay <<
237 "\n flags:" << QString::number(aniFlags, 2) <<
238 "\n xhot:" << imgXHot <<
239 "\n yhot:" << imgYHot <<
240 "\n unk0:" << unk0 <<
241 "\n unk1:" << unk1 <<
242 "\n rhdata:" << QString::number(realHdrSize, 16) <<
243 "\n dataOfs:" << QString::number(ipos+realHdrSize, 16) <<
244 "\n cdataOfs:" << QString::number(ipos+basicHdrSize+addonLen, 16)
246 // now check if we have enought data
247 if (ipos+realHdrSize+imgDataSize > (quint32)pos) {
248 qDebug() << "CursorFX: cursor data too big";
249 qWarning() << "CursorFX: cursor data too big";
250 return false;
252 // addon is the script; parse it later
253 QString script;
254 if (addonLen) {
255 if (addonOfs < 0x4c || addonOfs > realHdrSize) {
256 qDebug() << "CursorFX: invalid addon data offset";
257 qWarning() << "CursorFX: invalid addon data offset";
258 return false;
260 QByteArray bs(unp.mid(ipos+addonOfs, addonLen));
261 bs.append('\0'); bs.append('\0');
262 script = QString::fromUtf16((const ushort *)bs.constData());
263 qDebug() << "script:\n" << script;
265 if (imgWdt*imgHgt*4 != imgDataSize) {
266 qDebug() << "data size:" << imgDataSize << "but should be" << imgWdt*imgHgt*4;
267 continue;
269 // decode image
270 //k8:cant!:QImage img((const uchar *)unp.constData()+ipos+realHdrSize, imgWdt, imgHgt, QImage::Format_ARGB32/*_Premultiplied*/);
271 QImage img((const uchar *)unp.constData()+ipos+realHdrSize, imgWdt, imgHgt, QImage::Format_ARGB32);
272 img = img.mirrored(false, true);
274 const quint8 *idata = (const quint8 *)unp.constData(); idata += ipos+realHdrSize;
275 QImage img(imgWdt, imgHgt, QImage::Format_ARGB32);
276 for (int y = img.height()-1; y >= 0; y--) {
277 quint8 *line = (quint8 *)img.scanLine(y);
278 for (int x = 0; x < img.width(); x++, line += 4, idata += 4) {
279 // convert to 'premultiplied'
280 line[0] = alphaPreMul(idata[0], idata[3]);
281 line[1] = alphaPreMul(idata[1], idata[3]);
282 line[2] = alphaPreMul(idata[2], idata[3]);
283 line[3] = idata[3];
288 XCursorImages *cim = new XCursorImages(*nlst);
289 img.save(QString("_png/%1.png").arg(cim->name()));
290 quint32 frameWdt = img.width()/frameCnt, frX = 0;
291 qDebug() << "frameWdt:" << frameWdt << "left:" << img.width()%(frameWdt*frameCnt);
292 for (quint32 f = 0; f < frameCnt; f++) {
293 QImage frame(img.copy(frX, 0, frameWdt, img.height()));
294 //frame.save(QString("_png/%1_%2.png").arg(cim->name()).arg(QString::number(f)));
295 frX += frameWdt; // next frame
296 XCursorImage *i = new XCursorImage(QString("%1%2").arg(cim->name()).arg(QString::number(f)),
297 frame, imgXHot, imgYHot, imgDelay, 1
299 cim->append(i);
301 mList << cim;
303 return true;