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.
12 #include "xcrthemefx.h"
17 #include <QStringList>
18 #include <QTextStream>
25 static const char *curShapeName
[] = {
27 "help arrow (the one with '?')",
34 "north (vert) resize",
36 "west (vert-means horiz?) resize",
49 ///////////////////////////////////////////////////////////////////////////////
50 static QByteArray
zlibInflate (const void *buf
, int bufSz
, int destSz
) {
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
);
69 err
= inflateEnd(&stream
);
70 fprintf(stderr
, "Z_STREAM_END: inflate result: %i\n", err
);
71 if (err
!= Z_OK
) return QByteArray();
74 err
= inflateEnd(&stream
);
75 fprintf(stderr
, "Z_OK: inflate result: %i\n", err
);
76 if (err
!= Z_OK
) return QByteArray();
78 default: return QByteArray();
84 static quint32
baGetDW (QByteArray
&ba
, int &pos
) {
85 const uchar
*d
= (const uchar
*)ba
.constData();
89 for (int f
= 4; f
> 0; f
--, d
--) {
97 ///////////////////////////////////////////////////////////////////////////////
98 XCursorThemeFX::XCursorThemeFX (const QString
&aFileName
) : XCursorTheme() {
99 if (!parseCursorFXTheme(aFileName
)) {
106 inline static quint8
alphaPreMul (quint8 clr
, quint8 alpha
) {
107 quint32 c32
= clr
, a32
= alpha
;
109 if (c32
> a32
) c32
= a32
;
114 bool XCursorThemeFX::parseCursorFXTheme (const QString
&aFileName
) {
115 qDebug() << "loading" << aFileName
;
117 if (!fl
.open(QIODevice::ReadOnly
)) return false; // shit!
118 QByteArray
ba(fl
.readAll());
120 if (ba
.size() < 0xb8) return false; // shit!
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
132 for (int f
= 0; f
< 6; f
++) {
133 infoFields
[f
].ofs
= baGetDW(ba
, pos
);
134 infoFields
[f
].len
= baGetDW(ba
, pos
);
137 quint32 ihdrSize
= baGetDW(ba
, pos
);
140 qDebug() << "reading data from" << pos
;
141 QByteArray
unp(zlibInflate(ba
.constData()+pos
, ba
.size()-pos
, unDataSize
));
143 qDebug() << "CursorFX: can't depack data";
144 qWarning() << "CursorFX: can't depack data";
147 // process info section
148 for (int f
= 0; f
< 6; f
++) {
149 int len
= infoFields
[f
].len
;
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();
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;
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);
177 qDebug() << "CursorFX: invalid data chunk size";
178 qWarning() << "CursorFX: invalid data chunk size";
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
;
190 rtype
= baGetDW(unp
, cps
);
192 qDebug() << "CursorFX: invalid cursor chunk type:" << rtype
;
193 qWarning() << "CursorFX: invalid cursor chunk type:" << rtype
;
196 quint32 curShape
= baGetDW(unp
, cps
);
197 quint32 curType
= baGetDW(unp
, cps
);
199 qDebug() << "CursorFX: unknown cursor shape:" << curShape
;
200 qWarning() << "CursorFX: unknown cursor shape:" << curShape
;
204 qDebug() << "skiping 'press' cursor; shape no" << curShape
<< "named" << curShapeName
[curShape
];
206 // we need only 'normal' cursors
208 qDebug() << "cursor shape:" << curShape
;
209 const char **nlst
= findCursorByFXId((int)curShape
);
211 // unknown cursor type, skip it
212 qDebug() << "CursorFX: skiping cursor shape:" << curShapeName
[curShape
];
213 qWarning() << "CursorFX: skiping cursor shape:" << curShapeName
[curShape
];
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
);
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";
252 // addon is the script; parse it later
255 if (addonOfs
< 0x4c || addonOfs
> realHdrSize
) {
256 qDebug() << "CursorFX: invalid addon data offset";
257 qWarning() << "CursorFX: invalid addon data offset";
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;
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]);
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