/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * MacOS X application bundle fixer utility. Creates an application * bundle out of an executable and some optional icon image files. * * By Angelo Mottola. * * See readme.txt for copyright information. */ #define ALLEGRO_USE_CONSOLE #include #include #include #include #include #include #undef TRUE #undef FALSE #ifndef SCAN_DEPEND #include #endif #undef TRUE #undef FALSE #define TRUE -1 #define FALSE 0 /* 16x16 */ #define ICON_SMALL 0 /* 32x32 */ #define ICON_LARGE 1 /* 48x48 */ #define ICON_HUGE 2 /* 128x128 */ #define ICON_THUMBNAIL 3 #define F_SMALL_DEFINED 0x1 #define F_LARGE_DEFINED 0x2 #define F_HUGE_DEFINED 0x4 #define F_THUMBNAIL_DEFINED 0x8 #define F_ICONS_DEFINED 0xf #define F_MOVE 0x10 #define F_GOT_VERSION 0x20 #define F_GOT_LONG_VERSION 0x40 #define F_EMBED_FRAMEWORK 0x80 #define MAX_STRING_SIZE 1024 #define ONE_SIXTH (1.0 / 6.0) typedef struct ICON_DATA { BITMAP *original, *workspace, *scaled; int size; OSType data, mask8, mask1; int defined; } ICON_DATA; static ICON_DATA icon_data[4] = { { NULL, NULL, NULL, 16, kSmall32BitData, kSmall8BitMask, kSmall1BitMask, F_SMALL_DEFINED }, { NULL, NULL, NULL, 32, kLarge32BitData, kLarge8BitMask, kLarge1BitMask, F_LARGE_DEFINED }, { NULL, NULL, NULL, 48, kHuge32BitData, kHuge8BitMask, kHuge1BitMask, F_HUGE_DEFINED }, { NULL, NULL, NULL, 128, kThumbnail32BitData, kThumbnail8BitMask, 0, F_THUMBNAIL_DEFINED } }; static int flags = 0; static float cubic_bspline(float x) { float a, b, c, d; if (x <= -2.0) a = 0.0; else a = (x + 2.0) * (x + 2.0) * (x + 2.0); if (x <= -1.0) b = 0.0; else b = ((x + 1.0) * (x + 1.0) * (x + 1.0)) * -4.0; if (x <= 0) c = 0.0; else c = (x * x * x) * 6.0; if (x <= 1.0) d = 0.0; else d = ((x - 1.0) * (x - 1.0) * (x - 1.0)) * -4.0; return (a + b + c + d) * ONE_SIXTH; } static int scale_icon(ICON_DATA *icon) { BITMAP *shape; int size, x_ofs = 2, y_ofs = 2; int x, y, m, n; int i_x, i_y; float k, f_x, f_y, a, b, r1, r2; float red, green, blue, alpha; unsigned char *p; unsigned int color; if (icon->original->w > icon->original->h) { size = 4 + icon->original->w; y_ofs = 2 + ((icon->original->w - icon->original->h) / 2); } else { size = 4 + icon->original->h; x_ofs = 2 + ((icon->original->h - icon->original->w) / 2); } k = (float)(size - 4) / (float)icon->size; icon->workspace = create_bitmap_ex(32, size, size); if (!icon->workspace) return -1; icon->scaled = create_bitmap_ex(32, icon->size, icon->size); if (!icon->scaled) return -1; shape = create_bitmap_ex(32, icon->original->w, icon->original->h); if (!shape) return -1; blit(icon->original, shape, 0, 0, 0, 0, icon->original->w, icon->original->h); clear_to_color(icon->workspace, makeacol32(0, 0, 0, 255)); shape->vtable->mask_color = makeacol32(255, 0, 255, 0); masked_blit(shape, icon->workspace, 0, 0, x_ofs, y_ofs, shape->w, shape->h); destroy_bitmap(shape); for (y = 0; y < icon->size; y++) { f_y = (float)y * k; i_y = (int)floor(f_y); a = f_y - floor(f_y); for (x = 0; x < icon->size; x++) { f_x = (float)x * k; i_x = (int)floor(f_x); b = f_x - floor(f_x); red = green = blue = alpha = 0.0; for (m = -1; m < 3; m++) { r1 = cubic_bspline((float)m - a); for (n = -1; n < 3; n++) { r2 = cubic_bspline(b - (float)n); color = ((unsigned int *)(icon->workspace->line[i_y + m + 2]))[i_x + n + 2]; red += ((float)getr32(color) * r1 * r2); green += ((float)getg32(color) * r1 * r2); blue += ((float)getb32(color) * r1 * r2); alpha += ((float)geta32(color) * r1 * r2); } } color = makeacol32((int)floor(red), (int)floor(green), (int)floor(blue), 255 - (int)floor(alpha)); ((unsigned int *)(icon->scaled->line[y]))[x] = color; } } return 0; } static int load_resource(char *datafile, char *name, ICON_DATA *icon) { DATAFILE *data; BITMAP *temp, *bitmap = NULL; RLE_SPRITE *rle_sprite; PALETTE palette; int size, type, i; int result = 0; if (datafile[0] != '\0') { data = load_datafile_object(datafile, name); if (!data) { fprintf(stderr, "Error loading object '%s' from %s\n", name, datafile); return -1; } switch (data->type) { case DAT_BITMAP: temp = (BITMAP *)data->dat; bitmap = create_bitmap_ex(temp->vtable->color_depth, temp->w, temp->h); blit(temp, bitmap, 0, 0, 0, 0, temp->w, temp->h); break; case DAT_RLE_SPRITE: rle_sprite = (RLE_SPRITE *)data->dat; bitmap = create_bitmap_ex(rle_sprite->color_depth, rle_sprite->w, rle_sprite->h); clear_to_color(bitmap, bitmap->vtable->mask_color); draw_rle_sprite(bitmap, rle_sprite, 0, 0); break; case DAT_PALETTE: select_palette((RGB *)data->dat); unload_datafile_object(data); return 0; default: fprintf(stderr, "'%s' is not a BITMAP, RLE_SPRITE or PALETTE object in datafile '%s'\n", name, datafile); unload_datafile_object(data); return -1; } unload_datafile_object(data); } else { bitmap = load_bitmap(name, palette); select_palette(palette); if (!bitmap) { fprintf(stderr, "Unable to load '%s'\n", name); return -1; } } if (!icon) { size = MAX(bitmap->w, bitmap->h); if (size <= 16) type = ICON_SMALL; else if (size <= 32) type = ICON_LARGE; else if (size <= 48) type = ICON_HUGE; else type = ICON_THUMBNAIL; icon = &icon_data[type]; if (flags & icon->defined) { for (i = 0; i < 3; i++) { type = (type + 1) % 4; icon = &icon_data[type]; if (!(flags & icon->defined)) break; } if (flags & icon->defined) { fprintf(stderr, "Too many icon resources!"); result = -1; goto exit_error; } } } else { if (icon->scaled) { fprintf(stderr, "Multiple icon resources of the same size"); result = -1; goto exit_error; } } icon->original = create_bitmap_ex(bitmap->vtable->color_depth, bitmap->w, bitmap->h); blit(bitmap, icon->original, 0, 0, 0, 0, bitmap->w, bitmap->h); result = scale_icon(icon); flags |= icon->defined; exit_error: destroy_bitmap(bitmap); return result; } static void usage(void) { fprintf(stderr, "\nMacOS X application bundle fixer utility for Allegro " ALLEGRO_VERSION_STR "\n" "By Angelo Mottola, " ALLEGRO_DATE_STR "\n\n" "Usage: fixbundle exename [-m] [-o bundlename] [-v version] [-V long_version]\n" "\t\t[-e] [[-d datafile] [[palette] [-{16,32,48,128}] icon] ...]\n" "\twhere icon is either a datafile bitmap or a RLE sprite object, either\n" "\tan image file.\n" "Options:\n" "\t-m\t\tMoves executable inside bundle instead of copying it\n" "\t-o bundlename\tSpecifies a bundle name (default: exename.app)\n" "\t-v version\tSets application version string (default: 1.0)\n" "\t-V long_version\tSets long application version string\n" "\t-e\t\tEmbeds the Allegro framework into the application bundle\n" "\t-d datafile\tUses datafile as source for objects and palettes\n" "\t-{16,32,48,128}\tForces next icon image into the 16x16, 32x32, 48x48 or\n" "\t\t\t128x128 icon resource slot\n" "\n"); exit(EXIT_FAILURE); } static int copy_file(AL_CONST char *filename, AL_CONST char *dest_path) { char *buffer = NULL; char dest_file[1024]; PACKFILE *f; int size; if (!exists(filename)) return -1; buffer = malloc(size = file_size(filename)); if (!buffer) return -1; append_filename(dest_file, dest_path, get_filename(filename), 1024); f = pack_fopen(filename, F_READ); if (!f) { free(buffer); return -1; } pack_fread(buffer, size, f); pack_fclose(f); f = pack_fopen(dest_file, F_WRITE); if (!f) { free(buffer); return -1; } pack_fwrite(buffer, size, f); pack_fclose(f); free(buffer); return 0; } /* main: * Guess what this function does. */ int main(int argc, char *argv[]) { PACKFILE *f; CFURLRef cf_url_ref; FSRef fs_ref; FSSpec fs_spec; IconFamilyHandle icon_family; Handle raw_data; char datafile[MAX_STRING_SIZE]; char bundle[MAX_STRING_SIZE]; char bundle_dir[MAX_STRING_SIZE]; char bundle_contents_dir[MAX_STRING_SIZE]; char bundle_contents_resources_dir[MAX_STRING_SIZE]; char bundle_contents_macos_dir[MAX_STRING_SIZE]; char bundle_contents_frameworks_dir[MAX_STRING_SIZE]; char *bundle_exe = NULL; char bundle_plist[MAX_STRING_SIZE]; char bundle_pkginfo[MAX_STRING_SIZE]; char bundle_icns[MAX_STRING_SIZE]; char bundle_version[MAX_STRING_SIZE]; char bundle_long_version[MAX_STRING_SIZE]; char *buffer = NULL; int arg, type = 0, result = 0; int i, size, x, y, mask_bit, mask_byte; unsigned char *data; install_allegro(SYSTEM_NONE, &errno, &atexit); set_color_depth(32); set_color_conversion(COLORCONV_TOTAL | COLORCONV_KEEP_TRANS); _rgb_a_shift_32 = 24; _rgb_r_shift_32 = 16; _rgb_g_shift_32 = 8; _rgb_b_shift_32 = 0; if (argc < 2) usage(); datafile[0] = '\0'; bundle[0] = '\0'; select_palette(black_palette); /* Parse command line and load any given resource */ for (arg = 2; arg < argc; arg++) { if (!strcmp(argv[arg], "-m")) flags |= F_MOVE; else if (!strcmp(argv[arg], "-e")) flags |= F_EMBED_FRAMEWORK; else if (!strcmp(argv[arg], "-o")) { if ((argc < arg + 2) || (bundle[0] != '\0')) usage(); strcpy(bundle, argv[++arg]); } else if (!strcmp(argv[arg], "-v")) { if (argc < arg + 2) usage(); flags |= F_GOT_VERSION; strcpy(bundle_version, argv[++arg]); } else if (!strcmp(argv[arg], "-V")) { if (argc < arg + 2) usage(); flags |= F_GOT_LONG_VERSION; strcpy(bundle_long_version, argv[++arg]); } else if (!strcmp(argv[arg], "-d")) { if (argc < arg + 2) usage(); strcpy(datafile, argv[++arg]); } else if ((!strcmp(argv[arg], "-16")) || (!strcmp(argv[arg], "-32")) || (!strcmp(argv[arg], "-48")) || (!strcmp(argv[arg], "-128"))) { if (argc < arg + 2) usage(); switch (atoi(&argv[arg][1])) { case 16: type = 0; break; case 32: type = 1; break; case 48: type = 2; break; case 128: type = 3; break; } if (load_resource(datafile, argv[++arg], &icon_data[type])) { result = -1; goto exit_error; } } else { if (load_resource(datafile, argv[arg], NULL)) { result = -1; goto exit_error; } } } buffer = malloc(4096); if (!buffer) { result = -1; goto exit_error_bundle; } bundle_exe = argv[1]; if (!exists(bundle_exe)) { fprintf(stderr, "Cannot locate executable file '%s'\n", bundle_exe); result = -1; goto exit_error; } if (bundle[0] == '\0') strcpy(bundle, bundle_exe); replace_extension(bundle_dir, bundle, "app", MAX_STRING_SIZE); strcpy(bundle_contents_dir, bundle_dir); strcat(bundle_contents_dir, "/Contents"); strcpy(bundle_contents_resources_dir, bundle_contents_dir); strcat(bundle_contents_resources_dir, "/Resources"); strcpy(bundle_contents_macos_dir, bundle_contents_dir); strcat(bundle_contents_macos_dir, "/MacOS"); strcpy(bundle_contents_frameworks_dir, bundle_contents_dir); strcat(bundle_contents_frameworks_dir, "/Frameworks"); bundle_icns[0] = '\0'; bundle_plist[0] = '\0'; bundle_pkginfo[0] = '\0'; /* Create bundle structure */ if ((mkdir(bundle_dir, 0777) && (errno != EEXIST)) || (mkdir(bundle_contents_dir, 0777) && (errno != EEXIST)) || (mkdir(bundle_contents_resources_dir, 0777) && (errno != EEXIST)) || (mkdir(bundle_contents_macos_dir, 0777) && (errno != EEXIST))) { fprintf(stderr, "Cannot create %s\n", bundle_dir); result = -1; goto exit_error_bundle; } /* Copy/move executable into the bundle */ if (copy_file(bundle_exe, bundle_contents_macos_dir)) { fprintf(stderr, "Cannot create %s\n", bundle_contents_macos_dir); result = -1; goto exit_error_bundle; } strcat(bundle_contents_macos_dir, "/"); strcat(bundle_contents_macos_dir, get_filename(bundle_exe)); chmod(bundle_contents_macos_dir, 0755); if (flags & F_MOVE) unlink(bundle_exe); /* Embed Allegro framework if requested */ if (flags & F_EMBED_FRAMEWORK) { if (!file_exists("/Library/Frameworks/Allegro.framework", FA_RDONLY | FA_DIREC, NULL)) { fprintf(stderr, "Cannot find Allegro framework\n"); result = -1; goto exit_error_bundle; } if (!exists("/Library/Frameworks/Allegro.framework/Resources/Embeddable")) { fprintf(stderr, "Cannot embed system wide Allegro framework; install embeddable version first!\n"); result = -1; goto exit_error_bundle; } sprintf(buffer, "/Developer/Tools/pbxcp -exclude .DS_Store -exclude CVS -resolve-src-symlinks /Library/Frameworks/Allegro.framework %s", bundle_contents_frameworks_dir); if ((mkdir(bundle_contents_frameworks_dir, 0777) && (errno != EEXIST)) || (system(buffer))) { fprintf(stderr, "Cannot create %s\n", bundle_contents_frameworks_dir); result = -1; goto exit_error_bundle; } } /* Setup the .icns resource */ if (flags & F_ICONS_DEFINED) { strcat(bundle_contents_resources_dir, "/"); strcat(bundle_contents_resources_dir, get_filename(bundle)); replace_extension(bundle_icns, bundle_contents_resources_dir, "icns", MAX_STRING_SIZE); icon_family = (IconFamilyHandle)NewHandle(0); for (i = 0; i < 4; i++) { if (flags & icon_data[i].defined) { /* Set 32bit RGBA data */ PtrToHand(icon_data[i].scaled->line[0], &raw_data, icon_data[i].size * icon_data[i].size * 4); if (SetIconFamilyData(icon_family, icon_data[i].data, raw_data) != noErr) { DisposeHandle(raw_data); fprintf(stderr, "Error setting %dx%d icon resource RGBA data\n", icon_data[i].size, icon_data[i].size); result = -1; goto exit_error_bundle; } DisposeHandle(raw_data); /* Set 8bit mask */ raw_data = NewHandle(icon_data[i].size * icon_data[i].size); data = *(unsigned char **)raw_data; for (y = 0; y < icon_data[i].size; y++) { for (x = 0; x < icon_data[i].size; x++) { *data++ = geta32(((unsigned int *)(icon_data[i].scaled->line[y]))[x]); } } if (SetIconFamilyData(icon_family, icon_data[i].mask8, raw_data) != noErr) { DisposeHandle(raw_data); fprintf(stderr, "Error setting %dx%d icon resource 8bit mask\n", icon_data[i].size, icon_data[i].size); result = -1; goto exit_error_bundle; } DisposeHandle(raw_data); /* Set 1bit mask */ if (icon_data[i].mask1) { size = ((icon_data[i].size * icon_data[i].size) + 7) / 8; raw_data = NewHandle(size * 2); data = *(unsigned char **)raw_data; mask_byte = 0; mask_bit = 7; for (y = 0; y < icon_data[i].size; y++) { for (x = 0; x < icon_data[i].size; x++) { if (geta32(((unsigned int *)(icon_data[i].scaled->line[y]))[x]) >= 0xfd) mask_byte |= (1 << mask_bit); mask_bit--; if (mask_bit < 0) { *data++ = mask_byte; mask_byte = 0; mask_bit = 7; } } } memcpy(*raw_data + size, *raw_data, size); if (SetIconFamilyData(icon_family, icon_data[i].mask1, raw_data) != noErr) { DisposeHandle(raw_data); fprintf(stderr, "Error setting %dx%d icon resource 1bit mask\n", icon_data[i].size, icon_data[i].size); result = -1; goto exit_error_bundle; } DisposeHandle(raw_data); } } } f = pack_fopen(bundle_icns, F_WRITE); if (!f) { fprintf(stderr, "Cannot create %s\n", bundle_icns); result = -1; goto exit_error_bundle; } pack_fclose(f); cf_url_ref = CFURLCreateWithBytes(kCFAllocatorDefault, (unsigned char *)bundle_icns, strlen(bundle_icns), 0, NULL); if (!cf_url_ref) { fprintf(stderr, "Cannot create %s\n", bundle_icns); result = -1; goto exit_error_bundle; } CFURLGetFSRef(cf_url_ref, &fs_ref); CFRelease(cf_url_ref); if ((FSGetCatalogInfo(&fs_ref, kFSCatInfoNone, NULL, NULL, &fs_spec, NULL)) || (WriteIconFile(icon_family, &fs_spec) != noErr)) { fprintf(stderr, "Cannot create %s\n", bundle_icns); result = -1; goto exit_error_bundle; } DisposeHandle((Handle)icon_family); } /* Setup Info.plist */ sprintf(bundle_plist, "%s/Info.plist", bundle_contents_dir); f = pack_fopen(bundle_plist, F_WRITE); if (!f) { fprintf(stderr, "Cannot create %s\n", bundle_plist); result = -1; goto exit_error_bundle; } sprintf(buffer, "\n" "\n" "\n" "\n" "\tCFBundleExecutable\n" "\t%s\n" "\tCFBundleInfoDictionaryVersion\n" "\t6.0\n" "\tCFBundlePackageType\n" "\tAPPL\n" "\tCFBundleSignature\n" "\t%s\n" "\tCFBundleVersion\n" "\t%s\n" "\tCFBundleDocumentTypes\n" "\t\n" "\t\t\n" "\t\t\tCFBundleTypeExtensions\n" "\t\t\t\n" "\t\t\t\t*\n" "\t\t\t\n" "\t\t\tCFBundleTypeName\n" "\t\t\tNSStringPboardType\n" "\t\t\tCFBundleTypeOSTypes\n" "\t\t\t\n" "\t\t\t\t****\n" "\t\t\t\n" "\t\t\tCFBundleTypeRole\n" "\t\t\tViewer\n" "\t\t\n" "\t\n", get_filename(bundle_exe), "????", (flags & F_GOT_VERSION) ? bundle_version : "1.0"); pack_fputs(buffer, f); if (flags & F_GOT_LONG_VERSION) { sprintf(buffer, "\tCFBundleGetInfoString\n" "\t%s\n", bundle_long_version); pack_fputs(buffer, f); } if (flags & F_ICONS_DEFINED) { sprintf(buffer, "\tCFBundleIconFile\n" "\t%s\n", get_filename(bundle_icns)); pack_fputs(buffer, f); } pack_fputs("\n\n", f); pack_fclose(f); /* Setup PkgInfo */ sprintf(bundle_pkginfo, "%s/PkgInfo", bundle_contents_dir); f = pack_fopen(bundle_pkginfo, F_WRITE); if (!f) { fprintf(stderr, "Cannot create %s\n", bundle_pkginfo); result = -1; goto exit_error_bundle; } pack_fputs("APPL????", f); pack_fclose(f); exit_error: if (buffer) free(buffer); for (i = 0; i < 4; i++) { if (icon_data[i].original) destroy_bitmap(icon_data[i].original); if (icon_data[i].workspace) destroy_bitmap(icon_data[i].workspace); if (icon_data[i].scaled) destroy_bitmap(icon_data[i].scaled); } return result; exit_error_bundle: sprintf(buffer, "%s/%s", bundle_contents_macos_dir, get_filename(bundle_exe)); unlink(buffer); unlink(bundle_plist); unlink(bundle_pkginfo); unlink(bundle_icns); rmdir(bundle_dir); rmdir(bundle_contents_dir); rmdir(bundle_contents_resources_dir); rmdir(bundle_contents_macos_dir); goto exit_error; } END_OF_MAIN()