mirror of
https://github.com/deater/dos33fsprogs.git
synced 2026-04-20 09:17:14 +00:00
694 lines
13 KiB
C
694 lines
13 KiB
C
/* Diffs two 280x192 8-bit PNG file with correct palette to Apple II HGR */
|
|
|
|
/* v1 format:
|
|
repeat: (dest_offsetl:h is amount to skip before next patch)
|
|
(run_length maxes out at 254 bytes)
|
|
run_length,dest_offsetl,dest_offseth, (repeated) diff bytes
|
|
$ff at end
|
|
*/
|
|
|
|
|
|
#define VERSION "0.0.1"
|
|
|
|
#include <stdio.h> /* For FILE I/O */
|
|
#include <string.h> /* For strncmp */
|
|
#include <fcntl.h> /* for open() */
|
|
#include <unistd.h> /* for lseek() */
|
|
#include <sys/stat.h> /* for file modes */
|
|
#include <stdlib.h> /* free() */
|
|
|
|
#include <png.h>
|
|
|
|
#define OUTPUT_C 0
|
|
#define OUTPUT_ASM 1
|
|
#define OUTPUT_RAW 2
|
|
|
|
|
|
static int debug=0,color_warnings=0;
|
|
|
|
void print_stats(int differences, int diff_bytes, int max_run) {
|
|
|
|
fprintf(stderr,"Total differences: %d, %d bytes, max_run %d\n",
|
|
differences,diff_bytes,max_run);
|
|
|
|
printf("; Total differences: %d, %d bytes, max_run %d\n",
|
|
differences,diff_bytes,max_run);
|
|
|
|
}
|
|
|
|
|
|
static int hgr_offset_table[48]={
|
|
0x0000,0x0080,0x0100,0x0180,0x0200,0x0280,0x0300,0x0380,
|
|
0x0028,0x00A8,0x0128,0x01A8,0x0228,0x02A8,0x0328,0x03A8,
|
|
0x0050,0x00D0,0x0150,0x01D0,0x0250,0x02D0,0x0350,0x03D0,
|
|
};
|
|
|
|
static int hgr_lookup[192];
|
|
|
|
static int hgr_offset(int y) {
|
|
|
|
int temp,temp2,address;
|
|
temp=y/8;
|
|
temp2=y%8;
|
|
|
|
temp2=temp2*0x400;
|
|
|
|
address=hgr_offset_table[temp]+temp2;
|
|
|
|
return address;
|
|
}
|
|
|
|
static int hgr_lookup_init(void) {
|
|
|
|
int i;
|
|
for(i=0;i<192;i++) hgr_lookup[i]=hgr_offset(i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define HGR_LOOKUP_X 0
|
|
#define HGR_LOOKUP_Y 1
|
|
|
|
|
|
int hgr_addr_to_xy(int addr, int which) {
|
|
|
|
int i;
|
|
int tempx,tempy;
|
|
|
|
for(i=0;i<192;i++) {
|
|
if ((addr>hgr_lookup[i])&&(addr<hgr_lookup[i]+40)) break;
|
|
}
|
|
|
|
tempy=i;
|
|
tempx=addr-hgr_lookup[i];
|
|
|
|
if (which==HGR_LOOKUP_X) return tempx;
|
|
else return tempy;
|
|
|
|
}
|
|
|
|
|
|
|
|
int hgr_diff_v1(unsigned char *apple2_image1,unsigned char *apple2_image2) {
|
|
|
|
int i,j;
|
|
int last_diff=0,diff_bytes=0;
|
|
int diff_start=0,in_diff=0,diff_diff=0,run_length=0;
|
|
int max_run=0,differences=0;
|
|
|
|
for(i=0;i<8192;i++) {
|
|
if (apple2_image1[i]!=apple2_image2[i]) {
|
|
if (in_diff==0) {
|
|
diff_start=i;
|
|
in_diff=1;
|
|
diff_diff=i-last_diff;
|
|
last_diff=i;
|
|
}
|
|
else {
|
|
|
|
}
|
|
if (debug) {
|
|
fprintf(stderr,"; Difference at $%04x (%d,%d): %02x -> %02x\n",
|
|
i,hgr_addr_to_xy(i,HGR_LOOKUP_X)*7,
|
|
hgr_addr_to_xy(i,HGR_LOOKUP_Y),
|
|
apple2_image1[i],apple2_image2[i]);
|
|
}
|
|
differences++;
|
|
}
|
|
/* ended a diff */
|
|
else if (in_diff) {
|
|
// printf("Diff ended: %x %x %x\n",
|
|
// diff_diff,diff_start,i);
|
|
run_length=i-diff_start;
|
|
|
|
if (run_length>254) {
|
|
fprintf(stderr,"FIXME! Run too big %d!\n",run_length);
|
|
exit(-1);
|
|
}
|
|
|
|
printf(".byte $%02X,$%02X,$%02X",
|
|
run_length,diff_diff&0xff,(diff_diff>>8)&0xff);
|
|
|
|
diff_bytes+=3;
|
|
|
|
for(j=0;j<run_length;j++) {
|
|
printf(",$%02X",apple2_image2[i-run_length+j]);
|
|
diff_bytes++;
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
in_diff=0;
|
|
}
|
|
}
|
|
printf(".byte $ff\n");
|
|
diff_bytes++;
|
|
|
|
print_stats(differences,diff_bytes,max_run);
|
|
|
|
return diff_bytes;
|
|
}
|
|
|
|
|
|
/*
|
|
v2 format:
|
|
repeat:
|
|
|
|
dest_offset
|
|
0000 0000 = done
|
|
|
|
max run length 32
|
|
|
|
run_length
|
|
|
|
run_length top two bits
|
|
|
|
00 = immediate
|
|
run_length dest_offset_add byte byte byte
|
|
|
|
10 = run from current
|
|
run_length dest_offset_add addrl addrh
|
|
|
|
11 = run from other page
|
|
|
|
merge runs together
|
|
|
|
|
|
*/
|
|
|
|
|
|
#define MAX_DIFF 31
|
|
|
|
|
|
int hgr_diff_v2(unsigned char *apple2_old,
|
|
unsigned char *apple2_new,
|
|
unsigned char *apple2_alt) {
|
|
|
|
int i;
|
|
// int j,k;
|
|
|
|
// int found=0;
|
|
// int last_diff=0;
|
|
// int diff_start=0,in_diff=0,diff_diff=0
|
|
int run_length=1,addr_last=-1;
|
|
|
|
int differences=0,max_run=0,diff_bytes=0;
|
|
// int max_diff,local_diff;
|
|
|
|
i=0;
|
|
while(i<8192) {
|
|
|
|
/* If not match, find maximum diff length */
|
|
|
|
if (apple2_old[i]!=apple2_new[i]) {
|
|
printf(".byte $%0X,$%0X,$%0X ; $%x\n",
|
|
i-addr_last-1,run_length,apple2_new[i],i);
|
|
differences++;
|
|
diff_bytes+=3;
|
|
addr_last=i;
|
|
}
|
|
|
|
#if 0
|
|
max_diff=MAX_DIFF;
|
|
if (i+max_diff>8192) max_diff=8192-i;
|
|
|
|
local_diff=0;
|
|
for(k=max_diff;k>=0;k--) {
|
|
if (apple2_old[i+k]!=apple2_new[i+k]) local_diff++;
|
|
}
|
|
|
|
for(k=max_diff;k>=0;k--) {
|
|
fprintf(stderr,"%d %d %x %x\n",i,k,
|
|
apple2_old[i+k],apple2_new[i+k]);
|
|
if (apple2_old[i+k]!=apple2_new[i+k]) break;
|
|
}
|
|
max_diff=k+1;
|
|
|
|
printf(".byte $%02x,$%02x",max_diff,0);
|
|
for(j=0;j<max_diff;j++) {
|
|
printf(",$%02x",apple2_new[i+j]);
|
|
}
|
|
printf("; diff = %d\n",local_diff);
|
|
printf("\n");
|
|
if (max_diff>max_run) max_run=max_diff;
|
|
diff_bytes+=(2+max_diff);
|
|
i+=max_diff;
|
|
}
|
|
else {
|
|
#endif
|
|
i++;
|
|
|
|
|
|
}
|
|
printf(".byte $FF\n");
|
|
diff_bytes++;
|
|
|
|
print_stats(differences,diff_bytes,max_run);
|
|
|
|
return diff_bytes;
|
|
}
|
|
|
|
|
|
static int convert_color(int color) {
|
|
|
|
int c=0;
|
|
|
|
switch(color) {
|
|
/* These use the questionable palette my older code used */
|
|
/* Also handle the newer one */
|
|
/* Bitflipped because HGR is backwards, woz is crazy */
|
|
case 0x000000: c=0; break; /* black1 */
|
|
case 0x1bcb01: c=2; break; /* bright green */
|
|
case 0x14f53c: c=2; break; /* bright green */
|
|
case 0xe434fe: c=1; break; /* magenta */
|
|
case 0xe31e60: c=1; break; /* magenta */
|
|
case 0xffffff: c=3; break; /* white1 */
|
|
case 0xcd5b01: c=6; break; /* orange */
|
|
case 0xff6a3c: c=6; break; /* orange */
|
|
case 0x1b9afe: c=5; break; /* medium blue */
|
|
case 0x14cffd: c=5; break; /* medium blue */
|
|
|
|
case 0x010101: c=4; break; /* black2 */
|
|
case 0xfefefe: c=7; break; /* white2 */
|
|
|
|
default:
|
|
fprintf(stderr,"Unknown color %x\n",color);
|
|
break;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
/* expects a PNG where the xsize is *2 */
|
|
static int loadpng(char *filename,
|
|
unsigned char **image_ptr, int *xsize, int *ysize) {
|
|
|
|
int x,y;
|
|
int color;
|
|
FILE *infile;
|
|
unsigned char *image,*out_ptr;
|
|
int width, height;
|
|
int a2_color;
|
|
|
|
png_byte bit_depth;
|
|
png_structp png_ptr;
|
|
png_infop info_ptr;
|
|
png_bytep *row_pointers;
|
|
png_byte color_type;
|
|
int row_bytes,bytes_per_pixel;
|
|
|
|
unsigned char header[8];
|
|
|
|
/* open file and test for it being a png */
|
|
infile = fopen(filename, "rb");
|
|
if (infile==NULL) {
|
|
fprintf(stderr,"Error! Could not open %s\n",filename);
|
|
return -1;
|
|
}
|
|
|
|
/* Check the header */
|
|
fread(header, 1, 8, infile);
|
|
if (png_sig_cmp(header, 0, 8)) {
|
|
fprintf(stderr,"Error! %s is not a PNG file\n",filename);
|
|
return -1;
|
|
}
|
|
|
|
/* initialize stuff */
|
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
if (!png_ptr) {
|
|
fprintf(stderr,"Error create_read_struct\n");
|
|
exit(-1);
|
|
}
|
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
if (!info_ptr) {
|
|
fprintf(stderr,"Error png_create_info_struct\n");
|
|
exit(-1);
|
|
}
|
|
|
|
png_init_io(png_ptr, infile);
|
|
png_set_sig_bytes(png_ptr, 8);
|
|
|
|
png_read_info(png_ptr, info_ptr);
|
|
|
|
width = png_get_image_width(png_ptr, info_ptr);
|
|
height = png_get_image_height(png_ptr, info_ptr);
|
|
*xsize=width;
|
|
*ysize=height;
|
|
|
|
color_type = png_get_color_type(png_ptr, info_ptr);
|
|
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
|
|
|
if (width!=280) {
|
|
fprintf(stderr,"Unknown width %d\n",width);
|
|
return -1;
|
|
}
|
|
|
|
if (height!=192) {
|
|
fprintf(stderr,"Unknown height %d\n",height);
|
|
return -1;
|
|
}
|
|
|
|
image=calloc(width*height,sizeof(unsigned char));
|
|
if (image==NULL) {
|
|
fprintf(stderr,"Error allocating image\n");
|
|
return -1;
|
|
}
|
|
|
|
if (debug) {
|
|
fprintf(stderr,"PNG: width=%d height=%d depth=%d\n",
|
|
width,height,bit_depth);
|
|
if (color_type==PNG_COLOR_TYPE_RGB) {
|
|
fprintf(stderr,"Type RGB\n");
|
|
}
|
|
else if (color_type==PNG_COLOR_TYPE_RGB_ALPHA) {
|
|
fprintf(stderr,"Type RGBA\n");
|
|
}
|
|
else if (color_type==PNG_COLOR_TYPE_PALETTE) {
|
|
fprintf(stderr,"Type palette\n");
|
|
}
|
|
}
|
|
|
|
/* If palette, expand to RGB automatically */
|
|
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
|
png_set_expand(png_ptr);
|
|
}
|
|
|
|
png_read_update_info(png_ptr, info_ptr);
|
|
|
|
|
|
row_bytes = png_get_rowbytes(png_ptr, info_ptr);
|
|
// *pChannels = (int)png_get_channels(png_ptr, info_ptr);
|
|
bytes_per_pixel=row_bytes/width;
|
|
|
|
if (debug) {
|
|
fprintf(stderr,"Rowbytes=%d bytes per pixel=%d\n",
|
|
row_bytes,row_bytes/width);
|
|
}
|
|
|
|
row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
|
|
for (y=0; y<height; y++) {
|
|
row_pointers[y] = (png_byte*)malloc(row_bytes);
|
|
}
|
|
|
|
png_read_image(png_ptr, row_pointers);
|
|
|
|
png_read_end(png_ptr, NULL);
|
|
|
|
fclose(infile);
|
|
|
|
out_ptr=image;
|
|
|
|
|
|
for(y=0;y<height;y++) {
|
|
for(x=0;x<width;x++) {
|
|
|
|
color= (row_pointers[y][x*bytes_per_pixel]<<16)+
|
|
(row_pointers[y][x*bytes_per_pixel+1]<<8)+
|
|
(row_pointers[y][x*bytes_per_pixel+2]);
|
|
// if (debug) {
|
|
// fprintf(stderr,"%x ",color);
|
|
// }
|
|
|
|
a2_color=convert_color(color);
|
|
|
|
if (debug) {
|
|
fprintf(stderr,"%x",a2_color);
|
|
}
|
|
*out_ptr=a2_color;
|
|
out_ptr++;
|
|
}
|
|
if (debug) fprintf(stderr,"\nNR: ");
|
|
}
|
|
|
|
|
|
*image_ptr=image;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Converts a PNG to RAW 8K Hires Image */
|
|
|
|
|
|
static void print_help(char *name,int version) {
|
|
|
|
printf("\npng2hgr_diff version %s\n",VERSION);
|
|
|
|
if (version) exit(1);
|
|
|
|
printf("\nUsage: %s [-w which] PNGFILE1 PNGFILE2 PNGFILE3 \n\n",name);
|
|
printf("\n");
|
|
printf("\t-w : which version of format to use\n");
|
|
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/* Count both black/white variants */
|
|
static int color_high(int color) {
|
|
if (color>3) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int color_low(int color) {
|
|
if (color<4) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* also count any black/white */
|
|
static int color_high_bw(int color) {
|
|
if (color>3) return 1;
|
|
if ((color==0) || (color==3)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int color_low_bw(int color) {
|
|
if (color<4) return 1;
|
|
if ((color==4) || (color==7)) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int colors_to_bytes(unsigned char colors[14],
|
|
unsigned char *byte1,
|
|
unsigned char *byte2) {
|
|
|
|
int i;
|
|
int highbit1=0,highbit2=0,lowbit1=0,lowbit2=0;
|
|
int bwhigh1=0,bwlow1=0;
|
|
int bwhigh2=0,bwlow2=0;
|
|
int hb1,hb2;
|
|
int error=0;
|
|
|
|
*byte1=0;
|
|
*byte2=0;
|
|
|
|
for(i=0;i<7;i++) {
|
|
highbit1+=color_high(colors[i]);
|
|
lowbit1+=color_low(colors[i]);
|
|
bwhigh1+=color_high_bw(colors[i]);
|
|
bwlow1+=color_low_bw(colors[i]);
|
|
}
|
|
|
|
for(i=7;i<14;i++) {
|
|
highbit2+=color_high(colors[i]);
|
|
lowbit2+=color_low(colors[i]);
|
|
bwhigh2+=color_high_bw(colors[i]);
|
|
bwlow2+=color_low_bw(colors[i]);
|
|
}
|
|
|
|
if (highbit1==7) hb1=1; // all were high bit set
|
|
else if (lowbit1==7) hb1=0; // all were lo bit set
|
|
else if (bwhigh1==7) hb1=1; // ignore black/white
|
|
else if (bwlow1==7) hb1=0;
|
|
else {
|
|
error=1;
|
|
if (bwhigh1>bwlow1) hb1=1;
|
|
else hb1=0;
|
|
}
|
|
|
|
if (highbit2==7) hb2=1; // all were high bit set
|
|
else if (lowbit2==7) hb2=0; // all were lo bit set
|
|
else if (bwhigh2==7) hb2=1; // ignore black/white
|
|
else if (bwlow2==7) hb2=0;
|
|
else {
|
|
error=1;
|
|
if (bwhigh2>bwlow2) hb2=1;
|
|
else hb2=0;
|
|
}
|
|
|
|
/*
|
|
0 0 0 0 -> 00 00
|
|
1 1 1 1 -> 01 01
|
|
2 2 2 2 -> 10 10
|
|
3 3 3 3 -> 11 11
|
|
1 3 3 1 -> 01 11
|
|
|
|
1 1 2 2 3 3 0 -> 0 11 10 01
|
|
|
|
*/
|
|
|
|
|
|
|
|
*byte1|=(colors[0]&1)<<0;
|
|
*byte1|=(colors[1]&2)<<0;
|
|
*byte1|=(colors[2]&1)<<2;
|
|
*byte1|=(colors[3]&2)<<2;
|
|
*byte1|=(colors[4]&1)<<4;
|
|
*byte1|=(colors[5]&2)<<4;
|
|
*byte1|=(colors[6]&1)<<6;
|
|
*byte1|=hb1<<7;
|
|
|
|
*byte2|=(colors[7]&2)>>1;
|
|
*byte2|=(colors[8]&1)<<1;
|
|
*byte2|=(colors[9]&2)<<1;
|
|
*byte2|=(colors[10]&1)<<3;
|
|
*byte2|=(colors[11]&2)<<3;
|
|
*byte2|=(colors[12]&1)<<5;
|
|
*byte2|=(colors[13]&2)<<5;
|
|
*byte2|=hb2<<7;
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
static int load_image(unsigned char *a2_image,char *filename) {
|
|
|
|
int x,y,z,xsize,ysize;
|
|
unsigned char *image;
|
|
int color1,error;
|
|
unsigned char byte1,byte2,colors[14];
|
|
|
|
if (loadpng(filename,&image,&xsize,&ysize)<0) {
|
|
fprintf(stderr,"Error loading png!\n");
|
|
exit(-1);
|
|
}
|
|
|
|
fprintf(stderr,"Loaded image %s %d by %d\n",
|
|
filename,xsize,ysize);
|
|
|
|
for(y=0;y<192;y++) {
|
|
for(x=0;x<20;x++) {
|
|
for(z=0;z<14;z++) {
|
|
color1=image[y*280+x*14+z];
|
|
colors[z]=color1;
|
|
}
|
|
error=colors_to_bytes(colors,&byte1,&byte2);
|
|
if (error!=0) {
|
|
color_warnings++;
|
|
}
|
|
|
|
a2_image[hgr_offset(y)+(x*2)+0]=byte1;
|
|
a2_image[hgr_offset(y)+(x*2)+1]=byte2;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char apple2_old[8192];
|
|
static unsigned char apple2_new[8192];
|
|
static unsigned char apple2_alt[8192];
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
int c;
|
|
// int xsize=0,ysize=0,error;
|
|
// int c,x,y,z,color1;
|
|
// unsigned char *image;
|
|
// unsigned char byte1,byte2,colors[14];
|
|
|
|
char *filename_old=NULL,*filename_new=NULL,*filename_alt=NULL;
|
|
int version=0;
|
|
|
|
hgr_lookup_init();
|
|
|
|
/* Parse command line arguments */
|
|
|
|
while ( (c=getopt(argc, argv, "hvdw:") ) != -1) {
|
|
|
|
switch(c) {
|
|
|
|
case 'h':
|
|
print_help(argv[0],0);
|
|
break;
|
|
case 'v':
|
|
print_help(argv[0],1);
|
|
break;
|
|
case 'd':
|
|
debug=1;
|
|
break;
|
|
case 'w':
|
|
version=atoi(optarg);
|
|
break;
|
|
default:
|
|
print_help(argv[0],0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// printf("%d %d\n",optind,argc);
|
|
|
|
if (optind>=argc) {
|
|
fprintf(stderr,
|
|
"ERROR: Was expecting filename!\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (optind+1>=argc) {
|
|
fprintf(stderr,
|
|
"ERROR: Was expecting at least two filenames!\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* load alt image */
|
|
if ((optind+2>=argc) && (version==2)) {
|
|
fprintf(stderr,"Error, need 3 files for version 2\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
filename_old=strdup(argv[optind]);
|
|
filename_new=strdup(argv[optind+1]);
|
|
|
|
if (optind+2<argc) {
|
|
filename_alt=strdup(argv[optind+2]);
|
|
}
|
|
|
|
memset(apple2_old,0,8192);
|
|
memset(apple2_new,0,8192);
|
|
memset(apple2_alt,0,8192);
|
|
|
|
/* load old image */
|
|
load_image(apple2_old,filename_old);
|
|
|
|
/* load new image */
|
|
load_image(apple2_new,filename_new);
|
|
|
|
if (filename_alt!=NULL) {
|
|
load_image(apple2_alt,filename_alt);
|
|
}
|
|
|
|
switch(version) {
|
|
case 1:
|
|
hgr_diff_v1(apple2_old,apple2_new);
|
|
break;
|
|
case 2:
|
|
hgr_diff_v2(apple2_old,
|
|
apple2_new,apple2_alt);
|
|
break;
|
|
default:
|
|
fprintf(stderr,"Unknown version %d\n",version);
|
|
return -1;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|