Page 1 of 1

Simple byte level patcher

Posted: Thu Sep 12, 2013 6:41 pm
by erion
Hi all,
I threw this together, it might be useful for someone.
It works on two files as a differential patcher, up to about 1 GB of data (increase the two long variables for more space).
New features can be easily added, such as patching multiple files, integrity checking, diff file compression, etc.
Edit: Added 2 more functions, to generate and apply patches via memory.

Code: Select all

;----
;Patchdata File format
;byte: needs truncating
;long: current position
;long: number of replaced byte(s)
;byte(s): the actual replaced data
;----
;error codes
;0: success
;-1: old file not found
;-2 new file not found
;-3 patch file already exists
;----
Procedure.b GenPatch(oldfile.s, newfile.s, patchdata.s)
  If FileSize(oldfile)<1
    ProcedureReturn -1
  EndIf
  If FileSize(newfile)<1
    ProcedureReturn -2
  EndIf
  If FileSize(patchdata)>0
    ProcedureReturn -3
  EndIf
      Protected obyte.b, nbyte.b, bReplace.l, needTruncating.b, nrbpos.l, fpos.l
      Protected curpos.l=-1
      
      ReadFile(0,oldfile)
      ReadFile(1,newfile)
      OpenFile(2,patchdata)
      FileSeek(2,1,#PB_Relative)
      While Eof(0)=0
        obyte=ReadByte(0)
        If Eof(1)=0
          nbyte=ReadByte(1)
          If obyte<>nbyte
           If curpos<0
            curpos=Loc(0)-1
            WriteLong(2,curpos)
            nrbpos=Loc(2)
            FileSeek(2,SizeOf(long),#PB_Relative)
          EndIf
          WriteByte(2,nbyte)
          bReplace+1
        Else
        If curpos>-1
          fpos=Loc(2)
          FileSeek(2,nrbpos)
          WriteLong(2,bReplace)
          FileSeek(2,fpos)
        EndIf
        curpos=-1
        bReplace=0
        EndIf
      Else
        needTruncating=1
        Break  
      EndIf
    Wend
    If curpos>-1
      fpos=Loc(2)
      FileSeek(2,nrbpos)
      WriteLong(2,bReplace)
      FileSeek(2,fpos)
      bReplace=0
    EndIf
    If Eof(1)=0
      WriteLong(2,Loc(0))
      nrbpos=Loc(2)
      FileSeek(2,SizeOf(long),#PB_Relative)
      While Eof(1)=0
        bReplace+1
        WriteByte(2,ReadByte(1))
      Wend
      FileSeek(2,nrbpos)
      WriteLong(2,bReplace)
    EndIf
    FileSeek(2,0)
    WriteByte(2,needTruncating)
    CloseFile(0)
    CloseFile(1)
    CloseFile(2)
    ProcedureReturn 0
  EndProcedure
  
  Procedure Apply(oldfile.s, patchdata.s)
    OpenFile(0,oldfile)
    ReadFile(1,patchdata)
    Protected needsTrunc=ReadByte(1)
    Protected i, nrb.l
    While Eof(1)=0
      FileSeek(0,ReadLong(1))
      nrb=ReadLong(1)
      For i=1 To nrb
        WriteByte(0,ReadByte(1))
      Next
    Wend
    If needsTrunc
      ;FileSeek(0,-1,#PB_Relative)
      TruncateFile(0)
    EndIf
  CloseFile(0)
  CloseFile(1)
  
EndProcedure


Procedure.q GenerateMem(oldfile.s, newfile.s, BuffSize.q)
  If FileSize(oldfile)<1
    ProcedureReturn -1
  EndIf
  If FileSize(newfile)<1
    ProcedureReturn -2
  EndIf
      Protected obyte.b, nbyte.b, bReplace.l, needTruncating.b, *nrbpos, *fpos, msize.q, *nbuff
      Protected curpos.l=-1
      Protected *pBuff=AllocateMemory(BuffSize)
      If Not *pBuff
        ProcedureReturn -3
      EndIf
      Protected *ptmp=*pbuff
      
      ReadFile(0,oldfile)
      ReadFile(1,newfile)
      *ptmp+SizeOf(Byte)
      msize+SizeOf(Byte)
      While Eof(0)=0
        obyte=ReadByte(0)
        If Eof(1)=0
          nbyte=ReadByte(1)
          If obyte<>nbyte
           If curpos<0
            curpos=Loc(0)-1
            PokeL(*ptmp,curpos)
            *ptmp+SizeOf(Long)
            msize+SizeOf(long)
            *nrbpos=*ptmp
            *ptmp+SizeOf(Long)
            msize+SizeOf(long)  
          EndIf
          PokeB(*ptmp,nbyte)
          *ptmp+SizeOf(Byte)
          msize+SizeOf(Byte)
          bReplace+1
        Else
        If curpos>-1
          *fpos=*ptmp
          *ptmp=*nrbpos
          PokeL(*ptmp,bReplace)
          *ptmp=*fpos
        EndIf
        curpos=-1
        bReplace=0
        EndIf
      Else
        needTruncating=1
        Break  
      EndIf
    Wend
    If curpos>-1
      *fpos=*ptmp
      *ptmp=*nrbpos
      PokeL(*ptmp,bReplace)
      *ptmp=*fpos
      bReplace=0
    EndIf
    If Eof(1)=0
      PokeL(*ptmp,Loc(0))
      *ptmp+SizeOf(Long)
      msize+SizeOf(long)
      *nrbpos=*ptmp
      *ptmp+SizeOf(Long)
      msize+SizeOf(long)
      While Eof(1)=0
        bReplace+1
        PokeB(*ptmp,ReadByte(1))
        *ptmp+SizeOf(Byte)
        msize+SizeOf(Byte)
      Wend
      *ptmp=*nrbpos
      PokeL(*ptmp,bReplace)
    EndIf
    PokeB(*pBuff,needTruncating)
    *nbuff=AllocateMemory(msize)
    If *nbuff
      CopyMemory(*pBuff,*nbuff,msize)
    Else
      CloseFile(0)
      CloseFile(1)
      FreeMemory(*pBuff)
      ProcedureReturn -3
    EndIf
    FreeMemory(*pBuff)
    CloseFile(0)
    CloseFile(1)
    ProcedureReturn *nbuff
EndProcedure

Procedure ApplyMem(oldfile.s, *patchdata)
    OpenFile(0,oldfile)
    Protected *pd=*patchdata
    Protected pdsize=MemorySize(*patchdata)
    Protected needsTrunc=PeekB(*pd)
    *pd+SizeOf(Byte)
    Protected i, nrb.l
    While *pd<*patchdata+(pdsize-1)
      FileSeek(0,PeekL(*pd))
      *pd+SizeOf(long)
      nrb=PeekL(*pd)
      *pd+SizeOf(long)
      For i=1 To nrb
        WriteByte(0,PeekB(*pd))
        *pd+SizeOf(byte)
      Next
    Wend
    If needsTrunc
      ;FileSeek(0,-1,#PB_Relative)
      TruncateFile(0)
    EndIf
  CloseFile(0)
  FreeMemory(*patchdata)
  
EndProcedure
Erion