//////////////////////////////////////////////////////////////////////////////// // // M3U to SPL, Tao WMP PlayList Converter (M3U2SPL) - 03/03/07 - 8:30PM // //////////////////////////////////////////////////////////////////////////////// // Coded by roto ( http://mozy.org/ ) //////////////////////////////////////////////////////////////////////////////// // // * What is this? // This app takes playlist in .M3U format (WinAmp) and converts it // to .SPL format (what the Tao WMP accepts). // Pretty simple. Compile, then run "m3u2spl playlist.m3u" // // * Important Note: // You MP3's must be on the TaoWMP and in their correct folders, // or else they get wiped from SPL! // (Folder == \Disc\Music\\ // E.G.: If your M3U is called "80s.m3u", your MP3's must be in // \Music\80s\ before you add the SPL to the TaoWMP player. // // * VERY IMPORTANT NOTE! // M3U must have a NEWLINE at the end (if 15 songs, do 16 lines) // // * Standard Disclaimer: // I take no responsibility for what you do with this code. // To the best of my knowledge...it works. I can not be held // responsible for any damage that may incur if you use this code. // This code is fairly simple, so only YOU can make it do bad things. // You may use this code in your own applications, you may modify this // code and re-distribute it, but you must give credit to the original // creator. You may not sell any applications that contain this code. // This code looks crude and amateurish. That's because it is. // I am not a "programmer". There are things that could be improved, // fixed or even better implemented. That is up to you. //////////////////////////////////////////////////////////////////////////////// /* ToDo: Remove "debug" printf's that fly past command prompt. Maybe not needed. Done: v0.0 - Opening M3U, reading in tracks, 03/03/07 - 9:00PM v0.1 - Writing Header (with # of tracks), 03/11/07 - 11:00PM (Broke it.) v0.2 - Calculating Song/Track Name Length, 03/11/07 - 9:00PM v0.3 - Getting File Name and Length, cutting off .mp3, 03/26/07 - 10:26PM v0.4 - Writing out song filename and legth of trackname, 03/28/07 - 11:25PM v0.5 - Added full path to write for location on player's disc, 04/08/07 - 12:20AM v0.6 - Added Artist/Disc/Genre (basically rest of SPL structure), 04/08/07 - 1:00AM v0.7 - Added post-spl header track_count writing, 04/24/08 - 11:00PM v0.9c - Fixed SPL bug where it would not have correct "\Disc\" Path, 04/24/07 - 12:20AM - If path is incorrect, WMP KILLS OFF that song, and wipes it from SPL! See note above. - Also, it turns out the WMP DOES read MP3 "Title" info, if available... v0.9d - Fixed final filename bug (filename_hack was not flushed, kept bugging out) memset'd it to 0 v0.9e - Fixed output filename to be input-(.mp3)(+(.spl)), 05/28/07 - 11:28AM v1.0 - Added #EX.... check; if it's not a filename, skip it, 05/28/07 - 6:55PM v1.1 - Added multi-M3U input support via command line. Parses all M3U's via loop. - 04/10/08 - 11AM */ //////////////////////////////////////////////////////////////////////////////// #include #include #include #include #define VERSION "1.1" // TRACK NAME struct track_data { char track_name[255][255]; //[Max 255 songs][Max 225 chars for a song name] }; // HEADER (0x1C) char header[28] = { // \/--- IDENTIFIER OF # OF TRACKS (POS=6) 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xE9, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } ; // FILLER char filler[3] = {0x00, 0x00, 0x00}; // END OF TRACK SECTION char end[20] = { 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x20, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, } ; int main (int argc, char *argv[]) { // Standard File Pointers FILE *m3ufile; // Input FILE *splfile; // Output struct track_data track_m3u; // Use struct int x; int m3u_count; int track_count = 0; // Number of tracks in M3U int track_length = 0; // Length of song/track name (with .mp3 ext) char m3u_file_name[100] = ""; // Filename of M3U, max 100chars to be safe? char filename_hack[100] = ""; // Needed for writing filename to SPL..see notes char full_path[100]; // = "\\Disk\\Music\\"; char output_spl[100] = ""; // Output SPL char skip_check[100] = ""; // Check for #EXT, skip it // Check if we got an M3U name or not if (argc < 2 ) { printf("\n%s - Version %s\n", argv[0], VERSION); printf("\nUsage: %s playlist.m3u (and more m3u's if necessary)\n", argv[0]); exit(1); // No M3U given } //----- BEGIN MAIN PART OF PROGRAM -----// else { for( m3u_count = 1; m3u_count < argc; m3u_count++) { // Empty all variables for the next M3U (if we have one). Otherwise trouble starts memset(full_path, 0 ,sizeof(full_path)); memset(filename_hack, 0, sizeof(filename_hack)); memset(m3u_file_name, 0, sizeof(m3u_file_name)); memset(output_spl, 0, sizeof(output_spl)); track_count = 0; track_length = 0; //Reset if(!(m3ufile = fopen(argv[m3u_count], "r"))) { printf("ERROR: Unable to open M3U File: %s", argv[m3u_count]); exit(1); } else { // Make our output filename from our input filename, add ".spl" strncpy (output_spl, argv[m3u_count], strlen(argv[m3u_count]) - 4); strcat (output_spl, ".spl"); // OPEN OUTPUT FILE FOR WRITING if ((splfile = fopen(output_spl, "a+")) == NULL) { printf ("ERROR: Unable to open output.spl for writing!"); fclose(m3ufile); exit(1); } else { //header[6] = track_count; // Write # of Tracks in M3U into 0x06 of Header fwrite(header, sizeof(header), 1, splfile); // Output Header } // Don't close SPL, continue writing data below. //Get M3U filename, remove .mp3 extension strncpy (m3u_file_name, argv[m3u_count], strlen(argv[m3u_count]) - 4); printf("M3U File Name: %s\n\tFile Name Length:%i\n\n", m3u_file_name, strlen(m3u_file_name)); //----- Main M3U input and SPL output routine -----// // Read in a line one (track) at a time while(fgets(track_m3u.track_name[track_count], sizeof(track_m3u.track_name[track_count]), m3ufile) != NULL) { // Take care of checkfing for #EXT stuff strncpy (skip_check, track_m3u.track_name[track_count], strlen(track_m3u.track_name[track_count])); // Grab first 2 chars of input line, check if M3U control char or not if (strncmp(skip_check, "#E", 2) == 0) printf("SKIP LINE IN M3U, THIS IS AN M3U CONTROL CHAR!"); else { // DEBUG PRINT OF DATA, REMOVE LATER printf("Track Name: %s", track_m3u.track_name[track_count]); // Filename "Hack", works! //strcpy(filename_hack, track_m3u.track_name[track_count]); // Also, somehow 0x0A and 0x0D ended up at the end here, strncpy fixes that.... (04/08/07) strncpy(filename_hack, track_m3u.track_name[track_count], strlen(track_m3u.track_name[track_count]) - 1); // Cat path + m3u file name (dir on drive that holds songs) + song name strncpy(full_path, "\\Disk\\Music\\", strlen("\\Disk\\Music\\")); strcat(full_path, m3u_file_name); strcat(full_path, "\\"); strcat(full_path, filename_hack); // Don't move this around, for some reason it messes up. // Get Length of Track to put into SPL file (must be in HEX) track_length = strlen(track_m3u.track_name[track_count]) - 1; // REMOVE 0x00 AT END track_count++; // Increment number of tracks in M3u // DEBUG PRINT OF DATA, REMOVE LATER printf("\tTrack Name Size (Chars): %i, 0x%X\n", track_length, track_length); // Can't do a simple: // fwrite(track_m3u.track_name[track_count], strlen(track_m3u.track_name[track_count]), 1, splfile); // // Why? I don't know and can't think late. Created a placeholder "char filename_hack" // to store filename in there because writing out orig data doesn't work. // strcpy'd data from struct to local char placeholder, works fine. printf("\tWRITING Track Name: %s\n", filename_hack); // Start writing out track list fwrite(&track_length, 1, 1, splfile); // Write title length, 1byte of data fwrite(filler, 3, 1, splfile); // 3 bytes of "filler" // This is the title of the Song/Track, leave it alone fputs(filename_hack, splfile); // Ugh. //---------------------------------------------------------------------------\\ // This section deals with the "extra stuff" that need to be in the SPL. // Without parsing an MP3 (ID3 data), this data is not available. // But as long as we got the song title, who cares? //---------------------------------------------------------------------------// // This is for the "artist" part of the structure, obviously we wont have that fwrite(&track_length, 1, 1, splfile); // Write "artist" length, 1byte of data fwrite(filler, 3, 1, splfile); // 3 bytes of empty "filler" fputs(filename_hack, splfile); // This is for the "Disc/Compilation" part of the structure, obviously we wont have that fwrite(&track_length, 1, 1, splfile); // Write "Disc/Comp" length, 1byte of data fwrite(filler, 3, 1, splfile); // 3 bytes of empty "filler" fputs(filename_hack, splfile); // This is for the "Genre" part of the structure, obviously we wont have that track_length = 5; fwrite(&track_length, 1, 1, splfile); // Write "Disc/Comp" length, 1byte of data fwrite(filler, 3, 1, splfile); // 3 bytes of empty "filler" fputs("Blues", splfile); // Write entire path+filename track_length = 0; track_length = strlen(full_path); fwrite(&track_length, 1, 1, splfile); // Write "Disc/Comp" length, 1byte of data fwrite(filler, 3, 1, splfile); // 3 bytes of empty "filler" fputs(full_path, splfile); // Write end fwrite(end, sizeof(end), 1, splfile); // ^--- JUNK AT END OF FILE, NOT A PROBLEM THO? // Empty all variables for the next part of the loop // Otherwise trouble starts memset(full_path, 0 ,sizeof(full_path)); memset(filename_hack, 0, sizeof(filename_hack)); track_length = 0; //Reset } } } fclose(splfile); // Close file pointer fclose(m3ufile); printf("\n*** Number of Tracks in M3U: %i\n", track_count); // ----- // Stupid way of writing the track_count inside of header (post .spl creation) // Had to do it this way because....I can't remember why. But not going to break it. // ----- if ((splfile = fopen(output_spl, "rb+")) == NULL) { printf ("ERROR: Unable to open output.spl for track_count writing!"); fclose(m3ufile); exit(1); } else { // Write TrackCount into header fseek (splfile, 6L, SEEK_SET); fwrite(&track_count, 1, 1, splfile); } fclose(splfile); } if(argc > 2) { sleep(10); // If we have to work with more than one M3U, pause! printf("\n*** Converting next M3U\n"); } } // If we have more than one M3U, continue FOR loop. printf("\n\n *** CONVERSION DONE! ***\n"); return 0; // Wow, we actually finished. }