1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <errno.h>
7 #include <sys/mman.h>
8 
9 #include "pcd.h"
10 
11 char            pcd_rotor[] =
12 {'-', '\\', '|', '/'};
13 int             pcd_img_start[] =
14 {0 /*dummy */ , 8192, 47104, 196608};
15 int             pcd_def_width[] =
16 {0 /*dummy */ , 192, 384, 768, 1536, 3072, 6144};
17 int             pcd_def_height[] =
18 {0 /*dummy */ , 128, 256, 512, 1024, 2048, 4096};
19 char            pcd_errmsg[512];
20 
21 int
pcd_open(struct PCD_IMAGE * img,char * filename)22 pcd_open(struct PCD_IMAGE *img, char *filename)
23 {
24     int             fd;
25 
26     pcd_get_LUT_init();
27     memset(img, 0, sizeof(struct PCD_IMAGE));
28 
29     fd = open(filename, O_RDONLY);
30     if (-1 == fd) {
31 	sprintf(pcd_errmsg, "open %s: %s", filename, strerror(errno));
32 	return -1;
33     }
34     img->size = lseek(fd, 0, SEEK_END);
35     lseek(fd, 0, SEEK_SET);
36     img->mmap = mmap(NULL, img->size, PROT_READ, MAP_SHARED, fd, 0);
37     if ((void *) -1 == img->mmap) {
38 	sprintf(pcd_errmsg, "mmap %s: %s", filename, strerror(errno));
39 	pcd_close(img);
40 	return -1;
41     }
42     close(fd);
43     if (0 == strncmp("PCD_OPA", img->mmap, 7)) {
44 	/* this is the thumbnails file */
45 	img->thumbnails = (int) img->mmap[10] << 8 | (int) img->mmap[11];
46     } else {
47 	if (img->size < 786432) {
48 	    sprintf(pcd_errmsg, "%s: probably not a PhotoCD image (too small)",
49 		    filename);
50 	    pcd_close(img);
51 	    return -1;
52 	}
53     }
54     return img->thumbnails;
55 }
56 
57 int
pcd_get_rot(struct PCD_IMAGE * img,int nr)58 pcd_get_rot(struct PCD_IMAGE *img, int nr)
59 {
60     if (img->thumbnails) {
61 	return img->mmap[12 + nr] & 3;
62     } else {
63 	return img->mmap[0x48] & 3;
64     }
65 }
66 
67 int
pcd_get_maxres(struct PCD_IMAGE * img)68 pcd_get_maxres(struct PCD_IMAGE *img)
69 {
70     if (img->thumbnails) {
71 	return 1;
72     } else {
73 	if (img->size == 786432)
74 	    return 3;
75 	else
76 	    return 5;
77     }
78 }
79 
80 int
pcd_select(struct PCD_IMAGE * img,int res,int nr,int gray,int verbose,int rot,int * left,int * top,int * width,int * height)81 pcd_select(struct PCD_IMAGE *img, int res, int nr, int gray, int verbose,
82 	   int rot, int *left, int *top, int *width, int *height)
83 {
84     int             y;
85     unsigned char  *ptr;
86 
87     /* free old stuff */
88     pcd_free(img);
89 
90     /* sanity checks... */
91     if (0 == img->thumbnails) {
92 	if (res < 1 || res > 5) {
93 	    sprintf(pcd_errmsg, "invalid resolution (%i) specified", res);
94 	    return -1;
95 	}
96 	if (img->size == 786432 && res > 3) {
97 	    sprintf(pcd_errmsg,
98 	       "PhotoCD file contains only the three lower resolutions");
99 	    return -1;
100 	}
101     } else {
102 	if (nr < 0 || nr >= img->thumbnails) {
103 	    sprintf(pcd_errmsg,
104 		    "thumbnail number (%i) out of range", nr);
105 	    return -1;
106 	}
107     }
108 
109     /* width/height == 0: fill in default image size */
110     if (*left == 0 && *width == 0)
111 	*width = PCD_WIDTH(res, rot);
112     if (*top == 0 && *height == 0)
113 	*height = PCD_HEIGHT(res, rot);
114 
115     if (5 == res)
116 	*left &= ~7, *top &= ~7, *width &= ~7, *height &= ~7;
117     else if (4 == res)
118 	*left &= ~3, *top &= ~3, *width &= ~3, *height &= ~3;
119     else
120 	*left &= ~1, *top &= ~1, *width &= ~1, *height &= ~1;
121     if (*left < 0 || *top < 0 ||
122 	*width < 1 || *height < 1 ||
123 	*left + *width > PCD_WIDTH(res, rot) ||
124 	*top + *height > PCD_HEIGHT(res, rot)) {
125 	sprintf(pcd_errmsg, "specified area (%ix%i+%i+%i) invalid",
126 		*width, *height, *left, *top);
127 	return -1;
128     }
129     /* recalc coordinates (rotation) */
130     switch (rot) {
131     case 0:			/* none */
132 	img->left = *left;
133 	img->top = *top;
134 	img->width = *width;
135 	img->height = *height;
136 	break;
137     case 1:			/* 90� ccw */
138 	img->left = PCD_HEIGHT(res, rot) - *top - *height;
139 	img->top = *left;
140 	img->width = *height;
141 	img->height = *width;
142 	break;
143     case 2:			/* 180� */
144 	img->left = PCD_WIDTH(res, rot) - *left - *width;
145 	img->top = PCD_HEIGHT(res, rot) - *top - *height;
146 	img->width = *width;
147 	img->height = *height;
148 	break;
149     case 3:			/* 90� cw */
150 	img->left = *top;
151 	img->top = PCD_WIDTH(res, rot) - *left - *width;
152 	img->width = *height;
153 	img->height = *width;
154 	break;
155     default:
156 	sprintf(pcd_errmsg, "specified orientation (%i) invalid", rot);
157 	return -1;
158     }
159     /* prepeare */
160     img->res = res;
161     img->nr = nr;
162     img->gray = gray;
163     img->verbose = verbose;
164     img->rot = rot;
165     img->luma = malloc(img->height * sizeof(unsigned char *));
166     img->red = malloc(img->height * sizeof(unsigned char *) >> 1);
167     img->blue = malloc(img->height * sizeof(unsigned char *) >> 1);
168 
169     if (img->luma == NULL ||
170 	img->red == NULL ||
171 	img->blue == NULL) {
172 	sprintf(pcd_errmsg, "out of memory (malloc failed)");
173 	pcd_free(img);
174 	return -1;
175     }
176     if (res <= 3) {
177 	/* just fill in pointers */
178 	if (img->thumbnails) {
179 	    ptr = img->mmap + 10240 + 36864 * nr +
180 		(pcd_def_width[res] >> 1) * 3 * img->top;
181 	} else {
182 	    ptr = img->mmap + pcd_img_start[res] +
183 		(pcd_def_width[res] >> 1) * 3 * img->top;
184 	}
185 	for (y = 0; y < img->height; y += 2, ptr += (pcd_def_width[res] >> 1) * 6) {
186 	    img->luma[y] = ptr + img->left;
187 	    img->luma[y + 1] = ptr + img->left + (pcd_def_width[res] >> 1) * 2;
188 	    img->blue[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 4;
189 	    img->red[y >> 1] = ptr + (img->left >> 1) + (pcd_def_width[res] >> 1) * 5;
190 	}
191     } else {
192 	/* high res, have to malloc memory */
193 	img->data = malloc(img->width * img->height * 3 / 2);
194 	if (img->data == NULL) {
195 	    sprintf(pcd_errmsg, "out of memory (malloc failed)");
196 	    pcd_free(img);
197 	    return -1;
198 	}
199 	ptr = img->data;
200 	for (y = 0; y < img->height; y++, ptr += img->width)
201 	    img->luma[y] = ptr;
202 	for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1)
203 	    img->blue[y] = ptr;
204 	for (y = 0; y < img->height >> 1; y++, ptr += img->width >> 1)
205 	    img->red[y] = ptr;
206     }
207     return 0;
208 }
209 
210 int
pcd_free(struct PCD_IMAGE * img)211 pcd_free(struct PCD_IMAGE *img)
212 {
213     img->res = 0;
214     if (img->data)
215 	free(img->data);
216     if (img->luma)
217 	free(img->luma);
218     if (img->red)
219 	free(img->red);
220     if (img->blue)
221 	free(img->blue);
222     if (img->seq1)
223 	free(img->seq1);
224     if (img->len1)
225 	free(img->len1);
226     if (img->seq2)
227 	free(img->seq2);
228     if (img->len2)
229 	free(img->len2);
230     if (img->seq3)
231 	free(img->seq3);
232     if (img->len3)
233 	free(img->len3);
234     img->data = NULL;
235     img->luma = img->red = img->blue = NULL;
236     img->seq1 = img->seq2 = img->seq3 = NULL;
237     img->len1 = img->len2 = img->len3 = NULL;
238     return 0;
239 }
240 
241 int
pcd_close(struct PCD_IMAGE * img)242 pcd_close(struct PCD_IMAGE *img)
243 {
244     pcd_free(img);
245     munmap(img->mmap, img->size);
246     memset(img, 0, sizeof(struct PCD_IMAGE));
247 
248     return 0;
249 }
250