#include "xymms.h" #include "interface.h" #include "support.h" #include "callbacks.h" #include "ym2612.h" #include "ym2151.h" #include "sn76496.h" /* * Settings from DGen * int ym2612_base = 7520000; * int sn76496_base = 3478000; * * Settings from MSP * int ym2612_base = 7670442; * int sn76496_base = 3579580; */ /* Global vars */ int ym2612_clock = 100; int ym2612_base = 7670442; int sn76496_clock = 100; int sn76496_base = 3579580; int ext_samples_per_sec = 44100; int ext_bits_per_sample = 16; int ext_channels = 2; int samples_menu[] = { 48000, 44100, 32000, 22050, 0 }; int channels_menu[] = { 2, 1, 0 }; char *name = NULL; //char name[255]; unsigned char *ym_data = NULL; GYMTAG gymtag; int has_gymtag = 0; GtkWidget *fileinfo_win = NULL; GtkWidget *configure_win = NULL; GtkWidget *compress_label = NULL; GtkWidget *compress_button = NULL; /* Globals for this file */ static int audio_error = 0; static int is_playing = 0; static int is_eof = 0; static int seek_to = 0; static int want_stop = 0; static int song_length = -1; static int samples_per_sec = 44100; static int bits_per_sample = 16; static int channels = 2; static int bitrate = 0; static int compressed_filesize = 0; static int uncompressed_filesize = 0; static int file_type = 0; static pthread_t play_thread; /* * Plugin structure */ InputPlugin xmms_gym_ip = { NULL, /* void *handle */ NULL, /* char *filename */ "Sega Genesis GYM Plugin " VERSION, /* char *description */ init, /* void (*init) (void) */ about, /* void (*about) (void) */ configure, /* void (*configure) (void) */ is_our_file, /* int (*is_our_file) (char *filename) */ NULL, /* GList *(*scan_dir) (char *dirname) */ play_file, /* void (*play_file) (char *filename) */ stop, /* void (*stop) (void) */ pause_play, /* void (*pause) (short paused) */ seek, /* void (*seek) (int time) */ NULL, /* void (*set_eq) (int on, float preamp, float *bands) */ get_time, /* int (*get_time) (void) */ NULL, /* void (*get_volume) (int *l, int *r) */ NULL, /* void (*set_volume) (int l, int r) */ NULL, /* void (*cleanup) (void) */ NULL, /* InputVisType (*get_vis_type) (void) */ NULL, /* void (*add_vis_pcm) (int time, AFormat fmt, int nch, int length, void *ptr) */ NULL, /* void (*set_info) (char *title, int length, int rate, int freq, int nch) */ NULL, /* void (*set_info_text) (char *text) */ get_song_info, /* void (*get_song_info) (char *filename, char **title, int *length) */ file_info_box, /* void (*file_info_box) (char *filename) */ NULL /* OutputPlugin *output */ }; /* * Plugin initialization */ static void init(void) { ConfigFile *cfg = NULL; char *filename = NULL; filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL); if((cfg = xmms_cfg_open_file(filename)) != 0) { xmms_cfg_read_int(cfg, "XymMS", "samples_per_sec", &ext_samples_per_sec); xmms_cfg_read_int(cfg, "XymMS", "channels", &ext_channels); xmms_cfg_read_int(cfg, "XymMS", "bits_per_sample", &ext_bits_per_sample); xmms_cfg_read_int(cfg, "XymMS", "ym2612_clock", &ym2612_clock); xmms_cfg_read_int(cfg, "XymMS", "sn76496_clock", &sn76496_clock); xmms_cfg_free(cfg); } } /* * About dialog */ void about(void) { /* Need to add a better About box :) */ xmms_show_message("About XymMS " VERSION, "XymMS is an XMMS input plugin capable of playing Sega Genesis GYM files by\n" "rendering FM, DAC, and PSG signals through emulation of the YM2612 and SN76496\n" "sound chips found in the video game console. (CYM files will be supported in\n" "the beta release.) GYM files are created by various emulators such as DGen and\n" "Megasis. XymMS supports ZLib compression and decompression, and other various\n" "settings for output quality, etc. You can compress and decompress files along with\n" "updating ID tags using the File Info window.\n\n" "The emulation backend is courtesy the M.A.M.E. project. XymMS is based on code and\n" "ideas from the YMAMP and MSP Open Source projects.", "Ok", FALSE, NULL, NULL); } /* * Configuration dialog */ static void configure(void) { GtkWidget *widget = NULL; GtkWidget *label = NULL; GtkWidget *item = NULL; GtkAdjustment *adj = NULL; char string[16]; int i = 0; if(! configure_win) { configure_win = create_configure_win(); while(samples_menu[i] != ext_samples_per_sec) ++i ; widget = lookup_widget(GTK_WIDGET(configure_win), "sample_menu"); if(widget) { gtk_option_menu_set_history(GTK_OPTION_MENU(widget), i); widget = NULL; } i = 0; while(channels_menu[i] != ext_channels) ++i ; widget = lookup_widget(GTK_WIDGET(configure_win), "channel_menu"); if(widget) { gtk_option_menu_set_history(GTK_OPTION_MENU(widget), i); widget = NULL; } i = 0; widget = lookup_widget(GTK_WIDGET(configure_win), "ym2612_scale"); if(widget) { adj = gtk_range_get_adjustment(GTK_RANGE(widget)); if(adj) { adj->value = ym2612_clock; label = lookup_widget(GTK_WIDGET(configure_win), "ym2612_label"); if(label) { memset(string, 0, 16); sprintf(string, "%d", (int)(ym2612_clock * ym2612_base / 100)); gtk_label_set_text(GTK_LABEL(label), string); label = NULL; } adj = NULL; } widget = NULL; } widget = lookup_widget(GTK_WIDGET(configure_win), "sn76496_scale"); if(widget) { adj = gtk_range_get_adjustment(GTK_RANGE(widget)); if(adj) { adj->value = sn76496_clock; label = lookup_widget(GTK_WIDGET(configure_win), "sn76496_label"); if(label) { memset(string, 0, 16); sprintf(string, "%d", (int)(sn76496_clock * sn76496_base / 100)); gtk_label_set_text(GTK_LABEL(label), string); label = NULL; } adj = NULL; } widget = NULL; } gtk_widget_show(configure_win); } else { while(samples_menu[i] != ext_samples_per_sec) ++i ; widget = lookup_widget(GTK_WIDGET(configure_win), "sample_menu"); if(widget) { gtk_option_menu_set_history(GTK_OPTION_MENU(widget), i); widget = NULL; } i = 0; while(channels_menu[i] != ext_channels) ++i ; widget = lookup_widget(GTK_WIDGET(configure_win), "channel_menu"); if(widget) { gtk_option_menu_set_history(GTK_OPTION_MENU(widget), i); widget = NULL; } i = 0; widget = lookup_widget(GTK_WIDGET(configure_win), "ym2612_scale"); if(widget) { adj = gtk_range_get_adjustment(GTK_RANGE(widget)); if(adj) { adj->value = ym2612_clock; label = lookup_widget(GTK_WIDGET(configure_win), "ym2612_label"); if(label) { memset(string, 0, 16); sprintf(string, "%d", (int)(ym2612_clock * ym2612_base / 100)); gtk_label_set_text(GTK_LABEL(label), string); label = NULL; } adj = NULL; } widget = NULL; } widget = lookup_widget(GTK_WIDGET(configure_win), "sn76496_scale"); if(widget) { adj = gtk_range_get_adjustment(GTK_RANGE(widget)); if(adj) { adj->value = sn76496_clock; label = lookup_widget(GTK_WIDGET(configure_win), "sn76496_label"); if(label) { memset(string, 0, 16); sprintf(string, "%d", (int)(sn76496_clock * sn76496_base / 100)); gtk_label_set_text(GTK_LABEL(label), string); label = NULL; } adj = NULL; } widget = NULL; } gtk_widget_show(configure_win); gdk_window_raise(configure_win->window); } } /* * Check to see if we can handle the file. I don't check for magic here just yet, we want * to be able to play files without a header. We just add them later if it's not there. */ static int is_our_file(char *filename) { unsigned char *ext = NULL; ext = strrchr(filename, '.'); if(ext) if(! strcasecmp(ext, ".gym")) return TRUE ; return FALSE; } /* * Start the decoding thread */ static void *play_loop(void *arg) { int bitrate; if(is_playing && !is_eof && !want_stop) { play_gym_file(); } else xmms_usleep(10000) ; is_playing = 0; is_eof = 1; want_stop = 1; pthread_exit(NULL); } /* * No comment :) */ static void play_file(char *filename) { FILE *file = NULL; int tmp_filesize, len; char *string = NULL; unsigned char *tmp_buf = NULL; char buf[4]; has_gymtag = 0; memset(&gymtag, 0, sizeof(GYMTAG)); file = fopen(filename, "r"); if(! file) return ; fseek(file, 0, SEEK_END); tmp_filesize = ftell(file); rewind(file); fread(buf, 4, 1, file); rewind(file); if(read_gymtag(filename, &gymtag)) { has_gymtag = 1; tmp_filesize -= sizeof(GYMTAG); fread(&gymtag, sizeof(GYMTAG), 1, file); } tmp_buf = (unsigned char*)malloc(tmp_filesize); if(!tmp_buf) goto PLAY_FILE_ERROR ; fread(tmp_buf, tmp_filesize, 1, file); if(gymtag.compressed) { int zerr; uncompressed_filesize = gymtag.compressed; compressed_filesize = tmp_filesize; ym_data = (unsigned char*)malloc(uncompressed_filesize); if(!ym_data) goto PLAY_FILE_ERROR ; zerr = uncompress(ym_data, (unsigned long*)&uncompressed_filesize, tmp_buf, compressed_filesize); if(zerr != Z_OK) goto PLAY_FILE_ERROR ; gymtag.compressed = uncompressed_filesize; if(tmp_buf) { free(tmp_buf); tmp_buf = NULL; } } else { uncompressed_filesize = tmp_filesize; compressed_filesize = 0; if(ym_data) free(ym_data) ; ym_data = tmp_buf; } fclose(file); is_eof = 0; is_playing = 1; want_stop = 0; seek_to = -1; samples_per_sec = ext_samples_per_sec; bits_per_sample = ext_bits_per_sample; channels = ext_channels; xmms_gym_ip.output->open_audio((bits_per_sample == 16) ? FMT_S16_LE : FMT_U8, samples_per_sec, channels); if(has_gymtag && (*gymtag.song_title && *gymtag.game_title)) { len = strlen(gymtag.game_title) + strlen(" - ") + strlen(gymtag.song_title) + 1; string = (char*)malloc(len); memset(string, 0, len); snprintf(string, len, "%s - %s", gymtag.game_title, gymtag.song_title); } else { string = (char*)malloc(strlen(filename) + 1); string = strdup(strrchr(filename, '/') + 1); *strrchr(string, '.') = '\0'; string = realloc(string, strlen(string) + 1); } bitrate = samples_per_sec * channels * ((bits_per_sample / 8)); xmms_gym_ip.set_info(string, calc_gym_time_length(ym_data, uncompressed_filesize), 8 * bitrate, samples_per_sec, channels); if(string) free(string) ; pthread_create(&play_thread, NULL, play_loop, NULL); /* pthread_create(&play_thread, NULL, play_loop, &((int)file_type)); */ return; PLAY_FILE_ERROR: fclose(file); if(tmp_buf) free(tmp_buf); if(ym_data) free(ym_data); tmp_buf = NULL; ym_data = NULL; } static void stop(void) { is_playing = 0; want_stop = 1; is_eof = 1; pthread_join(play_thread, NULL); xmms_gym_ip.output->close_audio(); } static void pause_play(short paused) { xmms_gym_ip.output->pause(paused); } static void seek(int time) { seek_to = (time * 1000); } static int get_time(void) { if(audio_error) return(-2) ; if(is_playing) return xmms_gym_ip.output->output_time() ; else return(-1) ; } static void get_song_info(char *filename, char **title, int *length) { GYMTAG temp_gymtag; char *temp_string = NULL; int len = 0; if(read_gymtag(filename, &temp_gymtag) && (*temp_gymtag.song_title && *temp_gymtag.game_title)) { len = strlen(temp_gymtag.game_title) + strlen(" - ") + strlen(temp_gymtag.song_title) + 1; temp_string = (char*)malloc(len); memset(temp_string, 0, len); snprintf(temp_string, len, "%s - %s", temp_gymtag.game_title, temp_gymtag.song_title); } else { temp_string = (char*)malloc(strlen(filename) + 1); temp_string = strdup(strrchr(filename, '/') + 1); *strrchr(temp_string, '.') = '\0'; } song_length = -1; (*length) = song_length; /* * Memory leak? */ (*title) = (char*)realloc(temp_string, strlen(temp_string) + 1); /* (*title) = temp_string; */ /* * if(temp_string) * free(temp_string); */ } static void file_info_box(char *filename) { FILE *file = NULL; GtkWidget *widget = NULL; GYMTAG info_gymtag; unsigned char *title = NULL; int position = 0; int len; /* Probably want to change to just gray out fields? */ file = fopen(filename, "r+"); if(! file) { xmms_show_message("Error!", "Please make sure the file is writeable so you can edit the ID tag!", "Ok", FALSE, NULL, NULL); return ; } fclose(file); file = NULL; len = strlen(filename) + 1; name = (char*)realloc(name, len); memset(name, 0, len); strncpy(name, filename, len); fileinfo_win = create_fileinfo_win(); len = strlen("File information for - ") + strlen(strrchr(filename, '/')) + 1; title = (char*)malloc(len); memset(title, 0, len); memset(&info_gymtag, 0, sizeof(GYMTAG)); has_gymtag = read_gymtag(filename, &info_gymtag); if(! fileinfo_win) { snprintf(title, len - 1, "File information for - %s", (char*)(strrchr(filename, '/') + 1)); gtk_window_set_title(GTK_WINDOW(fileinfo_win), title); widget = lookup_widget(fileinfo_win, "songtitle_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.song_title); widget = lookup_widget(fileinfo_win, "gametitle_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.game_title); widget = lookup_widget(fileinfo_win, "gamepublisher_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.game_publisher); widget = lookup_widget(fileinfo_win, "rippedwith_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.ripped_with); widget = lookup_widget(fileinfo_win, "rippedby_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.ripped_by); widget = lookup_widget(fileinfo_win, "comments_textbox"); gtk_editable_delete_text(GTK_EDITABLE(widget), 0, -1); position = 0; gtk_editable_insert_text(GTK_EDITABLE(widget), info_gymtag.comments, strlen(info_gymtag.comments), &position); widget = lookup_widget(fileinfo_win, "compress_checkbox"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), (info_gymtag.compressed ? 1 : 0)); gtk_label_set_text(GTK_LABEL(compress_label), (info_gymtag.compressed ? "Decompress" : "Compress" )); gtk_widget_show(fileinfo_win); } else { snprintf(title, len - 1, "File information for - %s", (char*)(strrchr(filename, '/') + 1)); gtk_window_set_title(GTK_WINDOW(fileinfo_win), title); widget = lookup_widget(fileinfo_win, "songtitle_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.song_title); widget = lookup_widget(fileinfo_win, "gametitle_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.game_title); widget = lookup_widget(fileinfo_win, "gamepublisher_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.game_publisher); widget = lookup_widget(fileinfo_win, "rippedwith_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.ripped_with); widget = lookup_widget(fileinfo_win, "rippedby_entry"); gtk_entry_set_text(GTK_ENTRY(widget), info_gymtag.ripped_by); widget = lookup_widget(fileinfo_win, "comments_textbox"); gtk_editable_delete_text(GTK_EDITABLE(widget), 0, -1); position = 0; gtk_editable_insert_text(GTK_EDITABLE(widget), info_gymtag.comments, strlen(info_gymtag.comments), &position); widget = lookup_widget(fileinfo_win, "compress_checkbox"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), (info_gymtag.compressed ? 1 : 0)); gtk_label_set_text(GTK_LABEL(compress_label), (info_gymtag.compressed ? "Decompress" : "Compress" )); gtk_widget_show(fileinfo_win); gdk_window_raise(fileinfo_win->window); } if(title) free(title) ; } /* * Most of this was courtesy YMAMP/MSP. */ static int play_gym_file(void) { int loop = 0, loop2 = 0, update = 0, adjtime = 0, time_pos = 0, srate = 0, srate152 = 0, srate15 = 0, srate30 = 0, numsamples = 0; short *samp_buf = NULL, *ym_buf[2] = { NULL, NULL }, *snbuf = NULL; unsigned char *dac_data = NULL; unsigned long data_pos = 0; int dac_max = 0; srate = samples_per_sec; srate152 = (srate / 15) << 1; srate15 = srate / 15; srate30 = srate / 30; dac_data = (unsigned char*)malloc(srate30); if(!dac_data) { free(ym_data); want_stop = 1; return 0; } ym_buf[0] = (short *)malloc(srate15); if(!ym_buf[0]) { free(ym_data); free(dac_data); want_stop = 1; return 0; } ym_buf[1] = (short *)malloc(srate15); if(!ym_buf[1]) { free(ym_data); free(ym_buf[0]); want_stop = 1; return 0; } snbuf = (short *)malloc(srate15); if(!snbuf) { free(ym_data); free(ym_buf[0]); free(ym_buf[1]); want_stop = 1; return 0; } samp_buf = (short *)malloc(srate152 << 1); if(!samp_buf) { free(ym_data); free(ym_buf[0]); free(ym_buf[1]); free(snbuf); want_stop = 1; return 0; } do { loop = YM2612Init(1, ym2612_clock * ym2612_base / 100, srate, NULL, NULL); if(loop == DUP_INIT) YM2612Shutdown() ; else if(loop) { YM2612Shutdown(); free(ym_data); free(ym_buf[0]); free(ym_buf[1]); free(snbuf); free(samp_buf); return 0; } } while(loop) ; if(SN76496_init(0, sn76496_clock * sn76496_base / 100, srate)) { YM2612Shutdown(); free(ym_data); free(ym_buf[0]); free(ym_buf[1]); free(snbuf); free(samp_buf); return 0; } while(!want_stop && (data_pos < uncompressed_filesize)) { if(seek_to != -1) { time_pos = seek_to - (seek_to % 60); data_pos = get_gym_data_pos(time_pos); xmms_gym_ip.output->flush(seek_to); adjtime = 0; dac_max = 0; seek_to = -1; continue; } switch(ym_data[data_pos]) { case(0x00): data_pos++; if(update) { update = 0; if(dac_max) { short *dac_buf[2]; double update_cycle = (double)srate30 / dac_max; /* Dac port write cycle */ double stream_cnt = 0; for(loop = 0; loop < dac_max; loop++) { int old_cnt = (int)stream_cnt; int step; /* Update DAC port */ YM2612Write(0, 0, 0x2a); YM2612Write(0, 1, dac_data[loop]); if(loop == dac_max - 1) step = srate30 - old_cnt ; else { stream_cnt += update_cycle; step = (int)stream_cnt - old_cnt; } /* Update stream */ dac_buf[0] = &ym_buf[0][old_cnt]; dac_buf[1] = &ym_buf[1][old_cnt]; YM2612UpdateOne(0, (void **)dac_buf, step); } dac_max = 0; } else YM2612UpdateOne(0, (void **)ym_buf, srate30) ; SN76496Update_16(0, snbuf, srate30); if(channels == 2) { loop2 = 0; for(loop = 0; loop < srate30; loop++) { samp_buf[loop2] = mix(ym_buf[0][loop], snbuf[loop] >> 1); loop2++; samp_buf[loop2] = mix(ym_buf[1][loop], snbuf[loop] >> 1); loop2++; } } else for(loop = 0; loop < srate30; loop++) samp_buf[loop] = mix((ym_buf[0][loop] + ym_buf[1][loop]) >> 1, snbuf[loop] >> 1) ; xmms_gym_ip.add_vis_pcm(xmms_gym_ip.output->written_time(), (bits_per_sample == 16) ? FMT_S16_LE : FMT_U8, channels, srate, samp_buf); numsamples = srate30; if(channels == 1) numsamples >>= 1; if(adjtime == 2) { time_pos += 34; adjtime = 0; } else { time_pos += 33; adjtime++; } if(numsamples > srate30) { while(xmms_gym_ip.output->buffer_free() < srate152 && !want_stop) xmms_usleep(20) ; xmms_gym_ip.output->write_audio(samp_buf, srate152); numsamples -= srate30; while(xmms_gym_ip.output->buffer_free() < numsamples << 2 && !want_stop) xmms_usleep(20) ; xmms_gym_ip.output->write_audio(&samp_buf[srate15], numsamples << 2); } else { while(xmms_gym_ip.output->buffer_free() < numsamples << 2 && !want_stop) xmms_usleep(20) ; xmms_gym_ip.output->write_audio(samp_buf, numsamples << 2); } } else update = 1 ; break; case(0x01): data_pos++; if(ym_data[data_pos] == 0x2a) { data_pos++; if(dac_max < srate30) dac_data[dac_max++] = ym_data[data_pos] ; data_pos++; } else { YM2612Write(0, 0, ym_data[data_pos++]); YM2612Write(0, 1, ym_data[data_pos++]); } break; case(0x02): data_pos++; YM2612Write(0, 2, ym_data[data_pos++]); YM2612Write(0, 3, ym_data[data_pos++]); break; case(0x03): data_pos++; SN76496Write(0, ym_data[data_pos++]); break; default: /* Saves us from bad data */ data_pos++; break; } } YM2612Shutdown(); free(ym_data); free(ym_buf[0]); free(ym_buf[1]); free(snbuf); free(samp_buf); ym_buf[0] = ym_buf[1] = snbuf = NULL; ym_data = NULL; if(data_pos >= uncompressed_filesize) while(xmms_gym_ip.output->buffer_playing() && !want_stop) xmms_usleep(20) ; time_pos = 0; want_stop = 1; return 0; } InputPlugin *get_iplugin_info(void) { return(&xmms_gym_ip); } /* * Utility functions... */ static int convert_to_header(unsigned char *buf) { return((buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]); } /* * Pass a NULL tag and we will only check for the "GYMX" magic. */ int read_gymtag(char *filename, GYMTAG *tag) { int count = 0, has_tag = 0; unsigned char buf[4]; FILE *file = NULL; file = fopen(filename, "r"); if(! file) return(0); fread(buf, 4, 1, file); if(convert_to_header(buf) == (('G' << 24) + ('Y' << 16) + ('M' << 8) + 'X')) has_tag = 1; if(tag && has_tag) { rewind(file); memset(tag, 0, sizeof(GYMTAG)); count = fread(tag, sizeof(GYMTAG), 1, file); } fclose(file); return(has_tag); } int write_gymtag(char *filename, GYMTAG *tag) { FILE *file = NULL; int count = 0; file = fopen(filename, "r+"); if(file && tag) { rewind(file); count = fwrite(tag, sizeof(GYMTAG), 1, file); fclose(file); if(count) return(0); } if(file) fclose(file); return(1); } int write_gymdata(char *filename, unsigned char *data, unsigned int len) { FILE *file = NULL; int count = 0; file = fopen(filename, "r+"); if(! file) return(1); fseek(file, sizeof(GYMTAG), SEEK_SET); count = fwrite(data, len, 1, file); if(! count) return(1); if(ftruncate(fileno(file), (len + sizeof(GYMTAG)))) return(1); fclose(file); return(0); } static int get_gym_data_pos(int time_position) { unsigned loop, num_zeros = 0, num_zeros_target = 0; num_zeros_target = (int)((time_position / 1000.0) * 60.0); for(loop = 0; loop < uncompressed_filesize; loop++) { if(num_zeros == num_zeros_target) break; switch(ym_data[loop]) { case(0x00): num_zeros++; continue; case(0x01): loop += 2; continue; case(0x02): loop += 2; continue; case(0x03): loop += 1; continue; } } return loop; } static int calc_gym_time_length(unsigned char *data, int size) { int loop, num_zeros = 0; for(loop = 0; loop < size; loop++) { switch(data[loop]) { case(0x00): num_zeros++; continue; case(0x01): loop += 2; continue; case(0x02): loop += 2; continue; case(0x03): loop += 1; continue; } } return(int)((num_zeros / 60.0) * 1000.0); } static short mix(int a, int b) { int c = (a + b) * 2; if(c > 32767) c = 32767; else if( c < -32768) c = -32768; return (short)c; } int get_filesize(char *filename) { FILE *file = NULL; unsigned int tmp_filesize; file = fopen(filename, "r"); if(file) { fseek(file, 0, SEEK_END); tmp_filesize = ftell(file); fclose(file); return(tmp_filesize); } fclose(file); return(0); } /* * Handles writing a compressed or decompressed GYM file. */ int write_gym(char *filename, GYMTAG *tag) { unsigned int compressed_filesize = 0; unsigned int uncompressed_filesize = 0; unsigned int tmp_filesize = 0; unsigned char *src_buf = NULL; unsigned char *dest_buf = NULL; FILE *file = NULL; /* Check to see if we are writable I suppose. I didn't want to stat(), this was easier. */ file = fopen(filename, "r+"); if(! file) goto WRITE_GYM_ERROR; tmp_filesize = get_filesize(filename); if(! tmp_filesize) goto WRITE_GYM_ERROR; if(! read_gymtag(filename, tag)) goto WRITE_GYM_ERROR; tmp_filesize -= sizeof(GYMTAG); if(! tag->compressed) { int zerr; int count = 0; uncompressed_filesize = tmp_filesize; compressed_filesize = tmp_filesize; src_buf = (unsigned char*)malloc(uncompressed_filesize); if(! src_buf) goto WRITE_GYM_ERROR; fseek(file, sizeof(GYMTAG), SEEK_SET); count = fread(src_buf, uncompressed_filesize, 1, file); if(! count) goto WRITE_GYM_ERROR; fclose(file); file = NULL; dest_buf = (unsigned char*)malloc(uncompressed_filesize + (int)(uncompressed_filesize / 10) + 12); if(! dest_buf) goto WRITE_GYM_ERROR; zerr = compress2(dest_buf, (unsigned long*)&compressed_filesize, src_buf, uncompressed_filesize, 9); if(zerr != Z_OK) { goto WRITE_GYM_ERROR; } tag->compressed = uncompressed_filesize; dest_buf = (unsigned char*)realloc(dest_buf, compressed_filesize); if(! write_gymtag(filename, tag)) if(write_gymdata(filename, dest_buf, compressed_filesize)) goto WRITE_GYM_ERROR; if(src_buf) free(src_buf) ; if(dest_buf) free(dest_buf) ; src_buf = dest_buf = NULL; } else { int zerr; int count = 0; compressed_filesize = tmp_filesize; uncompressed_filesize = tag->compressed; src_buf = (unsigned char*)malloc(compressed_filesize); if(! src_buf) goto WRITE_GYM_ERROR; fseek(file, sizeof(GYMTAG), SEEK_SET); count = fread(src_buf, compressed_filesize, 1, file); if(! count) goto WRITE_GYM_ERROR; fclose(file); file = NULL; dest_buf = (unsigned char*)malloc(uncompressed_filesize + (int)(uncompressed_filesize / 10) + 12); if(! dest_buf) goto WRITE_GYM_ERROR; zerr = uncompress(dest_buf, (unsigned long*)&uncompressed_filesize, src_buf, compressed_filesize); if(zerr != Z_OK) { goto WRITE_GYM_ERROR; } tag->compressed = 0; dest_buf = (unsigned char*)realloc(dest_buf, uncompressed_filesize); if(! write_gymtag(filename, tag)) if(write_gymdata(filename, dest_buf, uncompressed_filesize)) goto WRITE_GYM_ERROR; if(src_buf) free(src_buf) ; if(dest_buf) free(dest_buf) ; src_buf = dest_buf = NULL; } return(0); WRITE_GYM_ERROR: if(src_buf) free(src_buf) ; if(dest_buf) free(dest_buf) ; src_buf = dest_buf = NULL; if(file) fclose(file) ; return(1); }