Пример #1
0
void TiledMap::saveTo(PACKFILE *file)
{
	ASSERT(file);

	// Version info
	// Version 1: No version number stored, one layer in map
	// Version 2: First int is version number, second one the number of layers
	// Version 3: Object list stored at end of tile data.
	pack_iputw(3, file);
	// The map header
	pack_iputw(nrLayers, file);

	// The tile data
	for (int i = 0; i < nrLayers; i++) {
		mapLayers[i]->saveTo(file);
	}

	// Object data
	list<Object*>::iterator i;
	pack_iputw(objects.size(), file);
	for (i = objects.begin(); i != objects.end(); i++) {
		pack_iputw(int(TILES_W * (*i)->x), file);
		pack_iputw(int(TILES_H * (*i)->y), file);
		pack_fputs((*i)->className, file);
		pack_fputs("\n", file);
	}	

	// Extra newline fixes last tile not loaded.
	pack_fputs("\n", file);
}
Пример #2
0
void Tile::saveTo(PACKFILE *file)
{
	// Write tile name to file
	if (tileType) {
		pack_fputs(tileType->getName(), file);
	}
	pack_fputs("\n", file);

	pack_iputw(obstacle, file);
}
Пример #3
0
/* exports a sample into an external file */
static int export_sample(AL_CONST DATAFILE *dat, AL_CONST char *filename)
{
   SAMPLE *spl = (SAMPLE *)dat->dat;
   int bps = spl->bits/8 * ((spl->stereo) ? 2 : 1);
   int len = spl->len * bps;
   int i;
   int16_t s;
   PACKFILE *f;

   errno = 0;
   
   f = pack_fopen(filename, F_WRITE);

   if (f) {
      pack_fputs("RIFF", f);                 /* RIFF header */
      pack_iputl(36+len, f);                 /* size of RIFF chunk */
      pack_fputs("WAVE", f);                 /* WAV definition */
      pack_fputs("fmt ", f);                 /* format chunk */
      pack_iputl(16, f);                     /* size of format chunk */
      pack_iputw(1, f);                      /* PCM data */
      pack_iputw((spl->stereo) ? 2 : 1, f);  /* mono/stereo data */
      pack_iputl(spl->freq, f);              /* sample frequency */
      pack_iputl(spl->freq*bps, f);          /* avg. bytes per sec */
      pack_iputw(bps, f);                    /* block alignment */
      pack_iputw(spl->bits, f);              /* bits per sample */
      pack_fputs("data", f);                 /* data chunk */
      pack_iputl(len, f);                    /* actual data length */

      if (spl->bits == 8) {
	 pack_fwrite(spl->data, len, f);     /* write the data */
      }
      else {
	 for (i=0; i < (int)spl->len * ((spl->stereo) ? 2 : 1); i++) {
	    s = ((int16_t *)spl->data)[i];
	    pack_iputw(s^0x8000, f);
	 }
      }

      pack_fclose(f);
   }

   return (errno == 0);
}
Пример #4
0
int eof_undo_add(int type)
{
	char fn[1024] = {0}, temp[1024] = {0};
	unsigned long ctr;

 	eof_log("eof_undo_add() entered", 1);

	if(eof_undo_states_initialized == -1)
	{	//The undo filename array couldn't be initialized previously
		return 0;
	}

	if(eof_validate_temp_folder())
	{	//Ensure the correct working directory and presence of the temporary folder
		eof_log("\tCould not validate working directory and temp folder", 1);
		return 0;
	}

	if(!eof_undo_states_initialized)
	{	//Initialize the undo filename array
		for(ctr = 0; ctr < EOF_MAX_UNDO; ctr++)
		{	//For each undo slot
			(void) snprintf(fn, sizeof(fn) - 1, "%seof%03u-%03lu.undo", eof_temp_path_s, eof_log_id, ctr);	//Build the undo filename in the format of "eof#-#.undo", where the first number is the EOF ID
			eof_undo_filename[ctr] = malloc(sizeof(fn) + 1);
			if(eof_undo_filename[ctr] == NULL)
			{
				allegro_message("Error initializing undo system.  Undo disabled");
				eof_undo_states_initialized = -1;
				return 0;
			}
			strcpy(eof_undo_filename[ctr], fn);	//Save the undo filename to the array
		}
		eof_undo_states_initialized = 1;
	}

	if((type == EOF_UNDO_TYPE_NOTE_LENGTH) && (eof_undo_last_type == EOF_UNDO_TYPE_NOTE_LENGTH))
	{
		return 0;
	}
	if((type == EOF_UNDO_TYPE_LYRIC_NOTE) && (eof_undo_last_type == EOF_UNDO_TYPE_LYRIC_NOTE))
	{
		return 0;
	}
	if((type == EOF_UNDO_TYPE_RECORD) && (eof_undo_last_type == EOF_UNDO_TYPE_RECORD))
	{
		return 0;
	}
	if((type == EOF_UNDO_TYPE_TEMPO_ADJUST) && (eof_undo_last_type == EOF_UNDO_TYPE_TEMPO_ADJUST))
	{
		return 0;
	}
	if(type == EOF_UNDO_TYPE_SILENCE)
	{
		(void) snprintf(fn, sizeof(fn) - 1, "%s%s.ogg", eof_temp_path_s, eof_undo_filename[eof_undo_current_index]);
		(void) eof_copy_file(eof_loaded_ogg_name, fn);
	}
	eof_undo_last_type = type;

	(void) eof_save_song(eof_song, eof_undo_filename[eof_undo_current_index]);
	eof_undo_type[eof_undo_current_index] = type;
	if(eof_recovery)
	{	//If this EOF instance is maintaining auto-recovery files
		PACKFILE *fp;
		(void) snprintf(fn, sizeof(fn) - 1, "%seof.recover", eof_temp_path_s);
		fp = pack_fopen(fn, "w");	//Open the recovery definition file for writing
		if(fp)
		{	//If the file opened
			(void) append_filename(temp, eof_song_path, eof_loaded_song_name, 1024);	//Construct the full path to the project file
			(void) pack_fputs(eof_undo_filename[eof_undo_current_index], fp);			//Write the undo file path
			(void) pack_fputs("\n", fp);												//Write a newline character
			(void) pack_fputs(temp, fp);												//Write the project path
			(void) pack_fclose(fp);
		}
	}
	eof_undo_current_index++;
	if(eof_undo_current_index >= EOF_MAX_UNDO)
	{
		eof_undo_current_index = 0;
	}
	eof_undo_count++;
	if(eof_undo_count >= EOF_MAX_UNDO)
	{
		eof_undo_count = EOF_MAX_UNDO;
	}
	eof_redo_count = 0;
	return 1;
}
Пример #5
0
int eof_export_bandfuse(EOF_SONG * sp, char * fn, unsigned short *user_warned)
{
	PACKFILE * fp;
	char buffer[600] = {0}, buffer2[600] = {0};
	EOF_PRO_GUITAR_TRACK *tp;
	char restore_tech_view = 0;			//If tech view is in effect, it is temporarily disabled so that the correct notes are exported
	unsigned long ctr, ctr2, ctr3, ctr4, ctr5, gemcount, bitmask, guitartracknum = 0, basstracknum = 0;

	eof_log("eof_export_bandfuse() entered", 1);

	if(!sp || !fn || !sp->beats || !user_warned)
	{
		eof_log("\tError saving:  Invalid parameters", 1);
		return 0;	//Return failure
	}
	fp = pack_fopen(fn, "w");
	if(!fp)
	{
		eof_log("\tError saving:  Cannot open file for writing", 1);
		return 0;	//Return failure
	}

	//Write the song metadata
	(void) pack_fputs("<?xml version='1.0' encoding='UTF-8'?>\n", fp);
	(void) pack_fputs("<Bandfuse>\n", fp);
	(void) pack_fputs("<!-- " EOF_VERSION_STRING " -->\n", fp);	//Write EOF's version in an XML comment
	expand_xml_text(buffer2, sizeof(buffer2) - 1, sp->tags->title, 64, 0);	//Expand XML special characters into escaped sequences if necessary, and check against the maximum supported length of this field
	(void) snprintf(buffer, sizeof(buffer) - 1, "  <title>%s</title>\n", buffer2);
	(void) pack_fputs(buffer, fp);
	expand_xml_text(buffer2, sizeof(buffer2) - 1, sp->tags->artist, 256, 0);	//Replace any special characters in the artist song property with escape sequences if necessary
	(void) snprintf(buffer, sizeof(buffer) - 1, "  <artistName>%s</artistName>\n", buffer2);
	(void) pack_fputs(buffer, fp);
	expand_xml_text(buffer2, sizeof(buffer2) - 1, sp->tags->album, 256, 0);	//Replace any special characters in the album song property with escape sequences if necessary
	(void) snprintf(buffer, sizeof(buffer) - 1, "  <albumName>%s</albumName>\n", buffer2);
	(void) pack_fputs(buffer, fp);
	expand_xml_text(buffer2, sizeof(buffer2) - 1, sp->tags->year, 32, 0);	//Replace any special characters in the year song property with escape sequences if necessary
	(void) snprintf(buffer, sizeof(buffer) - 1, "  <albumYear>%s</albumYear>\n", buffer2);
	(void) pack_fputs(buffer, fp);

	//Write tempo changes
	(void) pack_fputs("  <tempochanges>\n", fp);
	ctr = 0;	//Begin with the first beat
	while(ctr < sp->beats)
	{	//Until the last beat has been reached
		for(ctr2 = ctr + 1; ctr2 < sp->beats; ctr2++)
		{	//For each beat that follows
			if(sp->beat[ctr]->ppqn != sp->beat[ctr2]->ppqn)
			{	//If the following beat has a different tempo
				break;	//Break from inner for loop
			}
		}
		if(ctr2 < sp->beats)
		{	//If a beat with a different tempo was found, that beat is written as the end position of this tempo change
			(void) snprintf(buffer, sizeof(buffer) - 1, "    <tempo start=\"%lu\" end=\"%lu\" tempo=\"%f\">\n", sp->beat[ctr]->pos, sp->beat[ctr2]->pos, 60000000.0 / (double)sp->beat[ctr]->ppqn);
		}
		else
		{	//No remaining beats had a different tempo, the outer for loop's beat is written as the end position of this tempo change
			(void) snprintf(buffer, sizeof(buffer) - 1, "    <tempo start=\"%lu\" end=\"%lu\" tempo=\"%f\">\n", sp->beat[ctr]->pos, sp->beat[ctr]->pos, 60000000.0 / (double)sp->beat[ctr]->ppqn);
		}
		(void) pack_fputs(buffer, fp);
		ctr = ctr2;	//Advance the beat counter
	}
	(void) pack_fputs("  </tempochanges>\n", fp);

	for(ctr = 1; ctr < sp->tracks; ctr++)
	{	//For each track
		if(ctr >= EOF_TRACKS_MAX)
			break;		//Redundant bounds check to satisfy a false positive with Coverity
		if(sp->track[ctr]->track_format != EOF_PRO_GUITAR_TRACK_FORMAT)
			continue;	//If this isn't a pro guitar/bass track, skip it
		if(ctr == EOF_TRACK_PRO_GUITAR_B)
			continue;	//If this is the bonus Rocksmith arrangement, skip it

		tp = sp->pro_guitar_track[sp->track[ctr]->tracknum];
		restore_tech_view = eof_menu_track_get_tech_view_state(sp, ctr);
		eof_menu_track_set_tech_view_state(sp, ctr, 0);	//Disable tech view if applicable

		if(tp->notes)
		{	//If this track is populated
			eof_determine_phrase_status(sp, ctr);	//Update the trill and tremolo status of each note
			if(tp->arrangement != 4)
			{	//If this isn't a bass track
				guitartracknum++;	//Keep count of how many of this track type there have been
				(void) snprintf(buffer, sizeof(buffer) - 1, "  <arrangement name=\"Guitar %lu (%s)\">\n", guitartracknum, sp->track[ctr]->name);
				(void) pack_fputs(buffer, fp);
			}
			else
			{	//This is a bass track
				basstracknum++;	//Keep count of how many of this track type there have been
				(void) snprintf(buffer, sizeof(buffer) - 1, "  <arrangement name=\"Bass %lu (%s)\">\n", guitartracknum, sp->track[ctr]->name);
				(void) pack_fputs(buffer, fp);
			}
			(void) snprintf(buffer, sizeof(buffer) - 1, "    <tuning string0=\"%d\" string1=\"%d\" string2=\"%d\" string3=\"%d\" string4=\"%d\" string5=\"%d\" />\n", tp->tuning[0], tp->tuning[1], tp->tuning[2], tp->tuning[3], tp->tuning[4], tp->tuning[5]);
			(void) pack_fputs(buffer, fp);

			(void) eof_detect_difficulties(sp, ctr);	//Update eof_track_diff_populated_status[] to reflect all populated difficulties for this track
			for(ctr2 = 0; ctr2 < 6; ctr2++)
			{	//For each of the first 6 difficulties
				unsigned long anchorcount;
				char anchorsgenerated = 0;	//Is set to nonzero if fret hand positions are automatically generated for this difficulty and will have to be deleted

				if(!eof_track_diff_populated_status[ctr2])
					continue;	//If this difficulty is not populated, skip it

				(void) snprintf(buffer, sizeof(buffer) - 1, "    <level difficulty=\"%lu\">\n", ctr2);
				(void) pack_fputs(buffer, fp);

				//Count the number of note gems in this track difficulty
				for(ctr3 = 0, gemcount = 0; ctr3 < tp->notes; ctr3++)
				{	//For each note in the track
					if(eof_get_note_type(sp, ctr, ctr3) == ctr2)
					{	//If the note is in this difficulty
						gemcount += eof_note_count_rs_lanes(sp, ctr, ctr3, 2);	//Add this note's number of non-ghosted gems to a counter
					}
				}
				(void) snprintf(buffer, sizeof(buffer) - 1, "      <notes count=\"%lu\">\n", gemcount);
				(void) pack_fputs(buffer, fp);

				//Generate fret hand positions if there are none for this difficulty
				for(ctr3 = 0, anchorcount = 0; ctr3 < tp->handpositions; ctr3++)
				{	//For each hand position defined in the track
					if(tp->handposition[ctr3].difficulty == ctr2)
					{	//If the hand position is in this difficulty
						anchorcount++;
					}
				}
				if(!anchorcount)
				{	//If there are no anchors in this track difficulty, automatically generate them
					if((tp->arrangement != 4) || eof_warn_missing_bass_fhps)
					{	//Don't warn about missing FHPs in bass arrangements if user disabled that preference
						if((*user_warned & 1) == 0)
						{	//If the user wasn't alerted that one or more track difficulties have no fret hand positions defined
							allegro_message("Warning:  At least one track difficulty has no fret hand positions defined.  They will be created automatically.");
							*user_warned |= 1;
						}
					}
					eof_fret_hand_position_list_dialog_undo_made = 1;	//Ensure no undo state is written during export
					eof_generate_efficient_hand_positions(sp, ctr, ctr2, 0, 0);	//Generate the fret hand positions for the track difficulty being currently written
					anchorsgenerated = 1;
				}
				for(ctr3 = 0, anchorcount = 0; ctr3 < tp->handpositions; ctr3++)	//Re-count the hand positions
				{	//For each hand position defined in the track
					if(tp->handposition[ctr3].difficulty == ctr)
					{	//If the hand position is in this difficulty
						anchorcount++;
					}
				}

				//Write the notes to XML
				for(ctr3 = 0; ctr3 < tp->notes; ctr3++)
				{	//For each note in the track
					if(eof_get_note_type(sp, ctr, ctr3) != ctr2)
						continue;	//If the note is not in this difficulty, skip it

					for(ctr4 = 0, bitmask = 1; ctr4 < tp->numstrings; ctr4++, bitmask <<= 1)
					{	//For each string in this track
						EOF_RS_TECHNIQUES tech = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
						unsigned long notepos;
						unsigned long fret, finger;				//The fret and finger numbers used to play the gem on this string
						unsigned long trill_start, trill_stop;	//The start and stop positions of the note's trill phrase
						int trillwith = -1;						//If the note is in a trill phrase, this is set to the fret value of the next or previous gem on the string
						long next;
						unsigned long stringnum;				//The converted string number (ie. low E string is string 6)
						char *sustainpadding;

						if(!(tp->note[ctr3]->note & bitmask) || (tp->note[ctr3]->ghost & bitmask))
							continue;	//If this string does not have a gem, or if it has one that is ghosted, skip it

						trill_start = trill_stop = tp->note[ctr3]->pos;	//Initialize variables in order to track if the trill phrase can't be properly found
						if(tp->note[ctr3]->flags & EOF_NOTE_FLAG_IS_TRILL)
						{	//If this note is in a trill phrase
							//Find which trill phrase it's in
							for(ctr5 = 0; ctr5 < tp->trills; ctr5++)
							{	//For each trill phrase
								if((tp->trill[ctr5].difficulty == 0xFF) || (tp->trill[ctr5].difficulty == ctr2))
								{	//If this trill phrase applies to all difficulties or if it otherwise applies to this note's difficulty
									if((tp->trill[ctr5].start_pos <= tp->note[ctr3]->pos) && (tp->trill[ctr5].end_pos >= tp->note[ctr3]->pos))
									{	//If the note is inside this trill phrase
										trill_start = tp->trill[ctr5].start_pos;
										trill_stop = tp->trill[ctr5].end_pos;
										break;
									}
								}
							}
							//Determine if this is the first note in the trill phrase, and if so, what note it trills with
							if(trill_start != trill_stop)
							{	//If a trill phrase was identified
								for(ctr5 = 0; ctr5 < tp->notes; ctr5++)
								{	//For each note in the track
									if(eof_get_note_type(sp, ctr, ctr3) != ctr2)
										continue;	//If the note is not in this difficulty, skip it
									if(tp->note[ctr5]->pos > trill_stop)
										break;		//Stop looking at notes if the trill has already been passed
									if(tp->note[ctr5]->pos < trill_start)
										continue;	//If the note is not within the trill phrase

									if(ctr5 != ctr3)
									{	//If the note being written to XML is not this note
										break;	//Don't export it with trill technique
									}
									next = eof_fixup_next_pro_guitar_note(tp, ctr5);
									if((next > 0) && (tp->note[next]->pos <= trill_stop))
									{	//If the next note in the track difficulty is also in this trill phrase
										if((tp->note[next]->note & bitmask) && ((tp->note[next]->frets[next] & 0x7F) > (tp->note[ctr3]->frets[ctr4] & 0x7F)))
										{	//If the next note has a gem on the target string and its fret value is higher than that of the note being currently written to XML
											trillwith = tp->note[next]->frets[ctr4] & 0x7F;	//This is the fret value being trilled with
										}
									}
									break;
								}//For each note in the track
							}//If a trill phrase was identified
						}//If this note is in a trill phrase

						(void) eof_get_rs_techniques(sp, ctr, ctr3, ctr4, &tech, 2, 1);	//Determine techniques used by this note (honoring technotes where applicable)
						notepos = eof_get_note_pos(sp, ctr, ctr3);
						fret = tp->note[ctr3]->frets[ctr4] & 0x7F;	//Get the fret value for this string (mask out the muting bit)
						finger = eof_pro_guitar_note_lookup_string_fingering(tp, ctr3, ctr4, 1);	//Unless a more suitable fingering for this note can be determined, assume 1 (index)
						if(tech.length < 10)
						{	//The sustain is one digit
							sustainpadding = "   ";
						}
						else if(tech.length < 100)
						{	//The sustain is two digits
							sustainpadding = "  ";
						}
						else if(tech.length < 1000)
						{	//The sustain is three digits
							sustainpadding = " ";
						}
						else
						{	//It's four or more digits
							sustainpadding = "";
						}
						fret += tp->capo;							//Compensate for the the capo position if applicable
						stringnum = tp->numstrings - ctr4;			//Convert to tab string numbering (low E is string 6 in a 6 string track, string 4 on a 4 string track, etc)
						(void) snprintf(buffer, sizeof(buffer) - 1, "        <note time=\"%lu\" linkNext=\"%d\" bend=\"%lu\" fret=%s\"%lu\" hammerOn=\"%d\" harmonic=\"%d\" hopo=\"%d\" ignore=\"0\" mute=\"%d\" palmMute=\"%d\" pluck=\"%d\" pullOff=\"%d\" slap=\"%d\" slideTo=\"%ld\" string=\"%lu\" sustain=%s\"%lu\" tremolo=\"%d\" harmonicPinch=\"%d\" slideUnpitchTo=\"%ld\" trillwith=\"%d\" finger=\"%lu\" vibrato=\"%d\"/>\n", notepos, tech.linknext, tech.bendstrength_h, ((fret < 10) ? " " : ""), fret, tech.hammeron, tech.harmonic, tech.hopo, tech.stringmute, tech.palmmute, tech.pop, tech.pulloff, tech.slap, tech.slideto, stringnum, sustainpadding, tech.length, tech.tremolo, tech.pinchharmonic, tech.unpitchedslideto, trillwith, finger, tech.vibrato);
						(void) pack_fputs(buffer, fp);
					}//For each string in this track
				}//For each note in the track

				//Remove any automatically generated fret hand positions
				if(anchorsgenerated)
				{	//If anchors were automatically generated for this track difficulty, remove them now
					for(ctr3 = tp->handpositions; ctr3 > 0; ctr3--)
					{	//For each hand position defined in the track, in reverse order
						if(tp->handposition[ctr3 - 1].difficulty == ctr2)
						{	//If the hand position is in this difficulty
							eof_pro_guitar_track_delete_hand_position(tp, ctr3 - 1);	//Delete the hand position
						}
					}
				}
				(void) pack_fputs("      </notes>\n", fp);
				(void) pack_fputs("    </level>\n", fp);
			}//For each of the first 6 difficulties
			(void) pack_fputs("  </arrangement>\n", fp);
		}//If this track is populated

		eof_menu_track_set_tech_view_state(sp, ctr, restore_tech_view);	//Re-enable tech view if applicable
	}//For each track
	(void) pack_fputs("</Bandfuse>\n", fp);

	//Cleanup
	(void) pack_fclose(fp);

	return 1;	//Return success
}
Пример #6
0
/* saves a wave file to file pointer */
static int save_wav_fp(SAMPLE * sp, PACKFILE * fp)
{
	size_t channels, bits, freq;
	size_t data_size;
	size_t samples;
	size_t i, n;
	void * pval = NULL;
	unsigned short *data;

	eof_log("save_wav_fp() entered", 1);

	if(!sp || !fp)
	{
		return 0;
	}
	channels = sp->stereo ? (size_t)2 : (size_t)1;
	bits = (size_t)sp->bits;

	samples = (size_t)sp->len;
	data_size = samples * channels * (bits / 8);
	n = samples * channels;

	freq = (size_t)sp->freq;

	(void) pack_fputs("RIFF", fp);
	(void) pack_iputl(36 + (long)data_size, fp);
	(void) pack_fputs("WAVE", fp);

	(void) pack_fputs("fmt ", fp);
	(void) pack_iputl(16, fp);
	(void) pack_iputw(1, fp);
	(void) pack_iputw((int)channels, fp);
	(void) pack_iputl((long)freq, fp);
	(void) pack_iputl((long)(freq * channels * (bits / 8)), fp);	//ByteRate = SampleRate * NumChannels * BitsPerSample/8
	(void) pack_iputw((int)(channels * (bits / 8)), fp);
	(void) pack_iputw((int)bits, fp);

	(void) pack_fputs("data", fp);
	(void) pack_iputl((long)data_size, fp);

	if(bits == 8)
	{
		pval = sp->data;
		(void) pack_fwrite(pval, (long)(samples * channels), fp);
	}
	else if(bits == 16)
	{
		data = (unsigned short *)sp->data;
		for (i = 0; i < n; i++)
		{
			(void) pack_iputw((short)(data[i] - 0x8000), fp);
		}
	}
	else
	{
		(void) snprintf(eof_log_string, sizeof(eof_log_string) - 1, "Unknown audio depth (%lu) when saving wav ALLEGRO_FILE.", (unsigned long) bits);
		eof_log(eof_log_string, 1);
		return 0;
	}

	return 1;
}
Пример #7
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);
   
   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 */
	        raw_data = NewHandle(icon_data[i].size * icon_data[i].size * 4);
	    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]);
	          *data++ = getr32(((unsigned int *)(icon_data[i].scaled->line[y]))[x]);
	          *data++ = getg32(((unsigned int *)(icon_data[i].scaled->line[y]))[x]);
	          *data++ = getb32(((unsigned int *)(icon_data[i].scaled->line[y]))[x]);
	       }
	    }
	    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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
      "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
      "<plist version=\"1.0\">\n"
      "<dict>\n"
      "\t<key>CFBundleExecutable</key>\n"
      "\t<string>%s</string>\n"
      "\t<key>CFBundleInfoDictionaryVersion</key>\n"
      "\t<string>6.0</string>\n"
      "\t<key>CFBundlePackageType</key>\n"
      "\t<string>APPL</string>\n"
      "\t<key>CFBundleSignature</key>\n"
      "\t<string>%s</string>\n"
      "\t<key>CFBundleVersion</key>\n"
      "\t<string>%s</string>\n"
      "\t<key>CFBundleDocumentTypes</key>\n"
      "\t<array>\n"
      "\t\t<dict>\n"
      "\t\t\t<key>CFBundleTypeExtensions</key>\n"
      "\t\t\t<array>\n"
      "\t\t\t\t<string>*</string>\n"
      "\t\t\t</array>\n"
      "\t\t\t<key>CFBundleTypeName</key>\n"
      "\t\t\t<string>NSStringPboardType</string>\n"
      "\t\t\t<key>CFBundleTypeOSTypes</key>\n"
      "\t\t\t<array>\n"
      "\t\t\t\t<string>****</string>\n"
      "\t\t\t</array>\n"
      "\t\t\t<key>CFBundleTypeRole</key>\n"
      "\t\t\t<string>Viewer</string>\n"
      "\t\t</dict>\n"
      "\t</array>\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, "\t<key>CFBundleGetInfoString</key>\n"
         "\t<string>%s</string>\n", bundle_long_version);
      pack_fputs(buffer, f);
   }
   if (flags & F_ICONS_DEFINED) {
      sprintf(buffer, "\t<key>CFBundleIconFile</key>\n"
         "\t<string>%s</string>\n", get_filename(bundle_icns));
      pack_fputs(buffer, f);
   }
   pack_fputs("</dict>\n</plist>\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;
}