3 * originally written by Exophase as "bin_to_iso_ogg"
\r
4 * updated for cso/mp3 by notaz
\r
10 #include <sys/stat.h>
\r
15 #define MAX_PATH 1024
\r
19 #include <windows.h>
\r
21 #define DIR_SEPARATOR_CHAR '\\'
\r
22 #define PATH_SEPARATOR_CHAR ';'
\r
23 #define LAME_BINARY "lame.exe"
\r
24 #define CISO_BINARY "ciso.exe"
\r
25 #define NULL_REDIR "> NUL 2>&1"
\r
27 #define DIR_SEPARATOR_CHAR '/'
\r
28 #define PATH_SEPARATOR_CHAR ':'
\r
29 #define LAME_BINARY "lame"
\r
30 #define CISO_BINARY "ciso"
\r
31 #define NULL_REDIR "> /dev/null 2>&1"
\r
32 #define mkdir(x) mkdir(x, S_IRWXU)
\r
35 #define LAME_OPTIONS "-h --cbr"
\r
37 typedef unsigned char u8;
\r
38 typedef unsigned short int u16;
\r
39 typedef unsigned int u32;
\r
40 typedef unsigned long long int u64;
\r
41 typedef signed char s8;
\r
42 typedef signed short int s16;
\r
43 typedef signed int s32;
\r
44 typedef signed long long int s64;
\r
48 TRACK_FILE_TYPE_BINARY,
\r
49 TRACK_FILE_TYPE_WAVE,
\r
50 } track_file_type_enum;
\r
55 u32 physical_offset;
\r
67 track_file_type_enum type;
\r
71 } cd_track_file_struct;
\r
76 cd_track_file_struct track_files[100];
\r
81 u32 num_physical_tracks;
\r
83 s32 last_seek_track;
\r
85 cd_track_struct physical_tracks[100];
\r
86 cd_track_struct *logical_tracks[100];
\r
90 cd_bin_struct cd_bin;
\r
91 int opt_use_mp3 = 1;
\r
92 int opt_mp3_bitrate = 128;
\r
93 int opt_use_cso = 1;
\r
95 static void myexit(int code)
\r
103 char *skip_whitespace(char *str)
\r
105 while (isspace(*str))
\r
111 char *skip_whitespace_rev(char *str)
\r
113 while (isspace(*str))
\r
119 char *skip_nonspace_rev(char *str)
\r
121 while (!isspace(*str))
\r
127 s32 load_bin_cue(char *cue_file_name)
\r
129 FILE *cue_file = fopen(cue_file_name, "rb");
\r
131 printf("loading cue file %s\n", cue_file_name);
\r
135 char line_buffer[256];
\r
136 char *line_buffer_ptr;
\r
139 char bin_file_name[MAX_PATH];
\r
140 char *separator_pos;
\r
141 s32 current_physical_track_number = -1;
\r
142 u32 current_physical_offset;
\r
143 u32 current_pregap = 0;
\r
146 cd_track_struct *current_physical_track = NULL;
\r
150 // First, get filename. Only support binary right now.
\r
151 tmp = fgets(line_buffer, 255, cue_file);
\r
152 if (tmp == NULL) goto invalid;
\r
153 separator_pos = line_buffer + strlen(line_buffer) - 1;
\r
154 separator_pos = skip_whitespace_rev(separator_pos);
\r
155 if (separator_pos <= line_buffer) goto invalid;
\r
156 separator_pos = skip_nonspace_rev(separator_pos);
\r
157 if (separator_pos <= line_buffer) goto invalid;
\r
158 separator_pos = skip_whitespace_rev(separator_pos);
\r
159 if (separator_pos <= line_buffer) goto invalid;
\r
160 // see if what's there is a quote.
\r
161 if(*separator_pos == '"')
\r
163 separator_pos[0] = 0;
\r
164 separator_pos = strrchr(line_buffer, '"');
\r
165 if (separator_pos == NULL) goto invalid;
\r
166 strcpy(bin_file_name, separator_pos + 1);
\r
170 // Otherwise go to the next space.
\r
171 separator_pos[1] = 0;
\r
172 separator_pos = strrchr(line_buffer, ' ');
\r
173 if (separator_pos == NULL) goto invalid;
\r
174 strcpy(bin_file_name, separator_pos + 1);
\r
177 // Might have to change directory first.
\r
178 separator_pos = strrchr(cue_file_name, DIR_SEPARATOR_CHAR);
\r
182 char current_dir[MAX_PATH];
\r
183 getcwd(current_dir, MAX_PATH);
\r
185 *separator_pos = 0;
\r
187 chdir(cue_file_name);
\r
190 cd_bin.bin_file = open(bin_file_name, O_RDONLY);
\r
192 cd_bin.bin_file = fopen(bin_file_name, "rb");
\r
195 *separator_pos = DIR_SEPARATOR_CHAR;
\r
196 chdir(current_dir);
\r
201 cd_bin.bin_file = open(bin_file_name, O_RDONLY);
\r
203 cd_bin.bin_file = fopen(bin_file_name, "rb");
\r
207 if (cd_bin.bin_file == NULL)
\r
209 printf("can't open bin file: \"%s\"\n", bin_file_name);
\r
214 printf("found bin file: %s\n", bin_file_name);
\r
217 for(i = 0; i < 100; i++)
\r
219 cd_bin.logical_tracks[i] = NULL;
\r
222 cd_bin.first_track = -1;
\r
223 cd_bin.last_track = -1;
\r
224 cd_bin.num_physical_tracks = 0;
\r
225 cd_bin.num_sectors = 0;
\r
228 while(fgets(line_buffer, 256, cue_file))
\r
230 // Skip trailing whitespace
\r
231 line_buffer_ptr = skip_whitespace(line_buffer);
\r
233 // Dirty, but should work - switch on first character.
\r
234 switch(line_buffer_ptr[0])
\r
236 // New track number
\r
239 u32 new_track_number;
\r
240 char track_type[64];
\r
242 sscanf(line_buffer_ptr, "TRACK %d %s", &new_track_number,
\r
245 current_physical_track_number++;
\r
246 current_physical_track =
\r
247 cd_bin.physical_tracks + current_physical_track_number;
\r
249 current_physical_track->sector_size = 2352;
\r
251 if(!strcmp(track_type, "AUDIO"))
\r
253 current_physical_track->format_type = 0;
\r
254 current_physical_track->sector_size = 2352;
\r
257 if(!strcmp(track_type, "MODE1/2352"))
\r
259 current_physical_track->format_type = 4;
\r
260 current_physical_track->sector_size = 2352;
\r
263 if(!strcmp(track_type, "MODE1/2048"))
\r
265 current_physical_track->format_type = 4;
\r
266 current_physical_track->sector_size = 2048;
\r
269 cd_bin.logical_tracks[new_track_number] = current_physical_track;
\r
270 cd_bin.num_physical_tracks++;
\r
272 if((cd_bin.first_track == -1) ||
\r
273 (new_track_number < cd_bin.first_track))
\r
275 cd_bin.first_track = new_track_number;
\r
278 if((cd_bin.last_track == -1) ||
\r
279 (new_track_number > cd_bin.last_track))
\r
281 cd_bin.last_track = new_track_number;
\r
290 u32 minutes, seconds, frames;
\r
292 sscanf(line_buffer_ptr, "PREGAP %d:%d:%d", &minutes,
\r
293 &seconds, &frames);
\r
295 current_pregap += frames + (seconds * 75) + (minutes * 75 * 60);
\r
303 u32 minutes, seconds, frames;
\r
306 sscanf(line_buffer_ptr, "INDEX %d %d:%d:%d", &index_number,
\r
307 &minutes, &seconds, &frames);
\r
309 sector_offset = frames + (seconds * 75) + (minutes * 75 * 60);
\r
311 if(index_number == 1)
\r
313 current_physical_track->pregap_offset = current_pregap;
\r
314 current_physical_track->sector_offset = sector_offset;
\r
322 current_physical_offset = 0;
\r
324 for(i = 0; i < cd_bin.num_physical_tracks - 1; i++)
\r
326 cd_bin.physical_tracks[i].sector_count =
\r
327 cd_bin.physical_tracks[i + 1].sector_offset -
\r
328 cd_bin.physical_tracks[i].sector_offset;
\r
330 cd_bin.physical_tracks[i].physical_offset = current_physical_offset;
\r
331 current_physical_offset += (cd_bin.physical_tracks[i].sector_count *
\r
332 cd_bin.physical_tracks[i].sector_size);
\r
334 cd_bin.physical_tracks[i].sector_offset +=
\r
335 cd_bin.physical_tracks[i].pregap_offset;
\r
337 cd_bin.num_sectors += cd_bin.physical_tracks[i].sector_count;
\r
341 bin_file_size = lseek(cd_bin.bin_file, 0, SEEK_END);
\r
342 lseek(cd_bin.bin_file, 0, SEEK_SET);
\r
344 fseek(cd_bin.bin_file, 0, SEEK_END);
\r
345 bin_file_size = ftell(cd_bin.bin_file);
\r
346 fseek(cd_bin.bin_file, 0, SEEK_SET);
\r
349 // Set the last track data
\r
350 cd_bin.physical_tracks[i].physical_offset = current_physical_offset;
\r
351 cd_bin.physical_tracks[i].sector_offset +=
\r
352 cd_bin.physical_tracks[i].pregap_offset;
\r
353 cd_bin.physical_tracks[i].sector_count =
\r
354 (bin_file_size - current_physical_offset) /
\r
355 cd_bin.physical_tracks[i].sector_size;
\r
357 cd_bin.num_sectors += cd_bin.physical_tracks[i].sector_count;
\r
359 printf("finished loading cue %s\n", cue_file_name);
\r
360 printf("bin file: %s (%p)\n", bin_file_name, cd_bin.bin_file);
\r
361 printf("first track: %d, last track: %d\n", cd_bin.first_track,
\r
362 cd_bin.last_track);
\r
364 for(i = cd_bin.first_track; i <= cd_bin.last_track; i++)
\r
366 printf("track %d (%p):\n", i, cd_bin.logical_tracks[i]);
\r
367 if(cd_bin.logical_tracks[i] == NULL)
\r
369 printf(" (invalid)\n");
\r
373 printf(" physical offset 0x%x\n",
\r
374 cd_bin.logical_tracks[i]->physical_offset);
\r
375 printf(" sector offset 0x%x\n",
\r
376 cd_bin.logical_tracks[i]->sector_offset);
\r
377 printf(" sector size %d\n",
\r
378 cd_bin.logical_tracks[i]->sector_size);
\r
382 cd_bin.last_seek_track = 0;
\r
390 printf("error: invalid/unsupported .cue file\n");
\r
394 #define address8(base, offset) \
\r
395 *((u8 *)((u8 *)base + (offset))) \
\r
397 #define address16(base, offset) \
\r
398 *((u16 *)((u8 *)base + (offset))) \
\r
400 #define address32(base, offset) \
\r
401 *((u32 *)((u8 *)base + (offset))) \
\r
403 // This will only work on little endian platforms for now.
\r
405 s32 convert_bin_to_wav(FILE *bin_file, char *output_dir, char *wav_file_name,
\r
410 u8 *riff_header = wav_header + 0;
\r
411 u8 *fmt_header = wav_header + 0x0C;
\r
412 u8 sector_buffer[2352];
\r
413 u32 byte_length = sector_count * 2352;
\r
417 wav_file = fopen(wav_file_name, "wb");
\r
419 printf("writing wav %s, %x sectors\n", wav_file_name, sector_count);
\r
422 memcpy(riff_header + 0x00, "RIFF", 4);
\r
423 address32(riff_header, 0x04) = byte_length + 44 - 8;
\r
424 memcpy(riff_header + 0x08, "WAVE", 4);
\r
426 // WAVE file chunk: format
\r
427 memcpy(fmt_header + 0x00, "fmt ", 4);
\r
429 address32(fmt_header, 0x04) = 16;
\r
430 // Compression code: PCM
\r
431 address16(fmt_header, 0x08) = 1;
\r
432 // Number of channels: Stereo
\r
433 address16(fmt_header, 0x0a) = 2;
\r
434 // Sample rate: 44100Hz
\r
435 address32(fmt_header, 0x0c) = 44100;
\r
436 // Average bytes per second: sample rate * 4
\r
437 address32(fmt_header, 0x10) = 44100 * 4;
\r
438 // Block align (bytes per sample)
\r
439 address16(fmt_header, 0x14) = 4;
\r
441 address16(fmt_header, 0x16) = 16;
\r
443 // Write out header
\r
444 fwrite(wav_header, 36, 1, wav_file);
\r
447 fprintf(wav_file, "data");
\r
449 fwrite(&byte_length, 4, 1, wav_file);
\r
451 // Write out sectors
\r
452 for(i = 0; i < sector_count; i++)
\r
454 printf("\b\b\b%3i", i*100 / sector_count);
\r
456 fread(sector_buffer, 2352, 1, bin_file);
\r
457 fwrite(sector_buffer, 2352, 1, wav_file);
\r
459 printf("\b\b\b100\n");
\r
466 void convert_wav_to_ogg(char *wav_file_name, char *output_dir,
\r
467 char *ogg_file_name)
\r
469 char cmd_string[(MAX_PATH * 2) + 16];
\r
472 sprintf(cmd_string, "oggenc %s", wav_file_name);
\r
473 system(cmd_string);
\r
475 unlink(wav_file_name);
\r
479 void convert_wav_to_mp3(char *wav_file_name, char *output_dir,
\r
480 char *mp3_file_name)
\r
482 char cmd_string[(MAX_PATH * 2) + 16];
\r
485 sprintf(cmd_string, LAME_BINARY " " LAME_OPTIONS " -b %i \"%s\" \"%s\"",
\r
486 opt_mp3_bitrate, wav_file_name, mp3_file_name);
\r
487 if (system(cmd_string) != 0)
\r
489 printf("failed to encode mp3\n");
\r
493 unlink(wav_file_name);
\r
497 s32 convert_bin_to_iso(FILE *bin_file, char *output_dir, char *iso_file_name,
\r
501 u8 sector_buffer[2352];
\r
505 iso_file = fopen(iso_file_name, "wb");
\r
506 if (iso_file == NULL)
\r
508 printf("failed to open: %s\n", iso_file_name);
\r
511 printf("writing iso %s, %x sectors\n", iso_file_name, sector_count);
\r
513 for(i = 0; i < sector_count; i++)
\r
515 printf("\b\b\b%3i", i*100 / sector_count);
\r
517 fread(sector_buffer, 2352, 1, bin_file);
\r
518 fwrite(sector_buffer + 16, 2048, 1, iso_file);
\r
520 printf("\b\b\b100\n");
\r
527 void convert_iso_to_cso(char *output_dir, char *iso_file_name, char *cso_file_name)
\r
529 char cmd_string[(MAX_PATH * 2) + 16];
\r
532 sprintf(cmd_string, CISO_BINARY " 9 \"%s\" \"%s\"", iso_file_name, cso_file_name);
\r
533 if (system(cmd_string) != 0)
\r
535 printf("failed to convert iso to cso\n");
\r
539 unlink(iso_file_name);
\r
544 #define sector_offset_to_msf(offset, minutes, seconds, frames) \
\r
546 u32 _offset = offset; \
\r
547 minutes = (_offset / 75) / 60; \
\r
548 seconds = (_offset / 75) % 60; \
\r
549 frames = _offset % 75; \
\r
553 s32 convert_bin_cue(char *output_name_base)
\r
555 char output_file_name[MAX_PATH];
\r
556 FILE *output_cue_file;
\r
557 FILE *bin_file = cd_bin.bin_file;
\r
558 cd_track_struct *current_track;
\r
560 u32 current_pregap = 0;
\r
561 u32 last_pregap = 0;
\r
565 if(stat(output_name_base, &sb))
\r
566 mkdir(output_name_base);
\r
568 sprintf(output_file_name, "%s.cue", output_name_base);
\r
569 chdir(output_name_base);
\r
570 output_cue_file = fopen(output_file_name, "wb");
\r
573 // Every track gets its own file. It's either going to be of type ISO
\r
576 for(i = 0; i < 100; i++)
\r
578 current_track = cd_bin.logical_tracks[i];
\r
579 if(current_track != NULL)
\r
581 switch(current_track->format_type)
\r
583 char output_name_tmp[MAX_PATH];
\r
588 sprintf(output_file_name, "%s_%02d.mp3", output_name_base, i);
\r
589 sprintf(output_name_tmp, "%s_%02d.wav", output_name_base, i);
\r
591 fprintf(output_cue_file, "FILE \"%s\" %s\n",
\r
592 opt_use_mp3 ? output_file_name : output_name_tmp,
\r
593 opt_use_mp3 ? "MP3" : "WAVE");
\r
594 fprintf(output_cue_file, " TRACK %02d AUDIO\n", i);
\r
595 current_pregap = current_track->pregap_offset - last_pregap;
\r
596 last_pregap = current_track->pregap_offset;
\r
597 if(current_pregap > 0)
\r
599 sector_offset_to_msf(current_pregap, m, s, f);
\r
600 fprintf(output_cue_file, " PREGAP %02d:%02d:%02d\n", m, s, f);
\r
602 fprintf(output_cue_file, " INDEX 01 00:00:00\n");
\r
603 sector_offset_to_msf(current_track->sector_count, m, s, f);
\r
604 fprintf(output_cue_file, " REM LENGTH %02d:%02d:%02d\n", m, s, f);
\r
606 fseek(bin_file, current_track->physical_offset, SEEK_SET);
\r
607 convert_bin_to_wav(bin_file, output_name_base, output_name_tmp,
\r
608 current_track->sector_count);
\r
611 convert_wav_to_mp3(output_name_tmp, output_name_base,
\r
619 sprintf(output_file_name, "%s_%02d.cso", output_name_base, i);
\r
620 sprintf(output_name_tmp, "%s_%02d.iso", output_name_base, i);
\r
621 fprintf(output_cue_file, "FILE \"%s\" BINARY\n",
\r
622 opt_use_cso ? output_file_name : output_name_tmp);
\r
623 fprintf(output_cue_file, " TRACK %02d MODE1/2048\n", i);
\r
624 current_pregap = current_track->pregap_offset - last_pregap;
\r
625 last_pregap = current_track->pregap_offset;
\r
626 if(current_pregap > 0)
\r
628 sector_offset_to_msf(current_pregap, m, s, f);
\r
629 fprintf(output_cue_file, " PREGAP %02d:%02d:%02d\n", m, s, f);
\r
631 fprintf(output_cue_file, " INDEX 01 00:00:00\n");
\r
633 fseek(bin_file, current_track->physical_offset, SEEK_SET);
\r
634 convert_bin_to_iso(bin_file, output_name_base, output_name_tmp,
\r
635 current_track->sector_count);
\r
638 convert_iso_to_cso(output_name_base, output_name_tmp, output_file_name);
\r
645 fclose(output_cue_file);
\r
651 static void update_path(void)
\r
653 char buff1[MAX_PATH*4], *buff2;
\r
657 path = getenv("PATH");
\r
658 GetModuleFileNameA(NULL, buff1, sizeof(buff1));
\r
659 for (i = strlen(buff1)-1; i > 0; i--)
\r
660 if (buff1[i] == '\\') break;
\r
663 size = strlen(path) + strlen(buff1) + 3;
\r
664 buff2 = malloc(size);
\r
665 if (buff2 == NULL) return;
\r
667 snprintf(buff2, size, "%s;%s", path, buff1);
\r
668 SetEnvironmentVariableA("PATH", buff2);
\r
673 int main(int argc, char *argv[])
\r
675 char out_buff[MAX_PATH], *cue_file, *out_base;
\r
680 printf("bin/cue to cso/mp3 converter\n");
\r
681 printf("usage: %s [options] <input cue> [output base]\n", argv[0]);
\r
682 printf("options:\n"
\r
683 " -m output mp3 files for audio (default) (lame required)\n"
\r
684 " -b <rate> mp3 bitrate to use (default is 128)\n"
\r
685 " -w output wav files for audio\n"
\r
686 " -c output cso as data track (default) (ciso required)\n"
\r
687 " -i output iso as data track\n");
\r
691 for (a = 1; a < argc - 1; a++)
\r
693 if (strcmp(argv[a], "-m") == 0)
\r
695 else if (strcmp(argv[a], "-w") == 0)
\r
697 else if (strcmp(argv[a], "-c") == 0)
\r
699 else if (strcmp(argv[a], "-i") == 0)
\r
701 else if (strcmp(argv[a], "-b") == 0)
\r
703 opt_mp3_bitrate = atoi(argv[++a]);
\r
708 cue_file = argv[a];
\r
709 out_base = argv[a+1];
\r
711 /* some sanity checks */
\r
712 if(strlen(cue_file) < 4 || strcasecmp(cue_file + strlen(cue_file) - 4, ".cue") != 0)
\r
714 printf("error: not a cue file specified?\n");
\r
722 if(opt_use_mp3 && system(LAME_BINARY " --help " NULL_REDIR) != 0)
\r
724 printf("LAME seems to be missing.\n"
\r
726 "Download from http://lame.sourceforge.net/links.php#Binaries and extract\n"
\r
727 "lame.exe to the same directory as %s\n", argv[0]
\r
729 "Install lame using your packet manager, obtain binaries or build from\n"
\r
730 "sources at http://lame.sourceforge.net/\n"
\r
736 if(opt_use_cso && system(CISO_BINARY " " NULL_REDIR) != 0)
\r
738 printf("CISO seems to be missing.\n"
\r
740 "Download ciso.exe and extract to the same directory as %s\n"
\r
741 "You can take ciso.exe from yacc at http://yacc.pspgen.com/\n", argv[0]
\r
743 "Install ciso using your packet manager, obtain binaries or build from\n"
\r
744 "sources at http://ciso.tenshu.fr/\n"
\r
750 if(load_bin_cue(cue_file) == 0)
\r
752 if(out_base == NULL)
\r
755 strncpy(out_buff, cue_file, sizeof(out_buff));
\r
756 out_buff[sizeof(out_buff)-1] = 0;
\r
757 p = strrchr(out_buff, DIR_SEPARATOR_CHAR);
\r
762 memmove(out_buff, p, strlen(p)+1);
\r
764 out_buff[strlen(out_buff)-4] = 0;
\r
765 out_base = out_buff;
\r
767 if(convert_bin_cue(out_base) != 0)
\r
772 printf("error: could not load cue file %s\n", cue_file);
\r