OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
disklabel.c
Go to the documentation of this file.
1
23#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
24#define BigEndianToNative16(x) __builtin_bswap16(x)
25#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
26#define BigEndianToNative16(x) (x)
27#else
28#include <arpa/inet.h>
29#define BigEndianToNative16(x) ntohs(x)
30#endif
31
32#if defined(__APPLE__)
33#include <ApplicationServices/ApplicationServices.h>
34#include <CoreFoundation/CoreFoundation.h>
35#include <CoreGraphics/CoreGraphics.h>
36#elif defined(WIN32) && !defined(_ISOC99_SOURCE)
37#define _ISOC99_SOURCE
38#endif // __APPLE__
39
40#include <stdbool.h>
41#include <stdio.h>
42#include <stdint.h>
43#include <stdlib.h>
44#include <string.h>
45
46/* Reverse clut to pixels. */
47static const uint8_t palette[256] = {
48 [0x00] = /* 0xFF - */ 0x00,
49 [0xF6] = /* 0xFF - */ 0x11,
50 [0xF7] = /* 0xFF - */ 0x22,
51 [0x2A] = /* 0xFF - */ 0x33,
52 [0xF8] = /* 0xFF - */ 0x44,
53 [0xF9] = /* 0xFF - */ 0x55,
54 [0x55] = /* 0xFF - */ 0x66,
55 [0xFA] = /* 0xFF - */ 0x77,
56 [0xFB] = /* 0xFF - */ 0x88,
57 [0x80] = /* 0xFF - */ 0x99,
58 [0xFC] = /* 0xFF - */ 0xAA,
59 [0xFD] = /* 0xFF - */ 0xBB,
60 [0xAB] = /* 0xFF - */ 0xCC,
61 [0xFE] = /* 0xFF - */ 0xDD,
62 [0xFF] = /* 0xFF - */ 0xEE,
63 [0xD6] = /* 0xFF - */ 0xFF
64};
65
66/* To fit BootPicker. */
67#define LABEL_MAX_WIDTH 340
68#define LABEL_MAX_HEIGHT 12
69#define LABEL_TYPE_PALETTED 1
70#define LABEL_TYPE_BGRA 2
71
72#pragma pack(push, 1)
79#pragma pack(pop)
80
81#ifdef __APPLE__
82
83/* Antialiasing clut. */
84static const uint8_t clut[] = {
85 0x00, /* 0x00 0x00 0x00 white */
86 0xF6, /* 0x11 0x11 0x11 */
87 0xF7, /* 0x22 0x22 0x22 */
88 0x2A, /* 0x33 = 1*6^2 + 1*6 + 1 = 43 colors */
89 0xF8, /* 0x44 */
90 0xF9, /* 0x55 */
91 0x55, /* 0x66 = 2*(36 + 6 + 1) = 86 colors */
92 0xFA, /* 0x77 */
93 0xFB, /* 0x88 */
94 0x80, /* 0x99 = (3*43) = 129 colors*/
95 0xFC, /* 0xAA */
96 0xFD, /* 0xBB */
97 0xAB, /* 0xCC = 4*43 = 172 colors */
98 0xFE, /* 0xDD */
99 0xFF, /* 0xEE */
100 0xD6, /* 0xFF = 5*43 = 215 */
101};
102
103static CTLineRef create_text_line(CGColorSpaceRef color_space, const char *str, int scale) {
104 /* White text on black background, originating from OF/EFI bitmap. */
105 const CGFloat components[] = {
106 (CGFloat)1.0, (CGFloat)1.0
107 };
108
109 CTFontRef font_ref = CTFontCreateWithName(CFSTR("Helvetica"), 10.0 * scale, NULL);
110 CGColorRef color = CGColorCreate(color_space, components);
111 CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault,
112 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
113
114 CTLineRef result = NULL;
115
116 if (font_ref != NULL && color != NULL && dict != NULL) {
117 CFDictionarySetValue(dict, kCTForegroundColorAttributeName, color);
118 CFDictionarySetValue(dict, kCTFontAttributeName, font_ref);
119
120 CFStringRef tmp = CFStringCreateWithCString(kCFAllocatorDefault, str, kCFStringEncodingUTF8);
121 if (tmp != NULL) {
122 CFAttributedStringRef attr_tmp = CFAttributedStringCreate(kCFAllocatorDefault, tmp, dict);
123 if (attr_tmp != NULL) {
124 result = CTLineCreateWithAttributedString(attr_tmp);
125 CFRelease(attr_tmp);
126 }
127 CFRelease(tmp);
128 }
129 }
130
131 if (dict != NULL) {
132 CFRelease(dict);
133 }
134
135 if (color != NULL) {
136 CFRelease(color);
137 }
138
139 if (font_ref != NULL) {
140 CFRelease(font_ref);
141 }
142
143 return result;
144}
145
146static int render_label(const char *label, uint8_t *bitmap_data,
147 uint16_t width, uint16_t height, int scale, uint16_t *newwidth) {
148
149 CGColorSpaceRef color_space = CGColorSpaceCreateDeviceGray();
150 if (color_space == NULL) {
151 fprintf(stderr, "Could not obtain color space\n");
152 return 1;
153 }
154
155 CTLineRef line = create_text_line(color_space, label, scale);
156 if (line == NULL) {
157 fprintf(stderr, "Could not render text line\n");
158 CGColorSpaceRelease(color_space);
159 return 3;
160 }
161
162 CGContextRef context = CGBitmapContextCreate(bitmap_data, width, height,
163 8, width, color_space, kCGImageAlphaNone);
164
165 if(context == NULL) {
166 fprintf(stderr, "Could not init CoreGraphics context\n");
167 CFRelease(line);
168 CGColorSpaceRelease(color_space);
169 return 2;
170 }
171
172 CGRect rect = CTLineGetImageBounds(line, context);
173 CGContextSetTextPosition(context, 2.0 * scale, 2.0 * scale);
174 CTLineDraw(line, context);
175 CGContextFlush(context);
176 CFRelease(line);
177
178 *newwidth = (uint16_t)(rect.size.width + 4 * scale);
179
180 CGContextRelease(context);
181 CGColorSpaceRelease(color_space);
182 return 0;
183}
184
185static void *make_label(const char *label, int scale, bool bgra, uint16_t *label_size) {
188 DiskLabel *label_data = calloc(1, sizeof(DiskLabel) + width*height*(bgra*3+1));
189 if (label_data == NULL) {
190 fprintf(stderr, "Label allocation failure\n");
191 return NULL;
192 }
193
194 uint16_t newwidth;
195 int err = render_label(label, label_data->data, width, height, scale, &newwidth);
196 if (err != 0) {
197 free(label_data);
198 return NULL;
199 }
200
201 /* Cap at 340*scale pixels wide. */
202 if (newwidth > width) {
203 fprintf(stderr, "Label %s with %d pixels does not fit in %d pixels\n", label, newwidth, width);
204 newwidth = width;
205 }
206
207 /* Refit to new width (111111000111111000111111000 -> 111111111111111111). */
208 for (uint16_t row = 1; row < height; row++) {
209 memmove(&label_data->data[row * newwidth], &label_data->data[row * width], newwidth);
210 }
211
212 label_data->type = bgra ? LABEL_TYPE_BGRA : LABEL_TYPE_PALETTED;
213 label_data->width = htons(newwidth);
214 label_data->height = htons(height);
215
216 if (bgra) {
217 /* Perform conversion. */
218 for (uint16_t i = 0; i < newwidth * height; i++) {
219 uint32_t index = newwidth * height - i - 1;
220 uint8_t alpha = label_data->data[index];
221
222 label_data->data[index * 4 + 0] = 0;
223 label_data->data[index * 4 + 1] = 0;
224 label_data->data[index * 4 + 2] = 0;
225 label_data->data[index * 4 + 3] = 0xFF - alpha;
226 }
227 } else {
228 /* Perform antialiasing. */
229 for (uint16_t i = 0; i < newwidth * height; i++) {
230 label_data->data[i] = clut[label_data->data[i] >> 4U];
231 }
232 }
233
234 *label_size = sizeof(DiskLabel) + newwidth * height * (bgra*3+1);
235
236 return label_data;
237}
238
239static int write_file(const char *filename, uint8_t *buffer, size_t size) {
240 FILE *fh = fopen(filename, "wb+");
241 if (!fh) {
242 fprintf(stderr, "Cannot open file %s for writing!\n", filename);
243 return -1;
244 }
245
246 if (fwrite(buffer, size, 1, fh) != 1) {
247 fprintf(stderr, "Cannot write %zu bytes in %s!\n", size, filename);
248 fclose(fh);
249 return -1;
250 }
251
252 fclose(fh);
253 return 0;
254}
255#endif // __APPLE__
256
257static int read_file(const char *filename, uint8_t **buffer, size_t *size) {
258 FILE *fh = fopen(filename, "rb");
259 if (!fh) {
260 fprintf(stderr, "Missing file %s!\n", filename);
261 return -1;
262 }
263
264 if (fseek(fh, 0, SEEK_END)) {
265 fprintf(stderr, "Failed to find end of %s!\n", filename);
266 fclose(fh);
267 return -1;
268 }
269
270 long pos = ftell(fh);
271
272 if (pos <= 0) {
273 fprintf(stderr, "Invalid file size (%ld) of %s!\n", pos, filename);
274 fclose(fh);
275 return -1;
276 }
277
278 if (fseek(fh, 0, SEEK_SET)) {
279 fprintf(stderr, "Failed to rewind %s!\n", filename);
280 fclose(fh);
281 return -1;
282 }
283
284 *size = (size_t)pos;
285 *buffer = (uint8_t *)malloc(*size);
286
287 if (!*buffer) {
288 fprintf(stderr, "Failed to allocate %zu bytes for %s!\n", *size, filename);
289 fclose(fh);
290 return -1;
291 }
292
293 if (fread(*buffer, *size, 1, fh) != 1) {
294 fprintf(stderr, "Failed to read %zu bytes from %s!\n", *size, filename);
295 fclose(fh);
296 free(*buffer);
297 return -1;
298 }
299
300 fclose(fh);
301 return 0;
302}
303
304static int write_ppm(const char *filename, size_t width, size_t height, uint8_t *pixel, bool bgra) {
305 FILE *fh = fopen(filename, "wb");
306 if (!fh) {
307 fprintf(stderr, "Failed to open out file %s!\n", filename);
308 return -1;
309 }
310
311 // REF: http://netpbm.sourceforge.net/doc/pam.html
312 if (fprintf(fh, "P7\nWIDTH %zu\nHEIGHT %zu\nDEPTH 4\nMAXVAL 255\nTUPLTYPE RGB_ALPHA\nENDHDR\n", width, height) < 0) {
313 fprintf(stderr, "Failed to write ppm header to %s!\n", filename);
314 fclose(fh);
315 return -1;
316 }
317
318 for (size_t y = 0; y < height; ++y) {
319 for (size_t x = 0; x < width; ++x) {
320 uint8_t col[4];
321 if (bgra) {
322 col[0] = pixel[2];
323 col[1] = pixel[1];
324 col[2] = pixel[0];
325 col[3] = 0xFF - pixel[3];
326 pixel += 4;
327 } else {
328 // Colour can be any and depends on the representation.
329 col[0] = col[1] = col[2] = 0;
330 // PAM 4th channel is opacity (0xFF is fully opaque, 0x00 is fully transparent).
331 col[3] = palette[*pixel];
332 pixel++;
333 }
334 if (fwrite(col, sizeof(col), 1, fh) != 1) {
335 fprintf(stderr, "Failed to write ppm pixel %zux%zu to %s!\n", x, y, filename);
336 fclose(fh);
337 return -1;
338 }
339 }
340 }
341
342 fclose(fh);
343
344 return 0;
345}
346
347static int decode_label(const char *infile, const char *outfile) {
348 DiskLabel *label;
349 size_t size;
350
351 if (read_file(infile, (uint8_t **) &label, &size)) {
352 return -1;
353 }
354
355 if (size < sizeof(DiskLabel)) {
356 fprintf(stderr, "Too low size %zu!\n", size);
357 free(label);
358 return -1;
359 }
360
361 if (label->type != LABEL_TYPE_PALETTED && label->type != LABEL_TYPE_BGRA) {
362 fprintf(stderr, "Invalid version %02X!\n", label->type);
363 free(label);
364 return -1;
365 }
366
367 size_t width = BigEndianToNative16(label->width);
368 size_t height = BigEndianToNative16(label->height);
369
370 size_t exp_size = width * height;
371 bool bgra = label->type == LABEL_TYPE_BGRA;
372 if (bgra) {
373 exp_size *= SIZE_MAX / 4 >= exp_size ? 4 : 0;
374 }
375
376 if (width * height == 0 || size - sizeof(DiskLabel) != exp_size) {
377 fprintf(stderr, "Image type %d mismatch %zux%zu with size %zu!\n", label->type, width, height, size);
378 free(label);
379 return -1;
380 }
381
382 (void)remove(outfile);
383
384 int ret = write_ppm(outfile, width, height, label->data, bgra);
385 free(label);
386 return ret;
387}
388
389static int encode_label(const char *label, bool bgra, const char *outfile, const char *outfile2x) {
390#ifdef __APPLE__
391 for (int scale = 1; scale <= 2; scale++) {
392 const char *filename = scale == 1 ? outfile : outfile2x;
393 uint16_t label_size;
394 void *label_data = make_label(label, scale, bgra, &label_size);
395 bool ok = label_data != NULL && write_file(filename, label_data, label_size) == 0;
396 free(label_data);
397 if (!ok) {
398 return -2;
399 }
400 }
401 return 0;
402#else
403 (void) label;
404 (void) bgra;
405 (void) outfile;
406 (void) outfile2x;
407 fprintf(stderr, "Encoding labels is unsupported!\n");
408 return -1;
409#endif
410}
411
412int main(int argc, char *argv[]) {
413 if (argc == 4 && strcmp(argv[1], "-d") == 0) {
414 return decode_label(argv[2], argv[3]);
415 }
416
417 if (argc == 5 && strcmp(argv[1], "-e") == 0) {
418 return encode_label(argv[2], false, argv[3], argv[4]);
419 }
420
421 if (argc == 5 && strcmp(argv[1], "-bgra") == 0) {
422 return encode_label(argv[2], true, argv[3], argv[4]);
423 }
424
425 fprintf(stderr,
426 "Usage:\n"
427 " disklabel -d .disk_label image.ppm!\n"
428 " disklabel -e \"Label\" .disk_label .disk_label_2x\n"
429 " disklabel -bgra \"Label\" .disk_label .disk_label_2x\n");
430 return -1;
431}
UINT16 width
Definition BmfFile.h:85
UINT16 y
Definition BmfFile.h:84
UINT16 x
Definition BmfFile.h:83
UINT16 height
Definition BmfFile.h:86
UINT32 size
#define size_t
Definition Ubsan.h:87
#define uint16_t
Definition Ubsan.h:58
int main()
Definition acdtinfo.c:181
struct DiskLabel_ DiskLabel
#define LABEL_MAX_WIDTH
Definition disklabel.c:67
#define LABEL_MAX_HEIGHT
Definition disklabel.c:68
#define LABEL_TYPE_BGRA
Definition disklabel.c:70
#define BigEndianToNative16(x)
Definition disklabel.c:29
#define LABEL_TYPE_PALETTED
Definition disklabel.c:69
UINT8 uint8_t
#define memmove(dst, src, len)
UINT16 uint16_t
UINT32 uint32_t
#define malloc(Size)
Definition lzss.h:49
#define free(Ptr)
Definition lzss.h:50
uint8_t type
Definition disklabel.c:74
uint16_t height
Definition disklabel.c:76
uint8_t data[]
Definition disklabel.c:77
uint16_t width
Definition disklabel.c:75