Beispiel #1
0
/*
=======================================
    TGL IEL1 文件读取
=======================================
*/
CR_API sFMT_DAT*
load_tgl_iel1 (
  __CR_IO__ iDATIN*         datin,
  __CR_IN__ const sLOADER*  param
    )
{
    leng_t      pksz;
    leng_t      unsz;
    void_t*     data;
    void_t*     temp;
    sFMT_DAT*   rett;
    sIEL1_HDR   head;

    /* 这个参数可能为空 */
    if (datin == NULL)
        return (NULL);

    /* 读取 & 检查头部 */
    if (!(CR_VCALL(datin)->geType(datin, &head, sIEL1_HDR)))
        return (NULL);
    if (head.magic != mk_tag4("IEL1"))
        return (NULL);

    /* 读取所有后续数据 */
    temp = CR_VCALL(datin)->get(datin, &pksz, FALSE);
    if (temp == NULL)
        return (NULL);

    /* 分配目标数据缓冲 */
    head.unsize = DWORD_LE(head.unsize);
    data = mem_malloc32(head.unsize);
    if (data == NULL) {
        mem_free(temp);
        return (NULL);
    }
    unsz = (leng_t)(head.unsize);
    pksz = uncompr_lzss(data, unsz, temp, pksz, 0);
    mem_free(temp);
    if (pksz != unsz)
        goto _failure;

    /* 返回读取的文件数据 */
    rett = struct_new(sFMT_DAT);
    if (rett == NULL)
        goto _failure;
    CR_NOUSE(param);
    rett->type = CR_FMTZ_DEC;
    rett->unsz = unsz;
    rett->pksz = dati_get_size(datin);
    rett->data = data;
    rett->infor = "TGL LZSS Compressed file (IEL1)";
    return (rett);

_failure:
    mem_free(data);
    return (NULL);
}
Beispiel #2
0
/*
=======================================
    TGL IPAC 文件读取
=======================================
*/
CR_API sFMT_PRT*
load_tgl_ipac (
  __CR_IO__ iDATIN*         datin,
  __CR_IN__ const sLOADER*  param
    )
{
    int16u      idx;
    int16u      cnt;
    int32u      offs;
    int32u      size;
    sFMT_PRT*   rett;
    sIPAC_HDR   head;
    iPAK_IPAC*  port;
    sPAK_FILE*  list;
    ansi_t      str[17];

    /* 必须使用自己私有的读取接口 */
    datin = create_file_inX(param);
    if (datin == NULL)
        return (NULL);

    /* 读取 & 检查头部 */
    if (!(CR_VCALL(datin)->geType(datin, &head, sIPAC_HDR)))
        goto _failure1;
    if (head.magic != mk_tag4("IPAC"))
        goto _failure1;

    /* 分配子文件属性表 */
    cnt = WORD_LE(head.count);
    if (cnt != 0) {
        list = mem_talloc(cnt, sPAK_FILE);
        if (list == NULL)
            goto _failure1;
        str[16] = CR_AC(NIL);
        mem_tzero(list, cnt, sPAK_FILE);
    }
    else {
        list = NULL;    /* 支持空的包文件 */
    }

    /* 加载文件信息表 */
    for (idx = 0; idx < cnt; idx++)
    {
        /* 读取文件名不保证\0结尾 */
        if (CR_VCALL(datin)->read(datin, str, 16) != 16)
            goto _failure2;

        /* 文件的包内偏移和大小 */
        if (!CR_VCALL(datin)->getd_le(datin, &offs))
            goto _failure2;
        if (!CR_VCALL(datin)->getd_le(datin, &size))
            goto _failure2;

        /* 文件名统一使用 UTF-8 编码 */
        list[idx].name = local_to_utf8(param->page, str);
        if (list[idx].name == NULL)
            goto _failure2;

        /* 设置公用文件属性 */
        list[idx].skip = sizeof(sPAK_FILE);
        list[idx].attr = 0;
        list[idx].offs = offs;
        list[idx].pack = size;
        list[idx].size = size;
        list[idx].memo = "Store";
    }

    /* 生成读包接口对象 */
    port = struct_new(iPAK_IPAC);
    if (port == NULL)
        goto _failure2;
    port->m_cnt = cnt;
    port->m_file = datin;
    port->pack.__filelst__ = list;
    port->pack.__vptr__ = &s_pack_vtbl;
    if (!pack_init_list((iPACKAGE*)port, TRUE)) {
        mem_free(port);
        goto _failure2;
    }

    /* 返回读取的文件数据 */
    rett = struct_new(sFMT_PRT);
    if (rett == NULL) {
        iPAK_IPAC_release((iPACKAGE*)port);
        return (NULL);
    }
    rett->type = CR_FMTZ_PRT;
    rett->port = (iPORT*)port;
    rett->more = "iPACKAGE";
    rett->infor = "TGL IPAC Archive (IPAC)";
    return (rett);

_failure2:
    if (list != NULL) {
        for (cnt = 0; cnt < idx; cnt++) {
            TRY_FREE(list[cnt].find);
            mem_free(list[cnt].name);
        }
        mem_free(list);
    }
_failure1:
    CR_VCALL(datin)->release(datin);
    return (NULL);
}
Beispiel #3
0
/*
=======================================
    PNG 文件读取
=======================================
*/
CR_API sFMT_PIC*
load_cr_png (
  __CR_IO__ iDATIN*         datin,
  __CR_IN__ const sLOADER*  param
    )
{
    leng_t  nbpl;
    leng_t  sbpp;
    leng_t  dptr;
    uint_t  fcrh;
    uint_t  index;
    uchar*  image;
    uchar*  sdata;
    uchar*  ddata;
    leng_t  dsize;
    int32u  ssize;
    uint_t  ww, hh;
    byte_t  pal[768];
    /* ----------- */
    fsize_t     fsze;
    sPNG_HDR    head;
    sFMT_PIC*   rett;
    sFMT_FRAME  temp;

    /* 这个参数可能为空 */
    if (datin == NULL)
        return (NULL);

    /* 读取 & 检查头部 */
    if (!(CR_VCALL(datin)->geType(datin, &head, sPNG_HDR)))
        return (NULL);
    if (mem_cmp(&head, "\x89PNG\r\n\x1A\n\0\0\0\x0DIHDR", 16) != 0)
        return (NULL);
    if (head.info.compr != 0 || head.info.filter != 0 ||
        head.info.interlace != 0)
        return (NULL);

    /* 对宽高的截断检查 */
    if (cut_int32_u(&ww, DWORD_BE(head.info.w)))
        return (NULL);
    if (cut_int32_u(&hh, DWORD_BE(head.info.h)))
        return (NULL);

    /* 生成图片对象 */
    mem_zero(temp.wh, sizeof(temp.wh));
    switch (head.info.color)
    {
        case 0: /* 灰度图像 */
            if (head.info.depth != 1 &&
                head.info.depth != 2 &&
                head.info.depth != 4 &&
                head.info.depth != 8 &&
                head.info.depth != 16)
                return (NULL);
            fcrh = CR_INDEX8;
            temp.fmt = CR_PIC_GREY;
            temp.bpp = head.info.depth;
            temp.clr = "I";
            temp.wh[0] = head.info.depth;
            break;

        case 2: /* 真彩图像 */
            if (head.info.depth != 8 &&
                head.info.depth != 16)
                return (NULL);
            fcrh = CR_ARGB888;
            temp.fmt = CR_PIC_ARGB;
            temp.bpp = head.info.depth * 3;
            temp.clr = "BGR";
            temp.wh[0] = head.info.depth;
            temp.wh[1] = head.info.depth;
            temp.wh[2] = head.info.depth;
            break;

        case 3: /* 索引图像 */
            if (head.info.depth != 1 &&
                head.info.depth != 2 &&
                head.info.depth != 4 &&
                head.info.depth != 8)
                return (NULL);
            fcrh = CR_INDEX8;
            temp.fmt = CR_PIC_PALS;
            temp.bpp = head.info.depth;
            temp.clr = "P";
            temp.wh[0] = head.info.depth;
            break;

        case 4: /* α灰度图像 */
            if (head.info.depth != 8 &&
                head.info.depth != 16)
                return (NULL);
            fcrh = CR_ARGB8888;
            temp.fmt = CR_PIC_GREY;
            temp.bpp = head.info.depth * 2;
            temp.clr = "AI";
            temp.wh[0] = head.info.depth;
            temp.wh[1] = head.info.depth;
            break;

        case 6: /* α真彩图像 */
            if (head.info.depth != 8 &&
                head.info.depth != 16)
                return (NULL);
            fcrh = CR_ARGB8888;
            temp.fmt = CR_PIC_ARGB;
            temp.bpp = head.info.depth * 4;
            temp.clr = "ABGR";
            temp.wh[0] = head.info.depth;
            temp.wh[1] = head.info.depth;
            temp.wh[2] = head.info.depth;
            temp.wh[3] = head.info.depth;
            break;

        default:
            return (NULL);
    }
    sbpp = (temp.bpp - 1) / 8 + 1;
    temp.pic = image_new(0, 0, ww, hh, fcrh, FALSE, 4);
    if (temp.pic == NULL)
        return (NULL);

    /* 生成灰度调色板 */
    if (temp.fmt == CR_PIC_GREY)
        pal_set_gray8(temp.pic->pal, 256);

    /* 分配 IDAT 的内存 */
    fsze = dati_get_size(datin);
    if (fsze <= sizeof(sPNG_HDR) + sizeof(sIEND) * 2)
        goto _failure1;
    fsze -= sizeof(sPNG_HDR) + sizeof(sIEND) * 2;
    ddata = (byte_t*)mem_malloc64(fsze);
    if (ddata == NULL)
        goto _failure1;
    dsize = (leng_t)fsze;

    /* 读取数据块 */
    dptr = 0;
    fcrh = 256;         /* 这个保存调色板颜色数 */
    do
    {
        /* 数据块大小 */
        if (!(CR_VCALL(datin)->geType(datin, &head.info, sCHUNK)))
            goto _failure2;
        ssize = DWORD_BE(head.info.head.size);
        if (ssize > fsze)
            goto _failure2;

        if (head.info.head.name == mk_tag4("PLTE"))
        {
            /* 调色板, 安全检查 */
            if (ssize > 768 || ssize % 3 != 0)
                goto _failure2;
            if (CR_VCALL(datin)->read(datin, pal, ssize) != ssize)
                goto _failure2;

            /* 转换到 4B 格式 */
            fcrh = (uint_t)ssize / 3;
            pal_3b_to_4b_sw(temp.pic->pal, pal, fcrh);
        }
        else
        if (head.info.head.name == mk_tag4("IDAT"))
        {
            /* 检查缓冲溢出 */
            if (dsize < ssize)
                goto _failure2;
            if (CR_VCALL(datin)->read(datin, ddata + dptr, ssize) != ssize)
                goto _failure2;
            dptr  += ssize;
            dsize -= ssize;
        }
        else
        if (head.info.head.name == mk_tag4("tRNS"))
        {
            /* 透明数据 */
            if (head.info.color == 0) {
                if (ssize != 2)
                    goto _failure2;
                if (CR_VCALL(datin)->read(datin, pal, 2) != 2)
                    goto _failure2;

                /* 调色板的这个颜色为透明色 */
                if (head.info.depth != 16)
                    temp.pic->pal[pal[1]] &= CDWORD_LE(0x00FFFFFFUL);
            }
            else
            if (head.info.color == 2) {
                if (ssize != 6)
                    goto _failure2;
                if (CR_VCALL(datin)->read(datin, pal, 6) != 6)
                    goto _failure2;

                /* 这个颜色为透明色, 这里只能展开来写
                   否则 C++Builder 2010 编译器编译时会崩溃 */
                if (head.info.depth != 16) {
                    temp.pic->keycolor  = pal[1];
                    temp.pic->keycolor <<= 8;
                    temp.pic->keycolor |= pal[3];
                    temp.pic->keycolor <<= 8;
                    temp.pic->keycolor |= pal[5];
                    temp.pic->keycolor |= 0xFF000000UL;
                    temp.pic->keycolor = DWORD_LE(temp.pic->keycolor);
                }
            }
            else
            if (head.info.color == 3) {
                if (ssize > fcrh)
                    goto _failure2;
                if (CR_VCALL(datin)->read(datin, pal, ssize) != ssize)
                    goto _failure2;

                /* 设置调色板的 Alpha 通道 */
                for (fcrh = (uint_t)ssize, index = 0; index < fcrh; index++)
                    ((uchar*)temp.pic->pal)[index * 4 + 3] = pal[index];
            }
            else {
                goto _failure2;
            }
        }
        else
        {
            /* 跳过其他数据块 */
            if (!CR_VCALL(datin)->seek(datin, ssize, SEEK_CUR))
                goto _failure2;
        }

        /* 跳过 CRC-32 */
        if (!CR_VCALL(datin)->seek(datin, 4, SEEK_CUR))
            goto _failure2;
    } while (head.info.head.name != mk_tag4("IEND"));

    /* 无 IDAT 块 */
    if (dptr == 0)
        goto _failure2;

    /* 分配带 filter 的图形内存 */
    if (cut_mad(&dsize, ww, sbpp * hh, hh))
        goto _failure2;
    sdata = (byte_t*)mem_malloc(dsize);
    if (sdata == NULL)
        goto _failure2;

    /* 解压图形数据 */
    dptr = uncompr_zlib(sdata, dsize, ddata, dptr);
    mem_free(ddata);
    ddata = sdata;
    if (dptr <= hh)
        goto _failure2;
    image = temp.pic->data;

    /* 文件解码完毕, 解析图片的像素数据 */
    if (temp.pic->fmt == CR_INDEX8)
    {
        switch (head.info.depth)
        {
            case 1:
                if (ww % 8 == 0)
                    nbpl = ww / 8;
                else
                    nbpl = ww / 8 + 1;
                if (!png_filter(ddata, nbpl, 1, hh, dptr))
                    goto _failure2;
                for (index = hh; index != 0; index--) {
                    sdata = (uchar*)font1_h2l(image, sdata, ww);
                    image += temp.pic->bpl;
                }
                break;

            case 2:
                if (ww % 4 == 0)
                    nbpl = ww / 4;
                else
                    nbpl = ww / 4 + 1;
                if (!png_filter(ddata, nbpl, 1, hh, dptr))
                    goto _failure2;
                for (index = hh; index != 0; index--) {
                    sdata = (uchar*)font2_h2l(image, sdata, ww);
                    image += temp.pic->bpl;
                }
                break;

            case 4:
                if (ww % 2 == 0)
                    nbpl = ww / 2;
                else
                    nbpl = ww / 2 + 1;
                if (!png_filter(ddata, nbpl, 1, hh, dptr))
                    goto _failure2;
                for (index = hh; index != 0; index--) {
                    sdata = (uchar*)font4_h2l(image, sdata, ww);
                    image += temp.pic->bpl;
                }
                break;

            case 8:
                nbpl = ww;
                if (!png_filter(ddata, nbpl, 1, hh, dptr))
                    goto _failure2;
                for (index = hh; index != 0; index--) {
                    mem_cpy(image, sdata, nbpl);
                    sdata += nbpl;
                    image += temp.pic->bpl;
                }
                break;

            default:
            case 16:
                nbpl = ww;
                nbpl *= 2;
                if (!png_filter(ddata, nbpl, 2, hh, dptr))
                    goto _failure2;
                for (index = hh; index != 0; index--) {
                    for (fcrh = 0; fcrh < ww; fcrh++, sdata += 2)
                        image[fcrh] = sdata[0];
                    image += temp.pic->bpl;
                }
                break;
        }
    }
    else
    {
        nbpl = ww * sbpp;
        if (!png_filter(ddata, nbpl, sbpp, hh, dptr))
            goto _failure2;
        nbpl = ww * temp.pic->bpc;

        switch (head.info.color)
        {
            case 2:
                if (head.info.depth == 8)
                {
                    /* 直接逐行复制 */
                    for (index = hh; index != 0; index--) {
                        for (dsize = 0; dsize < nbpl; dsize += 3) {
                            image[dsize + 0] = sdata[2];
                            image[dsize + 1] = sdata[1];
                            image[dsize + 2] = sdata[0];
                            sdata += 3;
                        }
                        image += temp.pic->bpl;
                    }
                }
                else
                {
                    /* 跳开一个像素复制 */
                    for (index = hh; index != 0; index--) {
                        for (dsize = 0; dsize < nbpl; dsize += 3) {
                            image[dsize + 0] = sdata[4];
                            image[dsize + 1] = sdata[2];
                            image[dsize + 2] = sdata[0];
                            sdata += 6;
                        }
                        image += temp.pic->bpl;
                    }
                }
                break;

            case 4:
                if (head.info.depth == 8)
                {
                    /* 直接逐行复制 */
                    for (index = hh; index != 0; index--) {
                        for (dsize = 0; dsize < nbpl; dsize += 4) {
                            image[dsize + 0] = sdata[0];
                            image[dsize + 1] = sdata[0];
                            image[dsize + 2] = sdata[0];
                            image[dsize + 3] = sdata[1];
                            sdata += 2;
                        }
                        image += temp.pic->bpl;
                    }
                }
                else
                {
                    /* 跳开一个像素复制 */
                    for (index = hh; index != 0; index--) {
                        for (dsize = 0; dsize < nbpl; dsize += 4) {
                            image[dsize + 0] = sdata[0];
                            image[dsize + 1] = sdata[0];
                            image[dsize + 2] = sdata[0];
                            image[dsize + 3] = sdata[2];
                            sdata += 4;
                        }
                        image += temp.pic->bpl;
                    }
                }
                break;

            default:
            case 6:
                if (head.info.depth == 8)
                {
                    /* 直接逐行复制 */
                    for (index = hh; index != 0; index--) {
                        for (dsize = 0; dsize < nbpl; dsize += 4) {
                            image[dsize + 0] = sdata[2];
                            image[dsize + 1] = sdata[1];
                            image[dsize + 2] = sdata[0];
                            image[dsize + 3] = sdata[3];
                            sdata += 4;
                        }
                        image += temp.pic->bpl;
                    }
                }
                else
                {
                    /* 跳开一个像素复制 */
                    for (index = hh; index != 0; index--) {
                        for (dsize = 0; dsize < nbpl; dsize += 4) {
                            image[dsize + 0] = sdata[4];
                            image[dsize + 1] = sdata[2];
                            image[dsize + 2] = sdata[0];
                            image[dsize + 3] = sdata[6];
                            sdata += 8;
                        }
                        image += temp.pic->bpl;
                    }
                }
                break;
        }
    }
    mem_free(ddata);

    /* 返回读取的文件数据 */
    rett = struct_new(sFMT_PIC);
    if (rett == NULL)
        goto _failure1;
    rett->frame = struct_dup(&temp, sFMT_FRAME);
    if (rett->frame == NULL) {
        mem_free(rett);
        goto _failure1;
    }
    CR_NOUSE(param);
    rett->type = CR_FMTZ_PIC;
    rett->count = 1;
    rett->infor = "Portable Network Graphics (PNG)";
    return (rett);

_failure2:
    mem_free(ddata);
_failure1:
    image_del(temp.pic);
    return (NULL);
}
Beispiel #4
0
/*
=======================================
    FALCOM AIA 文件读取
=======================================
*/
CR_API sFMT_PRT*
load_flc_aia (
  __CR_IO__ iDATIN*         datin,
  __CR_IN__ const sLOADER*  param
    )
{
    leng_t  back;
    leng_t  size;
    byte_t* pals;
    byte_t* dats;
    uint_t  ww, hh;
    int32u  idx, cnt;
    /* ----------- */
    fdist_t     offs;
    sAIA_HDR    head;
    sAIA_IDX*   attr;
    sFMT_PRT*   rett;
    iPIC_AIA*   port;

    /* 这个参数可能为空 */
    if (datin == NULL)
        return (NULL);

    /* 读取 & 检查头部 */
    if (!(CR_VCALL(datin)->geType(datin, &head, sAIA_HDR)))
        return (NULL);
    if (head.magic != mk_tag4("AIA"))
        return (NULL);
    offs = DWORD_LE(head.idx_num);
    offs *= sizeof(int32u) * 4;
    if (head.version == CWORD_LE(0x140)) {
        offs += 32;
        pals = (byte_t*)(&head.img_size);
        dats = (byte_t*)(&head.scale1);
        mem_cpy(pals, dats, 4);
        pals = (byte_t*)(&head.ww2);
        mem_cpy(dats, pals, 4);
        head.ww2 = head.hh2 = 0;
        head.scale2 = 0.0f;
    }
    else
    if (head.version == CWORD_LE(0x150)) {
        offs += 40;
    }
    else {
        return (NULL);
    }

    /* 定位到已知数据区域 */
    if (!CR_VCALL(datin)->seek(datin, offs, SEEK_SET))
        return (NULL);

    /* 读取所有帧属性数据 */
    head.img_num = DWORD_LE(head.img_num);
    attr = mem_talloc32(head.img_num, sAIA_IDX);
    if (attr == NULL)
        return (NULL);

    /* 准备好一些属性值 */
    ww = WORD_LE(head.ww1);
    hh = WORD_LE(head.hh1);
    head.pal_num  = DWORD_LE(head.pal_num);
    head.img_size = DWORD_LE(head.img_size);

    /* 逐个读入有效的帧属性 */
    for (cnt = idx = 0; idx < head.img_num; idx++) {
        back = CR_VCALL(datin)->read(datin, &attr[cnt], sizeof(sAIA_IDX));
        if (back != sizeof(sAIA_IDX))
            goto _failure1;

        /* 跳过非法的废帧 */
        attr[cnt].offset = DWORD_LE(attr[cnt].offset);
        if (attr[cnt].offset >= head.img_size)
            continue;
        attr[cnt].pal_idx = WORD_LE(attr[cnt].pal_idx);
        if ((int32u)attr[cnt].pal_idx >= head.pal_num)
            continue;

        /* 跳过废帧 (请自己定义帧序号) */
        attr[cnt].x1 = WORD_LE(attr[cnt].x1);
        attr[cnt].y1 = WORD_LE(attr[cnt].y1);
        attr[cnt].x2 = WORD_LE(attr[cnt].x2);
        attr[cnt].y2 = WORD_LE(attr[cnt].y2);
        if ((uint_t)attr[cnt].x2 > ww ||
            (uint_t)attr[cnt].y2 > hh ||
            attr[cnt].x1 >= attr[cnt].x2 ||
            attr[cnt].y1 >= attr[cnt].y2)
            continue;
        attr[cnt].x2 = attr[cnt].x2 - attr[cnt].x1;
        attr[cnt].y2 = attr[cnt].y2 - attr[cnt].y1;
        cnt += 1;
    }

    /* 空图片检查 */
    if (cnt == 0)
        goto _failure1;

    /* 读取所有调色板数据 */
    pals = (byte_t*)mem_calloc32(head.pal_num, 1024);
    if (pals == NULL)
        goto _failure1;
    size = (leng_t)head.pal_num;
    size *= 1024;
    if (CR_VCALL(datin)->read(datin, pals, size) != size)
        goto _failure2;
    for (back = 0; back < size; back += 4)
        pals[back + 3] = 0xFF;

    /* 读取所有图形数据 */
    dats = (byte_t*)mem_malloc32(head.img_size);
    if (dats == NULL)
        goto _failure2;
    size = (leng_t)head.img_size;
    if (CR_VCALL(datin)->read(datin, dats, size) != size)
        goto _failure3;

    /* 生成多帧图片接口对象 */
    port = struct_new(iPIC_AIA);
    if (port == NULL)
        goto _failure3;
    port->m_ww = ww;
    port->m_hh = hh;
    port->m_attr = attr;
    port->m_dats = dats;
    port->m_pals = (int32u*)pals;
    port->m_size = head.img_size;
    port->pics.__count__ = cnt;
    port->pics.__vptr__ = &s_pics_vtbl;

    /* 返回读取的文件数据 */
    rett = struct_new(sFMT_PRT);
    if (rett == NULL) {
        iPIC_AIA_release((iPICTURE*)port);
        return (NULL);
    }
    CR_NOUSE(param);
    rett->type = CR_FMTZ_PRT;
    rett->port = (iPORT*)port;
    rett->more = "iPICTURE";
    rett->infor = "FALCOM YS AIA Image File (*.AIA)";
    return (rett);

_failure3:
    mem_free(dats);
_failure2:
    mem_free(pals);
_failure1:
    mem_free(attr);
    return (NULL);
}
Beispiel #5
0
/*
=======================================
    ARGB 文件保存
=======================================
*/
CR_API bool_t
save_img_argb (
  __CR_IN__ const sIMAGE*   img,
  __CR_IN__ const ansi_t*   name,
  __CR_IN__ uint_t          argc,
  __CR_IN__ ansi_t*         argv[]
    )
{
    uint_t  hh;
    leng_t  back;
    leng_t  nbpl;
    int32u  vals;
    file_t  file;
    byte_t* line;
    sIMAGE* cnvt;

    /* 创建文件 */
    CR_NOUSE(argc); CR_NOUSE(argv);
    file = file_openA(name, CR_FO_WO);
    if (file == NULL)
        return (FALSE);

    /* 保存文件头 */
    if (!file_putd(mk_tag4("BGRA"), file))
        goto _failure;
    if (!file_putd(0x08080808UL, file))
        goto _failure;
    vals = img->position.ww;
    if (!file_putd_le(vals, file))
        goto _failure;
    vals = img->position.hh;
    if (!file_putd_le(vals, file))
        goto _failure;

    /* 转换格式 */
    if (img->fmt == CR_ARGB8888) {
        cnvt = (sIMAGE*)img;
    }
    else {
        cnvt = img_auto_to_32(NULL, 0, 0, img);
        if (cnvt == NULL)
            goto _failure;
    }

    /* 写入文件 */
    line = cnvt->data;
    hh   = cnvt->position.hh;
    nbpl = cnvt->position.ww;
    nbpl *= sizeof(int32u);
    if (cnvt->gdi)
        line += cnvt->size - cnvt->bpl;
    for (; hh != 0; hh--) {
        back = file_write(line, nbpl, file);
        if (back != nbpl) {
            if (cnvt != (sIMAGE*)img)
                image_del(cnvt);
            goto _failure;
        }
        if (cnvt->gdi)
            line -= cnvt->bpl;
        else
            line += cnvt->bpl;
    }
    if (cnvt != (sIMAGE*)img)
        image_del(cnvt);
    file_close(file);
    return (TRUE);

_failure:
    file_close(file);
    file_deleteA(name);
    return (FALSE);
}