Written by @ItaloKnox
In this article, we are going to make a breakdown of all the installer code, written in Windows batch (*.bat
). See the previous page for more information on how the patch is installed, the dependencies and file it uses to complete the installation.
Below is a snapshot of the full code from a stable Higurashi installer release. The actual breakdown should be below the code.
Use the index below to navigate around the page:
Installer code¶
@echo off
SETLOCAL EnableDelayedExpansion
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set "DEL=%%a"
)
set version=v4.0.1
call :colorEcho a0 "Downloading graphics patch... (1 of 3)"
echo.
timeout /t 1 > nul
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CG.zip
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CGAlt.zip
timeout /t 1 > nul
call :colorEcho a0 "Downloading voice patch... (2 of 3)"
echo.
timeout /t 1 > nul
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-Voices.zip
timeout /t 1 > nul
call :colorEcho a0 "Downloading patch... (3 of 3)"
echo.
timeout /t 1 > nul
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/onikakushi/releases/download/%version%/Onikakushi.Voice.and.Graphics.Patch.%version%.zip
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Higurashi-Textboxes.zip
.\aria2c.exe https://github.com/07th-mod/resources/raw/master/onikakushi/updater.bat
timeout /t 1 > nul
call :colorEcho a0 "Checking for incomplete downloads..."
echo.
timeout /t 1 > nul
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/onikakushi/releases/download/%version%/Onikakushi.Voice.and.Graphics.Patch.%version%.zip
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Higurashi-Textboxes.zip
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-Voices.zip
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CG.zip
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CGAlt.zip
timeout /t 1 > nul
call :colorEcho a0 "Extracting files..."
echo.
timeout /t 1 > nul
.\7za.exe x Onikakushi-CGAlt.zip
.\7za.exe x Onikakushi-CG.zip
.\7za.exe x Onikakushi-Voices.zip
.\7za.exe x Onikakushi.Voice.and.Graphics.Patch.*.zip
.\7za.exe x Higurashi-Textboxes.zip -aoa
rmdir /S /Q ..\StreamingAssets\CG > nul
rmdir /S /Q ..\StreamingAssets\CGAlt > nul
timeout /t 1 > nul
call :colorEcho a0 "Moving folders..."
echo.
echo D | xcopy /E /Y .\Managed ..\Managed > nul
echo D | xcopy /E /Y .\CGAlt ..\StreamingAssets\CGAlt > nul
echo D | xcopy /E /Y .\CG ..\StreamingAssets\CG > nul
echo D | xcopy /E /Y .\voice ..\StreamingAssets\voice > nul
echo D | xcopy /E /Y .\StreamingAssets ..\StreamingAssets > nul
mkdir ..\StreamingAssets\BGMAlt
mkdir ..\StreamingAssets\voiceAlt
mkdir ..\StreamingAssets\SEAlt
call :colorEcho a0 "Deleting useless files..."
echo.
timeout /t 1 > nul
rmdir /S /Q .\CG > nul
rmdir /S /Q .\CGAlt > nul
rmdir /S /Q .\StreamingAssets > nul
rmdir /S /Q .\voice > nul
rmdir /S /Q .\Managed > nul
del .\*.zip > nul
del ..\StreamingAssets\CompiledUpdateScripts\*.mg > nul
timeout /t 1 > nul
call :colorEcho a0 "All done, finishing in three seconds..."
timeout /t 3 > nul
exit
:colorEcho
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1i
Colored text¶
@echo off
SETLOCAL EnableDelayedExpansion
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set "DEL=%%a"
)
...
exit
:colorEcho
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1i
These two pieces of code (first one at the start, last one at the end of the script file) will handle the colored text in the installer. You can find more information about it in this Stack Overflow question.
The colors are called by using call :colorEcho a0 "string"
, where a0
is the sample color used in this example. Usually, it's better to use echo.
in the following line.
timeout¶
timeout /t 1 > nul
timeout
is used to create a natural pause in the installation process. It's used in this installer to let the user read the text easier and artificially make the installation look less faster when multiple commands are being used. We use timeout
together with the command > nul
to hide the information from the user and make the screen cleaner. The sample above pauses the script for one second before running the next command.
nul¶
timeout /t 1 > nul
rmdir /S /Q .\CG > nul
echo D | xcopy /E /Y .\Managed ..\Managed > nul
The nul
command is widely used in the script to hide information from the user. It is often used to hide irrelevant information that is verbose and takes too much screen space.
aria2c¶
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/resources/releases/download/Nipah/Onikakushi-CG.zip
* --file-allocation=none
forces aria2c
to not allocate any space for the files. In case of failure, it will only take the actual downloaded space instead of the full space the downloaded file would normally take.
* --continue=true
continues the download if the file already exists. Incomplete downloads will resume and complete downloads will be skipped.
* -x 8
is the number of connections aria2c
will make. Allows faster downloading, maximum number of connections is 16.
.\aria2c.exe --file-allocation=none --continue=true -x 8 -i local.txt
* -i local.txt
uses the file local.txt
as the input for the download. The file can contain one or more links in a vertical list.
We use aria2c
because it offers us the best download speeds and flexibility. It can be easily changed for another program such as wget
considering the changes to keep the functionality have been made in the script.
7za¶
.\7za.exe x Higurashi-Textboxes.zip -aoa
* x
extracts the file with its full path.
* -aoa
overwrite files without the user input. Useful for applying updates.
The reason we use 7-zip is that of lzma
. It's free, open-source and offers a higher compression compared to .rar
files. The software can be easily changed to any other alternatives that support the commands above.
powershell¶
This method is broken since March 2018. We reverted to set. Last commit where this command was used: 08b15f45b1.
powershell -command "(convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url | set-content local.txt"
* powershell -command ""
calls Windows Powershell through the command line window and runs a command.
* (convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url
- a bit tricky: invoke-webrequest
calls the link from the API to get the latest release; convertfrom-json
converts the information to a readable state, looking especially for assets.browser_download_url
.
* |
- runs a follow up command.
* set-content local.txt
- instead of outputting the result in the screen, the command saves it in the file local.txt
for use later.
This command was introduced later in development. This command basically uses the Github API to get the latest release for the installed chapter. Previously, we used to add the link manually. This command eliminates the need of doing this work every time a new patch is released.
This command has been pretty reliable but there are cases where it will miss. It is hard to reproduce and is probably related to security settings in the end user computer. Fortunately, it seems to have a 99% uptime despite commonly showing some information that might look like an error. It can be changed by grep
with curl
on Linux.
set¶
set version=v4.0.1
* set
creates a variable
* version
is the variable name
* v4.0.1
is the value stored in this variable
Since March 2018, we had to rollback to use variables to download the patch. Unfortunately, there seems to be an issue with Github and Invoke-WebRequest
that results in a broken SSL/TLS communication. Using a variable allows us to just change a little bit of text for every update released, instead of manually fishing for every instance where the patch version is called. The downside of this method compared to powershell is that we have to manually update the installer code every time a new version is released. Powershell, though, didn't require any extra work since it would always get the latest version from the API.
The variable is called by using %version%
. Like in this example:
.\aria2c.exe --file-allocation=none --continue=true -x 8 https://github.com/07th-mod/onikakushi/releases/download/%version%/Onikakushi.Voice.and.Graphics.Patch.%version%.zip
xcopy¶
echo D | xcopy /E /Y .\Managed ..\Managed > nul
* echo D
will input automatically the D
key, that in this command relates to copying a directory instead of a file. This allows the user to walk away from the computer and not stall the installation for a simple input.
* xcopy /E /Y .\Managed ..\Managed
* /E
will copy every subfolder, even if empty;
* /Y
will overwrite files with the same name without your consent (currently not very useful but still kept there);
* > nul
- like already said above, hides the information from the user. After testing, it was decided to not display the copying process since it floods the screen with information that is hard to follow (the command copies a few hundred files).
We employed xcopy
because the patch use big filenames and paths. move
turned out to be very unreliable for the amount of errors it would cause because of the long paths. Since Windows XP hasn't been used in a long while, we can safely use xcopy
to move the folders. The downside is that we have to use extra space since it only copies the folders instead of moving them. It can be changed to rsync
on Linux.
rmdir, mkdir and del¶
rmdir /S /Q .\CG > nul
/S
removes the tree (directory and subdirectories)./Q
means quiet. Will remove without your consent.
mkdir ..\StreamingAssets\BGMAlt
- creates folders that can be used for alternative functions implemented by the modded Assembly-CSharp.dll.del .\*.zip > nul
deletes all the files with the.zip
extension in the current folder.
These are mostly used to clear up space after a successful installation. mkdir
will create a few folders that can be used in the future, but are currently empty. This should make the process easier for modders that want to fork our work.
Updater code¶
The updater is currently broken. See powershell and set.
Since most of the basic syntax used was also seen in the previous code, we will skip explaining those.
@echo off
SETLOCAL EnableDelayedExpansion
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
set "DEL=%%a"
)
:start
call :colorEcho a0 "Comparing local version to remote version..." && echo:
timeout /t 3 > nul
if exist local.txt (
goto :compare
) else (
echo No information about the local version found. Downloading the newest remote version available...
goto :update
)
:compare
call :colorEcho a0 "Downloading remote information for comparison"
timeout /t 3 > nul
powershell -command "(convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url | set-content remote.txt"
fc remote.txt local.txt > nul
if errorlevel 1 goto :newver
:samever
echo Looks like you have the latest version already installed, nothing to do here...
del remote.txt
exit
:newver
echo New version found, redirecting to download and installation...
goto :download
:update
call :colorEcho a0 "Downloading latest patch..." && echo:
powershell -command "(convertfrom-json (invoke-webrequest https://api.github.com/repos/07th-mod/onikakushi/releases/latest).content).assets.browser_download_url | set-content remote.txt"
goto :download
:download
.\aria2c.exe --file-allocation=none --continue=true -x 8 -i remote.txt
del local.txt
ren remote.txt local.txt
goto :install
:install
call :colorEcho a0 "Extracting files..."
echo.
timeout /t 1 > nul
.\7za.exe x *.Voice.and.Graphics.Patch.*.zip
call :colorEcho a0 "Moving folders..."
echo.
echo D | xcopy /E /Y .\Managed ..\Managed > nul
echo D | xcopy /E /Y .\StreamingAssets ..\StreamingAssets > nul
call :colorEcho a0 "Deleting leftovers..."
echo.
rmdir /S /Q .\StreamingAssets > nul
rmdir /S /Q .\Managed > nul
del ..\StreamingAssets\CompiledUpdateScripts\*.mg > nul
del *.zip
exit
:colorEcho
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1i
Flowchart¶
This flowchart shows how the updater behaves and the choices it will make:
check if local.txt exists >> if it exists >> download remote.txt >> compare both files >> if files are the same >> end updating process, nothing to update
^ ^ if files are different >> download patch >> install and clean up files >> change remote.txt to local.txt
^ if it doesn't exist >> download remote.txt >> download patch >> install and clean up files >> change remote.txt to local.txt
if exist¶
if exist local.txt (
goto :compare
) else (
echo No information about the local version found. Downloading the newest remote version available...
goto :update
)
if exist local.txt
looks for the file local.txt
(that holds the information about the current version, as seen on the previous page)
* if it exists, it will compare the versions (see the next command)
* if it doesn't exist (else
), it will download the newest remote version available
This simple code checks if the file local.txt
exists and proceeds to compare it to the file remote.txt
(can be seen in the next command). If the file doesn't exist, it will proceed to download the file remote.txt
and rename it to local.txt
after a successful installation.
fc¶
fc remote.txt local.txt > nul
if errorlevel 1 goto :newver
In case you have a file called local.txt
in the temp
directory, fc
will compare the file to another file called remote.txt
that was downloaded prior to the comparison. The result is hidden from the user since it is irrelevant to the process. if errorlevel 1
means that if the two files are different, it will use goto :newver
to go to the section where it will download the newest patch. If both files are the same, the updater will return a message saying that there is nothing to update.
The downside of this method is that both files can be tempered by the user to trigger the wrong result. It is a known flaw, but there is nothing to be afraid of as the user will most likely never touch it.