seems that import at least can parse CursorFX file
[xcurtheme.git] / src / xcr / xcrthemefx.cpp
blob81d37946a3f38562e76eb5fdb860b97ac3f9ccad
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 bool XCursorThemeFX::parseCursorFXTheme (const QString &aFileName) {
107 qDebug() << "loading" << aFileName;
108 QFile fl(aFileName);
109 if (!fl.open(QIODevice::ReadOnly)) return false; // shit!
110 QByteArray ba(fl.readAll());
111 fl.close();
112 if (ba.size() < 0xb8) return false; // shit!
113 int pos = 0;
114 if (baGetDW(ba, pos) != 1) return false; // invalid version
115 quint32 mainHdrSize = baGetDW(ba, pos);
116 if (mainHdrSize < 0xb8) return false; // invalid header size
117 quint32 unDataSize = baGetDW(ba, pos);
118 if (unDataSize < 0x4c) return false; // no cursors anyway
119 struct {
120 quint32 ofs;
121 quint32 len;
122 } infoFields[6];
123 pos = 0x84;
124 for (int f = 0; f < 6; f++) {
125 infoFields[f].ofs = baGetDW(ba, pos);
126 infoFields[f].len = baGetDW(ba, pos);
128 pos = 0xb4;
129 quint32 ihdrSize = baGetDW(ba, pos);
130 // now read data
131 pos = mainHdrSize;
132 qDebug() << "reading data from" << pos;
133 QByteArray unp(zlibInflate(ba.constData()+pos, ba.size()-pos, unDataSize));
134 if (unp.isEmpty()) {
135 qWarning() << "CursorFX: can't depack data";
136 return false;
138 // process resources
139 pos = ihdrSize;
140 qDebug() << "resources started at hex" << QString::number(pos, 16);
141 while (pos <= unp.size()-12) {
142 quint32 ipos = pos; // will be fixed later
143 quint32 rtype = baGetDW(unp, pos);
144 quint32 basicHdrSize = baGetDW(unp, pos);
145 quint32 itemSize = baGetDW(unp, pos);
146 qDebug() << "pos hex:" << QString::number(pos, 16) << "rtype:" << rtype << "bhdrsz hex:" << QString::number(basicHdrSize, 16) << "itemsz hex:" << QString::number(itemSize, 16);
147 if (itemSize < 12) {
148 // invalid data
149 qWarning() << "CursorFX: invalid data chunk size";
150 return false;
152 pos = ipos+itemSize; // skip it
153 if (rtype != 2) continue; // not cursor resource
154 if (itemSize < 0x4c) {
155 qWarning() << "CursorFX: invalid cursor chunk size:" << itemSize;
156 return false;
158 // cursor
159 int cps = ipos+3*4;
160 rtype = baGetDW(unp, cps);
161 if (rtype != 2) {
162 qWarning() << "CursorFX: invalid cursor chunk type:" << rtype;
163 return false;
165 quint32 curShape = baGetDW(unp, cps);
166 quint32 curType = baGetDW(unp, cps);
167 if (curShape > 19) {
168 qWarning() << "CursorFX: unknown cursor shape:" << curShape;
169 return false;
171 if (curType != 1) {
172 qDebug() << "skiping 'press' cursor; shape no" << curShape << "named" << curShapeName[curShape];
173 continue;
174 // we need only 'normal' cursors
176 qDebug() << "cursor shape:" << curShape;
177 const char **nlst = findCursorByFXId((int)curShape);
178 if (!nlst) {
179 // unknown cursor type, skip it
180 qWarning() << "CursorFX: skiping cursor shape:" << curShapeName[curShape];
181 continue;
183 qDebug() << "importing cursor" << *nlst;
184 cps += 4; // skip unknown field
185 quint32 frameCnt = baGetDW(unp, cps);
186 if (frameCnt < 1) frameCnt = 1; // just in case
187 quint32 imgWdt = baGetDW(unp, cps);
188 quint32 imgHgt = baGetDW(unp, cps);
189 quint32 imgDelay = baGetDW(unp, cps);
190 quint32 aniFlags = baGetDW(unp, cps);
191 cps += 4; // skip unknown field
192 quint32 imgXHot = baGetDW(unp, cps);
193 quint32 imgYHot = baGetDW(unp, cps);
194 quint32 realHdrSize = baGetDW(unp, cps);
195 quint32 imgDataSize = baGetDW(unp, cps);
196 quint32 addonOfs = baGetDW(unp, cps);
197 quint32 addonLen = baGetDW(unp, cps);
198 qDebug() <<
199 "cursor data:" <<
200 "\n frames:" << frameCnt <<
201 "\n width:" << imgWdt <<
202 "\n height:" << imgHgt <<
203 "\n delay:" << imgDelay <<
204 "\n flags:" << QString::number(aniFlags, 2) <<
205 "\n xhot:" << imgXHot <<
206 "\n yhot:" << imgYHot
208 // now check if we have enought data
209 if (ipos+realHdrSize+imgDataSize > (quint32)pos) {
210 qWarning() << "CursorFX: cursor data too big";
211 return false;
213 // addon is the script; parse it later
214 QString script;
215 if (addonLen) {
216 if (addonOfs < 0x4c || addonOfs > realHdrSize) {
217 qWarning() << "CursorFX: invalid addon data offset";
218 return false;
220 QByteArray bs(unp.mid(addonOfs, addonLen));
221 bs.append('\0'); bs.append('\0');
222 script = QString::fromUtf16((const ushort *)bs.constData());
223 qDebug() << "script:\n" << script;
225 // decode image
226 QImage img((const uchar *)unp.constData()+ihdrSize+realHdrSize, imgWdt, imgHgt, QImage::Format_ARGB32/*_Premultiplied*/);
227 //mImage = new QImage(img.copy());
228 XCursorImages *cim = new XCursorImages(*nlst);
229 quint32 frameWdt = img.width()/frameCnt, frX = 0;
230 for (quint32 f = 0; f < frameCnt; f++) {
231 QImage frame = img.copy(frX, 0, frameWdt, img.height());
232 frX += frameWdt; // next frame
233 XCursorImage *i = new XCursorImage(QString("%1%2").arg(cim->name()).arg(QString::number(f)),
234 frame, imgXHot, imgYHot, imgDelay, 1
236 cim->append(i);
238 mList << cim;
240 return true;