Search code examples
nsis

How to read into byte array file contents and then update them and write into another file?


I want in nsis to be able to read from file contents into byte array, update the array and then write the contents of the buffer into another file.

My Code

System::Call "kernel32::GetFileSize(i$0,i0)i.s"
Pop $7

System::Alloc $7  ;Reading file contents into a buffer
Pop $buffer
System::Call "kernel32::ReadFile(i$0,i$buffer,i$7,*i.r6,i0)i.s"

loop2:
        StrCpy $2 $buffer 1 $1
        StrCmp $2 '' end
        IntOp $1 $1 + 1

goto loop2

I wanted to be able to iterate over the byte array that i read from the file and to be able to change its contents. (OR bytes for instance) and i wanted to save time from reading byte from a file directly and then writing single byte to a new file. So thats why wanted to use buffer byte array allocated. Is that possible? I saw that nsis does not support arrays natively.


Solution

  • NSIS does not support byte arrays but the System plug-in struct syntax allows you to access raw bytes in a memory buffer:

    !include LogicLib.nsh
    !include Util.nsh
    ShowInstDetails show
    
    !macro DisplayTestFile
    FileOpen $0 "$PluginsDir\Test.txt" r
    FileRead $0 $1
    FileClose $0
    DetailPrint Line1=$1
    !macroend
    
    Section
    InitPluginsDir
    FileOpen $0 "$PluginsDir\Test.txt" w
    ${If} $0 P<> 0
        FileWrite $0 "HELLO World$\r$\n"
        FileClose $0
    ${EndIf}
    
    !insertmacro DisplayTestFile
    
    FileOpen $0 "$PluginsDir\Test.txt" a
    ${If} $0 P<> 0
        FileSeek $0 0 Set
        System::Call "KERNEL32::GetFileSize(pr0,p0)i.r1"
        ${IfThen} $1 = -1 ${|} Abort "Could not get file size" ${|}
        System::Call '*(&i$1,i0)p.r2' ; Allocate the file size + a zero terminator just in case
        ${If} $2 P<> 0
            System::Call "KERNEL32::ReadFile(pr0,pr2,ir1,*i,p0)i.r3"
            ${If} $3 <> 0
                ${For} $3 0 5 ; Change byte 0..5
                    System::Call '*$2(&i$3,&i1.r4)' ; Read a byte
                    IntOp $4 $4 | 32
                    System::Call '*$2(&i$3,&i1 r4)' ; Write a byte
                ${Next}
                FileSeek $0 0 Set
                System::Call "KERNEL32::WriteFile(pr0,pr2,ir1,*i,p0)i.r3"
            ${EndIf}
            System::Free $2
        ${EndIf}
        FileClose $0
    ${EndIf}
    
    !insertmacro DisplayTestFile
    SectionEnd