2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
6 /****************************************************************************************/
10 #include <datatypes/datatypes.h>
11 #include <intuition/intuition.h>
13 #include <proto/dos.h>
14 #include <proto/alib.h>
15 #include <proto/png.h>
16 #include <proto/datatypes.h>
18 #include <aros/bigendianio.h>
19 #include <aros/asmcall.h>
21 #include "icon_intern.h"
23 #include <aros/debug.h>
27 #define ATTR_ICONX 0x80001001
28 #define ATTR_ICONY 0x80001002
29 #define ATTR_DRAWERX 0x80001003
30 #define ATTR_DRAWERY 0x80001004
31 #define ATTR_DRAWERWIDTH 0x80001005
32 #define ATTR_DRAWERHEIGHT 0x80001006
33 #define ATTR_DRAWERFLAGS 0x80001007
34 #define ATTR_TOOLWINDOW 0x80001008 //OS4: STRPTR, tool window string, length including the tag
35 //must be a multiple of 8
36 #define ATTR_STACKSIZE 0x80001009
37 #define ATTR_DEFAULTTOOL 0x8000100a
38 #define ATTR_TOOLTYPE 0x8000100b
39 #define ATTR_VIEWMODES 0x8000100c //OS4 PNG use that
40 #define ATTR_DD_CURRENTX 0x8000100d //OS4 ULONG, drawer view X offset
41 #define ATTR_DD_CURRENTY 0x8000100e //OS4 ULONG, drawer view Y offset
42 #define ATTR_TYPE 0x8000100f //OS4 icon type (WBDISK...WBKICK)
43 #define ATTR_FRAMELESS 0x80001010 //OS4 ULONG, frameless property
44 #define ATTR_DRAWERFLAGS3 0x80001011 //OS4 ULONG, drawer flags
45 #define ATTR_VIEWMODES2 0x80001012 //OS4 ULONG, drawer view modes
46 #define ATTR_DRAWERFLAGS2 0x80001107 //written from AFA to store needed dopus Magellan settings
48 #define EFFECT_NONE (0)
49 #define EFFECT_LIGHTEN (1)
50 #define EFFECT_TINT_BLUE (2)
51 #define EFFECT_XOR (3)
53 #define EFFECT EFFECT_LIGHTEN
55 /****************************************************************************************/
58 ATTR_DRAWERFLAGS: AAABC
60 C : 1-bit flag : 0 = showonlyicons 1 = showallfiles
61 B : 1-bit flag : 0 = viewastext 1 = view as icons
62 AA : 2-bit value : 0 = viewbyname, 1 = viewbydata, 2 = viewbysize, 3 = viewbytype
65 static ULONG
flags_to_ddflags(ULONG flags
)
71 ret
= DDFLAGS_SHOWALL
;
75 ret
= DDFLAGS_SHOWICONS
;
81 static ULONG
flags_to_ddviewmodes(ULONG flags
)
91 ret
= (flags
>> 2) + DDVM_BYNAME
;
97 static ULONG
dd_to_flags(struct DiskObject
*dobj
)
99 ULONG drawerflags
= 0;
101 if (dobj
->do_DrawerData
->dd_Flags
& DDFLAGS_SHOWALL
)
106 if (dobj
->do_DrawerData
->dd_ViewModes
== DDVM_BYICON
)
112 drawerflags
|= ((dobj
->do_DrawerData
->dd_ViewModes
- 2) << 2);
118 /* Returns an ARGB image.
119 * Set &width == -1 and &height == -1 to get the size.
120 * Otherwise, sets the image size of width & height
122 ULONG
*ReadMemPNG(struct DiskObject
*icon
, APTR stream
, LONG
*width
, LONG
*height
, const CONST_STRPTR
*chunknames
, APTR
*chunkpointer
, struct IconBase
*IconBase
)
124 APTR PNGBase
= OpenLibrary("SYS:Classes/datatypes/png.datatype", 41);
129 D(bug("[%s] Can't open png.datatype\n", __func__
));
133 handle
= PNG_LoadImageMEM(stream
, -1, chunknames
, chunkpointer
, TRUE
);
137 LONG x
,y
, xoff
, yoff
;
139 PNG_GetImageInfo(handle
, &w
, &h
, NULL
, NULL
);
140 D(bug("[%s] Dest (%d x %d), Image (%d x %d)\n", __func__
,
141 *width
, *height
, w
, h
));
142 if (*width
== -1 && *height
== -1) {
147 PNG_GetImageData(handle
, (APTR
*)&src
, NULL
);
149 argb
= AllocMemIcon(icon
, (*width
) * (*height
) * sizeof(ULONG
), MEMF_PUBLIC
);
151 xoff
= ((*width
) - w
)/2;
152 yoff
= ((*height
) - h
)/2;
155 for (y
= 0; y
< *height
; y
++) {
164 for (x
= 0; x
< *width
; x
++, dst
++) {
178 PNG_FreeImage(handle
);
180 D(bug("[%s] PNG datatype can't parse data\n", __func__
));
183 CloseLibrary(PNGBase
);
188 /****************************************************************************************/
190 static void GetChunkInfo(APTR stream
, APTR
*chunkdata
, ULONG
*chunksize
)
192 png_unknown_chunkp chunkp
= stream
;
193 *chunksize
= chunkp
->size
;
194 *chunkdata
= chunkp
->data
;
197 BOOL
ReadIconPNG(struct DiskObject
*dobj
, BPTR file
, struct IconBase
*IconBase
)
199 static CONST_STRPTR
const chunknames
[] =
204 APTR chunkpointer
[] =
210 struct NativeIcon
*icon
;
214 icon
= NATIVEICON(dobj
);
216 D(bug("%s: File stream %p\n", __func__
, file
));
218 if (Seek(file
, 0, OFFSET_END
) < 0) return FALSE
;
219 if ((filesize
= Seek(file
, 0, OFFSET_BEGINNING
)) < 0) return FALSE
;
221 D(bug("[%s] Inspecting a %d byte file\n", __func__
, filesize
));
223 /* Need a copy of whole file in memory for icon saving :-\ Because
224 that should save file back as it was, only with modified or new
225 icOn chunk. And it must also work when loading an icon and then
226 saving it using another name. */
228 data
= AllocMemIcon(&icon
->ni_DiskObject
, filesize
, MEMF_PUBLIC
);
232 if (Read(file
, data
, filesize
) != filesize
)
234 D(bug("[%s] Can't read from file\n", __func__
));
238 icon
->ni_Extra
.Data
= data
;
239 icon
->ni_Extra
.Size
= filesize
;
241 icon
->ni_Extra
.PNG
[0].Offset
= 0;
242 icon
->ni_Extra
.PNG
[0].Size
= filesize
;
244 ULONG width
= ~0, height
= ~0;
246 icon
->ni_Image
[0].ARGB
= ReadMemPNG(&icon
->ni_DiskObject
, icon
->ni_Extra
.Data
+ icon
->ni_Extra
.PNG
[0].Offset
, &width
, &height
, chunknames
, chunkpointer
, IconBase
);
247 if (icon
->ni_Image
[0].ARGB
== NULL
) {
248 D(bug("[%s] Can't parse PNG image at 0\n", __func__
));
252 icon
->ni_Face
.Width
= width
;
253 icon
->ni_Face
.Height
= height
;
254 icon
->ni_Face
.Aspect
= ICON_ASPECT_RATIO_UNKNOWN
;
256 #define DO(x) (&x->ni_DiskObject)
258 DO(icon
)->do_Magic
= WB_DISKMAGIC
;
259 DO(icon
)->do_Version
= (WB_DISKVERSION
<< 8) | WB_DISKREVISION
;
260 DO(icon
)->do_Type
= 0; /* Invalid */
261 DO(icon
)->do_CurrentX
= NO_ICON_POSITION
;
262 DO(icon
)->do_CurrentY
= NO_ICON_POSITION
;
263 DO(icon
)->do_Gadget
.Width
= width
;
264 DO(icon
)->do_Gadget
.Height
= height
;
265 DO(icon
)->do_StackSize
= AROS_STACKSIZE
;
272 ULONG ttarraysize
= 0;
275 GetChunkInfo(chunkpointer
[0], (APTR
*)&chunkdata
, &chunksize
);
277 while(chunksize
>= 4)
281 BOOL need_drawerdata
= FALSE
;
283 attr
= (chunkdata
[0] << 24) | (chunkdata
[1] << 16) | (chunkdata
[2] << 8) | chunkdata
[3];
291 case ATTR_DRAWERWIDTH
:
292 case ATTR_DRAWERHEIGHT
:
293 case ATTR_DRAWERFLAGS
:
294 case ATTR_DRAWERFLAGS2
:
295 case ATTR_DRAWERFLAGS3
:
297 case ATTR_VIEWMODES2
:
298 case ATTR_DD_CURRENTX
:
299 case ATTR_DD_CURRENTY
:
300 need_drawerdata
= TRUE
;
310 val
= (chunkdata
[0] << 24) | (chunkdata
[1] << 16) | (chunkdata
[2] << 8) | chunkdata
[3];
320 /* case ATTR_UNKNOWN: */
321 case ATTR_DEFAULTTOOL
:
323 val
= (IPTR
)chunkdata
;
324 chunksize
-= strlen((char *)val
) + 1;
325 chunkdata
+= strlen((char *)val
) + 1;
334 /* Unknown attribute/tag. Impossible to handle correctly
335 if we don't know if it's a string attribute or not. */
343 if (need_drawerdata
&& !(DO(icon
)->do_DrawerData
))
345 DO(icon
)->do_DrawerData
= AllocMemIcon(DO(icon
), sizeof(struct DrawerData
), MEMF_PUBLIC
| MEMF_CLEAR
);
346 if (!(DO(icon
)->do_DrawerData
))
352 DO(icon
)->do_DrawerData
->dd_NewWindow
.LeftEdge
= 20;
353 DO(icon
)->do_DrawerData
->dd_NewWindow
.TopEdge
= 20;
354 DO(icon
)->do_DrawerData
->dd_NewWindow
.Width
= 300;
355 DO(icon
)->do_DrawerData
->dd_NewWindow
.Height
= 200;
356 DO(icon
)->do_Gadget
.UserData
= (APTR
)1; /* See DupDiskObject logic */
362 DO(icon
)->do_CurrentX
= val
;
366 DO(icon
)->do_CurrentY
= val
;
370 DO(icon
)->do_StackSize
= val
;
374 DO(icon
)->do_DrawerData
->dd_NewWindow
.LeftEdge
= (WORD
)val
;
378 DO(icon
)->do_DrawerData
->dd_NewWindow
.TopEdge
= (WORD
)val
;
381 case ATTR_DRAWERWIDTH
:
382 DO(icon
)->do_DrawerData
->dd_NewWindow
.Width
= (WORD
)val
;
385 case ATTR_DRAWERHEIGHT
:
386 DO(icon
)->do_DrawerData
->dd_NewWindow
.Height
= (WORD
)val
;
389 case ATTR_DRAWERFLAGS
:
390 DO(icon
)->do_DrawerData
->dd_Flags
= flags_to_ddflags(val
);
391 DO(icon
)->do_DrawerData
->dd_ViewModes
= flags_to_ddviewmodes(val
);
394 case ATTR_DEFAULTTOOL
:
395 DO(icon
)->do_DefaultTool
= AllocMemIcon(DO(icon
), strlen((char *)val
) + 1, MEMF_PUBLIC
| MEMF_CLEAR
);
396 if (DO(icon
)->do_DefaultTool
)
398 strcpy(DO(icon
)->do_DefaultTool
, (char *)val
);
406 NATIVEICON(icon
)->ni_Frameless
= val
? TRUE
: FALSE
;
412 D(bug("[Icon.PNG] Got tooltype number %u : %s\n", ttnum
- 1, val
));
414 if (ttarraysize
< ttnum
+ 1)
416 STRPTR
*old_tooltypes
= DO(icon
)->do_ToolTypes
;
420 DO(icon
)->do_ToolTypes
= AllocMemIcon(DO(icon
), ttarraysize
* sizeof(APTR
), MEMF_PUBLIC
| MEMF_CLEAR
);
421 if (DO(icon
)->do_ToolTypes
)
423 D(bug("[Icon.PNG] Allocated array of %u entries @ 0x%p (old 0x%p)\n", ttarraysize
, DO(icon
)->do_ToolTypes
, old_tooltypes
));
426 CopyMemQuick(old_tooltypes
, DO(icon
)->do_ToolTypes
, (ttnum
- 1) * sizeof(APTR
));
428 /* TODO: Free old array */
439 DO(icon
)->do_ToolTypes
[ttnum
- 1] = AllocMemIcon(DO(icon
), strlen((char *)val
) + 1, MEMF_PUBLIC
| MEMF_CLEAR
);
440 if (DO(icon
)->do_ToolTypes
[ttnum
- 1])
442 strcpy(DO(icon
)->do_ToolTypes
[ttnum
- 1], (char *)val
);
454 } /* while(chunksize >= 4) */
458 D(bug("=== Failure during icOn chunk parsing ===\n"));
459 FreeIconPNG(&icon
->ni_DiskObject
, IconBase
);
464 } /* if (chunkpointer[0]) */
469 * FIXME: Someone killed PNG Icon do_Type detection here which causes
470 * following lines to always free DrawerData even when it
471 * shouldn't be freed (only possible to know if do_Type is
472 * known). So for now following lines disabled and DrawerData
473 * is always kept (even when it shouldn't).
477 if (icon
->ni_DiskObject
.do_DrawerData
&&
478 (icon
->ni_DiskObject
.do_Type
!= WBDISK
) &&
479 (icon
->ni_DiskObject
.do_Type
!= WBDRAWER
) &&
480 (icon
->ni_DiskObject
.do_Type
!= WBGARBAGE
))
482 FreePooled(pool
, icon
->ni_DiskObject
.do_DrawerData
, sizeof(struct DrawerData
));
483 icon
->ni_DiskObject
.do_DrawerData
= NULL
;
489 /* Look for a possible 2nd PNG image attached onto the first one */
491 UBYTE
*filepos
= icon
->ni_Extra
.Data
+ icon
->ni_Extra
.PNG
[0].Offset
+ 8;
494 while(!done
&& filepos
< ((UBYTE
*)icon
->ni_Extra
.Data
+ icon
->ni_Extra
.Size
))
496 ULONG chunksize
= (filepos
[0] << 24) | (filepos
[1] << 16) |
497 (filepos
[2] << 8) | filepos
[3];
498 ULONG chunktype
= (filepos
[4] << 24) | (filepos
[5] << 16) |
499 (filepos
[6] << 8) | filepos
[7];
503 if (chunktype
== MAKE_ID('I', 'E', 'N', 'D'))
508 filepos
+= chunksize
;
511 if (filepos
+ 8 < (UBYTE
*)icon
->ni_Extra
.Data
+ icon
->ni_Extra
.Size
)
513 ULONG offset
= filepos
- (UBYTE
*)icon
->ni_Extra
.Data
;
515 icon
->ni_Extra
.PNG
[0].Size
= offset
;
516 icon
->ni_Extra
.PNG
[1].Offset
= offset
;
517 icon
->ni_Extra
.PNG
[1].Size
= (filesize
- icon
->ni_Extra
.PNG
[0].Size
);
518 icon
->ni_Image
[1].ARGB
= ReadMemPNG(&icon
->ni_DiskObject
, filepos
, &icon
->ni_Face
.Width
, &icon
->ni_Face
.Height
, NULL
, NULL
, IconBase
);
523 /* If there's no image for selected-state, generate one */
524 if (!icon
->ni_Image
[1].ARGB
)
526 ULONG size
= icon
->ni_Face
.Width
* icon
->ni_Face
.Height
;
528 if ((icon
->ni_Image
[1].ARGB
= AllocMemIcon(&icon
->ni_DiskObject
, size
* sizeof(ULONG
), MEMF_PUBLIC
)))
530 ULONG
*src
= (ULONG
*)icon
->ni_Image
[0].ARGB
;
531 ULONG
*dst
= (ULONG
*)icon
->ni_Image
[1].ARGB
;
535 ULONG pixel
= *src
++;
537 /* Effects like in changetoselectediconcolor.c */
539 #if EFFECT == EFFECT_LIGHTEN
541 pixel
= (pixel
& 0xFF000000) +
542 ((pixel
>> 1) & 0x007F7F7F) +
545 pixel
= (pixel
& 0x000000FF) +
546 ((pixel
>> 1) & 0x7F7F7F00) +
549 #elif EFFECT == EFFECT_TINT_BLUE
551 pixel
= (pixel
& 0xFF000000) +
552 ((pixel
>> 1) & 0x007F7F7F) +
555 pixel
= (pixel
& 0x000000FF) +
556 ((pixel
>> 1) & 0x7F7F7F00) +
560 #elif EFFECT == EFFECT_XOR
562 pixel
= (pixel
& 0xFF000000) +
563 ((pixel
& 0x00FFFFFF) ^ 0x00FFFFFF);
565 pixel
= (pixel
& 0x000000FF) +
566 ((pixel
& 0xFFFFFF00) ^ 0xFFFFFF00);
578 /****************************************************************************************/
580 STATIC VOID
MakeCRCTable(struct IconBase
*IconBase
)
585 IconBase
->ib_CRCTable
= AllocMem(256 * sizeof(ULONG
), MEMF_ANY
);
586 if (!IconBase
->ib_CRCTable
)
588 for (n
= 0; n
< 256; n
++)
590 c
= (unsigned long) n
;
591 for (k
= 0; k
< 8; k
++)
594 c
= 0xedb88320L
^ (c
>> 1);
598 IconBase
->ib_CRCTable
[n
] = c
;
602 /****************************************************************************************/
604 STATIC ULONG
UpdateCRC(ULONG crc
, UBYTE
*buf
, ULONG len
, struct IconBase
*IconBase
)
609 for (n
= 0; n
< len
; n
++)
611 c
= IconBase
->ib_CRCTable
[(c
^ buf
[n
]) & 0xff] ^ (c
>> 8);
618 /****************************************************************************************/
620 STATIC BOOL
WriteIconAttr(BPTR file
, ULONG id
, ULONG val
, ULONG
*chunksize
,
621 ULONG
*crc
, struct IconBase
*IconBase
)
634 if (Write(file
, buf
, 8) != 8) return FALSE
;
637 *crc
= UpdateCRC(*crc
, buf
, 8, IconBase
);
641 /****************************************************************************************/
643 STATIC BOOL
WriteIconStrAttr(BPTR file
, ULONG id
, char *val
, ULONG
*chunksize
,
644 ULONG
*crc
, struct IconBase
*IconBase
)
647 ULONG len
= strlen(val
) + 1;
654 if (Write(file
, buf
, 4) != 4) return FALSE
;
655 *crc
= UpdateCRC(*crc
, buf
, 4, IconBase
);
657 if (Write(file
, val
, len
) != len
) return FALSE
;
658 *crc
= UpdateCRC(*crc
, val
, len
, IconBase
);
660 *chunksize
+= 4 + len
;
665 /****************************************************************************************/
667 STATIC BOOL
WriteIconChunk(BPTR file
, struct DiskObject
*dobj
, struct IconBase
*IconBase
)
669 ULONG crc
= 0xFFFFFFFF;
671 ULONG sizeseek
= Seek(file
, 0, OFFSET_CURRENT
);
672 UBYTE buf
[] = {0x12, 0x34, 0x56, 0x78, 'i', 'c', 'O', 'n'};
674 if (sizeseek
< 0) return FALSE
;
676 /* Write Chunk size + chunk type */
677 if (Write(file
, buf
, 8) != 8) return FALSE
;
679 crc
= UpdateCRC(crc
, buf
+ 4, 4, IconBase
); /* chunksize is excluded from CRC */
681 /* Write Frameless */
682 if (!WriteIconAttr(file
, ATTR_FRAMELESS
, NATIVEICON(dobj
)->ni_Frameless
, &chunksize
, &crc
, IconBase
))
687 /* Write Stack Size */
689 if (!WriteIconAttr(file
, ATTR_STACKSIZE
, dobj
->do_StackSize
, &chunksize
, &crc
, IconBase
))
694 /* Write Icon X Position */
695 if (dobj
->do_CurrentX
!= NO_ICON_POSITION
)
697 if (!WriteIconAttr(file
, ATTR_ICONX
, dobj
->do_CurrentX
, &chunksize
, &crc
, IconBase
))
703 /* Write Icon Y Position */
704 if (dobj
->do_CurrentY
!= NO_ICON_POSITION
)
706 if (!WriteIconAttr(file
, ATTR_ICONY
, dobj
->do_CurrentY
, &chunksize
, &crc
, IconBase
))
712 if (dobj
->do_DrawerData
)
714 if (!WriteIconAttr(file
, ATTR_DRAWERX
, dobj
->do_DrawerData
->dd_NewWindow
.LeftEdge
,
715 &chunksize
, &crc
, IconBase
))
720 if (!WriteIconAttr(file
, ATTR_DRAWERY
, dobj
->do_DrawerData
->dd_NewWindow
.TopEdge
,
721 &chunksize
, &crc
, IconBase
))
726 if (!WriteIconAttr(file
, ATTR_DRAWERWIDTH
, dobj
->do_DrawerData
->dd_NewWindow
.Width
,
727 &chunksize
, &crc
, IconBase
))
732 if (!WriteIconAttr(file
, ATTR_DRAWERHEIGHT
, dobj
->do_DrawerData
->dd_NewWindow
.Height
,
733 &chunksize
, &crc
, IconBase
))
738 if (!WriteIconAttr(file
, ATTR_DRAWERFLAGS
, dd_to_flags(dobj
),
739 &chunksize
, &crc
, IconBase
))
744 } /* if (dobj->do_DrawerData) */
746 if (dobj
->do_DefaultTool
)
748 if (!WriteIconStrAttr(file
, ATTR_DEFAULTTOOL
, dobj
->do_DefaultTool
,
749 &chunksize
, &crc
, IconBase
))
756 if (dobj
->do_ToolTypes
)
758 STRPTR
*tt
= (STRPTR
*)dobj
->do_ToolTypes
;
760 for(tt
= (STRPTR
*)dobj
->do_ToolTypes
; *tt
; tt
++)
762 if (!WriteIconStrAttr(file
, ATTR_TOOLTYPE
, *tt
, &chunksize
,
777 if (Write(file
, buf
, 4) != 4) return FALSE
;
779 /* Write chunk's size */
780 if (Seek(file
, sizeseek
, OFFSET_BEGINNING
) < 0) return FALSE
;
782 buf
[0] = chunksize
>> 24;
783 buf
[1] = chunksize
>> 16;
784 buf
[2] = chunksize
>> 8;
786 if (Write(file
, buf
, 4) != 4) return FALSE
;
788 if (Seek(file
, 0, OFFSET_END
) < 0) return FALSE
;
793 /****************************************************************************************/
795 BOOL
WriteIconPNG(BPTR file
, struct DiskObject
*dobj
, struct IconBase
*IconBase
)
797 struct NativeIcon
*nativeicon
= NATIVEICON(dobj
);
798 UBYTE
*mempos
= nativeicon
->ni_Extra
.Data
+ nativeicon
->ni_Extra
.PNG
[0].Offset
;
801 D(bug("%s: ni=%p, ni->ni_Extra.Data = %p\n", __func__
, nativeicon
, nativeicon
->ni_Extra
.Data
));
802 if (nativeicon
->ni_Extra
.Data
== NULL
)
805 ObtainSemaphore(&IconBase
->iconlistlock
);
806 if (!IconBase
->ib_CRCTable
)
807 MakeCRCTable(IconBase
);
808 ReleaseSemaphore(&IconBase
->iconlistlock
);
809 if (!IconBase
->ib_CRCTable
)
812 /* Write PNG header */
813 if (Write(file
, mempos
, 8) != 8) return FALSE
;
819 ULONG chunksize
= (mempos
[0] << 24) | (mempos
[1] << 16) |
820 (mempos
[2] << 8) | mempos
[3];
821 ULONG chunktype
= (mempos
[4] << 24) | (mempos
[5] << 16) |
822 (mempos
[6] << 8) | mempos
[7];
826 if (chunktype
== MAKE_ID('I', 'E', 'N', 'D'))
828 if (!WriteIconChunk(file
, dobj
, IconBase
)) return FALSE
;
832 if (chunktype
!= MAKE_ID('i', 'c', 'O', 'n'))
834 if (Write(file
, mempos
, chunksize
) != chunksize
)
844 if (nativeicon
->ni_Extra
.PNG
[1].Offset
> 0)
846 ULONG size
= nativeicon
->ni_Extra
.PNG
[1].Size
;
848 /* 2nd PNG Image attached */
850 if (Write(file
, nativeicon
->ni_Extra
.Data
+ nativeicon
->ni_Extra
.PNG
[1].Offset
, size
) != size
) return FALSE
;
856 /****************************************************************************************/
858 VOID
FreeIconPNG(struct DiskObject
*dobj
, struct IconBase
*IconBase
)
862 /****************************************************************************************/