Probleme mit TLZMADecoder.Code

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Probleme mit TLZMADecoder.Code

Beitrag von Targion »

Hallo!
Für das Listaller-Projekt brauche ich unbedingt die Möglichkeit, mittels dem LZMA-SDK Dateien zu komprimieren. (Das schreiben die neuen IPK-Spezifikationen vor, die ich nicht schon wieder kippen will)
Also habe ich mir das SDK von hier heruntergeladen. Das Packen der Dateien funktioniert hervorragend, nur das entpacken nicht...
Ich benutze folgenden Code, der auch als Beispiel für die Dekomprimierung gelistet ist:

Code: Alles auswählen

procedure Decompress(infile: String;outfile: String);
var
  outdir : string;
var
    dictionary:integer;
    inStream:TBufferedFS;
    outStream:TBufferedFS;
    filesize:int64;
    i:integer;
    properties:array[0..4] of byte;
    decoder:TLZMADecoder;
    outSize:int64;
    v:byte;
const propertiessize=5;
begin
    inStream:=TBufferedFS.Create(infile,fmOpenRead or fmsharedenynone);
    outStream:=TBufferedFS.Create(outfile, fmcreate);
 
    if inStream.read(properties, propertiesSize) <> propertiesSize then
      raise Exception.Create('input .lzma file is too short');
    decoder := TLZMADecoder.Create;
    if not decoder.SetDecoderProperties(properties) then
      raise Exception.Create('Incorrect stream properties');
    outSize := 0;
    for i := 0 to 7 do begin
      v := {shortint}(ReadByte(inStream));
      if v < 0 then
        raise Exception.Create('Can''t read stream size');
      outSize := outSize or v shl (8 * i);
    end;
    if not decoder.Code(inStream, outStream, outSize) then
      raise Exception.Create('Error in data stream');
    decoder.Free;
    outStream.Free;
    inStream.Free;
end;

Input ist ein XZ-Komprimiertes TAR archiv, welches von anderen Anwendugen problemlos geöffnet werden kann.

Dummerweise verstehe ich den Dekompressionsalgorithmus nicht (der Code ist auch leider nicht dokumentiert). Auf jeden Fall erreicht er folgende Bedingung (weshalb er abbricht) (ungefähr Z. 382):

Code: Alles auswählen

if (rep0 >= nowPos64) or (rep0 >= m_DictionarySizeCheck) then begin
  m_OutWindow.Flush();
  result:=false;
  exit;
end;

rep0 ist im Fehlerfall genau gleich nowPos64.
(in der Datei ULZMADecoder.pas)

Wie bekomme ich das zum Laufen? Ich habe schon alles Mögliche probiert, inklusive einer kompletten Neuübersetzung einzelner Teile des SDK direkt aus dem Java/C-Code, gebracht hat's nichts.
Hat jemand eine Idee?

Übrigens: Es existiert hierzu ein Thema auf Delphipraxis, das aber noch ohne Antworten ist. (dort kennen nur wenige das LZMA-SDK)

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Probleme mit TLZMADecoder.Code

Beitrag von Hitman »

Probier's mal mit einem normalen TFileStream anstatt dem TBufferedFS. Ich hatte bei meinen LZMA Spielereien damit mal Schwierigkeiten.

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Probleme mit TLZMADecoder.Code

Beitrag von Targion »

Habe ich schon -> Hat damals nichts gebracht. Ich kann's natürlich nochmal ausprobieren :-P
Komischerweise tut es ja nichtmal die Demo des LZMADecoder. Kann es daran liegen, dass ich hier grade mit FPC 2.2.4 kompiliere?

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Probleme mit TLZMADecoder.Code

Beitrag von Hitman »

Nur zur Sicherheit: Tritt das Problem denn auch auf, wenn du was dekodierst, was du mit dem LZMA Pascal SDK kodiert hast? (Das müsste sich mit der beiliegenden Demo ja ganz gut testen lassen.)

Ach und noch was: kompilierst du für 32 oder 64bit?

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Probleme mit TLZMADecoder.Code

Beitrag von Targion »

Auch das, was mit dem Pascal-SDK kompiliert wurde lässt sich mit dem SDK nicht entpacken. (mit xz --decompress aber schon)
Ich kompiliere für 64bit :!: Daran, dass das Problem an der Architektur liegen könnte hatte ich bis jetzt nicht gedacht...

Hitman
Beiträge: 512
Registriert: Mo 25. Aug 2008, 18:17
OS, Lazarus, FPC: ArchLinux x86, WinVista x86-64, Lazarus 0.9.29, FPC 2.4.1
CPU-Target: x86
Wohnort: Chemnitz

Re: Probleme mit TLZMADecoder.Code

Beitrag von Hitman »

Wäre zumindest ein Ansatz. Ich hatte es bisher nur auf 32bit im Einsatz. Könntest du ja auch erstmal probieren, wenn's dort geht, weiß man wenigstens schonmal, wonach man Ausschau hält ... nämlich wahrscheinlich nach Pointern.

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Probleme mit TLZMADecoder.Code

Beitrag von Targion »

Habe es ausprobiert: Unter 32bit funktioniert es tadellos!
Und jetzt die Masterfrage: Was kann ich tun, damit LZMA auf 64bit-Systemen auch dekomprimiert? Es werden eigentlich keine Pointer verwendet. Was ist unter 64bit anders als unter 32bit und verursacht daher den Fehler?
Ich habe den Code nochmal durchgeschaut und konnte nichts finden, aber ich kenne mich mit den FPC-Interna auch nicht so super aus...

Targion
Beiträge: 688
Registriert: Mi 3. Okt 2007, 21:00
OS, Lazarus, FPC: Linux (L 0.9.29 FPC 2.4.2)
CPU-Target: x86_64

Re: Probleme mit TLZMADecoder.Code

Beitrag von Targion »

Problem gelöst! (Dank der FPC-Mailingliste)
In URangeDecoder.pas muss "TRangeDecoder.DecodeDirectBits" durch diesen Code ersetzt werden:

Code: Alles auswählen

function TRangeDecoder.DecodeDirectBits(const numTotalBits:integer):integer;
var i,t:integer;
begin
result:=0;
for i := numTotalBits downto 1 do begin
    range:=range shr 1;
    //Old Code:
    //t := ((Code - Range) shr 31);
    //New one:
    t := (DWORD(Code - Range) shr 31);
    Code := Code - Range and (t - 1);
    result := (result shl 1) or (1 - t);
    if ((Range and kTopMask) = 0) then begin
       Code := (Code shl 8) or ReadByte(stream);
       Range := Range shl 8;
       end;
    end;
end;

Die Fehlerquelle hat "JoshyFun" gefunden, mit folgender Begründung:

Code: Alles auswählen

DWORD fixes the problem, as:
 
32 bits: ($80000001 - 1) shr 31 = 1
64 bits: ($80000001 - 1) shr 31 = -1
64 bits: DWORD($80000001 - 1) shr 31 = -1
 
It looks like a problem in 64 bit versions :-? I think this topic
appears in the past about 32<->64 bits shift operations over signed
variables.

Warum sowas passiert, ist mir zwar immer noch nicht ganz klar, aber der Code funktioniert!

Antworten