#Kogan mp4 playlist generator by svyr@headfi
#v0.11 @ 23/08/2014 - initial release
#v0.12 @ 24/08/2014 - fixed file part length bug where it was set to a static value, fixed the pl num byte (hex not dec)
$rootFolder='j:\music'.toupper()
$plOutputFile='i:\Playlist\MSCLST.0'
$playListNum=[int]::parse($plOutputFile[$plOutputFile.Length-1])
$playListNumByte=$playListNum*32
$filter=@("*.flac","*.mp3","*.ape")
$foldCont = Get-ChildItem $rootFolder -Recurse -Include $filter
$toSkip=0
$limitSongs=0
###short filename code from http://www.tellingmachine.com/post/Converting-file-names-to-the-DOS-83-format-using-PowerShell.aspx
$DebugPreference = "continue"
function Get-Script-Directory
{
$scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
return Split-Path $scriptInvocation.MyCommand.Path
}
function Load-FileSystemHelper()
{
$Code =
@"
using System;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Globalization;
public class FileSystemHelper
{
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int GetShortPathName(
[MarshalAs(UnmanagedType.LPTStr)] string path,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder shortPath,
int shortPathLength);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.U4)]
private static extern int GetLongPathName(
[MarshalAs(UnmanagedType.LPTStr)]
string lpszShortPath,
[MarshalAs(UnmanagedType.LPTStr)]
StringBuilder lpszLongPath,
[MarshalAs(UnmanagedType.U4)]
int cchBuffer);
public static string GetShortPathName(string path)
{
StringBuilder shortPath = new StringBuilder(500);
if (0 == GetShortPathName(path, shortPath, shortPath.Capacity))
{
if (Marshal.GetLastWin32Error() == 2)
{
throw new Exception("File does not exist!");
}
else
{
throw new Exception("GetLastError returned: " + Marshal.GetLastWin32Error());
}
}
return shortPath.ToString();
}
public static string GetLongPathName(string shortPath)
{
if (String.IsNullOrEmpty(shortPath))
{
return shortPath;
}
StringBuilder builder = new StringBuilder(255);
int result = GetLongPathName(shortPath, builder, builder.Capacity);
if (result > 0 && result < builder.Capacity)
{
return builder.ToString(0, result);
}
else
{
if (result > 0)
{
builder = new StringBuilder(result);
result = GetLongPathName(shortPath, builder, builder.Capacity);
return builder.ToString(0, result);
}
else
{
throw new FileNotFoundException(
string.Format(
CultureInfo.CurrentCulture,
null,
shortPath),
shortPath);
}
}
}
}
"@
Add-Type -TypeDefinition $Code
}
function Get-DOSPathFromLongName([string] $Path)
{
Load-FileSystemHelper
$DOSPath = [FileSystemHelper]::GetShortPathName($Path)
Write-Debug $DOSPath
return $DOSPath
}
function Get-LongNameFromDOSPath([string] $Path)
{
Load-FileSystemHelper
$LongPath = [FileSystemHelper]::GetLongPathName($Path)
Write-Debug $LongPath
return $LongPath
}
#$DOSPath = Get-DOSPathFromLongName -Path "C:\Program Files (x86)\"
### end of short filename code from http://www.tellingmachine.com/post/Converting-file-names-to-the-DOS-83-format-using-PowerShell.aspx */
### byte array function from http://cyber-defense.sans.org/blog/2010/02/11/powershell-byte-array-hex-convert */
function Convert-ByteArrayToHexString
{
################################################################
#.Synopsis
# Returns a hex representation of a System.Byte[] array as
# one or more strings. Hex format can be changed.
#.Parameter ByteArray
# System.Byte[] array of bytes to put into the file. If you
# pipe this array in, you must pipe the [Ref] to the array.
# Also accepts a single Byte object instead of Byte[].
#.Parameter Width
# Number of hex characters per line of output.
#.Parameter Delimiter
# How each pair of hex characters (each byte of input) will be
# delimited from the next pair in the output. The default
# looks like "0x41,0xFF,0xB9" but you could specify "x" if
# you want the output like "x41xFFxB9" instead. You do
# not have to worry about an extra comma, semicolon, colon
# or tab appearing before each line of output. The default
# value is ",0x".
#.Parameter Prepend
# An optional string you can prepend to each line of hex
# output, perhaps like '$x += ' to paste into another
# script, hence the single quotes.
#.Parameter AddQuotes
# An switch which will enclose each line in double-quotes.
#.Example
# [Byte[]] $x = 0x41,0x42,0x43,0x44
# Convert-ByteArrayToHexString $x
#
# 0x41,0x42,0x43,0x44
#.Example
# [Byte[]] $x = 0x41,0x42,0x43,0x44
# Convert-ByteArrayToHexString $x -width 2 -delimiter "x" -addquotes
#
# "x41x42"
# "x43x44"
################################################################
[CmdletBinding()] Param (
[Parameter(Mandatory = $True, ValueFromPipeline = $True)] [System.Byte[]] $ByteArray,
[Parameter()] [Int] $Width = 10,
[Parameter()] [String] $Delimiter = ",0x",
[Parameter()] [String] $Prepend = "",
[Parameter()] [Switch] $AddQuotes )
if ($Width -lt 1) { $Width = 1 }
if ($ByteArray.Length -eq 0) { Return }
$FirstDelimiter = $Delimiter -Replace "^[,\:t]",""
$From = 0
$To = $Width - 1
Do
{
$String = [System.BitConverter]::ToString($ByteArray[$From..$To])
$String = $FirstDelimiter + ($String -replace "-",$Delimiter)
if ($AddQuotes) { $String = '"' + $String + '"' }
if ($Prepend -ne "") { $String = $Prepend + $String }
$String
$From += $Width
$To += $Width
} While ($From -lt $ByteArray.Length)
}
### end of byte array function from http://cyber-defense.sans.org/blog/2010/02/11/powershell-byte-array-hex-convert */
#from https://xaegr.wordpress.com/category/scripting/page/42/
function ConvertTo-Encoding ([string]$From, [string]$To){
Begin{
$encFrom = [System.Text.Encoding]::GetEncoding($from)
$encTo = [System.Text.Encoding]::GetEncoding($to)
}
Process{
$bytes = $encTo.GetBytes($_)
$bytes = [System.Text.Encoding]::Convert($encFrom, $encTo, $bytes)
$encTo.GetString($bytes)
}
}
$allRecords = new-object Byte[] 1
$k=0
foreach($afile in $foldcont){
if($afile.Attributes -ne "Directory"){
if($k -lt $toSkip){
Write-Debug ("Skipping #" + $k + " " + $afile.fullname)
$k++;
continue;
}
Write-Debug ("Adding #" + $k + " " + $afile.fullname)
$shortFn=(Get-DOSPathFromLongName($afile.fullname.ToUpper()))
$shortFn=$shortFn.Replace($rootFolder.Substring(0,3),'D:\')
$shortFnLen=$shortFn.length
$lastSlashInShortFn=$shortFn.LastIndexOf('\')+1
$fileNamePart=$shortFn.Substring($lastSlashInShortFn,($shortFnLen-$lastSlashInShortFn))
$filePartLength=$fileNamePart.length
$recLen=16+$shortFnLen+1;
$record= New-Object Byte[] $recLen
$record[0]=[Byte]$reclen
$record[2]=$playListNumByte
$record[3]=0xb0
$record[4]=$lastSlashInShortFn
$record[8]=$filePartLength
$record[$recLen-1]=0x0a
$offset=16;
for ($i=0; $i -lt $shortFnLen; $i++){
$record[$i+$offset]=$shortFn[$i]
}
$allRecords +=$record
$k++
if($limitSongs -gt 0 -and $k -eq ($toSkip+$limitSongs)){
break;
}
}
}
$allRecords2= new-object Byte[] ($allRecords.Length-1)
for ($i=1; $i -lt $allRecords.Length; $i++){
$allRecords2[$i-1]=$allRecords[$i]
}
function Get-Script-Directory
{
$scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
return Split-Path $scriptInvocation.MyCommand.Path
}
function Get-Script-Directory
{
$scriptInvocation = (Get-Variable MyInvocation -Scope 1).Value
return Split-Path $scriptInvocation.MyCommand.Path
}
$tempFileName=[environment]::GetEnvironmentVariable('TEMP') + "\temppl.0"
if([System.IO.File]::Exists($tempFileName)){
[System.IO.File]::Delete($tempFileName)
}
Add-Content -Path $tempFileName -value $allRecords2 -encoding byte
move $tempFileName $plOutputFile -force
#
#looking at the format of G:\Playlist\msclst.0
#so far it looks like you get a record for each file like this :
#00 ?? record size byte from here until the 0a (inclusive). This is basically 16 bytes + dos 8.3 filename length + 0a
#01 00
#02 ?? (00 for pl 0 - msclst.0, 20 for pl1, 40 for pl2, 60 for pl3)
#03 b0
#04 ?? length of the 8.3 dos filename till last slash (including the last slash) e.g. (D:\MUSIC\MISC\BRUNOM~1\)05THEL~1.FLA = 0x17 (23) or (D:\MUSIC\MISC\LS\)05MOON~1.FLA = 0x11 (17)
#05 00 (3 of the 0 bytes)
#06 00
#07 00
#08 ?? dos file name part length (e.g. ...\(tra~1.fla) excl the slash
#09 00 (7 of the 0 bytes)
#10 00
#11 00
#12 00
#13 00
#14 00
#15 00
#16 d:\dosfilename incl path (ascii)
#last rec char 0a