
-----------------------------------
skootles
Mon Apr 17, 2006 8:28 pm

Unicode Encoding and Byte Removal
-----------------------------------
Well, this is definately something I won't be able to figure out on myself.

So I made a program. It converts Windows Media Player 10 Playlist files ( *.wpl files) into a playlist file that will play on my MP3 Player (A samsung YH-925, and the playlist file is *.plp). 

Here's an example of a .wpl playlist file:


    
        
        
        Easy Listening
    
    
        
            
            
            
            
        
    


(The indentation won't be shown because of the forum, but whatever)

and here's an example of this file converted into a .plp playlist file:
PLP PLAYLIST
VERSION 1.20

HDD, System\MUSIC\Stained - So Far Away.mp3
HDD, System\MUSIC\06 - Crazy.mp3
HDD, System\MUSIC\Asher Lane - New days.mp3
HDD, System\MUSIC\Disney-Phil Collins - Tarzan - You'll Be In My Heart.mp3

So what the program has to do is seek out the file names, then put a "HDD, System\MUSIC\" infront of them. It also has to change all occurences of "&apos;" into apostrophes, as they should be. 

So, after this conversion, it won't play in my mp3 player. To make it work properly, I have to open it into notepad, and then re-save it, but change the encoding from ANSI to unicode. And then, I have to open the file in a hex editor, and remove the first two bytes, which have the hex values FF and FE, respectively. Then it will work on my mp3 player.

So I'm wondering, is there a way in turing for me to be able to encode the file using unicode, and then remove the first two bytes?

Thanks  :)

Oh, and here is a copy of my program. I know it may be a little inefficient in spots, but it's been half a year since I've had comp. sci., so it's all going off of memory.

var fn, fn2 : int
var textfile, character, playlist, yplay : string := ""

var location : int

View.Set ("graphics:800;600,position:middle;center")

put "Please enter the name of the Windows Media Player playlist"
put "(including the extension).", skip
put "NOTE: It must be in the same directory as this executable. ", skip
get playlist :*

put skip, "Please enter the name of the YH-925 Playlist (without the extension)", skip
get yplay :*

yplay += ".plp"

open : fn, playlist, get, seek
open : fn2, yplay, put, seek

put skip, "-------------   A copy of the output file:   -------------", skip

put "PLP PLAYLIST"              % Prints a copy of the playlist header to the screen
put "VERSION 1.20"
put ""

put : fn2, "PLP PLAYLIST"       % Prints a copy of the playlist header to the playlist file
put : fn2, "VERSION 1.20"
put : fn2, ""




loop
    location := 0
    exit when eof (fn)
    if character = chr (92) then
        tell : fn, location
        
        loop                                    % -----------   START "\" removal loop   -----------
            get : fn, character : 1
            if character = chr (92) then        % Description: The file is searched for any occurences of the "\" character. When it
                tell : fn, location             % is detected, a reference point is made, and the search continues for others. When there
            end if                              % are no more on the line, the reference point is seeked, and then moves on to the next loop.
            exit when character = chr (34)
        end loop                                % -----------   END "\" removal loop   -----------
        
        seek : fn, location

        loop                                                    % -----------   START &apos; removal and replacement loop   -----------
            get : fn, character : 1
            if character = "&" then
                tell : fn, location
                get : fn, character : 1
                if character = "a" then
                    get : fn, character : 1
                    if character = "p" then
                        get : fn, character : 1
                        if character = "o" then
                            get : fn, character : 1
                            if character = "s" then
                                get : fn, character : 1
                                if character = ";" then
                                    get : fn, character : 1
                                    textfile += "'"
                                else
                                    seek : fn, location - 1
                                end if
                            else
                                seek : fn, location - 1
                            end if
                        else
                            seek : fn, location - 1
                        end if
                    else
                        seek : fn, location - 1
                    end if
                else
                    seek : fn, location - 1
                end if
            end if
            exit when character = chr (34)                         % Exit when a quotation mark is detected
            textfile += character
        end loop                                                 % -----------   END &apos; removal and replacement loop   -----------

        put "HDD, System", chr (92), "MUSIC", chr (92) ..
        put : fn2, "HDD, System", chr (92), "MUSIC", chr (92) ..
        put textfile ..
        put : fn2, textfile ..
        put ""
        put : fn2, ""
        textfile := ""
    end if
    get : fn, character : 1
end loop


close : fn
close : fn2


-----------------------------------
Delos
Tue Apr 18, 2006 1:35 pm


-----------------------------------
Use read and write instead of put and get.  That's the first big step to take, since it will then write the file in binary format (i.e., free of ASCII).
This should work...if not, well try this first and then post up again.

-----------------------------------
skootles
Tue Apr 18, 2006 2:38 pm


-----------------------------------
When I replaced put with write, for example:
out : fn2, "PLP PLAYLIST"       
put : fn2, "VERSION 1.20"
put : fn2, ""
to
write : fn2, "PLP PLAYLIST"       
write : fn2, "VERSION 1.20"
write : fn2, ""
I get multiple "Argument to 'read' or 'write' statement is not a variable" errors. So can I only use variables for write?

-----------------------------------
Delos
Tue Apr 18, 2006 4:52 pm


-----------------------------------
Apparently.  That's not too much of a problem is it?  Unless you're regularly dealing with strings longer than 256 chars...

-----------------------------------
skootles
Tue Apr 18, 2006 7:01 pm


-----------------------------------
New code:
var fn, fn2 : int
var textfile, character, playlist, yplay : string := ""
var location : int

View.Set ("graphics:800;600,position:middle;center")



put "Please enter the name of the Windows Media Player playlist"
put "(including the extension).", skip
put "NOTE: It must be in the same directory as this executable. ", skip
get playlist :*


put skip, "Please enter the name of the YH-925 Playlist", skip
get yplay :*


yplay += ".plp"                         

open : fn, playlist, read, seek         % The Windows Media Player playlist file
open : fn2, yplay, write, seek          % The MP3 Player playlist file

put skip, "-------------   A copy of the outwrite file:   -------------", skip

var v1 : string := "PLP PLAYLIST"              
var v2 : string := "VERSION 1.20"
var v3 : string := ""

put v1
put v2
put v3

write : fn2, v1       % Prints a copy of the playlist header to the playlist file
write : fn2, v2
write : fn2, v3




loop
    location := 0
    exit when eof (fn)
    if character = chr (92) then
        tell : fn, location
        
        loop                                    % -----------   START "\" removal loop   -----------
            read : fn, character : 1
            if character = chr (92) then        % Description: The file is searched for any occurences of the "\" character. When it
                tell : fn, location             % is detected, a reference point is made, and the search continues for others. When there
            end if                              % are no more on the line, the reference point is seeked, and then moves on to the next loop.
            exit when character = chr (34)
        end loop                                % -----------   END "\" removal loop   -----------
        
        seek : fn, location

        loop                                                    % -----------   START &apos; removal and replacement loop   -----------
            read : fn, character : 1
            if character = "&" then
                tell : fn, location
                read : fn, character : 1
                if character = "a" then
                    read : fn, character : 1
                    if character = "p" then
                        read : fn, character : 1
                        if character = "o" then
                            read : fn, character : 1
                            if character = "s" then
                                read : fn, character : 1
                                if character = ";" then
                                    read : fn, character : 1
                                    textfile += "'"
                                else
                                    seek : fn, location - 1
                                end if
                            else
                                seek : fn, location - 1
                            end if
                        else
                            seek : fn, location - 1
                        end if
                    else
                        seek : fn, location - 1
                    end if
                else
                    seek : fn, location - 1
                end if
            end if
            exit when character = chr (34)                         % Exit when a quotation mark is detected. (Quotes signal the end of the filename)
            textfile += character
        end loop                                                 % -----------   END &apos; removal and replacement loop   -----------

        
        var test : string := "HDD, System"
        test += chr(92)
        test += "MUSIC"
        test += chr(92)
        test += textfile
        
        put test 
        write : fn2, test
        %put textfile 
        %write : fn2, textfile
        write : fn2, v3
        textfile := ""
        test := ""
    end if
    read : fn, character : 1
end loop


close : fn
close : fn2


and everything comes out horribly spaced:

 http://img215.imageshack.us/img215/9812/untitled4np1.png

Plus I don't know what those weird 3 characters are    :cry:

-----------------------------------
Delos
Tue Apr 18, 2006 11:13 pm


-----------------------------------
Using write outputs it in binary format - this is not something that Notepad can recognize very well.  However, your file headers should be less of a pain to work with.
Have you tested the new playlists with your MP3 player?  If they still don't work, we can work on some actual Hex editting stuff.  It's really quite a bit of fun, playing with obscure values that make no sense to the average eye.  Of course, we'll have made code efficient enough that we'll never have to deal with said values...

-----------------------------------
skootles
Wed Apr 19, 2006 2:14 pm


-----------------------------------
No, the playlist files still don't work. So, I guess let's get into Hex Editing  :P

I showed the problem to my old comp sci teacher, and he said he'd take a look at it, so I may get some help from him.

-----------------------------------
Delos
Wed Apr 19, 2006 3:39 pm


-----------------------------------
Right right, no problem.  Just post a copy of the same playlist in both formats, and we'll move on from there.  Also, what Hex editor are you using?

-----------------------------------
skootles
Wed Apr 19, 2006 4:26 pm


-----------------------------------
Alrighty. Here's the windows media player file (*.wpl):



    
        
        
        Easy Listening
    
    
        
            
            
            
            
        
    


and Here's the MP3 Player playlist file (*.plp):

PLP PLAYLIST
VERSION 1.20

HDD, System\MUSIC\Stained - So Far Away.mp3
HDD, System\MUSIC\06 - Crazy.mp3
HDD, System\MUSIC\Asher Lane - New days.mp3
HDD, System\MUSIC\Disney-Phil Collins - Tarzan - You'll Be In My Heart.mp3

Now if you copy the .plp file from above into notepad, you still have to save it with unicode encoding, then open it up in a hex editor (I'm using one that I found on my computer, translhextion) and remove the first two bytes.

-----------------------------------
Delos
Wed Apr 19, 2006 8:10 pm


-----------------------------------
Right right, no problem.  Just post a copy of the same playlist in both formats, and we'll move on from there.  Also, what Hex editor are you using?

Mayhaps I ought have spake that a little differently.  No damage:
Please attach a copy of the same playlist in both formats...

I'm not sure if you have the capability to save in .plp - considering your dilemma.  But if you can, awsome.  If not, then just post one of your editted files.

-----------------------------------
skootles
Wed Apr 19, 2006 9:17 pm


-----------------------------------
Haha, okay.

So I've included 4 files in the zip. 1 is the original Windows Media Player Playlist file (Easy Listening.wpl), and then there are 3 MP3 Player Playlist files:

The first, which has "NOT WORKING" is the raw conversion. It's the output from the program, so it doesn't work in the MP3 Player.

The second which has "ALMOST WORKING" is the first one, except I've opened it up in notepad, and re-saved it with Unicode encoding.

The third which has "WORKING FULLY" is the second one, but I've opened it in Translhextion and removed the first two bytes. Now it works perfectly in the MP3 Player.

Now just note that when you open up the second one in notepad, you can't see the first two bytes that need to be removed. My teacher got confused with this, so just clarifing  :)

-----------------------------------
Delos
Thu Apr 20, 2006 12:40 pm


-----------------------------------
Right, this is better now.  I'll probably be able to work on this a little tonight - not right now since studying is taking complete precedence...well, mostly at least :D.

For the time being, make sure you know how to convert between decimal and hexadecimal (and vice versa); and simple ASCII manipulations between decimal and their character equivalents.

For instance, if I were to throw the number 2E at you, I'd expect you to be able to tell me what the character equivalent was (it's ".", FYI).
Work on that for the time being, and we'll get to the rest soon enough.  (Don't worry if some of your conversion output as blank spaces - a lot of characters simply have no renderable equivalent.

-----------------------------------
Delos
Fri Apr 21, 2006 7:48 am


-----------------------------------
I'm going to guess that by this time you have figured out what I set out above.  On to the next step.
Now that conversions between decimal and hex are no problem, you're going to have to do a bit of logical analysis of the hex dumps.  The usefulness here is that you'll be able to create a specific type of file that goes beyond the mere suffix that is attached to it.
For instance, Turing is sneaky when it comes to saving pictures.  They claim that it can save in .jpg; when in fact the resultant pictures have none of the compression of JPEGs and are indeed bitmaps with different suffixes.  The hex headers remain the same.
That being said, your task to to do the following:
- determine the header for .plp files, according to what you have.  Post up the hex dump of it.  Don't worry about the text version, that is mostly meaningless.
- determine the format that will allow the MP3 player to recognise the following file:
"Hello - World.mp3"
Again, post up the hex for it.  You should find that the entire path for this inclues a little more than the name of the song...something about a system folder...

I'd be a little more instructive - just I don't have the time right now!  I'm sure you can handle this much though.  If you get stuck, say so and I'll give you a few more hints.

-----------------------------------
skootles
Fri Apr 21, 2006 3:02 pm


-----------------------------------
I understand, exams are inportant.  :P 

But I'm not quite sure what the header is, or where I'd find it. Methinks at the top, but there's nothing really there. To save some time, I just got a screenie of the hex editor when I've got the file open. Note the first two bytes - these are the ones that need to be removed before the playlist will play in the player.

http://img173.imageshack.us/img173/9639/inhex1ji.png

And here is the same file when opened in notepad:

PLP PLAYLIST
VERSION 1.20

HDD, System\MUSIC\Hello - World.mp3

The "System\MUSIC" part is obviously the path to the file, where all music that is synced to the device is put.

-----------------------------------
Delos
Fri Apr 21, 2006 6:55 pm


-----------------------------------
You're off to a good start.  Essentially, a header is a little bit of hex at the top of the file that gives some details about its contents.  These can be quite complex (hell, even bitmap headers are pretty long) to barely nonexistent (the .wpl don't really have headers).
When you're creating your playlists, you'll need to have a default header to be added to the beginning of each file.  You'll then fill the body with the data for each song, and finally...well...where there's a header...

You haven't quite completed the assigned tasks just yet, but keep plugging away at them.  Once you're past those - it's really quite a simple transition to create the playlist itself.

-----------------------------------
skootles
Fri Apr 21, 2006 11:52 pm


-----------------------------------
Okay, I think I've got it..
Header:
00000000  50 00 4c 00 50 00 20 00 50 00 4c 00 41 00 59 00 4c 00 49 00  P.L.P. .P.L.A.Y.L.I.
00000014  53 00 54 00 0d 00 0a 00 56 00 45 00 52 00 53 00 49 00 4f 00  S.T.....V.E.R.S.I.O.
00000028  4e 00 20 00 31 00 2e 00 32 00 30 00 0d 00 0a 00 0d 00 0a 00  N. .1...2.0.........


and format:

0000003c  48 00 44 00 44 00 2c 00 20 00 53 00 79 00 73 00 74 00 65 00  H.D.D.,. .S.y.s.t.e.
00000050  6d 00 5c 00 4d 00 55 00 53 00 49 00 43 00 5c 00 48 00 65 00  m.\.M.U.S.I.C.\.H.e.
00000064  6c 00 6c 00 6f 00 20 00 2d 00 20 00 57 00 6f 00 72 00 6c 00  l.l.o. .-. .W.o.r.l.
00000078  64 00 2e 00 6d 00 70 00 33 00 0d 00 0a 00                    d...m.p.3.....      

and I've attatched them as text files too  :)

-----------------------------------
Delos
Sat Apr 22, 2006 11:21 pm


-----------------------------------
Good work.  You should be all set to create your .plp files now.  To summarize:

- the file will start with the header.  This will be default to every file.
- file names being with the "HDD" path and end with the "0A 00" delimiter.
- to write the file in Binary format, use write instead of put.

Be careful when you're writing your files - you may have to play around a bit with it to get just the right catenation of your strings.  I've often encountered problems where I've written a linebreak where there oughtn't be one - resulting in extra characters, resulting in non-working files.
You may have a little bit of a challenge creating the file names, but now that you know the format it shouldn't be too bad.  Notice the characters ("00") inserted between each letter - make sure you follow that as well. 

See what you can do.  I can forsee only one possible problem to do with the way Turing writes files...but that souldn't be too much of a problem.  Post up your results, working or otherwise, and if need be we'll delve deeper into the intricacies of writing binary files.

-----------------------------------
skootles
Sun Apr 23, 2006 9:47 pm


-----------------------------------
Okay, so I've changed the gets to reads, puts to writes, and discovered a few inefficiencies in my code  ;)  But the output playlist file didn't work, and I've attatched the hexdump of it.

I wasn't sure how I could ge the spacing right (with the "00" 's between each character, and I've no clue how there got to be so many of them). There's a copy of my code also attatched.

-----------------------------------
Delos
Tue Apr 25, 2006 12:31 pm


-----------------------------------
Quick note:  will look at code soon...final stretch is almost over!

-----------------------------------
skootles
Tue Apr 25, 2006 2:41 pm


-----------------------------------
Haha, okay. Good luck!

-----------------------------------
Delos
Fri Apr 28, 2006 11:25 am


-----------------------------------
This morning I decided to try this without using hex...just because it was turning out to be quite the complicated deal using hex.  This is the result.  Load it up into your player and tell me if it works.
If so, great, I'll work you through recreating it.  If not...well, it's back to hex.
There's only one song on the playlist.

-----------------------------------
skootles
Sat Apr 29, 2006 12:31 pm


-----------------------------------
Sorry, but it didn't work. It just gave me the standard error, so I guess it's time for Hex.

-----------------------------------
Delos
Sun Apr 30, 2006 12:10 am


-----------------------------------
Well...wracked my brain a little over this, but try it out!
This one is based off a slightly modified "Easy Listening.wpl", saved as "Easy Listening 2.wpl" and included in the zip.  I had to modify the directories a little, and change the name of one of the songs to match the "Fully Working" plp that you'd provided.

Tell me if it works and we'll go from there.

-----------------------------------
skootles
Sun Apr 30, 2006 10:05 pm


-----------------------------------
Well, my hat goes off to you. It works!
Now it's time to share your secret  >=D
Haha   :D

-----------------------------------
Delos
Tue May 02, 2006 11:06 am


-----------------------------------
Thank you, thank you.  Always good to be appreciated.  And now on to the good stuff...

What I'll do is give you an outline of what to create.  I'll leave quite a few holes into which I'll try and make you stumble.  This will be a learning experience.
And since I don't get to do this too often, I'll indulge a bit and introduce you into some more interesting methods of programming - a bit closer to OOP but not quite there.  I'm guessing that you don't know too much about modules and the like, so we'll structure this around that level of programming.

To start things off, make sure you give the 
unit
module WPL_PLP
    export convert
    
    proc readWPLFile
    end readWPLFile
    
    proc parseSongNames
    end parseSongNames
    
    proc writePLPFile
    end writePLPFile

    proc convert
    end convert
end WPL_PLP


Since we'll be playing around with the file while creating it, comment out the 'unit' for now.  We have our stubs, and a general idea of what we'll be doing next.
For now, your job is to do the following:
- get both readWPLFile and parseSongNames working
- this means reading in the WPL file in its entirety, then searching through it and extracting anything that is defined as a file name
- you will then need to modify those file names to account for apostraphes and other possible character conversions used in the WPL format

Some hints:
- you will not know before hand how many file names are in a given file, so you'll want some flexibility in the way you parse the data
- WPL files follow a very defined structure.  That is to say, a lot of things are constant about it.
- consider this:  when parsing the file name, are you going to parse the entire path or just the song title?  Which do you need, which don't you need?  How can you distinguish between different paths?

I want to see some clean coding coming from you.  No extraneous code whatsoever.  That means that everything must be compartmentalized.  If I were to run the code without calling any of the procs from within your module, I would want nothing to occur.  (As a comparision, if we look at the final product from the Modules tutorial, running that file (sans 'unit') as it is will do nothing more than load the procs into memory.  That's what you're aiming for.)
Make use of functions as much as possible.  As Cervantes would say (and I believe he's quoting wtd), follow the DRY principle:  Don't Repeat Yourself.  If you see the same code twice, replace it with a function.  Procedures are alright, but try to push yourself towards fcns more.
Also the stubs I've lain out are just a guideline.  Don't let them restrict you.  The current task is quite simple, but be creative...well, try to at least.

I'll be expecting the finished task within 36 hours...give or take a few.  You don't have much else to do do you?  Unless of course you have some random tests coming up...then those come first... :wink:
It is now high noon.  Go!

-----------------------------------
skootles
Tue May 02, 2006 4:19 pm


-----------------------------------
Whoops! I forgot that WMP changed the ampersand (sp?)  (the & symbol) into  &amp;   
No worries. All fixed now   :)


var fn, fn2 : int                % Variables for the files to be opened

var currentsong : string := ""    % A temporary var for when a variable is being assigned to a song name.
var character : string := ""      % Used for the open file
var playlist : string := ""       % The Windows Media Player Playlist
var yplay : string := ""          % The Samsung YH-925 Playlist

var location : int := 0           % A location variable for the open file

var count : int := 0              % Used for knowing which var of an array to write a song name too.

var numofsongs : int := 0         % The number of songs in the WMP Playlist


View.Set ("graphics:800;600,position:middle;center")





open : fn2, yplay, put, seek



proc GetSongNames

    var songs : array 1 .. numofsongs of string

    seek : fn, 0
    loop
        location := 0
        exit when eof (fn)
        if character = chr (92) then
            tell : fn, location

            loop
                get : fn, character : 1
                if character = chr (92) then
                    tell : fn, location
                end if
                exit when character = chr (34)
            end loop

            seek : fn, location

            loop
                get : fn, character : 1                                             % The ' symbol is replaced with  &apos;
                if character = "&" then
                    tell : fn, location
                    get : fn, character : 1
                    if character = "a" then
                        get : fn, character : 1
                        if character = "p" then
                            get : fn, character : 1
                            if character = "o" then
                                get : fn, character : 1
                                if character = "s" then
                                    get : fn, character : 1
                                    if character = ";" then
                                        get : fn, character : 1
                                        currentsong += "'"
                                    else
                                        seek : fn, location - 1
                                    end if
                                else
                                    seek : fn, location - 1
                                end if
                            else
                                seek : fn, location - 1
                            end if
                        elsif character = "m" then
                        
                            get : fn, character : 1                                 %   The & symbol is replaced with &amp;
                            if character = "p" then
                                get : fn, character : 1
                                if character = ";" then
                                    get : fn, character : 1
                                    currentsong += "&"
                                else
                                    seek : fn, location - 1
                                end if
                            else
                                seek : fn, location - 1
                            end if
                            
                        else
                            seek : fn, location - 1
                        end if
                    else
                        seek : fn, location - 1
                    end if
                end if

                exit when character = chr (34)                     % Exit when a quotation mark is detected
                currentsong += character
            end loop

            count += 1
            songs (count) := currentsong
            currentsong := ""

        end if

        get : fn, character : 1

    end loop

    %  ---------  For testing purposes   (lists song names)  -----------

    put skip

    for i : 1 .. numofsongs
        put songs (i)
    end for


end GetSongNames




proc readWPLFile

    open : fn, playlist, get, seek
    loop
        exit when eof (fn)

        get : fn, character : 1
        if character = "