touhouSE_old.zip/touhouSE_src/png.c
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "lib.h"
#include "png.h"
COLOR pal[256];
int adler32(unsigned char *data,int len){
unsigned short s1,s2;
int i;
i = 0;
s1 = 1;
s2 = 0;
while(i < len){
s1 = (s1 + data[i])%65521;
s2 = (s2 + s1)%65521;
i++;
}
return (s2<<16) + s1;
}
void setchunk(char *p,int len,char *name,char *data){
*(unsigned int *)p = endian(len);
memcpy(&p[4],name,4);
memcpy(&p[8],data,len);
*(unsigned int *)&p[len+8] = endian(crc(&p[4],len+4));
}
void set_compresseddata(unsigned char *data,unsigned int height,unsigned int width,char *col,int flag){
unsigned char str[256];
char *c;
int i,h,w;
if(flag)*str = 01;
else *str = 0;
*(unsigned short *)&str[1] = height*width+height;
*(unsigned short *)&str[3] = ~(*(unsigned short *)&str[1]);
memcpy(data,str,5);
c = col;
i = 0;
h = 0;
while(h < height){
w = 0;
data[5+i] = 0;
i++;
while(w < width){
data[5+i] = *c;
c++;
i++;
w++;
}
h++;
}
}
void pngout(unsigned int width2,unsigned int height,unsigned int width,char *col,char *fn,int cn){
unsigned int h,w,i,count,len,a,b,d,num;
unsigned char str[768],s[1024],*data,*e,*c;
unsigned int adler;
FILE *fp;
fp = fopen2(fn,"wb");
*(int *)s = 0x474e5089;
*(int *)&s[4] = 0x0a1a0a0d;
fwrite(s,4,2,fp);
*(unsigned int *)str = endian(width);
*(unsigned int *)&str[4] = endian(height);
str[8] = 8;
if(cn == 8)str[9] = 3;
else str[9] = 6;
str[10] = 0;
str[11] = 0;
str[12] = 0;
setchunk(s,13,"IHDR",str);
fwrite(s,1,25,fp);
if(width2 != width){
*(unsigned int *)str = width2;
setchunk(s,4,"orIx",str);
fwrite(s,1,16,fp);
}
if(cn == 24 || cn == 32 || cn== 16)width *= 4;
if(cn == 8){
i = 0;
while(i < 256){
str[i*3+0] = pal[i].r;
str[i*3+1] = pal[i].g;
str[i*3+2] = pal[i].b;
i++;
}
setchunk(s,768,"PLTE",str);
fwrite(s,1,780,fp);
i = 0;
while(i < 256){
str[i] = pal[i].alpha;
i++;
}
setchunk(s,256,"tRNS",str);
fwrite(s,1,268,fp);
}
a = height*width+height;
num = (int)ceil(a/(double)0xffff);//分割数(lenが2byteなので)
data = malloc(18+10*num+a);
set_compresseddata(&data[10],height,width,col,1);
adler = endian(adler32(&data[15],height*width+height));
if(num == 1){
i = 1;
} else {
i = 0;
a = height;
e = &data[10];
c = col;
while(1){
d = a;
while(d*width+d > 0xFFFF)d--;
set_compresseddata(e,d,width,c,d == a);
i++;
if(d==a)break;
c += d*width;
e += 5+d*width+d;
a -= d;
}
}
a = height*width+height;
len = 6+5*i+a;
*(unsigned int *)data = endian(len);
memcpy(&data[4],"IDAT",4);
*(short *)&data[8] = endian16(0x7801);
*(unsigned int *)&data[len+4] = adler;
*(unsigned int *)&data[len+8] = endian(crc(&data[4],len+4));
fwrite(data,1,12+len,fp);
free(data);
setchunk(s,0,"IEND",NULL);
fwrite(s,1,12,fp);
fclose(fp);
}
int get_literal(unsigned char *data,char **p){
char btype[3][32] = {
"無圧縮",
"LZ77(静的圧縮)",
"LZ77+ハフマン符号(動的圧縮)"
};
int type,a;
unsigned short len;
type = (*data&0x06)>>1;
//printf("BTYPE:%d(%s)\n",type,btype[type]);
if(type != 0){
return 1;
}
len = *(unsigned short *)&data[1];
if((unsigned short)~len != *(unsigned short *)&data[3]){
printf("NLENとLENが一致しません(0x%04x:0x%04x)\n",len,*(unsigned short *)&data[3]);
return 1;
}
//printf("LEN:%d\n",len);
*p = &data[5+len];
//本来はここでやっちゃいけない
a = endian(adler32(&data[5],len));
if(a != *(int *)*p){
//printf("ADLER32が一致しません(0x%08x:0x%08x)\n",a,*(int *)*p);
return 1;
}
//printf("ADLER32:0x%08x\n",a);
return *data&0x01;
}
void check_idat(unsigned char *data){
unsigned char *s;
if(*data&0x0F != 8){
printf("データ圧縮方法に未知の値が指定されています(%d)\n",*data&0x0F);
}
//printf("CINFO:%d(%d)\n",(*data&0xF0)>>4,(int)pow(2.0,(((*data&0xF0)>>4)-2.0)));
//printf("FLEVEL:%d\n",(data[1]&0xC0)>>6);
//printf("FDICT:%d\n",(data[1]&0x20)>>5);
if((data[0]*256+data[1])%31 != 0){
printf("FCHECKエラー\n31の倍数になっていません\n");
}
s = &data[2];
while(!get_literal(s,&s));
}
char *GetChunk(FILE *fp){
unsigned int len,c;
static unsigned char name[5],*data;
char *ret;
if(fread(&len,4,1,fp) != 1){
printf("データ長が存在しません\nIENDチャンクが存在しませんでした\n");
return NULL;
}
len = endian(len);
if(fread(name,1,4,fp) != 4){
printf("チャンクタイプが存在しません\nIENDチャンクが存在しませんでした\n");
return NULL;
}
name[4] = '\0';
ret = name;
data = malloc(len+4);
if(data == NULL){
printf("メモリー確保に失敗しました\nチャンクサイズが大きすぎる可能性があります\n");
return NULL;
}
*(int *)data = *(int *)name;
if(fread(&data[4],1,len,fp) != len){
printf("チャンクデータが存在しません\nIENDチャンクが存在しませんでした\n");
ret = NULL;
goto end;
}
if(fread(&c,4,1,fp) != 1){
printf("CRCが存在しません\nIENDチャンクが存在しませんでした\n");
ret = NULL;
goto end;
}
if(c != endian(crc(data,len+4))){
printf("CRCが一致しません(%sチャンク)\n",name);
ret = NULL;
goto end;
}
//printf("\nチャンク名:%s\nデータ長:%d\nCRC32:%08x\n",name,len,c);
if(!strncmp(name,"IDAT",4)){
check_idat(&data[4]);
}
end:
if(ret == NULL){
free(data);
return NULL;
}
return data;
}
PNG *load_png(char *filename){
PNG *png;
char str[256],*c,*data;
int len,i;
FILE *fp;
png = malloc(sizeof(PNG));
fp = fopen(filename,"rb");
if(fp == NULL){
//printf("指定されたファイルが開けませんでした(%s)\n",filename);
free(png);
return NULL;
}
fread(str,1,8,fp);
if(*(int *)str != 0x474e5089 || *(int *)&str[4] != 0x0a1a0a0d){
printf("PNGシグネチャが間違っています\n");
free(png);
fclose(fp);
return NULL;
}
c = GetChunk(fp);
if(c == NULL){
free(png);
fclose(fp);
return NULL;
}
if(strncmp(c,"IHDR",4)){
printf("最初のチャンクがIHDRではありません\n");
free(png);
fclose(fp);
return NULL;
}
png->w = endian(*(int *)&c[4]);
png->h = endian(*(int *)&c[8]);
if(*(int *)&c[13] == 6)png->cn = 32;
else png->cn = 8;
free(c);
png->pal = NULL;
png->col = NULL;
c = NULL;
while(1){
c = GetChunk(fp);
if(c == NULL){
free(png);
fclose(fp);
return NULL;
}
if(strncmp(c,"PLTE",4) == 0){
} else if(strncmp(c,"IDAT",4) == 0){
if(png->col != NULL)free(png->col);
png->col = malloc(sizeof(COLOR) * png->w * png->h);
i = 0;
data = &c[6];
while(i < png->w * png->h){
len = *(unsigned short *)&data[1];//ここだけはエンディアン変換かけてはいけない
data = &data[5];
while(len > 0){
data++;
memcpy(&png->col[i],data,4*png->w);
data += 4*png->w;
len -= 4*png->w+1;
i += png->w;
}
}
}
if(strncmp(c,"IEND",4) == 0)break;
free(c);
}
//printf("問題ありませんでした\n");
fclose(fp);
return png;
}