I'm doing a bit manipulating routine to encode/decode mp3 files. However, I will need to encode them using C#, and decode them using Delphi XE 10. I wrote the code that encodes/decodes them, and it works when executed on the same platform (if I encode / decode in C#, the mp3 plays, and if I encode / decode in Delphi the mp3 plays) , but if I try to decode an mp3 that was encoded through C#, the mp3 doesn't play. When doing this process only with string, it worked, so it's probably when I tried to apply it to the MemoryStream
. At the end, I've edited the post, and added the functions that I used the encode/decode only strings, and I can successfully encode in C#, and decode in Delphi. I changed the hash string, but it's similar to the one used in the samples
Below is the code in C#:
private void btnCodificarArquivos_Click(object sender, EventArgs e)
{
Encoding ANSI = Encoding.GetEncoding(1252);
byte[] hash = ANSI.GetBytes(@"laki#~~2p3fijo3ij881*2f-|- a`asso`wpeofi#jdJD92jd9jdfjl2@38d8d");
string directory;
string originalExtension;
string finalExtension;
if (cbxCodificar.Checked)
{
originalExtension = "mp3";
finalExtension = "mpc";
}
else
{
originalExtension = "mpc";
finalExtension = "mp3";
}
directory = txbFrase.Text.Trim();
if (!Directory.Exists(directory))
throw new Exception("Directory does not exist => " + directory);
var files = Directory.GetFiles(directory, "*." + originalExtension);
foreach (var file in files)
{
using (FileStream streamFile = new FileStream(file, FileMode.Open, FileAccess.ReadWrite))
{
using (MemoryStream codedMemory = new MemoryStream())
{
streamFile.CopyTo(codedMemory);
byte[] fileArray = codedMemory.ToArray();
byte[] output = new byte[fileArray.Length];
for (int i = 0; i < fileArray.Length; i++)
output[i] = (byte)(fileArray[i] ^ ~hash[(i + 1) % hash.Length]);
using (MemoryStream memoryCodificado = new MemoryStream(output))
{
string fileDestination = Path.ChangeExtension(file, "." + finalExtension);
if (File.Exists(fileDestination))
File.Delete(fileDestination);
using (FileStream arquivoFinal = new FileStream(fileDestination, FileMode.Create, FileAccess.ReadWrite))
{
memoryCodificado.Position = 0;
memoryCodificado.CopyTo(arquivoFinal);
}
}
}
}
}
}
}
Below is the code in Delphi:
procedure TfrmEncodeDecode.btnCodificarArquivoClick(Sender: TObject);
const
Hash: string = 'laki#~~2p3fijo3ij881*2f-|- a`asso`wpeofi#jdJD92jd9jdfjl2@38d8d';
var
counter, i: Integer;
bufferStream : ^Byte;
streamSize: Int64;
streamFile: TFileStream;
listFiles: TStringDynArray;
codedMemory: TMemoryStream;
directory, originalExtension, finalExtension, fileDestination: string;
begin
if (cbxConverter.Checked) then
begin
originalExtension := 'mp3';
finalExtension := 'mpc';
end
else
begin
originalExtension := 'mpc';
finalExtension := 'mp3';
end;
directory := String(edtFrase.Text).Trim;
if (not TDirectory.Exists(directory, true)) then
raise Exception.Create('Diretório não existe => ' + directory);
listFiles := TDirectory.GetFiles(directory, '*.' + originalExtension);
if (Length(listFiles) > 0) then
begin
for counter := 0 to Pred(Length(listFiles)) do
begin
streamFile := TFileStream.Create(listFiles[counter], fmOpenRead, fmShareExclusive);
try
codedMemory := TMemoryStream.Create;
try
codedMemory.CopyFrom(streamFile, streamFile.Size);
fileDestination := TPath.ChangeExtension(listFiles[counter], '.' + finalExtension);
if (TFile.Exists(fileDestination, true)) then
TFile.Delete(fileDestination);
streamSize := codedMemory.Size;
bufferStream := codedMemory.Memory;
i := 0;
while (i < streamSize) do
begin
bufferStream^ := Byte(ord (bufferStream^) xor not (Ord (Hash[I mod Length (Hash) + 1])));
Inc(bufferStream);
Inc(i);
end;
codedMemory.SaveToFile(fileDestination);
finally
codedMemory.Free;
end;
finally
streamFile.Free;
end;
end;
end;
end;
EDIT
Complementing the question, below is the code for the string encode/decode. Doing this for the string, I can encode it in C# and decode it Delphi without a problem:
private string EncodeDecode(string str)
{
Encoding ANSI = Encoding.GetEncoding(1252);
byte[] hash = ANSI.GetBytes(@"laki#~~2p3fijo3ij881*2f-|- a`asso`wpeofi#jdJD92jd9jdfjl2@38d8d");
byte[] input = ANSI.GetBytes(str);
byte[] output = new byte[input.Length];
for (int i = 0; i < input.Length; i++)
output[i] = (byte)(input[i] ^ ~hash[(i + 1) % hash.Length]);
return ANSI.GetString(output);
}
and below is the Delphi code
function TfrmEncodeDecode.EncodeDecode(palavra: AnsiString): AnsiString;
const
Hash: string = 'laki#~~2p3fijo3ij881*2f-|- a`asso`wpeofi#jdJD92jd9jdfjl2@38d8d';
var
I: Integer;
begin
Result := palavra;
for I := 1 to Length (Result) do
Result[I] := AnsiChar (ord (Result[I]) xor not (Ord (Hash[I mod Length (Hash) + 1])));
end;
EDIT
After Ken`s comment, I changed the Delphi side so that the hash is converted to an array of bytes, as the code below shows, and now the mp3 actually does play when encoded in C# and decoded in Delphi, but the song sounds like a scratched record when it plays. Below is the new Delphi code:
procedure TfrmEncodeDecode.btnCodificarArquivoClick(Sender: TObject);
const
Hash: string = 'laki#~~2p3fijo3ij881*2f-|- a`asso`wpeofi#jdJD92jd9jdfjl2@38d8d';
var
counter, i: Integer;
bufferStream : ^Byte;
streamSize: Int64;
aHash: TArray<Byte>;
streamFile: TFileStream;
listFiles: TStringDynArray;
codedMemory: TMemoryStream;
directory, originalExtension, finalExtension, fileDestination: string;
begin
aHash := TEncoding.ANSI.GetBytes(Hash);
if (cbxConverter.Checked) then
begin
originalExtension := 'mp3';
finalExtension := 'mpc';
end
else
begin
originalExtension := 'mpc';
finalExtension := 'mp3';
end;
directory := String(edtFrase.Text).Trim;
if (not TDirectory.Exists(directory, true)) then
raise Exception.Create('Diretório não existe => ' + directory);
listFiles := TDirectory.GetFiles(directory, '*.' + originalExtension);
if (Length(listFiles) > 0) then
begin
for counter := 0 to Pred(Length(listFiles)) do
begin
streamFile := TFileStream.Create(listFiles[counter], fmOpenRead, fmShareExclusive);
try
codedMemory := TMemoryStream.Create;
try
codedMemory.CopyFrom(streamFile, streamFile.Size);
fileDestination := TPath.ChangeExtension(listFiles[counter], '.' + finalExtension);
if (TFile.Exists(fileDestination, true)) then
TFile.Delete(fileDestination);
streamSize := codedMemory.Size;
bufferStream := codedMemory.Memory;
i := 0;
while (i < streamSize) do
begin
bufferStream^ := Byte(ord (bufferStream^) xor not (Ord (aHash[I mod Length (aHash) + 1])));
Inc(bufferStream);
Inc(i);
end;
codedMemory.SaveToFile(fileDestination);
finally
codedMemory.Free;
end;
finally
streamFile.Free;
end;
end;
end;
end;
I've checked each of the 62 bytes in the array, comparing the C# values with the Delphi values, and they match up perfectly, so it's not an encoding problem when converting the string to the Byte array