installationdirectorynsisfile-copying

Nsis search folder with part of directory on all drives


I have this really niche problem,

I am an assetto corsa modder, I make add-ons based on cars already in the game. I can't give it all the files because it could give people dlc's they haven't bought. (not looking to be sued)

So I need to find the specific car folder on someone's computer, then copy all files in it and install some more with it. My problem; I am unable to automate the finding proces. Some have it on C:\ , some on D:\ , etc.

My current Manual methode, this works but I got loads of negative feedback about it:

!include "nsDialogs.nsh"
!include "LogicLib.nsh"

Name "Vismonger mod installation"
Outfile "Toyota Supra MKIV Tuned LHD.exe"
InstallDir "C:\Program Files (x86)\Steam\steamapps\common\assettocorsa\content\cars\ks_toyota_supra_mkiv_tuned_lhd" ;most probable $INSTDIR
RequestExecutionLevel admin
XPStyle on

Icon "D:\Downloads\assetto autos\installer\data\upgrade.ico" ;path to icon

Var Dialog
Var SOURCE_DIR

Page components
Page custom nsDialogsPage
Page directory
Page instfiles

Function nsDialogsPage
    nsDialogs::Create 1018
    Pop $Dialog
    
    ;make them select the folder themselves
    nsDialogs::SelectFolderDialog "Please select the 'ks_toyota_supra_mkiv_tuned' folder in assetto corsa's game files" "C:\Program Files (x86)\Steam\steamapps\common\assettocorsa\content\cars"
    Pop $SOURCE_DIR ;$SOURCE_DIR is now the wanted directory
    ${If} $Dialog == error
        Abort
    ${EndIf}
FunctionEnd

Section "Toyota Supra MKIV Tuned LHD" SEC01
    ; Copy files from the selected source directory to the destination directory
    SetOutPath $INSTDIR
    CopyFiles "$SOURCE_DIR\*.*" $INSTDIR
    
    ; Copy specific files from my fixed location and install to the destination directory
    SetOutPath $INSTDIR
    File /r "D:\Downloads\assetto autos\installer\data\ks_toyota_supra_mkiv_tuned_lhd\*.*"
SectionEnd

I tried this with the help of a million forums, just to find and present me the directory on the correct drive before even attempting to implement it into my installation code:

!include LogicLib.nsh
!include FileFunc.nsh

Name "SearchDirectoryExample"
OutFile "SearchDirectoryExample.exe"
RequestExecutionLevel user

Section "Search Directory" SEC01

  InitPluginsDir

  StrCpy $0 0 ; Drive index
  System::Call 'KERNEL32::GetLogicalDrives() i.r1'
  loop:
    IntOp $2 $1 & 1
    StrCmp $2 0 next
      System::Call 'KERNEL32::GetVolumeInformation(t " $0:\") i .r3 t .r4 i .r5 i .r6 .r7'
      StrCmp $3 "" next
      ; Check if the directory exists on this drive
      IfFileExists "$0:\Program Files (x86)\Steam\steamapps\common\assettocorsa\content\cars\ks_toyota_supra_mkiv_tuned\*" 0 found
        found:
          DetailPrint "Found directory on drive $0:"
          DetailPrint "$0:\Program Files (x86)\Steam\steamapps\common\assettocorsa\content\cars\ks_toyota_supra_mkiv_tuned"
          MessageBox MB_OK "Found directory on drive $0:`n$0:\Program Files (x86)\Steam\steamapps\common\assettocorsa\content\cars\ks_toyota_supra_mkiv_tuned"
          Goto done
  next:
    IntOp $0 $0 + 1
    IntOp $1 $1 >> 1
    StrCmp $0 26 loop

  done:

SectionEnd

But that doesn't seem to do anything.

I am really a newbie to this and I only know python and nsis. I have searched for hours but couldn't find anything that relates to my problem or what could be of interest to me. I would like my old code but with the manual directory selection changed out with a working automatic one. (which was my original idea obv.).


Solution

  • Enumerating drive letters:

    !include LogicLib.nsh
    Section
    
    StrCpy $0 0 ; Drive index
    System::Call 'KERNEL32::GetLogicalDrives()i.r1'
    loop:
      ${If} $1 & 1
        IntOp $2 $0 + 65
        System::Call 'USER32::wsprintf(t.r2, t "%c", ir2)?c'
        System::Call 'KERNEL32::GetDriveType(t "$2:\")i.r4'
        DetailPrint "Drive letter $2 is valid and is of type $4"
    
        ${If} $4 <> 1 ; Skip DRIVE_NO_ROOT_DIR
        ${AndIf} $4 <> 5 ; Skip DRIVE_CDROM
          StrCpy $3 ":\Program Files (x86)" ; Default
          ; Use the correct program files path if possible (optional)
          StrCpy $4 $ProgramFiles32 2 1 ; Extract substring so we can check if it is a drive or a network share
          ${IfThen} $4 == ":\" ${|} StrCpy $3 $ProgramFiles32 "" 1 ${|}
    
          StrCpy $2 "$2$3"
          DetailPrint "The file might be somewhere inside $2\..."
        ${EndIf}
      ${EndIf}
      ${If} $0 < 26
        IntOp $0 $0 + 1
        IntOp $1 $1 >>> 1
        Goto loop
      ${EndIf}
    
    SectionEnd
    

    Use FindFirst, FindNext and FindClose in a recursive function if you want to search inside all folders in a drive (might be too slow).