User Tools

Site Tools


NSIS Installer

NSIS Installer for theaters by PMC

The NSIS installer is quite nice as its free to download and very light weight, its script based so you can tweak the installer to do pretty much anything you want. Sure the script at first looks pretty intimidating, but dont let that scare you, we have the wiki to help you out.

Here in PMC Tactical we use NSIS installer to build our theater installers, also FreeFalcon/RedViper is using this method for their installation. Its very nice freeware open source program.

The scripting is relatively easy for a non-coder to figure out, but is still powerful and flexible. It has a strong user community and good open-source support. It also is “portable” in that there is a version which can run from almost any PC on USB memory stick.

Theater Subdirs

First you should setup your theater directories in preparations for the NSIS to build the installer. It doesn't really matter what you call the source directory as you can configure it on the script, but we call it here Theater_src_THEATERNAME where the theatername of course is like Europe, DesertStorm or Vietnam in our case.

In the “root” theater source dir you got two directories called, Theaters and Utilities. We chose to use the Utilities dir which is included in the default RedViper installation to store its utils/tools. And naturally the Theaters dir is the one which we use to suit PMC Theater Directory Structure which so far everyone (except Naldo) has agreed to use.

In the utilities dir you place the utils, these are lstupdate.exe, LxNormalFix.exe, SeasonSwitcher.exe and Htti.exe filenames.

So in short, you have directory of


Which would be your root directory for the ODS theater (later in this doc as we refer to root, this is the dir), in this root directory you have the art, campaign, objects and terrdata directories. Also there is theater selection TDF, TGA files and any readme files you wish the users to see (in our case, its the EULA.txt and version.txt).

Art dir

Art directory is for the user interface files. Our base system includes the art\resource\ directory which contains two UI files, these are campmap and select which in our experience are universal for all F4 versions. As you need to include the individual UI files for different F4 versions in the art-AF, art-OF and art-RV directories, the main art\ dir is where you place the resource dir.

As seen here


When we examine the individual directories more we'll find these following subdirs.



Notice how art-OF\ requires yet another “art” subdir to work.





You should refer to our user interface namespace for details how to edit Falcon 4 UI.

Campaign dir

Campaign dir includes the common files used for all campaigns, as well the individual campaign directory files required for all possible campaigns. The sub directories in main campaign dir are as follows.

Campaign subdirs for our DesertStorm theater


When we look closer, we find that the common\ directory contains these following files:, kneemap.gif, te_new.tac, desertstorm.idx/.wch, desertstorm.pak and desertstorm.thr. These are the files that are common/same for all the campaign directories we are creating.

In the common-AF\, common-OF\ and common-RV\ dirs we have files for each specific F4 version, in this case strings.idx/.wch files.

Finally in the desertstorm1\ and desertstorm2\ dirs we have the specific files required for each campaign, these are usually, save0.tri and save1/2 also.

You should refer to our campaign namespace for details how to edit Falcon 4 campaigns.

Objects dir

To install new objects, objects and including new skins to your theater, you need to create **objects* subdir into the root. In the objects dir create AF, OF and RV sub directories. In these subdirs you place the edited database files.

As seen here


In this case the falcon4.wld file is the edited loadouts for aircrafts.

You should refer to our database namespace for details how to edit Falcon 4 database.

Terrdata dir

Terrdata directory is where the terrain\ and texture\ sub directories are located.

As you can see


Of course if your theater uses only default korean tiles, you dont need texture\ subdir.

In the terrain\ sub dir you find theater.L2/.O2, and theater.mea files, rest of the theater files are created with the terrain rebuilder util.

In the texture\ sub dir (if you need it) you find fartiles.pal, texture.bin and files.

Example Script

Here is the latest release PMC theater script, its from Vietnam theater.

!define TNAME "Vietnam" 
!define FRIENDLYNAME "PMC Vietnam Theater Installer" 
!define APP "VietnamInstaller" 
;for some reason, this has to be #.#.#.# but I usually change the name after I build the exe 
!define VER "" 

; these establish some things about the installer the most important one to me is the outfile 
; outfile is the name of the installer.  Unless otherwise set, it will be the same directory as where the script is 
OutFile "PMC_${TNAME}_Theater_v${VER}.exe" 
Caption "${FRIENDLYNAME}" 
; these VI definitions are what is displayed if you hover the mouse over the icon 
VIProductVersion "${VER}" 
VIAddVersionKey ProductName "${FRIENDLYNAME}" 
VIAddVersionKey Comments "Installs the ${TNAME} theater." 
VIAddVersionKey LegalCopyright "PMC Tactical" 
VIAddVersionKey FileDescription "${FRIENDLYNAME}" 
VIAddVersionKey FileVersion "${VER}" 
VIAddVersionKey ProductVersion "${VER}" 
VIAddVersionKey InternalName "${FRIENDLYNAME}" 

;=== Runtime Switches 
; the CRC check is something I use on bigger installers to make sure everything checks out. 
CRCCheck On 
; this makes it more efficient 
AutoCloseWindow True 
; this is something I use to make sure it uses the LZMA compression, which I believe is the best 
SetCompressor lzma 
; branding text is just something that's displayed at the bottom of the installer 
BrandingText "" 

;=== Includes - this will include the necessary dlls and functions that I use in the installer 
; MUI 1.67 compatible ------ 
!include "MUI.nsh" 
!include "Registry.nsh" 
!include "WinMessages.nsh" 
!include "TextFunc.nsh" 
!include "Sections.nsh" 

; MUI Settings - the MUI is the Modern UI for NSIS, this section defines its parameters (there are alot more that I don't use) 
!define MUI_INSTALLCOLORS /windows 

; Welcome page - this is the first page of the installer (I also have a snippet for a splashscreen, but we'll keep this simple) 
;!define MUI_WELCOMEFINISHPAGE_BITMAP "@@:\FF4Installers\leftgraphic.bmp" 
; using the variables means we don't have to remember to change all these when we use the template for another theater 
!define MUI_WELCOMEPAGE_TEXT "Setup will install ${FRIENDLYNAME} ${VER} into your Falcon installation." 
!insertmacro MUI_PAGE_WELCOME 

!insertmacro MUI_PAGE_LICENSE "Theater_src_${TNAME}\Theaters\${TNAME}\EULA.txt" 

; Instfiles page 
!insertmacro MUI_PAGE_INSTFILES 
; Finish page - this is the last page people see 
!define MUI_FINISHPAGE_TITLE "${FRIENDLYNAME} ${VER} Installation completed." 
!define MUI_FINISHPAGE_TEXT "Click 'Finish' to close this wizard." 
!insertmacro MUI_PAGE_FINISH 

; Language files 
!insertmacro MUI_LANGUAGE "English" 
; MUI end ------ 

; now we're ready for the actual installer! 
Section "Installer" SEC01 

; F4registry check - if no F4 registry, it kicks to the end - the $0 is a variable that holds the -1 (not found) or 0 (found) 
; we can also start to determine which install we have 
${registry::KeyExists} "HKLM\Software\MicroProse\Falcon\4.0" $0 
${registry::KeyExists} "HKLM\Software\Lead Pursuit\Battlefield Operations\Falcon" $1 
; IntCmp compares the integer in $0 with our number (0) and if it is =, it goes to label "F4Exists" 
IntCmp $0 0 F4Exists 
IntCmp $1 0 AFInstall 
; this copies the string to $9 - I'm using that as an error label 
StrCpy $9 "No appropriate registry found." 
Goto Error 

; this determines if there's an AF reg entry, and if so, it only advances 1 line, if not, it goes to F4Install
IntCmp $1 0 0 F4Version

; at this point, we need to determine if the person wants to install over "original" F4 or RV

Messagebox::show MB_DEFBUTTON3|MB_TOPMOST "" "" "Select where you want the theater installed." "OF/RV" "AF" IDCANCEL
; this places the response 1, 2, or 3, into $0
Pop $0
IntCmp $0 3 Finish
IntCmp $0 2 AFInstall

; now we know they want OF/RV so we need to differentiate - I'm only going to look for RV, if it isn't RV, I'm going to assume OF for now
; this will also validate the install a bit
; this reads the specified registry value into $0
ReadRegStr $0 HKLM "Software\MicroProse\Falcon\4.0" "basedir"
; if the file exists, go to next line (don't skip a line) if it doesn't, go to label "NoFileFound"
IfFileExists $0\theater.lst 0 NoFileFound
IfFileExists $0\terrdata\objects\KoreaObj.Dxl RVFound
IfFileExists $0\terrdata\objects\KoreaObj.lod OFFound NoFileFound

StrCpy $9 "No F4 install found." 
Goto Error 

; I want to set up a variable to differentiate OF and RV 
; we'll use this for the lstupdate variable 
StrCpy $8 "F4" 
StrCpy $7 "RV" 
Goto F4Install 

StrCpy $8 "F4" 
StrCpy $7 "OF" 
Goto F4Install 

; get the AF base directory into a variable 
ReadRegStr $0 HKLM "Software\Lead Pursuit\Battlefield Operations\Falcon" "basedir" 
; sets the right campaign directory 
StrCpy $1 "$0\campaign\korea" ; campaing\save string 
StrCpy $2 "$0\Utilities\LxNormalFix.exe $$LP @${TNAME} @$0\Theaters\${TNAME}\terrdata" ; lxnormalfix string 
StrCpy $8 "AF" 
Goto CreateDirs 

ReadRegStr $0 HKLM "Software\MicroProse\Falcon\4.0" "basedir" 
StrCpy $1 "$0\campaign\save" 
StrCpy $2 "$0\Utilities\LxNormalFix.exe @${TNAME} @$0\Theaters\${TNAME}\terrdata" 

SetOutPath $0 
CreateDirectory "$0\Theaters\${TNAME}\campaign\vietnam1\"
CreateDirectory "$0\Theaters\${TNAME}\campaign\vietnam2\"
CreateDirectory "$0\Theaters\${TNAME}\terrdata\weather\"
CreateDirectory "$0\Theaters\${TNAME}\objects\"

; new intelligent campaing copy files list
CopyFiles /filesonly "$1\*.pri" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.txt" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.b" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.db" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.ia" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.dfs" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.dat" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.gbd" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\*.lst" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\default.*" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\strings.*" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\atc.ini" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\falcon4.aii" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\falcon4.rt" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$1\validac.*" "$0\Theaters\${TNAME}\campaign\vietnam1\"

; another campaign dir
CopyFiles /filesonly "$0\Theaters\${TNAME}\campaign\vietnam1\*.*" "$0\Theaters\${TNAME}\campaign\vietnam2\"
; database objects (same dir for all versions)
; this needs to be recursively because RV has koreaobj subdir.
CopyFiles "$0\terrdata\objects\*.*" "$0\Theaters\${TNAME}\objects\"
CopyFiles "$0\terrdata\korea\weather\*.*" "$0\Theaters\${TNAME}\terrdata\weather\"

; unpack the theater files 
; this is the subdir in the developer's PC that has the files for AF 
File /r "Theater_src_${TNAME}\*.*" 

; common campaign dirs
CopyFiles /filesonly "$0\Theaters\${TNAME}\campaign\common\*.*" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles /filesonly "$0\Theaters\${TNAME}\campaign\common\*.*" "$0\Theaters\${TNAME}\campaign\vietnam2\"

; beginning of falcon version DATABASE & UI

; database copy
StrCmp $7 "OF" DataBaseOF
StrCmp $7 "RV" DataBaseRV

CopyFiles "$0\Theaters\${TNAME}\art\art-AF\*.*" "$0\Theaters\${TNAME}\art\art\"
; database AF
CopyFiles "$0\Theaters\${TNAME}\objects\AF\*.*" "$0\Theaters\${TNAME}\objects\"
CopyFiles "$0\Theaters\${TNAME}\campaign\common-AF\*.*" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles "$0\Theaters\${TNAME}\campaign\common-AF\*.*" "$0\Theaters\${TNAME}\campaign\vietnam2\"
goto EndOfDataBaseCopy

; copy only one \art\ as we have the art1024 dir there too, heh hmm.
CopyFiles "$0\Theaters\${TNAME}\art\art-OF\*.*" "$0\Theaters\${TNAME}\art\"
; database OF
CopyFiles "$0\Theaters\${TNAME}\objects\OF\*.*" "$0\Theaters\${TNAME}\objects\"
CopyFiles "$0\Theaters\${TNAME}\campaign\common-OF\*.*" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles "$0\Theaters\${TNAME}\campaign\common-OF\*.*" "$0\Theaters\${TNAME}\campaign\vietnam2\"
goto EndOfDataBaseCopy

CopyFiles "$0\Theaters\${TNAME}\art\art-RV\*.*" "$0\Theaters\${TNAME}\art\art\"
; database RV
CopyFiles "$0\Theaters\${TNAME}\objects\RV\*.*" "$0\Theaters\${TNAME}\objects\"
CopyFiles "$0\Theaters\${TNAME}\campaign\common-RV\*.*" "$0\Theaters\${TNAME}\campaign\vietnam1\"
CopyFiles "$0\Theaters\${TNAME}\campaign\common-RV\*.*" "$0\Theaters\${TNAME}\campaign\vietnam2\"

RMDir /r "$0\Theaters\${TNAME}\objects\AF\"
RMDir /r "$0\Theaters\${TNAME}\objects\RV\"
RMDir /r "$0\Theaters\${TNAME}\objects\OF\"
RMDir /r "$0\Theaters\${TNAME}\art\art-AF\"
RMDir /r "$0\Theaters\${TNAME}\art\art-RV\"
RMDir /r "$0\Theaters\${TNAME}\art\art-OF\"
RMDir /r "$0\Theaters\${TNAME}\campaign\common-AF\"
RMDir /r "$0\Theaters\${TNAME}\campaign\common-OF\"
RMDir /r "$0\Theaters\${TNAME}\campaign\common-RV\"
RMDir /r "$0\Theaters\${TNAME}\campaign\common\"

; end of falcon version DATABASE & UI

StrCmp $8 "F4" F4curTheater TheaterAdmin 

StrCpy $9 "Unknown error before curTheater: $8." 
Goto Error 

; set current theater to new theater 
; this reads the specified registry value into $2 for use later
ReadRegStr $3 HKLM "Software\MicroProse\Falcon\4.0" "curTheater"
${registry::Write} "hklm\SOFTWARE\MicroProse\Falcon\4.0" "curTheater" "${TNAME}" "REG_SZ" $R0

; if it is an OF install, skip straight to theater admin otherwise, do RV stuff
StrCmp $7 "OF" TheaterAdmin

; this will be the area where we do stuff that it doesn't really matter which install it is 
SetOutPath $0 

ExecWait "$0\Utilities\lstupdate.exe $8 +Theaters\${TNAME}\${TNAME}1.tdf"
ExecWait "$0\Utilities\lstupdate.exe $8 +Theaters\${TNAME}\${TNAME}2.tdf"
ExecWait '$0\Utilities\SPTinstall.exe -auto "$0\Theaters\${TNAME}\terrdata"'
ExecWait "$0\Utilities\LxNormalFix.exe $2"

; just compress textures if F4 then finish 
StrCmp $8 "AF" Finish 
ExecWait "$0\Utilities\SeasonSwitcher.exe -0" 

MessageBox MB_YESNO "Do you want to make the new theater the current one?" IDYES Finish IDNO OldReg 
${registry::Write} "hklm\SOFTWARE\MicroProse\Falcon\4.0" "curTheater" "$3" "REG_SZ" $R0 

Goto Finish 

Messagebox MB_OK $9 



Sorry for the long code, but thats the whole script we used to build Vietnam theater installer.

Check out our offical PMC Tactical Forum NSIS installer topic.


Homepage of NSIS.

falcon4/tools/nsis_installer.txt · Last modified: 2017-10-13 19:54 by snakeman