他人の空似自作物置場

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;
}