[MODUL] PBM : Selbstgebautes LZMA PackModul

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
Bisonte
Beiträge: 2430
Registriert: 01.04.2007 20:18

[MODUL] PBM : Selbstgebautes LZMA PackModul

Beitrag von Bisonte »

Hallo...

Ich versuchte die genauen Abgründe der Compress und Uncompress Memory Geschichte zu ergründen,
und habe mir daraus dann ein Modul gezimmert.

Es wird ein Header vor den gepackten Speicher geschrieben, um Grösse, CRC32 und eine Kennung zu haben.
Soll heissen, es wird nur das entpackt, was das Modul auch gepackt hat.

Der Speicher der übergeben wird, wird automatisch freigegeben, wenn das Packen oder Entpacken erfolgreich
war, damit nichts doppelt (also gepackt und ungepackt) vorhanden ist.

Das ganze habe ich auf 2GB Speichergrösse limitiert, damit ich mit Longs arbeiten kann (und ZIP könnte momentan
auch keine grösseren Bereiche ent/packen)

Sollte eigentlich (da keine API drin) mit allen OS funktionieren.

Ich hoffe ich habe da keine Fehler eingebaut...
Kreative Vorschläge sind natürlich Pflicht ;)

So, genug geschwafelt... Hier das Modul :

Code: Alles auswählen

DeclareModule PBM
  
  ;:--------------------------------------------------------------------------
  ;:- Modul   : PB PackMemory (PBM)
  ;:- Author  : George Bisonte
  ;:- Date    : 18. June 2014
  ;:- PB      : 5.22 LTS
  ;:- OS      : Windows/Linux/MacOS
  ;:-           Use it at your own risk
  ;:--------------------------------------------------------------------------  
  
  Declare   InitPBM()
  Declare   PackMemory(*Memory)
  Declare   UnPackMemory(*Memory)
  Declare.s GetLastErrorMSG()
  
EndDeclareModule
Module        PBM
  
  ;:--------------------------------------------------------------------------
  ;:- Modul   : PB PackMemory (PBM)
  ;:- Author  : George Bisonte
  ;:- Date    : 18. June 2014
  ;:- PB      : 5.22 LTS
  ;:- OS      : Windows/Linux/MacOS
  ;:-           Use it at your own risk
  ;:--------------------------------------------------------------------------
  
  EnableExplicit
  
  #PBM_PackMagic = 27197109
  #PBM_MaxLong   = ((1024*1024*1024)*2)-1
  
  Structure pbm_pack_header
    Magic.l                 ; Kennung ob es ein mit diesem Modul gepackter Speicher ist
    UnCompressedSize.l      ; Die Originalgroesse des Speicher. (Auf 2GB limitiert wegen ZIP) 
    CRC32.l                 ; Die CRC32 Checksumme des Originalspeichers
  EndStructure
  
  Global LastErrorID
  Global PBM_Init = #False
  
  Procedure   InitPBM()
    
    If PBM_Init = #False
      LastErrorID  = 0
      UseLZMAPacker()
      PBM_Init = #True
    EndIf
    
    ProcedureReturn PBM_Init
    
  EndProcedure
  
  Procedure   PackMemory(*Memory)
    
    Protected *Header.pbm_pack_header
    Protected *Buffer, *Output
    Protected CompressedSize.l
    
    If Not PBM_Init : LastErrorID = 100 : ProcedureReturn #Null : EndIf
    
    If Not *Memory ; Kein gültiger Speicher
      LastErrorID = 1
      ProcedureReturn #Null
    EndIf
    
    If MemorySize(*Memory) => #PBM_MaxLong ; Der übergebene Speicher ist groesser als 2GB
      LastErrorID = 7
      ProcedureReturn #Null
    EndIf
    
    *Header = AllocateMemory(SizeOf(pbm_pack_header))
    If Not *Header ; Der Header konnte nicht reserviert werden
      LastErrorID = 2
      ProcedureReturn #Null
    EndIf
    
    *Header\Magic             = #PBM_PackMagic
    *Header\UnCompressedSize  = MemorySize(*Memory)
    *Header\CRC32             = CRC32Fingerprint(*Memory, MemorySize(*Memory))
    
    *Buffer = AllocateMemory(MemorySize(*Memory))
    If Not *Buffer ; Zwischenpuffer konnte nicht reserviert werden
      LastErrorID = 6
      FreeMemory(*Header)
      ProcedureReturn #Null
    EndIf
    
    CompressedSize = CompressMemory(*Memory, MemorySize(*Memory), *Buffer, MemorySize(*Buffer), #PB_PackerPlugin_LZMA)
    
    If CompressedSize = 0
      CompressedSize = *Header\UnCompressedSize
      CopyMemory(*Memory, *Buffer, *Header\UnCompressedSize)
    EndIf
    If CompressedSize > 0
      *Output = AllocateMemory(CompressedSize + SizeOf(pbm_pack_header))
      If Not *Output ; Ausgabespeicher konnte nicht reserviert werden
        LastErrorID = 4
        FreeMemory(*Buffer)
        FreeMemory(*Header)
        ProcedureReturn #Null
      EndIf
      CopyMemory(*Header, *Output, SizeOf(pbm_pack_header))
      CopyMemory(*Buffer, *Output + SizeOf(pbm_pack_header), CompressedSize)
      FreeMemory(*Buffer)
      FreeMemory(*Header)
      FreeMemory(*Memory)
      LastErrorID = 0
      ProcedureReturn *Output
    EndIf
    
    If *Header : FreeMemory(*Header) : EndIf
    If *Output : FreeMemory(*Output) : EndIf
    If *Buffer : FreeMemory(*Buffer) : EndIf
    
    LastErrorID = -1
    
    ProcedureReturn #Null ; Letzte Ausfahrt
    
  EndProcedure
  Procedure   UnPackMemory(*Memory)
    
    Protected *Header.pbm_pack_header
    Protected *Output
    Protected RealSize.l
    
    If Not PBM_Init : LastErrorID = 100 : ProcedureReturn #Null : EndIf
    
    If Not *Memory ; Kein gültiger Speicher
      LastErrorID = 1
      ProcedureReturn #Null
    EndIf
    
    *Header = AllocateMemory(SizeOf(pbm_pack_header))
    If Not *Header ; Der Header konnte nicht reserviert werden
      LastErrorID = 2
      ProcedureReturn #Null
    EndIf
    
    CopyMemory(*Memory, *Header, SizeOf(pbm_pack_header))
    
    If *Header\Magic <> #PBM_PackMagic ; Speicher wurde nicht mit PBM gepackt
      LastErrorID = 3
      FreeMemory(*Header)
      ProcedureReturn #Null
    EndIf
    
    If *Header\UnCompressedSize > 0
      *Output = AllocateMemory(*Header\UnCompressedSize)
      If Not *Output ; Ausgabespeicher konnte nicht reserviert werden
        LastErrorID = 4
        FreeMemory(*Header)
        ProcedureReturn #Null
      EndIf
      If MemorySize(*Memory) - SizeOf(pbm_pack_header) = *Header\UnCompressedSize
        RealSize = *Header\UnCompressedSize
        CopyMemory(*Memory + SizeOf(pbm_pack_header), *Output, *Header\UnCompressedSize)
      Else
        RealSize = UncompressMemory(*Memory + SizeOf(pbm_pack_header), MemorySize(*Memory) - SizeOf(pbm_pack_header), *Output, *Header\UnCompressedSize, #PB_PackerPlugin_LZMA)
      EndIf
      If RealSize <> *Header\UnCompressedSize Or CRC32Fingerprint(*Output, *Header\UnCompressedSize) <> *Header\CRC32
        ; Fehler beim Entpacken. Speicher evt. nicht mehr ok.
        LastErrorID = 5
        FreeMemory(*Header)
        FreeMemory(*Output)
        ProcedureReturn #Null
      EndIf
      FreeMemory(*Header)
      FreeMemory(*Memory)
      LastErrorID = 0
      ProcedureReturn *Output
    EndIf
    
    If *Output : FreeMemory(*Output) : EndIf
    If *Header : FreeMemory(*Header) : EndIf
    
    LastErrorID = -1
    
    ProcedureReturn #Null
    
  EndProcedure
  Procedure.s GetLastErrorMSG()
    
    Protected Result.s = ""
    Select LastErrorID
      Case 0    : Result = "Ok."
      Case 1    : Result = "*Memory nicht initialisiert."
      Case 2    : Result = "Header konnte nicht initialisiert werden."  
      Case 3    : Result = "*Memory ist nicht mit PBM gepackt worden."
      Case 4    : Result = "*Ausgabespeicher konnte nicht initalisiert werden."
      Case 5    : Result = "Speicher nicht korrekt (CRC32 ERROR)."
      Case 6    : Result = "Zwischenspeicher konnte nicht initialisiert werden."
      Case 7    : Result = "Zu packender Speicher >2GB"
      Case 100  : Result = "InitPBM() muss ausgeführt worden sein."
      Default 
        Result = "unkown"
    EndSelect
    
    ProcedureReturn Result
    
  EndProcedure
  Procedure   GetLastErrorID()
    ProcedureReturn LastErrorID  
  EndProcedure
    
EndModule

CompilerIf #PB_Compiler_IsMainFile
  
  ; Die Drei Speicher
  Define *Original, *Gepackt, *Entpackt
  ; Und der Rest
  Define Event, pSize
  
  ; kleine Prozedur um ein File in den Speicher zu laden
  Procedure ReadToMemory(File.s)
    
    Protected *Memory = #Null
    Protected fHnd
    
    fHnd = ReadFile(#PB_Any, File)
    If fHnd
      If Lof(fHnd) > 0
        *Memory = AllocateMemory(Lof(fHnd))
        If *Memory
          ReadData(fHnd, *Memory, Lof(fHnd))
        EndIf  
        CloseFile(fHnd)
        ProcedureReturn *Memory
      EndIf
    EndIf
    
    ProcedureReturn *Memory
    
  EndProcedure
  
  PBM::InitPBM() ; Initialisierung (wegen UseLZMAPacker)
  
  ; Wir holen uns ein Bild aus dem Netz
  
  InitNetwork()
  If Not ReceiveHTTPFile("http://www.suchguru.de/wallpaper_tiere/bilder/Astronaut2.BMP", GetTemporaryDirectory() + "Bild.bmp")
    End ; Hat nicht geklappt der Dwonload
  EndIf
  
  ; Bild in den Speicher holen
  *Original = ReadToMemory(GetTemporaryDirectory() + "Bild.bmp")
  
  If *Original
    *Gepackt = PBM::PackMemory(*Original) ; Einpacken
    Debug PBM::GetLastErrorMSG() ; Letzte Nachricht holen
    If *Gepackt
      pSize = MemorySize(*Gepackt) ; Die gepackte Groesse
    
      *Entpackt = PBM::UnPackMemory(*Gepackt)
      Debug PBM::GetLastErrorMSG() ; Letzte Nachricht holen
      If *Entpackt
        CatchImage(1, *Entpackt)
      EndIf
    EndIf
  EndIf
  
  If Not IsImage(1)
    End ; Irgendetwas stimmt nicht
  EndIf
  
  ; *Original und *Gepackt sind nicht mehr gültig !!! Da der Speicher intern freigegeben wurde !!!
  
  ; Den Speicher vom Bild brauchen wir nicht mehr
  FreeMemory(*Entpackt)
  
  ; Nun das Fenster um das Ergebnis zu zeigen
  
  OpenWindow(0, 0, 0, 640, 480, "TestWin", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  
  TextGadget(0, 10, 10, 200, 20, "Original : " + Str(FileSize(GetTemporaryDirectory() + "Bild.bmp")))
  TextGadget(1, 10, 40, 200, 20, "Gepackt  : " + Str(pSize))
  
  CanvasGadget(2, 10, 70, ImageWidth(1)/4, ImageHeight(1)/4)
  StartDrawing(CanvasOutput(2))
  DrawImage(ImageID(1), 0, 0, ImageWidth(1)/4, ImageHeight(1)/4)
  StopDrawing()
  
  Repeat
    Event = WaitWindowEvent()
    
  Until Event = #PB_Event_CloseWindow

CompilerEndIf
Zuletzt geändert von Bisonte am 19.06.2014 16:03, insgesamt 1-mal geändert.
PureBasic 6.10 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
matbal
Beiträge: 246
Registriert: 30.03.2011 20:53

Re: [MODUL] PBM : Selbstgebautes LZMA PackModul

Beitrag von matbal »

Dein Test-Code beendet sich bei mir unter Win7 32bit mit "Zu packender Speicher >2GB". Ursache ist die Konstante #PBM_MaxLong, die um 1 zu groß ist. Der größtmögliche Wert in Longs ist:

Code: Alles auswählen

#PBM_MaxLong   = $7FFFFFFF
Benutzeravatar
Bisonte
Beiträge: 2430
Registriert: 01.04.2007 20:18

Re: [MODUL] PBM : Selbstgebautes LZMA PackModul

Beitrag von Bisonte »

Ah autsch... das -1 hab ich wohl beim Kopieren verdaddelt .... Source ist korrigiert !
PureBasic 6.10 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
Antworten